NASM 编译 x86_64 当使用多个数据库声明时,ASM 标签地址在 Mach-O 中偏移 256 字节?
NASM compiling x86_64 ASM label addresses off by 256 bytes in Mach-O when using multiple db declarations?
简而言之,当我的 .data
部分中有多个 db
部分时,编译的 addresses/labels 在 NASM 编译时关闭。在我的测试中,它们在生成的 Mach-O 二进制文件中偏移了 256 个字节。
我正在使用的软件:
- OS X 10.10.5
nasm
NASM 版本 2.11.08,根据 x84_64 ASM 的要求通过 Homebrew 安装
gobjdump
GNU objdump (GNU Binutils) 2.25.1,通过 Homebrew 安装
clang
Apple LLVM 版本 6.1.0 (clang-602.0.53)(基于 LLVM 3.6.0svn)
什么有效:
以下面的"hello world"NASM程序集为例。
main.s
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msg]
mov rdx, len
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msg: db "Hello, world!", 10
len: equ $ - msg
编译并 运行 具有:
/usr/local/bin/nasm -f macho64 -o main.o main.s
clang -o main main.o
./main
效果很好,并产生以下输出:
Hello, world!
什么没有:
现在,要添加另一条消息,我们只需向数据部分添加另一个字符串,然后再添加一个 syscall
。够简单了。
main.s
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msga]
mov rdx, lena
syscall
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msgb]
mov rdx, lenb
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msga: db "Hello, world!", 10
lena: equ $ - msga
msgb: db "Break things!", 10
lenb: equ $ - msgb
编译和运行,和之前一样,得到:
Break things!
什么?!?我们不应该得到吗?:
Hello, world!
Break things!
怎么了?:
显然出了点问题。是时候反汇编生成的二进制文件,看看我们得到了什么。
$ gobjdump -d -M intel main
为 _main
生成以下内容:
0000000100000f7c <_main>:
100000f7c:b8 04 00 00 02 mov eax,0x2000004
100000f81:bf 01 00 00 00 mov edi,0x1
100000f86:48 8d 35 73 01 00 00 lea rsi,[rip+0x173] # 100001100 <msgb+0xf2>
100000f8d:ba 0e 00 00 00 mov edx,0xe
100000f92:0f 05 syscall
100000f94:b8 04 00 00 02 mov eax,0x2000004
100000f99:bf 01 00 00 00 mov edi,0x1
100000f9e:48 8d 35 69 00 00 00 lea rsi,[rip+0x69] # 10000100e <msgb>
100000fa5:ba 0e 00 00 00 mov edx,0xe
100000faa:0f 05 syscall
100000fac:b8 01 00 00 02 mov eax,0x2000001
100000fb1:bf 00 00 00 00 mov edi,0x0
100000fb6:0f 05 syscall
从注释# 100001100 <msgb+0xf2>
可以看出,它不是指向msga
符号,而是指向msgb
之后的0xf2
,或者100001100
(在这个地址有空字节,导致没有输出)。在十六进制编辑器中检查二进制文件,我在偏移量 1000
或地址 100001000
处找到了实际的 msga
字符串。这意味着编译二进制文件中的地址现在偏移了 0x100
/256
字节,仅仅是因为现在有第二个 db
标签。什么?!?
一个解决方法的抱歉借口:
作为实验,我决定尝试将两个 db
部分放入单独的 ASM/object 文件中,并将所有 3 个链接在一起。这样做有效。
main.s
global _main
extern _msga
extern _lena
extern _msgb
extern _lenb
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel _msga]
mov rdx, _lena
syscall
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel _msgb]
mov rdx, _lenb
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
msga.s
global _msga
global _lena
section .data
_msga: db "Hello, world!", 10
_lena: equ $ - _msga
msgb.s
global _msgb
global _lenb
section .data
_msgb: db "Break things!", 10
_lenb: equ $ - _msgb
编译并 运行 使用:
/usr/local/bin/nasm -f macho64 -o main.o main.s
/usr/local/bin/nasm -f macho64 -o msga.o msga.s
/usr/local/bin/nasm -f macho64 -o msgb.o msgb.s
clang -o main msga.o msgb.o main.o
./main
结果:
Hello, world!
Break things!
虽然这确实有效,但我很难相信这是最好的解决方案。
出了什么问题?
一定有办法在一个 ASM 文件中包含多个 db
标签吗?我写ASM的方式有问题吗?这是 NASM 中的错误吗?这是某种预期的行为吗,在这种情况下 为什么 ?我的解决方法是额外的工作和混乱,所以我将非常感谢这方面的任何帮助。
是的,这是 Nasm-2.11.08 中的错误。 Nasm-2.11.06 应该可以。 Nasm-2.11.09rc1 应该可以工作(?)。对不起!
相关问题可以在这里找到:
Bug 3392306 - Issue with relative addressing and data section
Homebrew 可用的当前版本 2.11.08 使用以下 diff 文件修补此问题:
https://raw.githubusercontent.com/Homebrew/patches/7a329c65e/nasm/nasm_outmac64.patch
From 4920a0324348716d6ab5106e65508496241dc7a2 Mon Sep 17 00:00:00 2001
From: Cyrill Gorcunov <gorcunov@gmail.com>
Date: Sat, 9 May 2015 18:07:47 +0300
Subject: [PATCH] output: outmac64 -- Fix the case when first hit matches the
symbol
In case if we're looking up for a symbol and it's first
one in symbol table we might endup with error because of
using GE here (78f477b35f) ending cycle with @nearest = NULL.
http://bugzilla.nasm.us/show_bug.cgi?id=3392306
Reprted-by: Benjamin Randazzo <benjamin@linuxcrashing.org>
Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
output/outmac64.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/output/outmac64.c b/output/outmac64.c
index c07dcbc..1d30e64 100644
--- a/output/outmac64.c
+++ b/output/outmac64.c
@@ -304,7 +304,7 @@ static struct symbol *get_closest_section_symbol_by_offset(uint8_t fileindex, in
for (sym = syms; sym; sym = sym->next) {
if ((sym->sect != NO_SECT) && (sym->sect == fileindex)) {
- if ((int64_t)sym->value >= offset)
+ if ((int64_t)sym->value > offset)
break;
nearest = sym;
}
--
2.4.10.GIT
因此,如果您通过 Homebrew 安装,这个问题现在应该已经解决了。
简而言之,当我的 .data
部分中有多个 db
部分时,编译的 addresses/labels 在 NASM 编译时关闭。在我的测试中,它们在生成的 Mach-O 二进制文件中偏移了 256 个字节。
我正在使用的软件:
- OS X 10.10.5
nasm
NASM 版本 2.11.08,根据 x84_64 ASM 的要求通过 Homebrew 安装
gobjdump
GNU objdump (GNU Binutils) 2.25.1,通过 Homebrew 安装clang
Apple LLVM 版本 6.1.0 (clang-602.0.53)(基于 LLVM 3.6.0svn)
什么有效:
以下面的"hello world"NASM程序集为例。
main.s
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msg]
mov rdx, len
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msg: db "Hello, world!", 10
len: equ $ - msg
编译并 运行 具有:
/usr/local/bin/nasm -f macho64 -o main.o main.s
clang -o main main.o
./main
效果很好,并产生以下输出:
Hello, world!
什么没有:
现在,要添加另一条消息,我们只需向数据部分添加另一个字符串,然后再添加一个 syscall
。够简单了。
main.s
global _main
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msga]
mov rdx, lena
syscall
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel msgb]
mov rdx, lenb
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
section .data
msga: db "Hello, world!", 10
lena: equ $ - msga
msgb: db "Break things!", 10
lenb: equ $ - msgb
编译和运行,和之前一样,得到:
Break things!
什么?!?我们不应该得到吗?:
Hello, world!
Break things!
怎么了?:
显然出了点问题。是时候反汇编生成的二进制文件,看看我们得到了什么。
$ gobjdump -d -M intel main
为 _main
生成以下内容:
0000000100000f7c <_main>:
100000f7c:b8 04 00 00 02 mov eax,0x2000004
100000f81:bf 01 00 00 00 mov edi,0x1
100000f86:48 8d 35 73 01 00 00 lea rsi,[rip+0x173] # 100001100 <msgb+0xf2>
100000f8d:ba 0e 00 00 00 mov edx,0xe
100000f92:0f 05 syscall
100000f94:b8 04 00 00 02 mov eax,0x2000004
100000f99:bf 01 00 00 00 mov edi,0x1
100000f9e:48 8d 35 69 00 00 00 lea rsi,[rip+0x69] # 10000100e <msgb>
100000fa5:ba 0e 00 00 00 mov edx,0xe
100000faa:0f 05 syscall
100000fac:b8 01 00 00 02 mov eax,0x2000001
100000fb1:bf 00 00 00 00 mov edi,0x0
100000fb6:0f 05 syscall
从注释# 100001100 <msgb+0xf2>
可以看出,它不是指向msga
符号,而是指向msgb
之后的0xf2
,或者100001100
(在这个地址有空字节,导致没有输出)。在十六进制编辑器中检查二进制文件,我在偏移量 1000
或地址 100001000
处找到了实际的 msga
字符串。这意味着编译二进制文件中的地址现在偏移了 0x100
/256
字节,仅仅是因为现在有第二个 db
标签。什么?!?
一个解决方法的抱歉借口:
作为实验,我决定尝试将两个 db
部分放入单独的 ASM/object 文件中,并将所有 3 个链接在一起。这样做有效。
main.s
global _main
extern _msga
extern _lena
extern _msgb
extern _lenb
section .text
_main:
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel _msga]
mov rdx, _lena
syscall
mov rax, 0x2000004
mov rdi, 1
lea rsi, [rel _msgb]
mov rdx, _lenb
syscall
mov rax, 0x2000001
mov rdi, 0
syscall
msga.s
global _msga
global _lena
section .data
_msga: db "Hello, world!", 10
_lena: equ $ - _msga
msgb.s
global _msgb
global _lenb
section .data
_msgb: db "Break things!", 10
_lenb: equ $ - _msgb
编译并 运行 使用:
/usr/local/bin/nasm -f macho64 -o main.o main.s
/usr/local/bin/nasm -f macho64 -o msga.o msga.s
/usr/local/bin/nasm -f macho64 -o msgb.o msgb.s
clang -o main msga.o msgb.o main.o
./main
结果:
Hello, world!
Break things!
虽然这确实有效,但我很难相信这是最好的解决方案。
出了什么问题?
一定有办法在一个 ASM 文件中包含多个 db
标签吗?我写ASM的方式有问题吗?这是 NASM 中的错误吗?这是某种预期的行为吗,在这种情况下 为什么 ?我的解决方法是额外的工作和混乱,所以我将非常感谢这方面的任何帮助。
是的,这是 Nasm-2.11.08 中的错误。 Nasm-2.11.06 应该可以。 Nasm-2.11.09rc1 应该可以工作(?)。对不起!
相关问题可以在这里找到:
Bug 3392306 - Issue with relative addressing and data section
Homebrew 可用的当前版本 2.11.08 使用以下 diff 文件修补此问题:
https://raw.githubusercontent.com/Homebrew/patches/7a329c65e/nasm/nasm_outmac64.patch
From 4920a0324348716d6ab5106e65508496241dc7a2 Mon Sep 17 00:00:00 2001
From: Cyrill Gorcunov <gorcunov@gmail.com>
Date: Sat, 9 May 2015 18:07:47 +0300
Subject: [PATCH] output: outmac64 -- Fix the case when first hit matches the
symbol
In case if we're looking up for a symbol and it's first
one in symbol table we might endup with error because of
using GE here (78f477b35f) ending cycle with @nearest = NULL.
http://bugzilla.nasm.us/show_bug.cgi?id=3392306
Reprted-by: Benjamin Randazzo <benjamin@linuxcrashing.org>
Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
output/outmac64.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/output/outmac64.c b/output/outmac64.c
index c07dcbc..1d30e64 100644
--- a/output/outmac64.c
+++ b/output/outmac64.c
@@ -304,7 +304,7 @@ static struct symbol *get_closest_section_symbol_by_offset(uint8_t fileindex, in
for (sym = syms; sym; sym = sym->next) {
if ((sym->sect != NO_SECT) && (sym->sect == fileindex)) {
- if ((int64_t)sym->value >= offset)
+ if ((int64_t)sym->value > offset)
break;
nearest = sym;
}
--
2.4.10.GIT
因此,如果您通过 Homebrew 安装,这个问题现在应该已经解决了。