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

Связывание C-программы напрямую с ld завершается неудачей с undefined ссылкой на `__libc_csu_fini`

Я пытаюсь скомпилировать C-программу под Linux. Однако из любопытства я пытаюсь выполнить несколько шагов вручную: я использую:

  • интерфейс gcc для создания кода ассемблера
  • затем запустите сборщик GNU для получения объектного файла
  • а затем связать его со средой выполнения C, чтобы получить рабочий исполняемый файл.

Теперь я застрял со связующей частью.

Программа - очень простой "Hello world":

#include <stdio.h>
int main() {
   printf("Hello\n");
   return 0;
}

Я использую следующую команду для создания кода сборки:

gcc hello.c -S -masm=intel

Я говорю gcc, чтобы завершить работу после компиляции и сбросить код сборки с помощью синтаксиса Intel.

Затем я использую ассемблер GNU для создания объектного файла:

as -o hello.o hello.s

Затем я пытаюсь использовать ld для создания окончательного исполняемого файла:

ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello

Но я продолжаю получать следующее сообщение об ошибке:

/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'

Символы __libc_csu_fini/init кажутся частью glibc, но я не могу их найти нигде! Я попытался связать с libc статически (против /usr/lib/libc.a) с тем же результатом.

В чем проблема?

4b9b3361

Ответ 1

Я нашел еще одно сообщение, в котором содержалась подсказка: -dynamic-linker /lib/ld-linux.so.2.

Попробуйте следующее:

$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$ 

Ответ 2

/usr/lib/libc.so является компоновщиком script, который сообщает компоновщику, чтобы он вытащил в общей библиотеке /lib/libc.so.6 и не разделяемую часть, /usr/lib/libc_nonshared.a.

__libc_csu_init и __libc_csu_fini относятся к /usr/lib/libc_nonshared.a. Они не найдены, потому что ссылки на символы в не разделяемых библиотеках должны появляться перед архивом, который определяет их в линии компоновщика. В вашем случае /usr/lib/crt1.o (который ссылается на них) появляется после /usr/lib/libc.so (который их втягивает), поэтому он не работает.

Фиксирование заказа на линии ссылки приведет вас немного дальше, но тогда вы, вероятно, получите новую проблему, где __libc_csu_init и __libc_csu_fini (которые теперь найдены) не могут найти _init и _fini. Чтобы вызвать функции библиотеки C, вы также должны связать /usr/lib/crti.o (после crt1.o, но перед библиотекой C) и /usr/lib/crtn.o (после библиотеки C), которые содержат код инициализации и завершения.

Добавление этих файлов должно предоставить вам успешно связанный исполняемый файл. Он по-прежнему не работает, потому что он использует динамически связанную библиотеку C, не указав, что такое динамический компоновщик. Вам также нужно сообщить компоновщику, что-то вроде -dynamic-linker /lib/ld-linux.so.2 (для 32-битного x86, по крайней мере, имя стандартного динамического компоновщика зависит от разных платформ).

Если вы сделаете все это (по сути, ответ Роба), вы получите то, что работает в простых случаях. Но вы можете столкнуться с дополнительными проблемами с более сложным кодом, поскольку GCC предоставляет некоторые из своих собственных библиотечных процедур, которые могут потребоваться, если ваш код использует определенные функции. Они будут похоронены где-то глубоко внутри каталогов установки GCC...

Вы можете видеть, что делает gcc, запустив его с помощью параметра -v (который покажет вам команды, которые он вызывает при запуске), или параметр -### (который просто печатает команды, которые он run, со всеми котировками аргументов, но на самом деле ничего не запускает). Результат будет запутан, если вы не знаете, что он обычно вызывает ld косвенно через один из своих компонентов, collect2 (который используется для клей в конструкторах конструктора С++ в правой точке).

Ответ 3

Предполагая, что нормальный вызов gcc -o hello hello.c создает рабочую сборку, запустите эту команду:

gcc --verbose -o hello hello.c

и gcc расскажут вам, как это связывает вещи. Это должно дать вам хорошее представление обо всем, что вам может понадобиться, чтобы учесть ваш шаг на ссылке.

Ответ 4

Вот как я исправил его на ubuntu 11.10:

apt-get remove libc-dev

Скажите "да", чтобы удалить все пакеты, но скопируйте список для повторной установки после.

apt-get install libc-dev

Ответ 5

В Ubuntu 14.04 (GCC 4.8) минимальная команда связывания:

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
  /usr/lib/x86_64-linux-gnu/crt1.o \
  /usr/lib/x86_64-linux-gnu/crti.o \
  -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
  -lc -lgcc -lgcc_s \
  hello.o \
  /usr/lib/x86_64-linux-gnu/crtn.o

Хотя они могут и не быть необходимыми, вы также должны ссылаться на -lgcc и -lgcc_s, так как GCC может вызывать вызовы функций, присутствующих в этих библиотеках, для операций, которые ваше оборудование не реализует изначально, например. long long int операции с 32-битным. Смотрите также: Мне действительно нужен libgcc?

Мне пришлось добавить:

  -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \

потому что компоновщик по умолчанию script не включает этот каталог, и именно там было расположено libgcc.a.

Как уже упоминалось Майклом Берром, вы можете найти пути с помощью gcc -v. Точнее, вам нужно:

gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'

Ответ 6

Если вы используете 64-разрядную ОС, ваш glibc (-devel) может быть сломан. Рассматривая this и this, вы можете найти эти 3 возможных решения:

  • добавить lib64 в LD_LIBRARY_PATH
  • использовать lc_noshared
  • переустановить glibc-devel

Ответ 7

Поскольку вы выполняете процесс связи вручную, вы забываете связать инициализатор времени выполнения C или что бы он ни назывался.

Чтобы не вникать в специфику того, где и что вы должны связывать для своей платформы, после получения вашего файла asm asm, используйте gcc для генерации (компиляции и ссылки) вашего исполняемого файла.

просто выполнение gcc hello.c -o hello должно работать.