使用连接器构建程序,但运行可执行文件时显示“没有这个文件或目录”。

3
我写了一个包含两个文件main.ccomp.c的程序。 main.c
#include <stdio.h>
extern int secure_func(int, int);
void main()
{
    printf("hello, world\n");
    int result = secure_func(1, 1);
}

comp.c

int secure_func(int text, int key)
{
    return text * key * key;
}

首先,我直接使用gcc编译程序并运行可执行文件,这很好。
gcc -o main main.c comp.c
./main
hello, world
然后我尝试使用工具链逐步构建程序,就像这样。
gcc -c -o comp.o comp.c
gcc -c -o main.o main.c
ld -o main main.o comp.o -lc --entry main
生成了一个名为main的文件。但是如果我尝试运行它,会显示错误。
bash: ./main: No such file or directory
可执行文件具有X权限。
列表信息。
>    ~/test/segtest2$ ls -l
>    total 24
>    -rw-rw-r-- 1 kail kail   88 Sep 28 21:20 comp.c
>    -rw-rw-r-- 1 kail kail   37 Sep 28 21:20 comp.h
>    -rw-rw-r-- 1 kail kail 1248 Sep 28 21:22 comp.o
>    -rwxrwxr-x 1 kail kail 3241 Sep 28 21:22 main
>    -rwxrwxr-- 1 kail kail  137 Sep 28 21:20 main.c
>    -rw-rw-r-- 1 kail kail 1568 Sep 28 21:21 main.o

我有什么遗漏吗?欢迎任何建议。谢谢!

1
你能展示一下当前目录的“ls -l”吗? - rkachach
你确定该文件具有执行权限吗?更具体地说,你是否有执行该文件的权限? - Snappawapa
@Snappawapa - 如果没有执行权限,您将不会收到该错误。 - rghome
你可以使用 gcc -v 命令来查看 gcc 编译程序时所调用的命令。 - fuz
你试图通过这样做达到什么目的?你能发布 strace ./main 的输出吗? - fuz
显示剩余2条评论
3个回答

2
您传递给 ld 的参数 --entry=main 并不是您想象中的那样。程序的入口点是程序开始的地方。但这不是 main,而是一个名为 _start 的函数,在系统上某个位置定义在一个对象文件 crt0.o 中。要手动链接 C 程序,请像这样调用 ld:
ld -o main main.o comp.o /path/to/crt0.o -lc

除非您知道它的作用,否则不要提供--entry


如果我想完全控制构建步骤,该怎么办?我能定义 crt0.o 的路径或者自己定义 _start 吗?我使用 gcc -v 查看构建日志,但没有 ld 的输出。 - KyL
@KyL 对象文件 crt0.oC语言的运行时环境的一部分。如果你想使用libc,你的程序应该从这个文件开始运行,否则各种事情可能会出错(比如mallocstdio),因为对应的初始化没有被执行。crt0.o中的代码基本上是设置一个堆栈,计算你的程序的参数,然后调用libc进行初始化,然后调用main和最后调用exit。你可以跳过它,但是那么你需要手动设置所有这些东西。 - fuz
@KyL你可以手动提供该路径,但重新查看后,我认为C编译器将更多的目标文件链接到最终二进制文件中。除非您完全不使用libc(也要手动编写启动代码),否则我不建议您手动执行此操作,因为C编译器用于链接二进制文件的确切步骤不稳定,并且可能会在没有通知的情况下更改。 - fuz

1

让我们看看gcc在详细模式下的工作方式。

gcc -v -o main main.o comp.o

gcc使用以下命令链接对象文件

/usr/lib/gcc/x86_64-linux-gnu/4.4.3/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main -z relro /usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.3/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../.. main.o comp.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.4.3/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crtn.o
如果你用ld替换/usr/lib/gcc/x86_64-linux-gnu/4.4.3/collect2,那么链接过程将成功完成。

ld --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main -z relro /usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.3/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../.. main.o comp.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.4.3/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.3/../../../../lib/crtn.o

这段代码是关于编译器链接的,它包含了一些选项和库文件的路径,最终生成可执行文件 "main"。

参考此答案以获取更多信息。


如果我使用gcc编译程序,我能否获得中间链接器脚本? - KyL
与ld类似,gcc也支持使用-T选项的链接器脚本选项。 - Gangadhar
我的意思是,如果我使用gcc或ld构建程序而没有使用-T选项,我猜测gcc将生成默认的链接脚本来处理目标文件。我能获取到这个生成的脚本吗? - KyL
如果您不指定-T选项,gcc或ld将使用内部链接器脚本。如果您使用--verbose选项执行上述命令,则可以获取链接器脚本。 - Gangadhar
1
谢谢。它有效。我通过gcc -Wl,--verbose得到了脚本。 - KyL

1
当我尝试运行时,我看到类似的东西:
-bash: ./main: /lib/ld64.so.1: bad ELF interpreter: No such file or directory

所以你可能没有正确地调用ld。不要使用ld,而是使用gcc来调用它,因为它会处理你在使用ld时必须担心的细节。

gcc -c -o comp.o comp.c
gcc -c -o main.o main.c
gcc -o main main.o comp.o

出于某种目的,我必须逐步构建可执行文件。 - KyL
1
@KyL 你还在做那件事啊,只是换了个工具罢了。 - dbush
如果我错过了ld的某些选项,我必须知道我错过了哪些选项。这是我目前工作的关键点。 - KyL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接