如果你声明test1()
hidden ( __attribute__((__visibility__("hidden")))
,跳转将是直接的。
现在test1()
可能不会在其源翻译单元中被定义为隐藏,但我相信除了 C 语言保证之外,&test1 == &test1
如果其中一个指针是通过隐藏引用获得的,一个指针是通过公共引用(公共引用可能已通过预加载或位于查找范围内当前引用之前的 DSO 插入,而隐藏引用(导致直接跳转)有效防止任何类型的插入)
处理这个问题的更合适的方法是定义两个名称test1()
——一个公共名称和一个私有/隐藏名称。
在 gcc 和 clang 中,这可以通过一些别名魔法来完成,这只能在定义符号的翻译单元中完成。
宏可以让它更漂亮:
#define PRIVATE __attribute__((__visibility__("hidden")))
#define PUBLIC __attribute__((__visibility__("default")))
#define PRIVATE_ALIAS(Alias,OfWhat) \
extern __typeof(OfWhat) Alias __attribute((__alias__(#OfWhat), \
__visibility__("hidden")))
#if HERE
PUBLIC void test1(void) { }
PRIVATE_ALIAS(test1__,test1);
#else
PUBLIC void test1(void);
PRIVATE void test1__(void);
#endif
void call_test1(void) { test1(); }
void call_test1__(void) { test1__(); }
void call_ext0(void) { void ext0(void); ext0(); }
void call_ext1(void) { PRIVATE void ext1(void); ext1(); }
上面将 (-O3, x86-64) 编译成:
call_test1:
jmp test1@PLT
call_test1__:
jmp test1__
call_ext0:
jmp ext0@PLT
call_ext1:
jmp ext1
(定义 HERE=1 额外内联了 test1 调用,因为它很小且是本地的,并且 -O3 已打开)。
https://godbolt.org/g/eZvmp7上的实时示例。