我想在寄存器中存储一个过程的地址,如下所示:
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
标题>标题>标题>