如何强制gcc直接在PIC代码中调用函数?

fuz*_*fuz 6 c x86 gcc position-independent-code plt

考虑以下功能:

extern void test1(void);
extern void test2(void) {
    test1();
}
Run Code Online (Sandbox Code Playgroud)

这是gcc -fpic在amd64 Linux上生成的代码:

test2:
    jmp test1
Run Code Online (Sandbox Code Playgroud)

当我编译时-fpic,gcc显式调用PLT来启用符号插入:

test2:
    jmp test1@PLT
Run Code Online (Sandbox Code Playgroud)

然而,这对于与位置无关的代码并不是严格需要的,如果我不想支持,可能会被遗漏.如有必要,链接器仍然会将跳转目标重写为PLT符号.

如何在不更改源代码且不使编译代码不适合共享库的情况下,使函数调用直接转到其目标而不是通过PLT显式转换?

PSk*_*cik 3

如果你声明test1()隐藏(__attribute__((__visibility__("hidden"))),则跳转将是直接的。

\n\n

Nowtest1()可能不会在其源翻译单元中定义为隐藏,但我相信这种差异不会造成任何损害,除了 C 语言保证&test1 == &test1在运行时被破坏,如果其中一个指针是通过隐藏引用获得的,而另一个指针是通过公共引用(公共引用可能是通过预加载或位于查找范围中当前引用之前的 DSO 插入的,而隐藏引用(导致直接跳转)有效防止任何类型的插入)

\n\n

处理这个问题的更合适的方法是定义两个名称test1()\xe2\x80\x94a 公共名称和私有/隐藏名称定义两个名称。

\n\n

在 gcc 和 clang 中,这可以通过一些别名魔法来完成,这只能在定义符号的翻译单元中完成。

\n\n

宏可以使它更漂亮:

\n\n
#define PRIVATE __attribute__((__visibility__("hidden")))\n#define PUBLIC __attribute__((__visibility__("default")))\n#define PRIVATE_ALIAS(Alias,OfWhat) \\\n    extern __typeof(OfWhat) Alias __attribute((__alias__(#OfWhat), \\\n                                 __visibility__("hidden")))\n\n#if HERE\nPUBLIC void test1(void) { }\nPRIVATE_ALIAS(test1__,test1);\n#else\nPUBLIC void test1(void);\nPRIVATE void test1__(void);\n#endif\n\nvoid call_test1(void) { test1(); }\nvoid call_test1__(void) { test1__(); }\n\nvoid call_ext0(void) { void ext0(void); ext0(); }\nvoid call_ext1(void) { PRIVATE void ext1(void); ext1(); }\n
Run Code Online (Sandbox Code Playgroud)\n\n

上面将 (-O3, x86-64) 编译为:

\n\n
call_test1:\n        jmp     test1@PLT\ncall_test1__:\n        jmp     test1__\ncall_ext0:\n        jmp     ext0@PLT\ncall_ext1:\n        jmp     ext1\n
Run Code Online (Sandbox Code Playgroud)\n\n

(定义 HERE=1 还会内联 test1 调用,因为它很小且是本地的,并且 -O3 处于打开状态)。

\n\n

现场示例https://godbolt.org/g/eZvmp7

\n