直接将C程序与ld链接失败,未定义引用`__libc_csu_fini`
我正在尝试在Linux下编译一个C程序。 然而,出于好奇,我正在尝试手动执行一些步骤:我使用:
- gcc前端生成汇编代码
- 然后运行GNU汇编程序以获取目标文件
- 然后将其与C运行时链接以获取可用的可执行文件。
现在我被连接部分困住了。
该程序是一个非常基本的“Hello world”:
#include 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
)。
问题是什么?
我找到了另一个包含线索的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 $
/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提供了一些自己的库例程,如果您的代码使用某些function,可能需要这些例程。 这些将被埋在GCC安装目录的深处…
你可以通过-v
选项(它将显示它在运行时调用的命令)运行它来查看gcc
正在做什么,或者-###
选项(它只打印它将运行的命令,所有这些)参数引用,但实际上并没有运行任何东西)。 除非你知道它通常通过它自己的一个组件collect2
(它用于在正确的点上粘合C ++构造函数调用)间接调用ld
,否则输出会令人困惑。
假设正常调用gcc -o hello hello.c
会生成一个工作版本,请运行以下命令:
gcc --verbose -o hello hello.c
和gcc会告诉你它是如何联系的。 这应该可以让您对链接步骤中可能需要考虑的所有内容有所了解。
这就是我在ubuntu 11.10上修复它的方法:
apt-get remove libc-dev
说是删除所有包但复制列表后重新安装。
apt-get install libc-dev
在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
操作。 另请参阅: 我真的需要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'
如果您运行的是64位操作系统,则您的glibc(-devel)可能会损坏。 通过查看此信息 ,您可以找到以下3种可能的解决方案:
- 将lib64添加到LD_LIBRARY_PATH
- 使用lc_noshared
- 重新安装glibc-devel
由于您正在手动执行链接过程,因此您忘记链接C运行时初始值设定项或其调用的任何内容。
为了不了解您应该为您的平台链接的位置和内容,在获取您的intel asm文件后,使用gcc生成(编译和链接)您的可执行文件。
简单地做gcc hello.c -o hello
应该可行。