Подтвердить что ты не робот

Gcc debug symbols (-g flag) vs linker -rdynamic option

glibc предоставляет backtrace() и backtrace_symbols(), чтобы получить трассировку стека запущенной программы. Но для этого для работы программа должна быть построена с флагом компоновщика -rdynamic.

В чем разница между флагом -g, переданным в флаг gcc vs linker -rdynamic? Для примера кода я действительно прочитал, чтобы сравнить выходы. -rdynamic похоже, дает больше информации в разделе Symbol table '.dynsym' Но я не совсем уверен, что такое дополнительная информация.

Даже если я strip бинарная программа, построенная с использованием -rdynamic, backtrace_symbols() продолжает работать.

Когда strip удаляет все символы из двоичного кода, почему он оставляет все, что было добавлено флагом -rdynamic?

Изменить: последующие вопросы, основанные на ответе Мата ниже.

Для того же кода кода, который вы взяли, это разница, которую я вижу с помощью -g и -rdynamic

без каких-либо опций.

    Symbol table '.dynsym' contains 4 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000000000   218 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)
         2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
         3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    Symbol table '.symtab' contains 70 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 
         2: 000000000040021c     0 SECTION LOCAL  DEFAULT    2 

с -g имеется больше разделов, больше записей в таблице .symtab, но .dynsym остается тем же.

      [26] .debug_aranges    PROGBITS         0000000000000000  0000095c
           0000000000000030  0000000000000000           0     0     1
      [27] .debug_pubnames   PROGBITS         0000000000000000  0000098c
           0000000000000023  0000000000000000           0     0     1
      [28] .debug_info       PROGBITS         0000000000000000  000009af
           00000000000000a9  0000000000000000           0     0     1
      [29] .debug_abbrev     PROGBITS         0000000000000000  00000a58
           0000000000000047  0000000000000000           0     0     1
      [30] .debug_line       PROGBITS         0000000000000000  00000a9f
           0000000000000038  0000000000000000           0     0     1
      [31] .debug_frame      PROGBITS         0000000000000000  00000ad8
           0000000000000058  0000000000000000           0     0     8
      [32] .debug_loc        PROGBITS         0000000000000000  00000b30
           0000000000000098  0000000000000000           0     0     1

    Symbol table '.dynsym' contains 4 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000000000   218 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)
         2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
         3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

    Symbol table '.symtab' contains 77 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
         1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 

с -rdynamic нет дополнительных разделов отладки, записи .symtab - 70 (так же, как invalid gcc invalid), но больше .dynsym записей.

    Symbol table '.dynsym' contains 19 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
         1: 0000000000000000   218 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)
         2: 00000000005008e8     0 OBJECT  GLOBAL DEFAULT  ABS _DYNAMIC
         3: 0000000000400750    57 FUNC    GLOBAL DEFAULT   12 __libc_csu_fini   
         4: 00000000004005e0     0 FUNC    GLOBAL DEFAULT   10 _init
         5: 0000000000400620     0 FUNC    GLOBAL DEFAULT   12 _start
         6: 00000000004006f0    86 FUNC    GLOBAL DEFAULT   12 __libc_csu_init   
         7: 0000000000500ab8     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
         8: 00000000004006de    16 FUNC    GLOBAL DEFAULT   12 main
         9: 0000000000500aa0     0 NOTYPE  WEAK   DEFAULT   23 data_start
        10: 00000000004007c8     0 FUNC    GLOBAL DEFAULT   13 _fini
        11: 00000000004006d8     6 FUNC    GLOBAL DEFAULT   12 foo
        12: 0000000000500ab8     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
        13: 0000000000500a80     0 OBJECT  GLOBAL DEFAULT  ABS _GLOBAL_OFFSET_TABLE_
        14: 0000000000500ac0     0 NOTYPE  GLOBAL DEFAULT  ABS _end
        15: 00000000004007d8     4 OBJECT  GLOBAL DEFAULT   14 _IO_stdin_used
        16: 0000000000500aa0     0 NOTYPE  GLOBAL DEFAULT   23 __data_start
        17: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
        18: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__    

    Symbol table '.symtab' contains 70 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
         1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 
         2: 000000000040021c     0 SECTION LOCAL  DEFAULT    2 

