gcc -v

对比 ubuntu 16.04 LTSubuntu 16.10gcc -v 命令,可以看出配置有区别:

16.04 LTS:

Configured with: ../src/configure -v 
--with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.4' 
--with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs 
--enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ 
--prefix=/usr
--program-suffix=-5 
--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 
--with-system-zlib
--disable-browser-plugin
--enable-java-awt=gtk 
--enable-gtk-cairo 
--with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre
--enable-java-home 
--with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64
--with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64
--with-arch-directory=amd64
--with-ecj-jar=/usr/share/java/eclipse-ecj.jar
--enable-objc-gc
--enable-multiarch
--disable-werror
--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

16.10:

Configured with: ../src/configure -v
--with-pkgversion='Ubuntu 6.2.0-5ubuntu12'
--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
--enable-objc-gc 
--enable-multiarch
--disable-werror
--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

通过比较得知,16.10 多了两个配置参数:--program-prefix=x86_64-linux-gnu---enable-default-pie,其余参数均一致。重要的就是 --enable-default-pie,这个开启了 PIE (Position Independent Executable,位置无关的执行),这个特性和 PIC (Position Independent Code) 一致,以及 ASLR (Address Space Layout Randomization)。

关于 PIC,可以参考此文 PIC

详情还可以参考发行注记:

这个新的改变是出于系统安全考虑,很多发行版都在将来会引进。但它会影响到调试符号信息,因为程序每次执行时地址都是随机的,也就是说各个函数的地址都是随机的。例如简单的 Hello world! 的可执行程序:

$ readelf -h a.out
ELF Header:
Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
Class:                             ELF64
Data:                              2's complement, little endian
Version:                           1 (current)
OS/ABI:                            UNIX - System V
ABI Version:                       0
Type:                              DYN (Shared object file)
Machine:                           Advanced Micro Devices X86-64
Version:                           0x1
Entry point address:               0x570
Start of program headers:          64 (bytes into file)
Start of section headers:          8808 (bytes into file)
Flags:                             0x0
Size of this header:               64 (bytes)
Size of program headers:           56 (bytes)
Number of program headers:         9
Size of section headers:           64 (bytes)
Number of section headers:         34
Section header string table index: 31

可以看到 Entry point address 不再是开始于 0x400000

由于函数地址随机,意味着诸如 addr2line 等工具根据地址跟踪函数名的方式会失效。因此,如果不需要此特性,可以有两种方法:

  • 编译时给 gcc 指定编译参数:-fno-pie
  • 使用 ubuntu 16.04 LTS,此版本未开启此选项,而且是长期支持版,支持到2021年4月。