首页 > 解决方案 > __i686.get_pc_thunk 和 __x86.get_pc_thunk 有什么区别?

问题描述

GCC 和 Clang 在 32 位 x86 位置无关代码中使用这些辅助函数将当前执行地址放入寄存器,例如:

call    __i686.get_pc_thunk.bx
addl    $_GLOBAL_OFFSET_TABLE_, %ebx
movl    $2, 4(%esp)
leal    .LC0@GOTOFF(%ebx), %eax
movl    %eax, (%esp)
call    dlopen@PLT

似乎实现是等效的:

__x86.get_pc_thunk.bx:
    movl    (%esp), %ebx
    ret

__i686.get_pc_thunk.bx:
    movl (%esp), %ebx
    ret

除了更名之外还有什么区别(似乎i686更旧)?有 i686 前缀而不是 i386 的原因吗?

标签: gccassemblyx86position-independent-code

解决方案


因此,在对提交历史和错误跟踪器进行了一些挖掘之后,我想我基本上已经弄清楚了。

很久以前,glibc 曾经有自己的 PIC 代码处理方式,其中涉及调用/弹出模式来获取 GOT 地址。

大约在 2002 年__i686.get_pc_thunk.*完成类似任务的 , 被添加到 GCC,最初是作为内部符号。

不久之后它也出现在 glibc 中,可能是为了避免在使用 GCC 编译时出现代码重复。

但是,当为 Pentium 2 或更高版本 ( -march=i686) 构建时,GCC 定义了预处理器宏__i686=1,破坏了 glibc 对存根代码的编译。这个问题很早就被发现了,但是几年来 glibc 使用了各种变通方法来处理这个问题。

在 2011 年(GCC 4.7?),名称更改为,__x86.get_pc_thunk.*并且 glibc添加了一些检查以使用匹配的名称。最终对旧 GCC 版本的支持与旧名称一起被删除。GCC 和 glibc__x86.get_pc_thunk.*现在都只能使用(尽管 GCC 也可以生成内联调用/弹出版本)。

所以,总结一下:

两者之间没有实际区别,由于预定义的宏冲突,名称更改只是历史性的。

参考:

https://gcc.gnu.org/git/?p=gcc.git&a=search&h=HEAD&st=commit&s=get_pc_thunk

https://sourceware.org/git/?p=glibc.git&a=search&st=commit&s=get_pc_thunk

https://sourceware.org/bugzilla/show_bug.cgi?id=411

https://sourceware.org/bugzilla/show_bug.cgi?id=4507


推荐阅读