Gui*_*TeK 3 linux assembly x86-64 nasm elf
我正在用 NASM 构建一个共享库。在那个库中,在某些函数中,我需要我们称之为C 中的静态变量。基本上,我认为它是 .data 部分中的一些空间:
SECTION .data
last_tok: dq 0 ; Define a QWORD
Run Code Online (Sandbox Code Playgroud)
当我尝试在我的函数中访问 last_tok时出现问题。
我阅读了NASM 手册:8.2 编写 Linux/ELF 共享库,其中解释了问题并提供了解决方案。
SECTION .data
last_tok: dq 0 ; Define a QWORD
SECTION .text
EXTERN _GLOBAL_OFFSET_TABLE_
GLOBAL strtok:function
strtok:
enter 0, 0
push rbx
call .get_GOT
.get_GOT:
pop rbx
add rbx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_GOT wrt ..gotpc
mov [rbx + last_tok wrt ..gotoff], rdi ; Store the contents of RDI at last_tok
mov rbx, [rbp - 8]
leave
ret
Run Code Online (Sandbox Code Playgroud)
它可能适用于 ELF32,但使用 ELF64 时出现以下错误:
nasm -f elf64 -o strtok.o strtok.s
strtok:15: error: ELF64 requires ..gotoff references to be qword
<builtin>: recipe for target 'strtok.o' failed
make: *** [strtok.o] Error 1
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
有效地址格式仅允许符号扩展为 64 位的 32 位位移。根据错误消息,您需要完整的 64 位。您可以通过寄存器添加它,例如:
mov rax, last_tok wrt ..gotoff
mov [rbx + rax], rdi
Run Code Online (Sandbox Code Playgroud)
此外,这call .get_GOT是一个 32 位解决方案,在 64 位模式下,您可以在那里使用 rip 相对寻址。虽然上面可以编译,但我不确定它会起作用。幸运的是,简单的解决方案是使用提到的 rip 相对寻址来访问您的变量,因此:
SECTION .data
GLOBAL last_tok
last_tok: dq 0 ; Define a QWORD
SECTION .text
GLOBAL strtok:function
strtok:
mov rcx, [rel last_tok wrt ..gotpc] ; load the address from the GOT
mov rax, [rcx] ; load the old dq value from there
; and/or
mov [rcx], rdi ; store arg at that address
ret
Run Code Online (Sandbox Code Playgroud)
请注意,对于私有(静态)变量,您可以直接使用,[rel last_tok]而不必弄乱 got 。
在 PIE 可执行文件中,编译器使用(相当于)[rel symbol]访问全局变量,前提是主可执行文件不需要或不希望为自己的符号插入符号。
(符号插入,或在其他共享库中定义的符号,是从 x86-64 上的 GOT 加载符号地址的唯一原因。但mov rdx, [rel stdin]在 PIE 可执行文件中,即使是类似的东西也是安全的:https : //godbolt.org/z/eTf87e - 链接器在可执行文件中创建变量的定义,因此它在 RIP 相对寻址的范围内和链接时间常数偏移量处。)