我是很新,在Mac上的x64组装,所以我越来越糊涂在64位移植一些32位代码。
该方案应该简单地通过打印出消息printf
从C非标准库函数。
我已经开始使用此代码:
section .data
msg db 'This is a test', 10, 0 ; something stupid here
section .text
global _main
extern _printf
_main:
push rbp
mov rbp, rsp
push msg
call _printf
mov rsp, rbp
pop rbp
ret
与NASM这样编译它:
$ nasm -f macho64 main.s
返回以下错误:
main.s:12: error: Mach-O 64-bit format does not support 32-bit absolute addresses
我试图解决这个问题字节改变的代码如下:
section .data
msg db 'This is a test', 10, 0 ; something stupid here
section .text
global _main
extern _printf
_main:
push rbp
mov rbp, rsp
mov rax, msg ; shouldn't rax now contain the address of msg?
push rax ; push the address
call _printf
mov rsp, rbp
pop rbp
ret
它编译罚款与nasm
上面的命令,但现在有一个警告,同时编制与目标文件gcc
实际程序:
$ gcc main.o
ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not
allowed in code signed PIE, but used in _main from main.o. To fix this warning,
don't compile with -mdynamic-no-pic or link with -Wl,-no_pie
因为它是不是一个错误,我执行了一个警告a.out
文件:
$ ./a.out
Segmentation fault: 11
希望任何人知道我在做什么错。
64位OS X ABI符合在大到系统V ABI - AMD64架构处理器补充 。 它的代码模型非常相似, 小的位置无关的代码模型(PIC)与差异来解释这里 。 在该代码中模型的所有本地和小型的数据被直接利用RIP相对寻址访问。 如在由Z玻色子的评论所指出的,对于64位的Mach-O可执行文件映像基超出了虚拟地址空间的第一4吉布,因此push msg
不仅放的地址的无效方式msg
在堆栈上,但它也是因为不可能一个PUSH
不支持64位立即值。 该代码应相当类似于:
lea rax, [rel msg] ; RIP-relative addressing
push rax
但在这种特殊情况下,毋须在堆栈上推价值可言。 64位调用约定强制要求的拳头6整数/指针参数在寄存器传递RDI
, RSI
, RDX
, RCX
, R8
和R9
,恰好以该顺序。 前8个浮点或载体争论进入XMM0
, XMM1
,..., XMM7
。 仅在所有可用的寄存器被用于或有不能适合在任何这些寄存器(例如80位的参数long double
被使用的堆栈值)。 64位立即推动使用执行MOV
(所述QWORD
变体),而不是PUSH
。 简单的返回值在回传RAX
寄存器。 调用者还必须提供堆栈空间被调用者节省一些寄存器。
printf
是一个特殊的功能,因为它需要不同数量的参数。 当调用这些功能RAX
应设置为的浮点参数,在矢量寄存器传递的数量。 还要注意的是RIP
-relative寻址优选用于数据位于代码的2吉布内。
下面是如何gcc
转换printf("This is a test\n");
成在OS X组件:
xorb %al, %al (1)
leaq L_.str(%rip), %rdi (2)
callq _printf (3)
L_.str:
.asciz "This is a test\n"
(这是AT&T样式组件,源被左,目的地是正确的,注册名称的前缀%
,数据宽度被编码为一个后缀的指令名)
在(1)
零投入RAX
因为没有浮点参数被传递。 在(2)
的字符串的地址被加载在RDI
。 注意:值实际上是如何从当前值的偏移RIP
。 由于汇编程序不知道这个值是,它把目标文件中重新定位请求。 然后,链接程序将搬迁,并把链接时正确的值。
我不是NASM大师,但我认为下面的代码应该这样做:
section .data
msg db 'This is a test', 10, 0 ; something stupid here
section .text
global _main
extern _printf
_main:
push rbp
mov rbp, rsp
xor al, al
lea rdi, [rel msg]
call _printf
mov rsp, rbp
pop rbp
ret
没有答案还解释了为什么NASM报告
Mach-O 64-bit format does not support 32-bit absolute addresses
究其原因NASM不会做这在解释昂纳雾的优化汇编手册中的第3.3节寻址下的第模式标题在64位模式的32位绝对寻址他写道:
32位的绝对地址不能在Mac OS X,其中的地址是上面的2 ^ 32默认使用。
这不是在Linux或Windows的一个问题。 事实上,我已经展示这个工作在静态链接与-的glibc-不呼-主 。 这世界你好代码使用32位绝对与ELF64寻址和运行正常。
@HristoIliev使用RIP相对寻址建议,但并不说明32位绝对在Linux的解决将正常工作。 事实上,如果你改变lea rdi, [rel msg]
到lea rdi, [msg]
它组装并运行良好与nasm -efl64
但未能与nasm -macho64
像这样:
section .data
msg db 'This is a test', 10, 0 ; something stupid here
section .text
global _main
extern _printf
_main:
push rbp
mov rbp, rsp
xor al, al
lea rdi, [msg]
call _printf
mov rsp, rbp
pop rbp
ret
您可以检查,这是一个绝对的32位地址,而不是撕裂相对与objdump
。 但是,它指出的首选方法仍然是RIP相对寻址是非常重要的。 瓦格纳在同一个手动写:
我们绝对没有理由使用绝对地址进行简单的存储操作数。 RIP-相对地址做出指示短,但省去了在加载时搬迁的需要,他们是安全的使用所有的系统。
因此,当将使用使用32位的绝对地址在64位模式? 静态数组是一个很好的候选人。 请参见下面的小节寻址静态数组在64位模式 。 简单的情况是例如:
mov eax, [A+rcx*4]
其中A是静态数组的绝对值的32位地址。 这正常工作与Linux,但再次因为图像基数大于2 ^ 32默认情况下,你不能在Mac OS X这样做。 为了这个Mac OS X上看到例如昂纳手册3.11c和3.11d。 在例如3.11c,你可以做
mov eax, [(imagerel A) + rbx + rcx*4]
当您使用从马赫澳外部引用__mh_execute_header
来获得图像的基础。 在示例3.11c您使用RIP相对寻址和加载这样的地址
lea rbx, [rel A]; rel tells nasm to do [rip + A]
mov eax, [rbx + 4*rcx] ; A[i]
根据针对x86 64位指令集的文档http://download.intel.com/products/processor/manual/325383.pdf
PUSH只接受8,16和32位的立即值(64位寄存器和寄存器寻址的存储器块被允许虽然)。
PUSH msg
其中msg是一个64位立即地址将无法编译,你发现了。
_printf被定义为在64位库什么调用约定?
被期待它的堆栈上的参数或使用寄存器快速调用约定其中的参数? 因为X86-64使更多的通用寄存器,可快速调用约定更常用。