gcc

非侵入式获取gcc编译选项

Posted by Shi Hai's Blog on December 9, 2021

本文背景

由于需要对部门内以C语言服务进行编译参数排查,确保C语言构建项目配置了安全编译选项,如:-fPIC等。当然挨个去看构建工程可以很直观,但是服务太多,本人太懒,所以想看看是否有比较自动化的方式能从构建服务中直接获取各个服务的构建参数。因此,本文尝试寻找一种非侵入式代码方式即可获得各服务执行gcc编译所配置的参数。

技术方案探索

-frecord-gcc-switches

此参数是gcc自带的编译参数。具体的介绍可以看gcc文档描述。具体介绍如下:

This switch causes the command line used to invoke the compiler to be recorded into the object file that is being
created.  
This switch is only implemented on some targets and the exact format of the recording is target and binary file format dependent, but it usually takes the form of a section containing ASCII text.  
This switch is related to the -fverbose-asm switch, but that switch only records information in the assembler output file as comments, so it never reaches the object file.  
See also -grecord-gcc-switches for another way of storing compiler options into the object file.
# 我本地有一个测试代码test_macros.c带-frecord-gcc-switches选项上进行编译,可以在此目录下获得一个a.out编译产物
gcc -frecord-gcc-switches -Wall test_macros.c
# 解析此编译文件
readelf -p .GCC.command.line a.out

# 可以获取到如下参数即为我们编译输入的相关参数
String dump of section '.GCC.command.line':
  [     0]  test_macros.c
  [     e]  -mtune=generic
  [    1d]  -march=x86-64
  [    2b]  -Wall
  [    31]  -frecord-gcc-switches

-wrapper

此参数用于调试在编译期间发生的事情,-wrapper后面我们可以接入我们想嵌入的调用代码。

Invoke all subcommands under a wrapper program.  The name of the wrapper program and its parameters are passed as a
comma separated list.  
gcc -c t.c -wrapper gdb,--args  
This invokes all subprograms of gcc under gdb --args, thus the invocation of cc1 is gdb --args cc1 ....
# 我本地有一个测试代码test_macros.c带-wrapper echo选项上进行编译
gcc -frecord-gcc-switches -Wall test_macros.c
# 获得如下输出结果,这里就可以把执行过程获取出来。
$ gcc -Wall test_macros.c -wrapper echo
/usr/local/libexec/gcc/x86_64-pc-linux-gnu/9.3.0/cc1 -quiet test_macros.c -quiet -dumpbase test_macros.c -mtune=generic -march=x86-64 -auxbase test_macros -Wall -o /tmp/ccuFvYav.s
as --64 -o /tmp/ccYIXVd5.o /tmp/ccuFvYav.s
/usr/local/libexec/gcc/x86_64-pc-linux-gnu/9.3.0/collect2 -plugin /usr/local/libexec/gcc/x86_64-pc-linux-gnu/9.3.0/liblto_plugin.so -plugin-opt=/usr/local/libexec/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper -plugin-opt=-fresolution=/tmp/cc6PM2gF.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /lib/../lib64/crt1.o /lib/../lib64/crti.o /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.3.0/crtbegin.o -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.3.0 -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../.. /tmp/ccYIXVd5.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.3.0/crtend.o /lib/../lib64/crtn.o

命令行中转

如果C项目是用makefile组织编译过程,那可以通过export CFLAGS="xxx"方式引入-frecord-gcc-switches或者-wrapper两个参数获取到编译选项。
但对于没有用到makefile组织编译工程的项目,则还是需要在编译文件中添加两个选项才能获取编译参数,这个离我们想要通过非侵入式方式直接获取编译参数还有一定举例。因此我YY了另一个思路。此思路的灵感是来源于四/七层路由转发:在系统上调用的gcc命令行完全也可以重新构造一个中转的gcc来达成相关目标,类似于安卓系统中的gcc-wrapp

参考文献