Сбой связывания программы на C напрямую с ld с неопределенной ссылкой на `__libc_csu_fini` - PullRequest
15 голосов
/ 12 июля 2011

Я пытаюсь скомпилировать программу на 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) с тем же результатом.

В чем может быть проблема?

Ответы [ 7 ]

33 голосов
/ 12 июля 2011

/usr/lib/libc.so - это скрипт компоновщика, который сообщает компоновщику, что нужно извлечь общую библиотеку /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 (который покажет вам команды, которые он вызываетзапускается) или параметр -### (который просто печатает команды, которые он будет выполнять, со всеми аргументами в кавычках, но фактически ничего не запускает).Вывод будет сбивать с толку, если только вы не знаете, что он обычно вызывает ld косвенно через один из своих собственных компонентов, collect2 (который используется для склеивания вызовов конструктора C ++ в нужной точке).

4 голосов
/ 12 июля 2011

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

gcc --verbose -o hello hello.c

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

3 голосов
/ 12 июля 2011

Я нашел другой пост , который содержал подсказку: -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
$ 
1 голос

В 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/ \

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

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

gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
1 голос
/ 10 ноября 2011

Вот как я это исправил в Ubuntu 11.10:

apt-get remove libc-dev

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

apt-get install libc-dev
0 голосов
/ 12 июля 2011

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

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

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

0 голосов
/ 12 июля 2011

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

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