【问题标题】:`bash: ./a.out: No such file or directory` on running executable produced by `ld``bash: ./a.out: No such file or directory` 运行由`ld`生成的可执行文件
【发布时间】:2016-03-02 09:50:19
【问题描述】:

这是一个 C 语言的 Hello World 代码:

// a.c
#include <stdio.h>

int main() {
    printf("Hello world\n");
    return 0;
}

我将它编译为gcc a.c,它按预期生成a.out./a.out 按预期打印Hello world...。

现在,如果我分别进行编译和链接: gcc -c a.c; ld -lc a.o,它运行 a.out 生成为 ./a.out 我收到消息:

bash: ./a.out: No such file or directory

我用谷歌搜索了那个错误,似乎当生成的可执行文件是 32 位 ELF 并且机器架构是 64 位时会发生这种情况。

我正在运行 64 位机器并运行 file a.out 给出:

a.out: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

为什么会这样?

编辑:

uname -m的输出

$ uname -m
x86_64

ldd a.out的输出

$ ldd a.out
    linux-vdso.so.1 =>  (0x00007ffeeedfb000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa13a7b8000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fa13abab000)

gcc a.c 生成运行正确的a.out

【问题讨论】:

  • 64 位机器并不一定意味着 64 位操作系统。 uname -m 的输出是什么?
  • ldd a.out 产生什么?当您执行gcc a.c 时会发生什么?
  • 将输出添加为问题的编辑
  • 可以执行:gcc -v a.c 并检查链接阶段(是否需要与启动对象链接通常crt0.o)
  • 为什么不想让 gcc 处理链接?

标签: c linux gcc linker dynamic-linking


【解决方案1】:

ld -lc a.o

这个命令行有几个问题:

  1. 一般来说,用户级代码从不直接使用ld,并且总是使用适当的编译器前端(此处为gcc)来执行链接。

    正如您所发现的,gcc 构造的链接命令行相当复杂,而且您在 Joan Esteban 的回答中接受的命令行是错误的。

    如果您想查看 实际 链接命令,请检查来自 gcc -v a.o 的输出。

    还请注意,当您稍微更改gcc 命令时,链接命令会发生显着变化(例如,某些操作系统需要不同的crt1.o,具体取决于您是否链接多线程可执行文件),并且命令行始终是 OS-具体(这是从不直接使用ld的另一个原因)。

  2. 库应遵循命令行上的目标文件。所以ld -lc a.o从不正确,应该总是ld a.o -lc的(一个变体)。 Explanation

【解决方案2】:

使用 gcc foo.o 链接动态可执行文件(为 CRT 和 libc 使用正确的路径,以及动态链接器/ELF 解释器 ld-linux-x86-64.so.2)。
或者 gcc -nostartfiles foo.o 用于 libc 而不是 CRT _start,如果你有手写的_start

(对于没有libc或CRT的静态可执行文件,您可以直接使用ldgcc -nostdlib -static。)

gcc -v foo.o 将向您显示 GCC 在您的系统上使用的实际路径。


其他答案仅涉及如何避免这种情况1,而不是发生了什么的实际问题

你给出的gcc -c a.c; ld -lc a.o 命令产生了一个非常明显的警告:

ld: warning: cannot find entry symbol _start; defaulting to 0000000000400260

所以即使这个文件可以执行,它也可能会立即崩溃。 请参阅@EmployedRussian 的回答,了解您应该做什么。


为什么它甚至无法执行的问题仍然很有趣:

$ strace ./a.out 
execve("./a.out", ["./a.out"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)

execve(2) 返回 ENOENT 因为它找不到解释器(我从file 等处发现,见下文)。尝试运行以

开头的文件时,您会遇到同样的错误
#!/usr/non-existant-path/bin/bash

正如您所发现的,出现此错误消息的通常原因是在未安装正确的动态链接器和动态库的系统上运行 ELF 二进制文件(例如,未安装 32 位支持的 64 位系统)。在您的情况下,这是因为您使用了错误的链接命令并使用错误的解释器路径制作了动态可执行文件。


我在 Ubuntu 15.10 上,其中 GNU file 版本 5.22 报告:

a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld64.so.1, not stripped

我的系统上没有/lib/ld64.so.1ldd 的输出令人困惑,因为 ldd 使用其默认的 ELF 解释器,而不是二进制文件指定的解释器。

$ ldd a.out
        linux-vdso.so.1 =>  (0x00007ffc18d2b000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e0a79f000)
        /lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x0000559dbc9d2000)

所以它假设二进制文件中的运行时解释器解析为 ldd 使用它自己,我猜。

您的ldd 输出也可能来自旧版本,因为它只显示该行的/lib64/ld-linux-x86-64.so.2。对于这种奇怪的情况,不要做出错误的猜测可能是更好的行为,但不能帮助您看到您的二进制文件具有奇怪的解释器路径。

readelf -l a.out

将为您解码 ELF 标头,包括解释器路径。 (感谢@EmployedRussian 的评论指出这一点。)

【讨论】:

  • 查看给定二进制文件的解释器的标准方法是readelf -l a.out | grep interpreter
【解决方案3】:

使用它:

  ld -o a.out -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc c.o /usr/lib/crtn.o

【讨论】:

  • 首先,crt1.ocrti.ocrtn.o 存在于我机器上的 /usr/lib/x86_64-linux-gnu/ 中。在更改并运行 ld 然后运行 ​​a.out 后,我收到错误:bash: ./a.out: Accessing a corrupted shared library
  • @pratyaksh 您还需要将链接器调整为/lib64/ld-linux-x86-64.so.2
  • @piggs_boson:这是将动态可执行文件与gcc c.ogcc -nostartfiles c.o 手动链接而不是ld 的一个很好的理由 - 您的发行版将配置它以使用正确的路径为您系统。使用gcc -v 查看这些路径在您的系统上,而不是其他人在 Internet 上的路径。
猜你喜欢
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-24
  • 2016-01-05
  • 2016-11-01
相关资源
最近更新 更多