`var@GOTPCREL(%rip)` 是什么意思?

What does `var@GOTPCREL(%rip)` mean?

<some symbol>@GOTPCREL(%rip) 是什么意思?

我遇到过这一行 mov var@GOTPCREL(%rip), %rax 并且对奇怪的语法感到有点困惑。

有人可以推荐我应该阅读以理解这一点的相关文档吗? 谢谢!

foo@GOTPCREL(%rip) 是符号 foo 的 GOT 条目,使用 RIP 相对寻址模式访问。

GOT表项由动态链接器填充(支持符号插入),保存符号的绝对地址foo,所以mov foo@GOTPCREL(%rip), %rax加载&foo 到 RAXhttps://en.wikipedia.org/wiki/Global_Offset_Table. Often this is followed by mov (%rax), %eax or similar to actually get the value of a global variable like int foo;, in a shared library where our definition of the symbol may not be the one the main executable is using. (See Thiago Macieira's blog: Sorry state of dynamic libraries on Linux from 2012; it pre-dates gcc -fno-plt, and also predates PIE executables,但共享库访问全局变量的情况并没有改善。)

通常你只会使用 foo@GOTPCREL(%rip) 作为全局变量地址 , not an executable (not even a PIE executable). Compilers assume that the main executable's global vars won't be "shadowed" by symbol interposition. (In a shared library, you can ,知道它们不会参与符号插入。)

对于int foo,只需mov foo(%rip), %eax加载它或lea foo(%rip), %rdi获取它的地址而不通过GOT。


但是对于函数调用,如果你想要一个指向像sin这样的库函数的指针,你当然可以通过从sin@GOTPCREL加载一个指针来获取libm本身的最终地址,而不仅仅是使用 mov $sin, %edi 获取指向其 PLT 存根的指针(并让链接器将 sin 重写为 sin@plt 当在静态链接的任何内容中都找不到该符号时,只有共享库)。 GCC 选择使用哪个取决于您的编译方式。 (PIE vs. 传统位置相关,and/or -fno-plt 或不。)

或者像 gcc -fno-plt 模式一样,使用 call *sin@gotpcrel(%rip) 调用库函数以通过其 GOT 条目使用间接调用,基本上内联几乎与 PLT 存根相同的内容,并强制提前绑定而不是惰性(在启动时解析 GOT 条目,而不是在第一次调用时解析。)

NASM 等价于 call [rel printf wrt ..got]


请注意 foo(%rip) uses the relative offset from here to the foo label/symbol,并没有像您猜测的那样将其绝对地址添加到该指令的末尾,或者像 123(%rip) 那样。但是 GOTPCREL 的 PCREL 部分显然指的是从此处到 GOT 条目的 PC 相对偏移量。


与@gotpcrel 类似,您可以执行 call printf@plt 之类的操作,通过 PLT 条目显式调用函数。不幸的是,我没有在 GNU as 手册中找到 @gotpcrel 的记录。 https://sourceware.org/binutils/docs/as/.