用objcopy来给elf可执行文件加上源码的git的提交版本信息
objcopy 把一个文件全部内容作为elf格式的section插入
# git rev-parse HEAD
0af0d9e7fb4cbecd5b22f00f1a136c9db933b26d
# git rev-parse --short HEAD
0af0d9e
# cat /tmp/git_commit_hash
git_commit_hash=0af0d9e7fb4cbecd5b22f00f1a136c9db933b26d
# objcopy --add-section .git_commit_hash=/tmp/git_commit_hash /usr/bin/test /tmp/test
# file /tmp/test
/tmp/test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
/tmp/test: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 001ac282 08048c00 08048c00 00000c00 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .plt 00000170 081f4ea0 081f4ea0 001acea0 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .rodata 000f1970 081f6000 081f6000 001ae000 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .typelink 0000083c 082e7970 082e7970 0029f970 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .gosymtab 00000000 082e81ac 082e81ac 002a01ac 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .gopclntab 0009640a 082e81c0 082e81c0 002a01c0 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .dynsym 000001c0 0837eb60 0837eb60 00336b60 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .rel 00000008 0837e5d0 0837e5d0 003365d0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .gnu.version 00000038 0837e5e0 0837e5e0 003365e0 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .gnu.version_r 00000070 0837e620 0837e620 00336620 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .hash 00000090 0837e6a0 0837e6a0 003366a0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
11 .rel.plt 000000b0 0837e740 0837e740 00336740 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
12 .dynstr 000001a5 0837e9a0 0837e9a0 003369a0 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
13 .got 00000004 0837f000 0837f000 00337000 2**2
CONTENTS, ALLOC, LOAD, DATA
14 .got.plt 00000064 0837f020 0837f020 00337020 2**2
CONTENTS, ALLOC, LOAD, DATA
15 .dynamic 00000098 0837f0a0 0837f0a0 003370a0 2**2
CONTENTS, ALLOC, LOAD, DATA
16 .noptrdata 00015d0c 0837f140 0837f140 00337140 2**5
CONTENTS, ALLOC, LOAD, DATA
17 .data 00003b54 08394e60 08394e60 0034ce60 2**5
CONTENTS, ALLOC, LOAD, DATA
18 .bss 000066c0 083989c0 083989c0 003509b4 2**5
ALLOC
19 .noptrbss 0000d860 0839f080 0839f080 003509b4 2**5
ALLOC
20 .interp 00000013 08048bed 08048bed 00000bed 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
21 .tbss 00000008 00000000 00000000 003509b4 2**2
ALLOC, THREAD_LOCAL
22 .debug_abbrev 000000fd 00000000 00000000 003509b4 2**0
CONTENTS, READONLY, DEBUGGING
23 .debug_line 00028509 00000000 00000000 00350ab1 2**0
CONTENTS, READONLY, DEBUGGING
24 .debug_frame 00025720 00000000 00000000 00378fba 2**0
CONTENTS, READONLY, DEBUGGING
25 .debug_info 00098d4b 00000000 00000000 0039e6da 2**0
CONTENTS, READONLY, DEBUGGING
26 .debug_pubnames 00031ae3 00000000 00000000 00437425 2**0
CONTENTS, READONLY, DEBUGGING
27 .debug_pubtypes 00011d87 00000000 00000000 00468f08 2**0
CONTENTS, READONLY, DEBUGGING
28 .debug_aranges 0000001c 00000000 00000000 0047ac8f 2**0
CONTENTS, READONLY, DEBUGGING
29 .git_commit_hash 00000039 00000000 00000000 0047acab 2**0
CONTENTS, READONLY
# objdump -t /tmp/test |grep git_commit
00000000 l d .git_commit_hash 00000000 .git_commit_hash
# readelf -S /tmp/test
There are 34 section headers, starting at offset 0x47ae24:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08048c00 000c00 1ac282 00 AX 0 0 16
[ 2] .plt PROGBITS 081f4ea0 1acea0 000170 04 AX 0 0 4
[ 3] .rodata PROGBITS 081f6000 1ae000 0f1970 00 A 0 0 32
[ 4] .typelink PROGBITS 082e7970 29f970 00083c 00 A 0 0 4
[ 5] .gosymtab PROGBITS 082e81ac 2a01ac 000000 00 A 0 0 1
[ 6] .gopclntab PROGBITS 082e81c0 2a01c0 09640a 00 A 0 0 32
[ 7] .dynsym DYNSYM 0837eb60 336b60 0001c0 10 A 13 0 4
[ 8] .rel REL 0837e5d0 3365d0 000008 08 A 7 0 4
[ 9] .gnu.version VERSYM 0837e5e0 3365e0 000038 02 A 7 0 2
[10] .gnu.version_r VERNEED 0837e620 336620 000070 00 A 13 2 4
[11] .hash HASH 0837e6a0 3366a0 000090 04 A 7 0 4
[12] .rel.plt REL 0837e740 336740 0000b0 08 A 7 2 1
[13] .dynstr STRTAB 0837e9a0 3369a0 0001a5 00 A 0 0 1
[14] .got PROGBITS 0837f000 337000 000004 04 WA 0 0 4
[15] .got.plt PROGBITS 0837f020 337020 000064 04 WA 0 0 4
[16] .dynamic DYNAMIC 0837f0a0 3370a0 000098 08 WA 13 0 4
[17] .noptrdata PROGBITS 0837f140 337140 015d0c 00 WA 0 0 32
[18] .data PROGBITS 08394e60 34ce60 003b54 00 WA 0 0 32
[19] .bss NOBITS 083989c0 3509b4 0066c0 00 WA 0 0 32
[20] .noptrbss NOBITS 0839f080 3509b4 00d860 00 WA 0 0 32
[21] .interp PROGBITS 08048bed 000bed 000013 00 A 0 0 1
[22] .tbss NOBITS 00000000 3509b4 000008 00 WAT 0 0 4
[23] .debug_abbrev PROGBITS 00000000 3509b4 0000fd 00 0 0 1
[24] .debug_line PROGBITS 00000000 350ab1 028509 00 0 0 1
[25] .debug_frame PROGBITS 00000000 378fba 025720 00 0 0 1
[26] .debug_info PROGBITS 00000000 39e6da 098d4b 00 0 0 1
[27] .debug_pubnames PROGBITS 00000000 437425 031ae3 00 0 0 1
[28] .debug_pubtypes PROGBITS 00000000 468f08 011d87 00 0 0 1
[29] .debug_aranges PROGBITS 00000000 47ac8f 00001c 00 0 0 1
[30] .git_commit_hash PROGBITS 00000000 47acab 000039 00 0 0 1
[31] .shstrtab STRTAB 00000000 47ace4 000140 00 0 0 1
[32] .symtab SYMTAB 00000000 47b374 01bf90 10 33 218 4
[33] .strtab STRTAB 00000000 497304 02b58f 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
# readelf -p .git_commit_hash /tmp/test
String dump of section '.git_commit_hash':
[ 0] git_commit_hash=0af0d9e7fb4cbecd5b22f00f1a136c9db933b26d
把调试符号信息抽取到独立文件
# objcopy --only-keep-debug /tmp/test /tmp/test.debuginfo
# ls -lh /tmp
-rw-r--r-- 1 root root 57 8月 26 11:50 git_commit_hash
-rwxr-xr-x 1 root root 4.8M 8月 26 13:28 test
-rwxr-xr-x 1 root root 1.5M 8月 26 13:44 test.debuginfo
生成不包含调试信息的文件
# objcopy --strip-debug /tmp/test /tmp/test.nodebuginfo
把调试信息合并到可执行文件
# objcopy --add-gnu-debuglink=/tmp/test.debuginfo /tmp/test.nodebuginfo
把一个文件作为二进制object文件,然后链接到可执行文件使用
# objcopy -I binary -O elf32-i386 -B i386 /tmp/git_commit_hash /tmp/git_commit_hash.o
# file /tmp/git_commit_hash.o
/tmp/git_commit_hash.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
# readelf -S /tmp/git_commit_hash.o
There are 5 section headers, starting at offset 0x90:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .data PROGBITS 00000000 000034 000039 00 WA 0 0 1
[ 2] .shstrtab STRTAB 00000000 00006d 000021 00 0 0 1
[ 3] .symtab SYMTAB 00000000 000158 000050 10 4 2 4
[ 4] .strtab STRTAB 00000000 0001a8 000067 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
# nm /tmp/git_commit_hash.o |grep git
00000039 D _binary__tmp_git_commit_hash_end
00000039 A _binary__tmp_git_commit_hash_size
00000000 D _binary__tmp_git_commit_hash_start
可以看到符号是用文件名加 start/end来表示开始和结尾的
# strings /tmp/git_commit_hash.o
git_commit_hash=0af0d9e7fb4cbecd5b22f00f1a136c9db933b26d
链接使用这个object文件
g++ -g -std=c++11 -O2 -o clock_gettime_test /tmp/git_commit_hash.o main.c -lrt
这样生成的elf格式没有新section 但是有这个符号
# nm clock_gettime_test
08049d84 d _DYNAMIC
080489b0 t _GLOBAL__sub_I_main
08048b90 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
08049f25 D _binary__tmp_git_commit_hash_end
00000039 A _binary__tmp_git_commit_hash_size
08049eec D _binary__tmp_git_commit_hash_start
可以看到 _binary__tmp_git_commit_hash_start 和 _binary__tmp_git_commit_hash_end 符号有正确的添加进可执行文件。 在c代码里面可以用extern 来引用这个几个符号使用。
int main(void)
{
extern char _binary__tmp_git_commit_hash_start;
extern char _binary__tmp_git_commit_hash_end;
char * p = &_binary__tmp_git_commit_hash_start;
while (p != & _binary__tmp_git_commit_hash_end) {
std::cout << *p++;
}
std::cout << std::endl;
return 0;
}
测试一下
# g++ -g -std=c++11 -O2 -o clock_gettime_test /tmp/git_commit_hash.o main.c -lrt
# ./clock_gettime_test
git_commit_hash=0af0d9e7fb4cbecd5b22f00f1a136c9db933b26d
cmake
可以在cmake里面使用 execute_process 命令的OUTPUT_VARIABLE OUTPUT_FILE 等把上述 的objcopy 命令集成的编译过程里面去。 让生成elef 自动携带 git commit log 的信息。
source file 的 property 设置 EXTERNAL_OBJECT, 告诉cmake这个一个object文件不是普通的源码文件
总结一下
- 可以用objcopy 插入一个 自定义的elf section。 这个可以在编译完成之后才进行。
- 可以用objcopy 可以把文件先编译成为object文件,然后编译出来的elf可以通过 符号访问这个文件内容。
- 可以自动生成一个c源码文件, 把git commit 的信息字符 赋值给 static char[] 数组。这样每个模块再 链接这个c文件就可以了。这个也是通用性最好的办法了。 还可以以通过编译环境里面宏里面来来来赋值: volatile const char git_commit_hash[]= GIT_COMMIT_HASH; gcc -DGIT_COMMIT_HASH=”git_commit_hash=xxxxx”