直接将C程序与ld链接失败,未定义引用`__libc_csu_fini`

时间:2011-07-11 20:45:06

标签: c gcc linker glibc ld

我正在尝试在Linux下编译C程序。然而,出于好奇,我正在尝试手动执行一些步骤:我使用:

  • 生成汇编代码的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)。

问题是什么?

8 个答案:

答案 0 :(得分:33)

/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;标准动态链接器的名称因平台而异)。

如果你做了所有这些(基本上按照Rob的回答),你会得到一些在简单情况下有效的东西。但是,您可能会遇到更复杂代码的问题,因为GCC提供了一些自己的库例程,如果您的代码使用某些功能,可能需要这些例程。这些将被埋在GCC安装目录的深处......

您可以使用gcc选项(将显示它在运行时调用的命令)或-v选项(使用-###选项运行它来查看ld正在执行的操作只打印它将运行的命令,带有所有参数引号,但实际上并没有运行任何东西)。除非你知道它通常通过它自己的一个组件collect2间接调用{{1}}(它用于在正确的点上粘合C ++构造函数调用),否则输出将会引起混淆。

答案 1 :(得分:4)

假设正常调用gcc -o hello hello.c会产生有效的构建,请运行以下命令:

gcc --verbose -o hello hello.c

和gcc会告诉你它是如何链接的。这应该可以让您对链接步骤中可能需要考虑的所有内容有所了解。

答案 2 :(得分:3)

我发现another post包含一条线索:-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
$ 

答案 3 :(得分:1)

这就是我在ubuntu 11.10上修复它的方法:

apt-get remove libc-dev

说是删除所有软件包,但复制列表后重新安装。

apt-get install libc-dev

答案 4 :(得分: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可能会调用这些库中存在的函数来执行您的硬件本身未实现的操作,例如:对{32}进行long long int次操作。另见:Do I really need libgcc?

我不得不补充道:

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

因为默认链接描述文件不包含该目录,而libgcc.a所在的位置。

如Michael Burr所述,您可以找到gcc -v的路径。更准确地说,您需要:

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

答案 5 :(得分:0)

如果您运行的是64位操作系统,则您的glibc(-devel)可能会损坏。通过查看thisthis,您可以找到以下3种可能的解决方案:

  1. 将lib64添加到LD_LIBRARY_PATH
  2. 使用lc_noshared
  3. 重新安装glibc-devel

答案 6 :(得分:0)

由于您正在手动执行链接过程,因此您忘记链接C运行时初始化程序或其所谓的。

要获取您的intel asm文件,请不要深入了解您应该为您的平台链接的位置和内容,使用gcc生成(编译和链接)您的可执行文件。

只需执行gcc hello.c -o hello即可。

答案 7 :(得分:0)

接受:

    $ echo 'main(){puts("ok");}' > hello.c
    $ gcc -c hello.c -o hello.o
    
    $ ld hello.o -o hello.exe /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o \
-dynamic-linker /lib/ld-linux.so.2 -lc
    
    $ ./hello.exe
    ok

使用--prefix = / usr

配置 glibc 时,/ usr / lib / crt * .o的路径。