我想在编译时从代码中删除未使用的函数。然后我写一些代码(main.c):
#include <stdio.h>
const char *get1();
int main()
{
puts( get1() );
}
和getall.c:
const char *get1()
{
return "s97symmqdn-1";
}
const char *get2()
{
return "s97symmqdn-2";
}
const char *get3()
{
return "s97symmqdn-3";
}
Makefile
test1 :
rm -f a.out *.o *.a
gcc -ffunction-sections -fdata-sections -c main.c getall.c
ar cr libgetall.a getall.o
gcc -Wl,--gc-sections main.o -L. -lgetall
运行make test1 && objdump --sym a.out | grep get
之后,我仅找到接下来的两行输出:
0000000000000000 l df *ABS* 0000000000000000 getall.c
0000000000400535 g F .text 000000000000000b get1
我猜想get2
和get3
已删除。但是,当我通过vim打开a.out时,发现s97symmqdn-1 s97symmqdn-2 s97symmqdn-3
存在。
函数get2
get3
确实被删除了吗?如何删除符号s97symmqdn-2 s97symmqdn-3
?谢谢您的回复。
我的系统是centos7,gcc版本是4.8.5
答案 0 :(得分:3)
编译选项-ffunction-sections -fdata-sections
和链接选项--gc-sections
在您的示例中正常工作。您的静态库是多余的,因此它可以
简化为:
$ gcc -ffunction-sections -fdata-sections -c main.c getall.c
$ gcc -Wl,--gc-sections main.o getall.o -Wl,-Map=mapfile
在其中我还要求链接器的映射文件。
可执行文件中缺少未使用的功能get2
和get3
:
$ nm a.out | grep get
0000000000000657 T get1
和映射文件显示未使用的功能节.text.get2
和.text.get3
,其中get2
和get3
是
分别定义的已被丢弃在链接中:
映射文件(1)
...
Discarded input sections
...
.text.get2 0x0000000000000000 0xd getall.o
.text.get3 0x0000000000000000 0xd getall.o
...
尽管如此,正如您所发现的,所有三个字符串文字"s97symmqdn-(1|2|3)"
在程序中:
$ strings a.out | egrep 's97symmqdn-(1|2|3)'
s97symmqdn-1
s97symmqdn-2
s97symmqdn-3
这是因为-fdata-sections
仅适用于与
__attribute__ ((__section__("name")))
适用于 1 ,即定义
包含static storage duration的变量。它不适用于像您这样的匿名字符串文字
"s97symmqdn-(1|2|3)"
。它们都照常放置在.rodata
部分中,
在那里我们找到了它们:
$ objdump -s -j .rodata a.out
a.out: file format elf64-x86-64
Contents of section .rodata:
06ed 73393773 796d6d71 646e2d31 00733937 s97symmqdn-1.s97
06fd 73796d6d 71646e2d 32007339 3773796d symmqdn-2.s97sym
070d 6d71646e 2d3300 mqdn-3.
--gc-sections
不允许链接程序从程序中丢弃.rodata
因为它不是未使用的部分:它包含"s97symmqdn-1"
,被引用
在程序中由get1
以及未引用的字符串"s97symmqdn-2"
和"s97symmqdn-3"
修复
要将这三个字符串文字分成不同的数据部分,您可以 需要将它们分配给不同的命名对象,例如
getcall.c(2)
const char *get1()
{
static const char s[] = "s97symmqdn-1";
return s;
}
const char *get2()
{
static const char s[] = "s97symmqdn-2";
return s;
}
const char *get3()
{
static const char s[] = "s97symmqdn-3";
return s;
}
如果我们重新编译并重新链接该更改,则会看到:
映射文件(2)
...
Discarded input sections
...
.text.get2 0x0000000000000000 0xd getall.o
.text.get3 0x0000000000000000 0xd getall.o
.rodata.s.1797
0x0000000000000000 0xd getall.o
.rodata.s.1800
0x0000000000000000 0xd getall.o
...
现在有两个新的废弃数据节,其中包含 我们不需要在目标文件中看到的两个字符串文字:
$ objdump -s -j .rodata.s.1797 getall.o
getall.o: file format elf64-x86-64
Contents of section .rodata.s.1797:
0000 73393773 796d6d71 646e2d32 00 s97symmqdn-2.
和:
$ objdump -s -j .rodata.s.1800 getall.o
getall.o: file format elf64-x86-64
Contents of section .rodata.s.1800:
0000 73393773 796d6d71 646e2d33 00 s97symmqdn-3.
现在只有引用的字符串"s97symmqdn-1"
出现在程序中的任何位置:
$ strings a.out | egrep 's97symmqdn-(1|2|3)'
s97symmqdn-1
,它是程序.rodata
中的唯一字符串:
$ objdump -s -j .rodata a.out
a.out: file format elf64-x86-64
Contents of section .rodata:
06f0 73393773 796d6d71 646e2d31 00 s97symmqdn-1.
-function-sections
具有与限定
每个带有foo
的函数
__attribute__ ((__section__(".text.foo")))
的定义