Теперь это те вопросы, которые у меня есть.

  • В gdb вы можете сделать bt, чтобы получить bactrace. Если это работает только с -g для чего нам нужно -rdynamic для работы backtrace_symbols?

  • Сравнивая дополнения с .symtab с -g и добавлениями к .dynsym с -rdynamic, они не совсем одинаковы. Предоставляет ли одна из них лучшую информацию об отладке по сравнению с другой? FWIW, размер произведенного результата выглядит следующим образом: с -g > с -rdynamic > без опции

  • Что такое использование .dynsym? Это все символы, экспортируемые этим двоичным кодом? В этом случае почему foo входит в .dynsym, потому что мы не компилируем код как библиотеку.

  • Если я свяжу свой код с использованием всех статических библиотек, то для работы backtrace_symbols не требуется -rdynamic?

4b9b3361

Ответ 1

Согласно документам:

Это дает ссылку компоновщику добавлять все символы, а не только используемые, в таблицу динамических символов.

Это не символы отладки, они являются динамическими символами-линкерами. Они не удаляются с помощью strip, поскольку он (в большинстве случаев) прерывает исполняемый файл - они используются компоновщиком времени выполнения для выполнения заключительной стадии ссылки вашего исполняемого файла.

Пример:

$ cat t.c
void foo() {}
int main() { foo(); return 0; }

Компилировать и связывать без -rdynamic (и без оптимизации, очевидно)

$ gcc -O0 -o t t.c
$ readelf -s t

Symbol table '.dynsym' contains 3 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

Symbol table '.symtab' contains 50 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400270     0 SECTION LOCAL  DEFAULT    1 
....
    27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS t.c
    28: 0000000000600e14     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    29: 0000000000600e40     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC

Таким образом, исполняемый файл имеет .symtab со всем. Но обратите внимание, что .dynsym вообще не упоминает foo - в нем есть простые вещи. Для работы backtrace_symbols недостаточно информации. Он опирается на информацию, представленную в этом разделе, для соответствия кодовых адресов с именами функций.

Теперь скомпилируйте с помощью -rdynamic:

$ gcc -O0 -o t t.c -rdynamic
$ readelf -s t

Symbol table '.dynsym' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     5: 0000000000601008     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
     6: 0000000000400734     6 FUNC    GLOBAL DEFAULT   13 foo
     7: 0000000000601028     0 NOTYPE  GLOBAL DEFAULT  ABS _end
     8: 0000000000601008     0 NOTYPE  WEAK   DEFAULT   24 data_start
     9: 0000000000400838     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    10: 0000000000400750   136 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    11: 0000000000400650     0 FUNC    GLOBAL DEFAULT   13 _start
    12: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    13: 000000000040073a    16 FUNC    GLOBAL DEFAULT   13 main
    14: 0000000000400618     0 FUNC    GLOBAL DEFAULT   11 _init
    15: 00000000004007e0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    16: 0000000000400828     0 FUNC    GLOBAL DEFAULT   14 _fini

Symbol table '.symtab' contains 50 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400270     0 SECTION LOCAL  DEFAULT    1 
....
    27: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS t.c
    28: 0000000000600e14     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    29: 0000000000600e40     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC

То же самое для символов в .symtab, но теперь foo имеет символ в разделе динамического символа (и там появляется и ряд других символов). Это делает работу backtrace_symbols - теперь у нее достаточно информации (в большинстве случаев) для сопоставления кодовых адресов с именами функций.

Разделите это:

$ strip --strip-all t
$ readelf -s t

