访问位置独立代码中的 .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 时间常数偏移处。)