1

除了说“CreateObject”之外,我的库不公开任何功能。尽管如此,它们的所有函数都是间接调用的,所以我在性能报告中看到高达 1.65% 的时间花费在 __i686.get_pc_thunk.bx 中。函数(类方法)被调用了 1.6 亿次,它们在共享库内部,即未公开。

我想知道是否可以在没有重定位的情况下编译内部方法——即使用相对偏移或类似的东西。

gcc 是 4.5.2

更新:实际上我认为这是因为 -O0 留在了makefile中。所以现在这没什么大不了的,但我仍然想对 -O0 做同样的事情,因为它可以减少探查器的“垃圾”。我想知道执行此操作的 -O2 “真实”选项是什么。

UPDATE2:嗯,不是-O2,可能是--dynamic-list 降低了pc_thunk 的性能,但它仍然存在......所以甚至不确定--dynamic-list 是否真的有帮助。隐藏符号是否仍应包含间接 thunk,是否正确?

UPDATE3:我创建了一个测试项目,对于内部库函数,我将属性可见性设置为隐藏,我在 gcc 4.7 和 -O2 和 LTO 启用的情况下编译,我将 --dynamic-list 传递给链接器,但没有内部函数,但调用到 get_pc_thunk 仍然存在。

这是测试共享库中的代码:

#include <stdio.h>

__attribute__((visibility("hidden"), noinline)) void lib1f2()
{
    puts("I should have PLT disabled");
}

void lib1f()
{
    puts("I'm lib1");
    lib1f2();
}

在 gdb 中,我仍然在 lib1f2 中看到 thunk。

有趣的是,使用 -fwhole-program lib1f2 被内联到主可执行文件中,但仍然包含对 thunk 的调用。

UPDATE4:好的,我快接近了(意识到我很愚蠢),程序(和上面的代码)使用数据,即使它只是一个 const 字符串,所以它需要 GOT 调用。所以现在的问题是:

  1. 不过,我可以避免 GOT 的重击吗?
  2. (相关)通过,也许,在没有 -fPIC 的情况下编译 - 会有什么缺点?
4

3 回答 3

1

您可能对 GCC 可见性支持感兴趣

http://gcc.gnu.org/wiki/Visibility

要将所有符号设为私有,您可以使用 -fvisibility=hidden 选项。还记得使用属性((visibility ("default")))将 CreateObject 方法标记为 public

于 2012-12-03T14:01:19.200 回答
1

1) 不过,我可以避免 GOT 的重击吗?

我想不是。至少不在 i686 上。问题是代码可以自动进行相对跳转......或者更确切地说,x86 上的所有跳转都是相对的,除了间接跳转 IIRC。另一方面,没有办法索引相对于当前程序计数器的数据。这个问题实际上在 x86_64 中得到了解决,因为有一个新的指令指针相对寻址可以完全用于这种情况。

您的测试,使用 gcc -fPIC -shared -O2 -flto 编译

在 32 位上:

00000530 <lib1f2.2321>:
push   %ebx
call   52b <__x86.get_pc_thunk.bx>
add    $0x1aca,%ebx
sub    $0x18,%esp
lea    -0x1a67(%ebx),%eax
mov    %eax,(%esp)
call   3f0 <puts@plt>
add    $0x18,%esp
pop    %ebx
ret
00000560 <lib1f>:
push   %ebx
call   52b <__x86.get_pc_thunk.bx>
add    $0x1a9a,%ebx
sub    $0x18,%esp
lea    -0x1a4c(%ebx),%eax
mov    %eax,(%esp)
call   3f0 <puts@plt>
add    $0x18,%esp
pop    %ebx
jmp    530 <lib1f2.2321>
nop

在 64 位

00000000000006b0 <lib1f2.2352>:
lea    0x2a(%rip),%rdi
jmpq   590 <puts@plt>

00000000000006c0 <lib1f>:
lea    0x35(%rip),%rdi
sub    $0x8,%rsp
callq  590 <puts@plt>
xor    %eax,%eax
add    $0x8,%rsp
jmp    6b0 <lib1f2.2352>

2)(相关)通过,也许,在没有 -fPIC 的情况下编译 - 会有什么缺点?

好吧,虽然这很尴尬,但我不得不承认我在这里有点困惑。乍一看,我会说共享库是用 -fPIC 编译的。相反,以下两个命令都有效

gcc -fPIC -m32 -shared -O2 -flto test.c -o test.so
gcc -m32 -shared -O2 -flto test.c -o test.so

在非-fPIC 情况下,代码也不需要对get_pc_thunk 进行任何调用。诀窍是动态加载器在运行时使用正确的数据地址修复库代码。

虽然这是一个问题,因为您获得了一些速度以避免 thunk,但是您失去了实际共享共享库的能力,因为操作系统必须为包含重定位的库的每个代码页创建一个新副本。另一方面,当使用 GOT 时,只需复制 GOT 页,当许多应用程序链接到同一个库时,大大减少了内存占用。

有趣的是,在 64bit 模式下是不可能在非 pic 模式下编译库的,以下命令失败

gcc -m64 -shared -O2 -flto test.c -o test.so

尽管如此,由于处理器提供了对代码相对寻址的支持,这不是问题。

于 2012-12-06T22:08:04.373 回答
0

在函数中使用可见性属性,尤其是hidden. 你可以定义

 #ifdef __GNUC__
  #define MODULE_VISIBILITY  __attribute__ ((visibility ("hidden")))
 #else
  #define MODULE_VISIBILITY
 #endif

然后声明你的函数,例如

 extern void MODULE_VISIBILITY my_module_fun(int);

使用最近的(4.6 或 4.7)GCC 编译器,您还可以编译和链接链接时间优化,例如CXX= g++ -flto在您的Makefile.

于 2012-12-03T13:57:12.873 回答