我使用GCC 8.3编译了您的tmp.c
文件,并分别使用-flto -ffat-lto-objects
和不使用这些选项,以-S
模式(输出汇编语言)。在两种情况下,都会发出相同的常量基本定义:
其中,tmp.c
是文件名,-flto -ffat-lto-objects
是编译选项,-S
是输出汇编代码。
.comm constantFOO,18,16
.comm constantBAR,52,32
大部分由LTO发出的额外数据都会进入名为
.gnu.lto_.something
的ELF部分。 LTO模式会添加一个额外的标记对象:最初的回答。
.comm __gnu_lto_v1,1,1
在经过LTO编译的对象中出现,但在未经编译的对象中未出现。
从表面上看,这不应该对这些符号的nm
输出产生任何影响,而较低级别的工具readelf -s
为它们生成匹配的输出:
最初的回答:
该符号出现在经过LTO编译的目标文件中,而在未经编译的目标文件中未出现。这本质上不会影响这些符号的nm
输出,而较低级别的工具readelf -s
则为它们生成匹配的输出。
$ readelf -s tmp-normal.o
Symbol table '.symtab' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 4
7: 0000000000000010 18 OBJECT GLOBAL DEFAULT COM constantFOO
8: 0000000000000020 52 OBJECT GLOBAL DEFAULT COM constantBAR
$ readelf -s tmp-lto.o
Symbol table '.symtab' contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 4
6: 0000000000000000 0 SECTION LOCAL DEFAULT 5
7: 0000000000000000 0 SECTION LOCAL DEFAULT 6
8: 0000000000000000 0 SECTION LOCAL DEFAULT 7
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 9
11: 0000000000000000 0 SECTION LOCAL DEFAULT 10
12: 0000000000000000 0 SECTION LOCAL DEFAULT 12
13: 0000000000000000 0 SECTION LOCAL DEFAULT 11
14: 0000000000000010 18 OBJECT GLOBAL DEFAULT COM constantFOO
15: 0000000000000020 52 OBJECT GLOBAL DEFAULT COM constantBAR
16: 0000000000000001 1 OBJECT GLOBAL DEFAULT COM __gnu_lto_v1
因此,我认为
nm
的行为是一个bug,应该向GNU binutils的维护者报告(请参见
https://sourceware.org/binutils/ )。
至于符号值与数组长度匹配的“原始输出”,其背后的原因是通常情况下,
nm
显示的符号值是它在目标文件中所在节的偏移量。然而,普通符号不属于任何节,并且没有偏移量,因此
nm
将符号的大小打印为其值。如果我没记错的话,这是历史行为,可以追溯到添加类似FORTRAN的公共数据支持的System V的哪个版本。请注意,
readelf -s
将18和52打印为对象的
大小,并将每个符号的第三个参数(每个符号的期望对齐方式)作为它们的值。
如果您使用
-fno-common
编译,则会看到不同的输出:
$ gcc -c -fno-common tmp.c -o tmp-nc.o
$ nm tmp-nc.o
0000000000000020 B constantBAR
0000000000000000 B constantFOO
$ readelf -s tmp-nc.o | grep constant
7: 0000000000000000 18 OBJECT GLOBAL DEFAULT 3 constantFOO
8: 0000000000000020 52 OBJECT GLOBAL DEFAULT 3 constantBAR
因为现在你的数组在
.bss部分,并且具有该部分内的定义偏移量。
请注意,
char constantFOO [0x12];
定义了一个可写的0x12个
char
数组。如果您希望它实际上是常量,则需要使用
const char
。(然后它将放置在对象文件的
.rodata
部分中,
nm
和
readelf
的输出将再次不同。)