символы отладки gcc (флаг -g) против опции -rdynamic компоновщика - PullRequest
30 голосов
/ 24 декабря 2011

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

В чем разница между флагом -g, переданным в флаг gcc против компоновщика -rdynamic?Для примера кода я сделал readelf, чтобы сравнить результаты.-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 __libc_start_main@GLIBC_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 __libc_start_main@GLIBC_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 (то же самоекак ванильный вызов gcc), но больше .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 __libc_start_main@GLIBC_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 

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

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

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

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

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

1 Ответ

40 голосов
/ 24 декабря 2011

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

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

Это не символы отладки, это символы динамического компоновщика.Они не удаляются 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 __libc_start_main@GLIBC_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 __libc_start_main@GLIBC_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 __libc_start_main@GLIBC_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 не использует символы отладки

Отсюда и поведение, которое вы заметили.


Для ваших конкретных вопросов:

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