gcc 6.3包含太多调试符号,尤其是extern全局声明

时间:2018-08-13 20:56:42

标签: c gcc debug-symbols

这个问题may have been asked,但是我有一个玩具程序可以很好地说明这个问题。

简介

使用gcc 4.9用gcc -g进行编译时,所包含的调试符号(尤其是对于外部声明的全局变量而言)是预期的和熟悉的数量。切换到gcc 6.3时,所包含的调试符号数量似乎过多且多余。尝试使用以下标志:

  • -feliminate-unused-debug-symbols
  • -feliminate-unused-debug-types
  • -femit-struct-debug-baseonly

没有差异。这在使用非常大的代码库时会产生一个问题,该代码库会产生较大的库归档文件,而新的编译器与较旧的编译器相比,库的归档文件最终(文件大小)要大几个数量级。

示例程序

a.c:

#include <stdlib.h>
#include <stdio.h>
#include "big.h"

int main (void) {
    printf("%s\n", big_time);
    return 0;
}

b.c:

#include "big.h"

char* big_time = "BIG_TIME";
char* big_mama = "BIG_MAMA";

c.c:

#include "big.h"

char* big_stuf = "BIG_STUF";
char* big_leee = "BIG_LEEE";

big.h:

#ifndef _BIG_H_
#define _BIG_H_

extern char* big_time;
extern char* big_mama;
extern char* big_stuf;
extern char* big_leee;

#endif // _BIG_H_

Makefile:

CC = gcc
CFLAGS  = -g
CFLAGS += -feliminate-unused-debug-symbols

test: prog
        @for i in a.o b.o c.o prog; \
                do echo $$i; \
                readelf --debug-dump $$i | grep big_; \
        done

prog: a.o b.o c.o
        gcc $(CFLAGS) -o $@ $^

clean:
        @$(RM) a.o b.o c.o prog

使用gcc 4.9的结果

$ make
gcc -g -feliminate-unused-debug-symbols   -c -o a.o a.c
gcc -g -feliminate-unused-debug-symbols   -c -o b.o b.c
gcc -g -feliminate-unused-debug-symbols   -c -o c.o c.c
gcc -g -feliminate-unused-debug-symbols -o prog a.o b.o c.o
a.o
    <a5>   DW_AT_name        : (indirect string, offset: 0xb4): big_time
  0x000000b0 696e7400 6269675f 74696d65 00756e73 int.big_time.uns
b.o
    <1e>   DW_AT_name        : (indirect string, offset: 0x9): big_time
    <40>   DW_AT_name        : (indirect string, offset: 0x0): big_mama
  0x00000000 6269675f 6d616d61 00626967 5f74696d big_mama.big_tim
c.o
    <1e>   DW_AT_name        : (indirect string, offset: 0x9): big_stuf
    <40>   DW_AT_name        : (indirect string, offset: 0x0): big_leee
  0x00000000 6269675f 6c656565 00626967 5f737475 big_leee.big_stu
prog
    <a5>   DW_AT_name        : (indirect string, offset: 0x95): big_time
    <ce>   DW_AT_name        : (indirect string, offset: 0x95): big_time
    <f0>   DW_AT_name        : (indirect string, offset: 0xd7): big_mama
    <123>   DW_AT_name        : (indirect string, offset: 0xe9): big_stuf
    <145>   DW_AT_name        : (indirect string, offset: 0xe0): big_leee
  0x00000090 20696e74 00626967 5f74696d 6500756e  int.big_time.un
  0x000000d0 7a657479 70650062 69675f6d 616d6100 zetype.big_mama.
  0x000000e0 6269675f 6c656565 00626967 5f737475 big_leee.big_stu

使用gcc 6.3的结果

$ make
gcc -g -feliminate-unused-debug-symbols   -c -o a.o a.c
gcc -g -feliminate-unused-debug-symbols   -c -o b.o b.c
gcc -g -feliminate-unused-debug-symbols   -c -o c.o c.c
gcc -g -feliminate-unused-debug-symbols -o prog a.o b.o c.o
a.o
    <312>   DW_AT_name        : (indirect string, offset: 0x20e): big_time
    <31d>   DW_AT_name        : (indirect string, offset: 0x1eb): big_mama
    <328>   DW_AT_name        : (indirect string, offset: 0x286): big_stuf
    <333>   DW_AT_name        : (indirect string, offset: 0x90): big_leee
  0x00000090 6269675f 6c656565 006c6f6e 6720696e big_leee.long in
  0x000001e0 74005f49 4f5f4649 4c450062 69675f6d t._IO_FILE.big_m
  0x00000280 6636345f 74006269 675f7374 7566005f f64_t.big_stuf._
