64 NASM:推存储器地址压入堆栈&呼叫功能(x64 nasm: pushing memory a

2019-07-02 17:25发布

我是很新,在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

希望任何人知道我在做什么错。

Answer 1:

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整数/指针参数在寄存器传递RDIRSIRDXRCXR8R9 ,恰好以该顺序。 前8个浮点或载体争论进入XMM0XMM1 ,..., 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


Answer 2:

没有答案还解释了为什么NA​​SM报告

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]


Answer 3:

根据针对x86 64位指令集的文档http://download.intel.com/products/processor/manual/325383.pdf

PUSH只接受8,16和32位的立即值(64位寄存器和寄存器寻址的存储器块被允许虽然)。

PUSH msg

其中msg是一个64位立即地址将无法编译,你发现了。


_printf被定义为在64位库什么调用约定?

被期待它的堆栈上的参数或使用寄存器快速调用约定其中的参数? 因为X86-64使更多的通用寄存器,可快速调用约定更常用。



文章来源: x64 nasm: pushing memory addresses onto the stack & call function