如何检索用于编译给定ELF可执行文件的GCC版本?

82
我想获取用于编译给定可执行文件的GCC版本。我尝试使用readelf但没有得到信息。有什么想法吗?
5个回答

94

通常它存储在评论部分。

strings -a <binary/library> |grep "GCC: ("

返回 GCC: (GNU) X.X.X

strip -R .comment <binary>
strings -a <binary/library> |grep "GCC: ("

没有返回任何输出

通常会通过去除.comment(以及.note)部分来减小大小

strip --strip-all -R .note -R .comment <binary>
strip --strip-unneeded -R .note -R .comment <library>
注意:busybox strings默认指定-a选项,这对于.comment节是必需的。
编辑:与Berendra Tusla的答案相反,此方法无需使用任何调试标志来编译。
二进制示例:
# echo "int main(void){}">a.c
# gcc -o a a.c -s
# strings -a a |grep GCC
GCC: (GNU) 4.3.4
# strip -R .comment a
# strings -a a |grep GCC
#

对象示例:

# gcc -c a.c -s
# strings -a a.o |grep GCC
GCC: (GNU) 4.3.4
# strip -R .comment a.o
# strings -a a |grep GCC
#

注意没有任何 -g(调试)标志的存在,以及存在剥离不必要符号的 -s 标志。GCC 信息仍然可用,除非删除 .comment 部分。如果您需要保持此信息完整,可能需要检查您的 makefile(或适用的构建脚本)以验证 $CFLAGS 中不存在 -fno-ident 并且 $STRIP 命令缺少 -R .comment。-fno-ident 防止 GCC 一开始在注释部分生成这些符号。


1
“Normally” 是什么意思?当使用默认选项进行编译时,我的编译器/版本不会存储此信息。 - Konrad Rudolph
如果我在源代码中包含字面字符串,那么这些字符串会被strings正确找到。 - Konrad Rudolph
2
objdump -s --section .comment foo.o 会在屏幕上输出注释部分的内容。 - MarcH
警告:如果不清楚,“strip -R”是具有破坏性的:“$ man strip”“-R sectionname-从输出文件中删除名为sectionname的任何部分。(...)请注意,不适当地使用此选项可能会使输出文件无法使用。” - michael
@technosaurus 任何“修改”值/文件/变量的操作,在计算机科学中都被定义为“破坏性”的;这只是一个具有精确含义的技术术语。否则,我同意该命令不会在常规意义上“销毁”文件。然而,我还要补充一点,现在随着二进制文件——jar、lib、exe等——任何修改md5/sha1的操作实际上会使文件“无法使用”,即不可信且不应使用,但并非真正“无法执行”。(许多工具(例如包管理器、构建工具)因此故意在此条件下失败。) - michael
显示剩余3条评论

23
补充其他人所说的:除非您使用调试信息进行编译,否则它不会存储在对象(或exe)文件中!(选项-g)。如果您使用调试信息进行编译,则可以使用readelf将其取回:
$ cat a.c
int main(void){ return 0; }
$ gcc a.c
$ readelf -wi a.out
$ gcc a.c -g       
$ readelf -wi a.out
Contents of the .debug_info section:

  Compilation Unit @ offset 0x0:
   Length:        0x42 (32-bit)
   Version:       2
   Abbrev Offset: 0
   Pointer Size:  4
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    < c>   DW_AT_producer    : (indirect string, offset: 0x0): GNU C 4.4.3 20100108 (prerelease)    
    <10>   DW_AT_language    : 1    (ANSI C)
    <11>   DW_AT_name        : a.c  
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x22): /tmp    
    <19>   DW_AT_low_pc      : 0x8048394    
    <1d>   DW_AT_high_pc     : 0x804839e    
    <21>   DW_AT_stmt_list   : 0x0  
 <1><25>: Abbrev Number: 2 (DW_TAG_subprogram)
    <26>   DW_AT_external    : 1    
    <27>   DW_AT_name        : (indirect string, offset: 0x27): main    
    <2b>   DW_AT_decl_file   : 1    
    <2c>   DW_AT_decl_line   : 1    
    <2d>   DW_AT_prototyped  : 1    
    <2e>   DW_AT_type        : <0x3e>   
    <32>   DW_AT_low_pc      : 0x8048394    
    <36>   DW_AT_high_pc     : 0x804839e    
    <3a>   DW_AT_frame_base  : 0x0  (location list)
 <1><3e>: Abbrev Number: 3 (DW_TAG_base_type)
    <3f>   DW_AT_byte_size   : 4    
    <40>   DW_AT_encoding    : 5    (signed)
    <41>   DW_AT_name        : int  

看到它是如何显示 GNU C 4.4.3 20100108 (prerelease)


12

我刚刚在这里读到了另外两种(可能更简单的)方法:https://unix.stackexchange.com/questions/719/can-we-get-compiler-information-from-an-elf-binary

$ readelf -p .comment /usr/lib64/flash-plugin/libflashplayer.so

String dump of section '.comment':
  [     1]  GCC: (GNU) 4.3.2 20081105 (Red Hat 4.3.2-7)
  [    2e]  GCC: (GNU) 4.3.2
...

并且

$ objdump -s --section .comment /usr/lib64/flash-plugin/libflashplayer.so

/usr/lib64/flash-plugin/libflashplayer.so:     file format elf64-x86-64

Contents of section .comment:
 0000 00474343 3a202847 4e552920 342e332e  .GCC: (GNU) 4.3.
 0010 32203230 30383131 30352028 52656420  2 20081105 (Red 
 0020 48617420 342e332e 322d3729 00004743  Hat 4.3.2-7)..GC
 0030 433a2028 474e5529 20342e33 2e320000  C: (GNU) 4.3.2..
 ...

稍微扩展一下:在我的古老的 readelf(来自 binutils 2.14)中不存在 -p 选项,因此我必须找到 .comment 部分的索引,然后像这样进行十六进制转储:readelf --hex-dump=$(readelf -S <so_file> | grep .comment | awk '{ print $1 }' | tr -d '[]') <so_file> - tkocmathla

2

这些信息并没有存储在编译后的对象(c)中。

实际上,对于C代码而言,你是完全没有办法的。但是,对于C++代码,你可以从符号版本中找到一些信息。一些来自C++运行时库的函数是特定于版本的,并在目标文件中被标记为这样。尝试这个:

readelf -Wa file.exe | grep 'GCC[[:alnum:]_.]*' --only-match | sort | uniq | tail -n 1

然而,它不会显示使用的GCC版本。它显示的是运行时提供给编译器的符号版本。通常,运行时是编译器发货的,其版本不低于上述命令显示的版本。


好的,谢谢大家!无法理解为什么这么重要的信息没有出现在ELF头文件中。我的目标实际上是一个嵌入式Linux内核。 - Ilyes Gouta

1
你可以使用elfinfo实用程序。它还支持检测Go和FPC的编译器版本,以及GCC。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接