b.o
    <1e>   DW_AT_name        : (indirect string, offset: 0x9): big_time
    <36>   DW_AT_name        : (indirect string, offset: 0x0): big_mama
    <41>   DW_AT_name        : (indirect string, offset: 0x12): big_stuf
    <4c>   DW_AT_name        : (indirect string, offset: 0x1b): big_leee
  0x00000000 6269675f 6d616d61 00626967 5f74696d big_mama.big_tim
  0x00000010 65006269 675f7374 75660062 69675f6c e.big_stuf.big_l
c.o
    <1e>   DW_AT_name        : (indirect string, offset: 0x9): big_time
    <36>   DW_AT_name        : (indirect string, offset: 0x0): big_mama
    <41>   DW_AT_name        : (indirect string, offset: 0x12): big_stuf
    <4c>   DW_AT_name        : (indirect string, offset: 0x1b): big_leee
  0x00000000 6269675f 6d616d61 00626967 5f74696d big_mama.big_tim
  0x00000010 65006269 675f7374 75660062 69675f6c e.big_stuf.big_l
prog
    <312>   DW_AT_name        : (indirect string, offset: 0x1d2): big_time
    <31d>   DW_AT_name        : (indirect string, offset: 0x1af): big_mama
    <328>   DW_AT_name        : (indirect string, offset: 0x245): big_stuf
    <333>   DW_AT_name        : (indirect string, offset: 0x84): big_leee
    <379>   DW_AT_name        : (indirect string, offset: 0x1d2): big_time
    <391>   DW_AT_name        : (indirect string, offset: 0x1af): big_mama
    <39c>   DW_AT_name        : (indirect string, offset: 0x245): big_stuf
    <3a7>   DW_AT_name        : (indirect string, offset: 0x84): big_leee
    <3f2>   DW_AT_name        : (indirect string, offset: 0x1d2): big_time
    <40a>   DW_AT_name        : (indirect string, offset: 0x1af): big_mama
    <415>   DW_AT_name        : (indirect string, offset: 0x245): big_stuf
    <420>   DW_AT_name        : (indirect string, offset: 0x84): big_leee
  0x00000080 656e6400 6269675f 6c656565 005f666c end.big_leee._fl
  0x000001d0 73006269 675f7469 6d65002f 686f6d65 s.big_time./home
  0x00000240 36345f74 00626967 5f737475 66005f49 64_t.big_stuf._I

GCC版本信息

gcc 4.9:

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10+deb8u1' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.9.2 (Debian 4.9.2-10+deb8u1)

gcc 6.3:

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/6/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 6.3.0-18+deb9u1' --with-bugurl=file:///usr/share/doc/gcc-6/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-6 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --enable-default-pie --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-6-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-6-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-6-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-target-system-zlib --enable-objc-gc=auto --enable-multiarch --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)

观察和评论

在两种情况下,最终prog可执行文件中包含的符号都是每个目标文件中的符号的总和(正如预期的那样),因此我认为行为没有错。但是关键的区别在于每个目标文件。对于较旧的编译器,仅包含与模块相关的符号。在较新的版本中,即使在该模块中未使用或引用该变量,也可以获得每个外部声明的变量的每个符号。这对最终可执行文件的调试信息量具有乘积作用。

此玩具程序模仿的是正在使用的大型第三方SDK中看到的源代码结构。此问题的最终结果是图书馆档案文件比以前大了几个数量级。

-g调试信息需要保留,因此不能排除该信息或使用strip。使用-g1确实可以将调试信息的数量降低到正常(或更小)数量,但是一旦有用的调试信息进入gdb内,其代价就是。

Obi Wan Kenobi,请帮助我。你是我唯一的希望。

0 个答案:

没有答案