访问位置独立代码中的 .data 部分
Access .data section in Position Independent Code
我正在与 NASM 构建一个共享库。在那个库中,在某些函数中,我需要我们在 C 中称之为 静态变量 的东西。基本上,我认为它是 .data 部分中的一些 space:
SECTION .data
last_tok: dq 0 ; Define a QWORD
当我尝试在我的函数中 access last_tok 时出现问题。
我阅读了NASM Manual: 8.2 Writing Linux/ELF Shared Libraries,其中解释了问题并给出了解决方案。
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
它可能适用于 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
我做错了什么?
有效地址格式只允许32位位移,符号扩展为64位。根据错误消息,您需要完整的 64 位。可以通过寄存器添加,例如:
mov rax, last_tok wrt ..gotoff
mov [rbx + rax], rdi
此外,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
请注意,对于私有(静态)变量,您可以直接使用 [rel last_tok]
,而不必弄乱 got。
在 PIE 可执行文件中,编译器使用(等效于)[rel symbol]
来访问全局变量,假设主可执行文件不需要或不需要为其自己的符号插入符号。
(符号插入,或其他共享库中定义的符号,是在 x86-64 上从 GOT 加载符号地址的唯一原因。但即使像 mov rdx, [rel stdin]
这样的东西在 PIE 可执行文件中也是安全的:https://godbolt.org/z/eTf87e - linker 在可执行文件中创建了一个变量的定义,因此它在范围内并且在 RIP 相对寻址的 link 时间常数偏移处。)
我正在与 NASM 构建一个共享库。在那个库中,在某些函数中,我需要我们在 C 中称之为 静态变量 的东西。基本上,我认为它是 .data 部分中的一些 space:
SECTION .data
last_tok: dq 0 ; Define a QWORD
当我尝试在我的函数中 access last_tok 时出现问题。
我阅读了NASM Manual: 8.2 Writing Linux/ELF Shared Libraries,其中解释了问题并给出了解决方案。
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
它可能适用于 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
我做错了什么?
有效地址格式只允许32位位移,符号扩展为64位。根据错误消息,您需要完整的 64 位。可以通过寄存器添加,例如:
mov rax, last_tok wrt ..gotoff
mov [rbx + rax], rdi
此外,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
请注意,对于私有(静态)变量,您可以直接使用 [rel last_tok]
,而不必弄乱 got。
在 PIE 可执行文件中,编译器使用(等效于)[rel symbol]
来访问全局变量,假设主可执行文件不需要或不需要为其自己的符号插入符号。
(符号插入,或其他共享库中定义的符号,是在 x86-64 上从 GOT 加载符号地址的唯一原因。但即使像 mov rdx, [rel stdin]
这样的东西在 PIE 可执行文件中也是安全的:https://godbolt.org/z/eTf87e - linker 在可执行文件中创建了一个变量的定义,因此它在范围内并且在 RIP 相对寻址的 link 时间常数偏移处。)