gpt4 book ai didi

macos - x64 nasm : pushing memory addresses onto the stack & call function

转载 作者:行者123 更新时间:2023-12-04 16:47:55 27 4
gpt4 key购买 nike

对于Mac上的x64汇编,我还很陌生,因此对于在64位中移植一些32位代码感到困惑。
该程序应仅通过C标准库中的printf函数打印出一条消息。
我从以下代码开始:

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大体上符合System V ABI - AMD64 Architecture Processor Supplement。它的代码模型与小位置无关代码模型(PIC)非常相似,其区别在于here。在该代码模型中,所有本地数据和小数据都可以使用RIP相对寻址直接访问。如Z Boson的评论所述,用于64位Mach-O可执行文件的镜像库超出了虚拟地址空间的前4 GiB,因此push msg不仅是将msg的地址放入堆栈的一种无效方法,但是这也是不可能的,因为PUSH不支持64位立即数。该代码应该看起来类似于:

   ; this is what you *would* do for later args on the stack
lea rax, [rel msg] ; RIP-relative addressing
push rax

但是在那种特殊情况下,根本不需要将值压入堆栈。 64位调用约定要求将第6个整数/指针参数严格按顺序传递到寄存器 RDIRSIRDXRCXR8R9中。前8个浮点或矢量参数进入 XMM0XMM1,..., XMM7。仅在使用了所有可用寄存器或存在不适合任何这些寄存器的参数(例如80位 long double值)之后,才使用堆栈。 64位立即推送是使用 MOV( QWORD变体)而不是 PUSH执行的。简单的返回值将传递回 RAX寄存器中。调用方还必须为被调用方提供堆栈空间,以保存一些寄存器。
printf是一个特殊的函数,因为它接受可变数量的参数。调用此类函数时,应将 AL(RAX的低字节)设置为在向量寄存器中传递的浮点参数的数量。另请注意,相对于代码的2 GiB之内的数据,首选 RIP相对寻址。

这是 gcc在OS X上将 printf("This is a test\n");转换为程序集的方式:
    xorl    %eax, %eax             # (1)
leaq L_.str(%rip), %rdi # (2)
callq _printf # (3)

L_.str:
.asciz "This is a test\n"

(这是AT&T样式的汇编,源在左边,目的地在右边,寄存器名称以 %开头,数据宽度被编码为指令名称的后缀)

由于没有传递浮点参数,因此在 (1)处将零放入 AL中(通过将整个RAX设置为零,以避免部分寄存器延迟)。在 (2)处,字符串的地址被加载到 RDI中。请注意,该值实际上是如何从 RIP的当前值偏移的。由于汇编器不知道该值是什么,因此它将重定位请求放入目标文件中。链接器然后看到重定位,并在链接时放置正确的值。

我不是NASM专家,但我认为以下代码可以做到:
default rel             ; make [rel msg] the default for [msg]
section .data
msg: db 'This is a test', 10, 0 ; something stupid here

section .text
global _main
extern _printf

_main:
push rbp ; re-aligns the stack by 16 before call
mov rbp, rsp

xor eax, eax ; al = 0 FP args in XMM regs
lea rdi, [rel msg]
call _printf

mov rsp, rbp
pop rbp
ret

关于macos - x64 nasm : pushing memory addresses onto the stack & call function,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13091987/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com