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

Big*_*Cat 3 c linux gcc

我想在编译时从代码中删除未使用的函数。然后我写了一些代码(main.c):

#include <stdio.h>

const char *get1();

int main()
{
    puts( get1() );
}
Run Code Online (Sandbox Code Playgroud)

和 getall.c:

const char *get1()
{
    return "s97symmqdn-1";
}


const char *get2()
{
    return "s97symmqdn-2";
}

const char *get3()
{
    return "s97symmqdn-3";
}
Run Code Online (Sandbox Code Playgroud)

生成文件

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
Run Code Online (Sandbox Code Playgroud)

在 run 之后make test1 && objdump --sym a.out | grep get,我只找到了接下来的 2 行输出:

0000000000000000 l    df *ABS*  0000000000000000              getall.c
0000000000400535 g     F .text  000000000000000b              get1
Run Code Online (Sandbox Code Playgroud)

我猜get2get3被删除了。但是当我通过 vim 打开 a.out 时,我发现s97symmqdn-1 s97symmqdn-2 s97symmqdn-3存在。功能get2 get3真的去掉了吗?如何删除符号s97symmqdn-2 s97symmqdn-3?感谢你的回复。我的系统是centos7,gcc版本是4.8.5

Mik*_*han 6

编译选项-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
Run Code Online (Sandbox Code Playgroud)

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

未使用的函数get2get3可执行文件中不存在:

$ nm a.out | grep get
0000000000000657 T get1
Run Code Online (Sandbox Code Playgroud)

并且 mapfile 显示未使用的函数部分.text.get2和分别.text.get3在其中定义get2get3在链接中被丢弃:

地图文件 (1)

...
Discarded input sections
...
 .text.get2     0x0000000000000000        0xd getall.o
 .text.get3     0x0000000000000000        0xd getall.o
...
Run Code Online (Sandbox Code Playgroud)

尽管如此,正如您发现的,所有三个字符串文字"s97symmqdn-(1|2|3)" 都在程序中:

$ strings a.out | egrep 's97symmqdn-(1|2|3)'
s97symmqdn-1
s97symmqdn-2
s97symmqdn-3
Run Code Online (Sandbox Code Playgroud)

这是因为-fdata-sections仅适用于__attribute__ ((__section__("name")))适用于1的相同数据对象 ,即具有静态存储持续时间变量定义。它不适用于匿名字符串文字,例如您的 . 它们都像往常一样放置在该部分中,我们在那里找到它们:"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.
Run Code Online (Sandbox Code Playgroud)

--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;
}
Run Code Online (Sandbox Code Playgroud)

如果我们重新编译并重新链接该更改,我们会看到:

地图文件 (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
...
Run Code Online (Sandbox Code Playgroud)

现在有两个新丢弃的数据部分,其中包含我们不需要的两个字符串文字,正如我们在目标文件中看到的:

$ 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.
Run Code Online (Sandbox Code Playgroud)

和:

$ 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.
Run Code Online (Sandbox Code Playgroud)

"s97symmqdn-1"现在只有引用的字符串出现在程序中的任何地方:

$ strings a.out | egrep 's97symmqdn-(1|2|3)'
s97symmqdn-1
Run Code Online (Sandbox Code Playgroud)

它是程序中唯一的字符串.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.
Run Code Online (Sandbox Code Playgroud)


[1]同样地,-function-sections具有与合格的每一个函数的定义相同的效果foo__attribute__ ((__section__(".text.foo")))