Symbol table '.dynsym' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [email protected]_2.2.5 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
     5: 0000000000601008     0 NOTYPE  GLOBAL DEFAULT   24 __data_start
     6: 0000000000400734     6 FUNC    GLOBAL DEFAULT   13 foo
     7: 0000000000601028     0 NOTYPE  GLOBAL DEFAULT  ABS _end
     8: 0000000000601008     0 NOTYPE  WEAK   DEFAULT   24 data_start
     9: 0000000000400838     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
    10: 0000000000400750   136 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
    11: 0000000000400650     0 FUNC    GLOBAL DEFAULT   13 _start
    12: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    13: 000000000040073a    16 FUNC    GLOBAL DEFAULT   13 main
    14: 0000000000400618     0 FUNC    GLOBAL DEFAULT   11 _init
    15: 00000000004007e0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    16: 0000000000400828     0 FUNC    GLOBAL DEFAULT   14 _fini
$ ./t
$

Нет .symtab, но динамическая таблица символов все еще существует, и выполняется исполняемый файл. Так что backtrace_symbols все еще работает.

Разделите таблицу динамических символов:

$ strip -R .dynsym t
$ ./t
./t: relocation error: ./t: symbol , version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

... и вы получите сломанный исполняемый файл.

Интересно, для чего используются .symtab и .dynsym: Внутри таблиц символов ELF. Одной из замечаний является то, что .symtab не требуется во время выполнения, поэтому он отбрасывается загрузчиком. Этот раздел не сохраняется в памяти процесса. .dynsym, с другой стороны, необходим во время выполнения, поэтому он сохраняется в образе процесса. Таким образом, он доступен для таких вещей, как backtrace_symbols для сбора информации о текущем процессе изнутри.

Короче говоря:

  • динамические символы не делятся на strip, так как это сделает исполняемый невыгружаемый
  • backtrace_symbols нужны динамические символы, чтобы выяснить, какой код принадлежит той функции
  • backtrace_symbols не использует отладочные символы

Следовательно, поведение, которое вы заметили.


По вашим конкретным вопросам:

  • gdb - отладчик. Он использует отладочную информацию в исполняемом файле и библиотеках для отображения соответствующей информации. Он намного сложнее, чем backtrace_symbols, и проверяет фактические файлы на вашем диске в дополнение к текущему процессу. backtrace_symbols нет, он полностью находится в процессе - поэтому он не может обращаться к разделам, которые не загружаются в исполняемый образ. Отладочные разделы не загружаются в образ среды выполнения, поэтому они не могут их использовать.
  • .dynsym не является частью отладки. Это раздел, используемый динамическим компоновщиком. .symbtab также не является частью отладки, но он может использоваться отладчиком, имеющим доступ к исполняемым (и библиотечным) файлам. -rdynamic не генерирует отладочные разделы, а только эту расширенную таблицу динамических символов. Выполняемый рост из -rdynamic полностью зависит от количества символов в этом исполняемом (и соображениях выравнивания/дополнения). Он должен быть значительно меньше -g.
  • За исключением статически связанных двоичных файлов, исполняемым файлам требуются внешние зависимости, разрешенные во время загрузки. Как ссылка printf и некоторые процедуры запуска приложений из библиотеки C. Эти внешние символы должны быть указаны где-то в исполняемом файле: для этого используется .dynsym, и поэтому exe имеет .dynsym, даже если вы не указали -rdynamic. Когда вы это укажете, компоновщик добавляет другие символы, которые не нужны для работы процесса, но могут использоваться такими вещами, как backtrace_symbols.
  • backtrace_symbols не будет разрешать имена функций, если вы статически ссылаетесь. Даже если вы укажете -rdynamic, раздел .dynsym не будет отправлен в исполняемый файл. Таблицы символов не загружаются в исполняемый образ, поэтому backtrace_symbols не может сопоставлять кодовые адреса с символами.