Что происходит во время ошибки "Перемещение имеет неверный индекс символа"?

Вот тест, воспроизводящий проблему:

$ echo "void whatever() {}" > prog.c
$ gcc prog.c

Это приводит к следующей ошибке в GCC 4.8.4:

    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 1 has invalid symbol index 12
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 2 has invalid symbol index 2
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 3 has invalid symbol index 2
    ... etc ...
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 18 has invalid symbol index 13
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 19 has invalid symbol index 21
    /usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): relocation 0 has invalid symbol index 2
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
    (.text+0x20): undefined reference to `main'
    collect2: error: ld returned 1 exit status

Обратите внимание, что в GCC 6.2.0 ошибки, связанные с этим вопросом, исчезают, и вместо этого он производит только:

/usr/lib/x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status

Об этом сообщается несколько раз рядом пользователей, в переполнении стека и в других местах.

Я хотел бы понять эту ошибку, а не решить ее (она уже решена).

Эта ошибка возникает при выполнении gcc-4.8 prog.c без функции main() внутри prog.c.


Я выполнил текстовый поиск этой ошибки в пакете binutils-source. Утомленный googling дал мне только одну полезную ссылку, помогающую мне лучше понять концепцию обработки переводов.

Количество ошибок, по-видимому, не зависит от программы, что предполагает, что рассматриваемые перемещения не возникают в этом файле, а как прямой результат отсутствия функции main(). Я предположил, что 3 из этих перемещений с неправильными индексами могут быть для main(), argc и argv, но многие остаются, и это всего лишь недоказанная гипотеза.

Это совершенно выше моей головы, и любая информация, которая поможет мне лучше понять ее или что изменилось в более поздних версиях GCC, будет тепло приветствоваться.

Ответ 1

C Функции программы (Unix-подобные)

  • каждая программа компилируется отдельно в формат elf
  • c программа может использовать ссылку на внешнюю переменную/функцию, которая позже связана
  • main не является началом программы, как вы изначально предполагали, c lib имеет стартовую программу (crt1.o), которая имеет программу _start, которая выведет наш main и выполнит очистку после main
  • заключает вышеприведенный оператор, мы можем знать, что даже очень простая программа, как показано в OP, должна быть связана

Формат ELF

ELF имеет два заголовка, как показано ниже:

  • заголовок раздела - используется для связи нескольких эльфов для создания образа процесса.
  • заголовок программы - используется для загрузки образа процесса.

Здесь мы сосредоточимся только на структуре заголовка раздела:

    mapping<var_name, offset, size...>
    // and special cases
    mapping<external_var_name, offset, size...>

Каждая программа скомпилирована отдельно, что означает, что распределение адресов аналогично (в ранней версии Linux каждая скомпилированная программа начинается с того же виртуального адреса - 0x08000000, и многие атаки могут использовать это, поэтому он изменяется на добавив некоторую случайную дельта для адресации, чтобы облегчить проблему), поэтому может существовать некоторая область наложения. Вот почему необходимо перемещение адреса.

Перемещение

Информация о перемещении (смещение, значение и т.д.) сохраняется в разделе .rel.*:

    Relocation section '.rel.text' at offset 0x7a4 contains 2 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    0000000d  00000e02 R_386_PC32        00000000   main
    00000015  00000f02 R_386_PC32        00000000   exit

    Relocation section '.rel.debug_info' at offset 0x7b4 contains 43 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    00000006  00000601 R_386_32          00000000   .debug_abbrev
    0000000c  00000901 R_386_32          00000000   .debug_str

Когда компоновщик хочет установить адрес main в процессе перемещения, он не может найти символ в файле скомпилированного эльфа, поэтому он жалуется на это и останавливает процесс связывания.

Пример

Здесь - упрощенная версия os-реализаций, start.c соответствует crt1.o исходному коду:

    int entry(char *); // corresponds to main

    void _start(char *args) {
        entry(args);
        exit();
    }

Ссылка

Ответ 2

Вместо того, чтобы компилировать код напрямую, пройдите все этапы компиляции, чтобы выяснить, где возникает ошибка (насколько я знаю, такие ошибки возникают при связывании). Следующие аргументы gcc будут полезны:

  • -E Только предварительная обработка; не компилировать, не собирать или не связывать
  • -S Только компиляция; не собирайте или не связывайте
  • -c Скомпилируйте и соберите, но не связывайте

Сейчас:

gcc -E prog.c
gcc -S prog.c
gcc -c prog.c

С указанной вами программой/кодом все эти шаги работают с gcc 4.8.4. Но во время компоновки, когда вы компилируете с помощью gcc prog.c, компилятор не может связываться с соответствующей библиотекой, поскольку он не упоминался. Кроме того, у нас нет функции main в файле prog.c. Итак, нам нужно указать переключатель -nostartfiles. Следовательно, вы можете скомпилировать prog.c как:

gcc prog.c -lc -nostartfiles

Это вызывает предупреждение:

/usr/bin/ld: предупреждение: не удается найти символ входа _start; по умолчанию - 00000000004002a3

Это из-за последовательности. т.е. init вызывает функцию _start, а функция _start вызывает функцию main. Это предупреждение означает, что функция _start не может найти функцию main, где вызов init не может найти _start. Обратите внимание, что это всего лишь предупреждение. Чтобы избежать этого предупреждения, нам нужно изменить команду для компиляции без предупреждений следующим образом.

gcc prog.c -lc --entry whatever -nostartfiles

С помощью этой команды мы инструктируем ядро ​​скомпилировать prog.c с помощью gcc, связав библиотеку libc.so с начальной точкой как функцию whatever, где этот код не содержит функции main.

Это контекст с gcc 4.8.4, который я скомпилировал.

В связи с gcc 6.2.0, я думаю, что все эти связующие материалы заботятся самим компилятором. Следовательно, вы можете просто указать команду компиляции, как показано ниже.

gcc -c prog.c -nostartfiles

Если он вызывает какие-либо другие ошибки или предупреждения, вы можете использовать упомянутые выше переключатели.

Также обратите внимание, что crt0 через crtN (N зависит от файла ELF) выполняются перед вызовами init _start, который дает метаданные о памяти и других параметрах, зависящих от машины. Ошибки связи, показанные как

/usr/bin/ld:/usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): перемещение 0 имеет недопустимый индекс символов 11

не предоставляют полную информацию для исправления проблемы, поскольку машины не так умны, как люди, в определении точки ошибки.

Это создает полный исполняемый файл. Обратите внимание, что такой код (без основной функции) используется, когда мы работаем над библиотеками/модулями в рамках проекта.

Все предоставленные данные выполняются пошаговым анализом. Вы можете воссоздать все упомянутые шаги. Надеюсь, это очистит ваши сомнения. Добрый день!

Ответ 3

Мы можем разбить это на две части:

Undefined ссылка на `main '

