如何在寄存器x86_64 nasm中正确存储过程的地址?



我想在寄存器中存储一个过程的地址,如下所示:

extern  _printf
section .text
global _foo
_foo :
mov     rax, [rel _printf]
call    rax
ret

,但我得到这个错误时,编译与主写在C:

ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _foo from procedure.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie
final section layout:
__TEXT/__text addr=0x100000E70, size=0x000000AD, fileOffset=0x00000E70, type=1
__TEXT/__stubs addr=0x100000F1E, size=0x00000018, fileOffset=0x00000F1E, type=28
__TEXT/__stub_helper addr=0x100000F38, size=0x00000038, fileOffset=0x00000F38, type=32
__TEXT/__cstring addr=0x100000F70, size=0x00000036, fileOffset=0x00000F70, type=13
__TEXT/__unwind_info addr=0x100000FA8, size=0x00000050, fileOffset=0x00000FA8, type=22
__DATA/__nl_symbol_ptr addr=0x100001000, size=0x00000008, fileOffset=0x00001000, type=29
__DATA/__got addr=0x100001008, size=0x00000010, fileOffset=0x00001008, type=29
__DATA/__la_symbol_ptr addr=0x100001018, size=0x00000020, fileOffset=0x00001018, type=27
ld: 32-bit RIP relative reference out of range (-4294971162 max is +/-2GB): from _foo (0x100000F13) to _printf@0x00000000 (0x00000000) in '_foo' from procedure.o for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我试着用-Wl,-no_pie编译,我得到这个:

ld: illegal text-relocation to '_printf' in /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/libSystem.tbd from '_foo' in procedure.o for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

那么我该如何解决这个问题??

有两种可能的方法:静态链接和动态链接。我将展示两者。我怀疑(基于你的代码)你是静态链接。

前两部分是针对Linux的。最后一部分是针对mac的。

<标题>

静态链接我认为下面的工作方式是你想要的。我不用rel,因为我不需要;因为printf是静态链接的,所以我可以直接得到它的地址。

push rbp(8字节)是必需的;它确保在调用printf推送返回地址(多8个字节)之后,堆栈是16字节对齐的。调用可能使用xmm寄存器的函数要求您将堆栈对齐在16字节边界上,否则您可能会得到段错误,printf使用这些寄存器。

同样,printf期望fp参数的数量在rax中,所以我将其设置为零,并使用rdx来保存地址。

; hello-static.asm
; nasm -felf64 hello-static.asm && gcc -static -o hello-static hello-static.o
extern  printf
section .text
global main
main:   push    rbp
mov     rdx, printf
mov     rdi, fmt
xor     eax, eax
call    rdx
pop     rbp
ret
fmt:    db "Hello", 10, 0
<标题>动态链接在动态链接的可执行文件中执行此操作的传统方法是通过PLT。下面的代码就是这样做的。
; hello.asm
; nasm -felf64 hello.asm && gcc -o hello hello.o
extern  printf
section .text
global main
main:   push    rbp
lea     rdx, [rel printf wrt ..plt]
mov     rdi, fmt
xor     eax, eax
call    rdx
pop     rbp
ret
fmt:    db "Hello", 10, 0

解释几句。首先,通过调用printf,推入rbp确保堆栈在16字节边界上对齐。其次,我使用rel来获取PLT中相对于rip的值的跳转地址。第三,我用rdx代替rax;后一种寄存器由printf用于浮点参数的数量。

rdx中的地址将不包含printf的实际地址。为此,您需要到GOT并从那里获取它。

下面的代码从GOT中获取地址。

; hello-got.asm
; nasm -felf64 hello-got.asm && gcc -o hello-got hello-got.o
extern  printf
extern  _GLOBAL_OFFSET_TABLE_
section .text
global main
main:   push    rbx
lea     rbx, [rel _GLOBAL_OFFSET_TABLE_]
lea     rdx, [printf wrt ..got]
add     rdx, rbx
mov     rdx, [rdx]
mov     rdi, fmt
xor     eax, eax
call    rdx
pop     rbx
ret
fmt:    db "Hello", 10, 0

这里,我首先使用rip相对寻址获得GOT的地址。然后我将偏移量放入printf函数向量的GOT中。然后,我将偏移量添加到GOT的基址,以获得printf向量的绝对地址。最后,我将向量移动到rdx中,以便以后调用它。

我不pushrbp因为我不需要保存堆栈帧;我必须保留rbx(它传统上用于保存GOT的基址),因为它是一个被调用者保存的寄存器,它负责堆栈对齐。

<标题>

Mac (macho64)Mac的过程几乎是相同的:在全局偏移表中查找符号以找到向量,然后对其解引用以获得过程的实际地址。使用wrt ..gotpcrel代替查找GOT,然后使用wrt ..got

; hello-mac.asm
; nasm -fmacho64 hello-mac.asm && ld -o hello-mac hello-mac.o -lc
extern _printf
global _main
section .text
_main:  push    rbp
mov     rbp, rsp
mov     rdi, msg
mov     rsi, 17
lea     rsi, [rel _printf wrt ..gotpcrel]
xor     eax, eax
call    [rsi]
xor     eax, eax
leave
ret
section .data
msg:    db "printf: 0x%lx", 10, 0

最新更新