Я скомпилировал ваш tmp.c
как с -flto -ffat-lto-objects
, так и без него, в режиме -S
(язык ассемблера вывода), используя GCC 8.3.В обоих случаях выводятся одинаковые базовые определения ваших констант:
.comm constantFOO,18,16
.comm constantBAR,52,32
Большая часть дополнительных данных, излучаемых LTO, попадает в секции ELF с именем .gnu.lto_.something
.В режиме LTO добавляется дополнительный маркерный объект:
.comm __gnu_lto_v1,1,1
появляется в скомпилированном 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
является ошибкой, о которой следует сообщитьсопровождающим GNU binutils (см. https://sourceware.org/binutils/).
Что касается «оригинального вывода» со значением символа, совпадающим с длиной массива, то происходит то, что обычно значение символа, показанное nm
, является его смещением вего секция объектного файла. Общие символы, однако, не находятся ни в одном разделе и не имеют смещения, поэтому nm
печатает размер символа в качестве его значения. Это, IIRC, историческое поведение, уходящее полностью назадк любой итерации System V. добавлена поддержка общих данных, подобных FORTRAN. Обратите внимание, как readelf -s
печатает 18 и 52 как размеры объектов и третий аргументвведите .comm
(желаемое выравнивание каждого символа) в качестве их значений.
Если вы скомпилируете с -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
снова будут другими.)