如何防止链接器丢弃函数?

ron*_*dau 5 c linker attributes gcc

我的 C 代码中有一个函数被隐式调用,并被链接器转储。我怎样才能防止这种现象?

我正在使用 gcc 和链接器标志 -gc-sections 进行编译,我不想从标志中排除整个文件。我尝试使用属性:“used”和“externally_visible”,但都没有奏效。

void __attribute__((section(".mySec"), nomicromips, used)) func(){
...
}
Run Code Online (Sandbox Code Playgroud)

在地图文件上,我可以看到该函数已编译但未链接。我用错了吗?有没有其他方法可以做到?

Mik*_*han 7

你误解了 used属性

用过的

附加到函数的此属性意味着必须为该函数发出代码,即使该函数似乎没有被引用...

编译器必须发出函数定义,即使函数看起来没有被引用。如果函数具有外部链接,编译器将永远不会得出未引用函数的结论。所以在这个程序中:

main1.c

static void foo(void){}

int main(void)
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译:

$ gcc -c -O1 main1.c
Run Code Online (Sandbox Code Playgroud)

foo根本没有发出的定义:

$ nm main1.o
0000000000000000 T main
Run Code Online (Sandbox Code Playgroud)

因为 foo在翻译单元中没有被引用,也不是外部的,所以可能会被优化掉。

但是在这个程序中:

main2.c

static void __attribute__((used)) foo(void){}

int main(void)
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

__attribute__((used)) 强制编译器发出本地定义:

$ gcc -c -O1 main2.c
$ nm main2.o
0000000000000000 t foo
0000000000000001 T main
Run Code Online (Sandbox Code Playgroud)

但这并不能阻止链接器丢弃foo已定义的部分-gc-sections,即使foo 外部的,如果该部分未使用:

main3.c

void foo(void){}

int main(void)
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

用函数部分编译:

$ gcc -c -ffunction-sections -O1 main3.c
Run Code Online (Sandbox Code Playgroud)

的全局定义foo在目标文件中:

$ nm main3.o
0000000000000000 T foo
0000000000000000 T main
Run Code Online (Sandbox Code Playgroud)

但链接后:

$ gcc -Wl,-gc-sections,-Map=mapfile main3.o
Run Code Online (Sandbox Code Playgroud)

foo未在定义程序

$ nm a.out | grep foo; echo Done
Done
Run Code Online (Sandbox Code Playgroud)

并且函数部分定义foo被丢弃:

地图文件

...
...
Discarded input sections
 ...
 ...
 .text.foo      0x0000000000000000        0x1 main3.o
 ...
 ...
Run Code Online (Sandbox Code Playgroud)

根据 Eric Postpischil 的评论,要强制链接器保留明显未使用的函数部分,您必须告诉它假设程序引用未使用的函数,并使用链接器选项{-u|--undefined} foo

main4.c

void __attribute__((section(".mySec"))) foo(void){}

int main(void)
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果你不告诉它:

$ gcc -c main4.c
$ gcc -Wl,-gc-sections main4.o
$ nm a.out | grep foo; echo Done
Done
Run Code Online (Sandbox Code Playgroud)

foo程序中没有定义。如果你告诉它:

$ gcc -c main4.c
$ gcc -Wl,-gc-sections,--undefined=foo main4.o
$ nm a.out | grep foo; echo Done
0000000000001191 T foo
Done
Run Code Online (Sandbox Code Playgroud)

它被定义。属性没有用used