Что происходит во время ошибки "Перемещение имеет неверный индекс символа"?
Вот тест, воспроизводящий проблему:
$ 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'