-ffunction-sections -fdata-sections和--gc-sections不起作用吗?

时间:2019-03-05 05:52:11

标签: c linux gcc

我想在编译时从代码中删除未使用的函数。然后我写一些代码(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

我猜想get2get3已删除。但是,当我通过vim打开a.out时,发现s97symmqdn-1 s97symmqdn-2 s97symmqdn-3存在。 函数get2 get3确实被删除了吗?如何删除符号s97symmqdn-2 s97symmqdn-3?谢谢您的回复。 我的系统是centos7,gcc版本是4.8.5

1 个答案:

答案 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

在其中我还要求链接器的映射文件。

可执行文件中缺少未使用的功能get2get3

$ nm a.out | grep get
0000000000000657 T get1

和映射文件显示未使用的功能节.text.get2.text.get3,其中get2get3是 分别定义的已被丢弃在链接中:

映射文件(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.


[1]同样,-function-sections具有与限定 每个带有foo

的函数__attribute__ ((__section__(".text.foo")))的定义