/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status

Это просто потому, что библиотека C Runtime (crt1.o) пытается вызвать вашу функцию (отсутствует) main(). Существует хороший обзор различных файлов времени выполнения здесь/здесь.

Перемещение X имеет недопустимый индекс символа Y

Примечание. Для меня это была небольшая миссия (возможность обучения). Я потратил несколько дней на исследование и понимание в ограниченном свободном времени, которое у меня есть. Это то, о чем я долго задумывался, но не смотрел... Надеюсь, мое понимание правильное (хотя явно не полное - я его обновлю, если смогу).

Это немного сложнее и спрятано.

Просто прочитав сообщение, мы можем понять, что оно связано с отладочной информацией (упоминаются разделы .debug_info и .debug_line файла crt1.o debug). Обратите внимание на путь /usr/lib/debug/, который содержит только отладочную информацию, другой crt1.o является "разделенным" файлом...

Строка формата находится в проекте binutils, в частности bfd/elfcode.h. BFD является Двоичный файловый дескриптор - способ GNU для обработки объектных файлов по нескольким системным архитектурам.

BFD - это промежуточный формат, используемый для двоичных файлов. GCC будет использовать BFD, прежде чем он, наконец, напишет a.out, ELF или другой двоичный файл.

Заглянув в руководство, мы можем найти интересные фрагменты знаний:

[...] с каждой записью в хэш-таблице, компоновщик a.out сохраняет индекс, который имеет символ в конечном выходном файле (этот номер индекса используется, чтобы при перемещении ссылки индекс символа, используемый в выходной файл может быть быстро заполнен при копировании через reloc). [источник]

Стандартные записи содержат только адрес, индекс символа и поле типа. [источник]

Это означает, что эти ошибки выдаются из-за перемещений, связанных с конкретными (отсутствующими?) символами. "Символ" в этом контексте - это любое имя "вещь" - например: функции и переменные.

Поскольку эти "недопустимые символы" кажутся разрешенными просто объявлением main(), я бы предположил, что некоторые (все?) этих индексов символов получены из main(), его отладочной информации и/или отношений.

Я не могу сказать вам, что должно быть в упомянутых символьных индексах (2, 11, 12, 13, 21), но интересно, что мои тесты дали тот же список индексов символов.

Выполнение ld только с crt1.o дает нам аналогичный вывод:

$ ld /usr/lib/x86_64-linux-gnu/crt1.o
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 1 has invalid symbol index 12
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 2 has invalid symbol index 2
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 3 has invalid symbol index 2
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 4 has invalid symbol index 11
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 5 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 6 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 7 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 8 has invalid symbol index 12
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 9 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 10 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 11 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 12 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 13 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 14 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 15 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 16 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 17 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 18 has invalid symbol index 13
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 19 has invalid symbol index 21
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): relocation 0 has invalid symbol index 2
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x12): undefined reference to `__libc_csu_fini'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x19): undefined reference to `__libc_csu_init'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x25): undefined reference to `__libc_start_main'