我正在尝试在Linux下编译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
)。
问题是什么?
答案 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)可能会损坏。通过查看this和this,您可以找到以下3种可能的解决方案:
答案 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的路径。