直接将C程序与ld链接失败,引用未定义为`\u libc\u csu\u fini`
我正在尝试在Linux下编译一个C程序。然而,出于好奇,我尝试手动执行一些步骤:我使用:直接将C程序与ld链接失败,引用未定义为`\u libc\u csu\u fini`,c,gcc,linker,glibc,ld,C,Gcc,Linker,Glibc,Ld,我正在尝试在Linux下编译一个C程序。然而,出于好奇,我尝试手动执行一些步骤:我使用: 用于生成汇编代码的gcc前端 然后运行GNU汇编程序获取一个对象文件 然后将其与C运行时链接,以获得一个工作的可执行文件 现在我被连接部分卡住了 该程序是一个非常基本的“Hello world”: 我告诉gcc在编译并转储带有英特尔语法的汇编代码后退出 然后我使用GNU汇编程序生成目标文件: as -o hello.o hello.s 然后我尝试使用ld生成最终的可执行文件: ld hello.o /
- 用于生成汇编代码的gcc前端
- 然后运行GNU汇编程序获取一个对象文件
- 然后将其与C运行时链接,以获得一个工作的可执行文件
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'
这些符号似乎是glibc的一部分,但我在任何地方都找不到!我尝试静态地链接libc(针对/usr/lib/libc.a
),结果相同
问题可能是什么?如果您运行的是64位操作系统,您的glibc(-devel)可能会损坏。通过查看,您可以找到以下3种可能的解决方案:
-dynamiclinker/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
$
由于您是手动执行链接过程,因此您忘记了链接C运行时初始值设定项,或者不管它叫什么 为了不详细说明平台的链接位置和内容,在获取英特尔asm文件后,请使用gcc生成(编译和链接)可执行文件
只需执行
gcc-hello.c-o hello
就可以了。假设正常调用gcc-o hello.c
会生成一个工作版本,请运行以下命令:
gcc --verbose -o hello hello.c
gcc会告诉你它是如何连接的。这会让你对链接步骤中可能需要说明的所有内容有一个很好的了解。
/usr/lib/libc.so
是一个链接器脚本,它告诉链接器拉入共享库/lib/libc.so.6
,以及非共享部分/usr/lib/libc\u nonshared.a
\uu libc\u csu\u init
和\uu libc\u csu\u fini
来自/usr/lib/libc\u nonshared.a
。找不到它们,因为对非共享库中符号的引用需要出现在链接器行上定义它们的存档之前。在您的例子中,/usr/lib/crt1.o
(引用它们)出现在/usr/lib/libc.so
(将它们拉入)之后,因此它不起作用
修正链接行上的顺序会让您走得更远,但是您可能会遇到一个新问题,\uu libc\u csu\u init
和\uu libc\u csu\u fini
(现在可以找到)找不到\u init
和\u 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安装目录的深处
您可以通过使用-v
选项(该选项将向您显示它运行时调用的命令)或-####
选项(该选项仅打印它将运行的命令,所有参数都带引号,但实际上不运行任何命令)来查看gcc
正在做什么。除非您知道它通常通过它自己的一个组件间接调用“代码> LD <代码>,它将使您感到困惑。(在Point中,它用于粘贴C++构造函数调用)。< / P> < P>这是我如何在Ubuntu 11.10:/P>上固定的。
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_
,因为GCC可能会对那些库中存在的函数发出调用,以执行硬件本机未实现的操作,例如32位上的long long int
操作。另见:
我必须补充:
-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'
拿它来说:
$ 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的路径将显示您的crt1.o的objdump显示了什么。我的代码显示了这些符号-修复对象文件顺序并将/usr/lib/crti.o
添加到混合中可以让我通过链接器,但生成的可执行文件不会运行(bash:./hello:目录中没有这样的文件
),因此缺少其他内容。在汇编中手动编写的代码也是如此:Brilliant。解释清楚,简洁,精简到要点。我一直在穿越丛林,也就是ld,将一个直接链接的可执行文件从Solaris移植到Linux,最后来到了这里,因为在执行刚链接的可执行文件时,我试图解决一个“权限被拒绝”的错误,因此我不得不交换链接器行上对象的顺序。对于
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
$ 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