Неверный вывод `nm` для файловых объектов GCC LTO fat - PullRequest
2 голосов
/ 12 марта 2019

Если у меня есть tmp.c:

char constantFOO[0x12];
char constantBAR[0x34];

вижу gcc -c tmp.c -o tmp.o && nm tmp.o показывает

0000000000000034 C constantBAR
0000000000000012 C constantFOO

Но если я скомпилирую с -flto -ffat-lto-objects, nm выведет нули для значений символов:

00000000 C constantBAR
00000000 C constantFOO

Я могу указать значения 34 и 12 в шестнадцатеричном формате обоих файлов .o.

Мои вопросы

  1. Ожидается ли поведение nm в толстом файле LTO? Я просто даю ему ввод, который он не ожидает, и выводит мусор?

  2. Чем объясняется исходный вывод (значение символа соответствует длине неинициализированного массива)? Этот вопрос , похоже, не помог в вопросе о массивах, но, возможно, я неправильно понял.

1 Ответ

2 голосов
/ 12 марта 2019

Я скомпилировал ваш 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 снова будут другими.)

...