`bash: ./a.out: No such file or directory` on running executable produced by `ld`
Asked Answered
I

3

17

Here is a Hello World code in C:

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

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

I compile it as gcc a.c, which produces a.out as expected and ./a.out prints Hello world... as expected.

Now if I do the compile and link separately: gcc -c a.c; ld -lc a.o, it run the a.out produced as ./a.out I get the message:

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

I Googled that error and it seems that happens when the executable produced is a 32-bit ELF and the machine architecture is 64-bit.

I'm running a 64-bit machine and running file a.out gives:

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

Why does this happen?

EDIT:

Output of uname -m

$ uname -m
x86_64

Output of 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 produces a.out which runs correctly.

Informality answered 28/11, 2015 at 10:16 Comment(8)
A 64-bit machine doesn't necessarily mean a 64-bit OS. What's the output of uname -m?Diphenyl
What does ldd a.out produce? What happens when you do gcc a.c?Stillhunt
Added the outputs as edits to the questionInformality
Have you tried running the problematic a.out under gdb?Odessaodetta
@MichaelBurr same error, /bin/bash: /home/pratyaksh/temp/a.out: No such file or directory Informality
you can execute: gcc -v a.c and check the link stage (do you need to link with the startup object usually crt0.o)Skillful
@arrowd Tried that, doesn't change things :-)Informality
Why don't you want to let gcc handle the linking?Jesselton
M
9

Link dynamic executables with gcc foo.o (to use the right paths for CRT and libc, and the dynamic linker / ELF interpreter ld-linux-x86-64.so.2).
Or gcc -nostartfiles foo.o for libc but not CRT _start, if you have a hand-written _start

(For static executables without libc or CRT, you can use ld directly or gcc -nostdlib -static.)

gcc -v foo.o will show you the actual paths GCC used on your system.


The other answers only address how to avoid this1, not the actual question of what happened.

The gcc -c a.c; ld -lc a.o commands you gave produce a pretty obvious warning:

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

So even if this file could be executed, it will probably crash right away. See @EmployedRussian's answer for an explanation of what you should have done.


The question of why it can't even be executed is still interesting:

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

execve(2) returns ENOENT because it can't find the interpreter (which I figured out from file and so on, see below). You'd get the same error from trying to run a file that started with

#!/usr/non-existant-path/bin/bash

As you discovered, the usual reason for this error message is when running an ELF binary on a system without the right dynamic linker and dynamic libraries installed (e.g. a 64bit system without 32bit support installed). In your case, it's because you used a bad link command and made a dynamic executable with a bad interpreter path.


I'm on Ubuntu 15.10, where GNU file version 5.22 reports:

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

There is no /lib/ld64.so.1 on my system. ldd output is confusing, because ldd uses its default ELF interpreter, not the one specified by the binary.

$ 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)

So it assumes that the runtime interpreter in the binary resolved to the one ldd used itself, I guess.

Your ldd output is probably from an old version too, since it just shows /lib64/ld-linux-x86-64.so.2 for that line. Not taking a bad guess is probably better behaviour, for a weird case like this, but doesn't help you see that your binary has a weird interpreter path.

readelf -l a.out

will decode the ELF headers for you, including the interpreter path. (Thanks to @EmployedRussian's comment for pointing this out.)

Maples answered 29/11, 2015 at 5:3 Comment(1)
The standard way to see what the interpreter is for a given binary is readelf -l a.out | grep interpreterElectrotherapeutics
E
11

ld -lc a.o

There are several things wrong with this command line:

  1. In general, user-level code should never use ld directly, and always use appropriate compiler front end (gcc here) to perform the link.

    As you have discovered, the link command line that gcc constructs is quite complicated, and the command line that you've accepted in Joan Esteban's answer is wrong.

    If you want to see the actual link command, examine output from gcc -v a.o.

    Also note that link command changes significantly when you change gcc command only slightly (e.g. some OSes require different crt1.o depending on whether you are linking multi-threaded executable or not), and the command line is always OS-specific (which is one more reason to never use ld directly).

  2. Libraries should follow object files on command line. So ld -lc a.o is never correct, and should always be (a variant of) ld a.o -lc. Explanation.

Electrotherapeutics answered 29/11, 2015 at 3:32 Comment(1)
explanation archived link web.archive.org/web/20180627210132/http://webpages.charter.net/…Glynda
M
9

Link dynamic executables with gcc foo.o (to use the right paths for CRT and libc, and the dynamic linker / ELF interpreter ld-linux-x86-64.so.2).
Or gcc -nostartfiles foo.o for libc but not CRT _start, if you have a hand-written _start

(For static executables without libc or CRT, you can use ld directly or gcc -nostdlib -static.)

gcc -v foo.o will show you the actual paths GCC used on your system.


The other answers only address how to avoid this1, not the actual question of what happened.

The gcc -c a.c; ld -lc a.o commands you gave produce a pretty obvious warning:

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

So even if this file could be executed, it will probably crash right away. See @EmployedRussian's answer for an explanation of what you should have done.


The question of why it can't even be executed is still interesting:

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

execve(2) returns ENOENT because it can't find the interpreter (which I figured out from file and so on, see below). You'd get the same error from trying to run a file that started with

#!/usr/non-existant-path/bin/bash

As you discovered, the usual reason for this error message is when running an ELF binary on a system without the right dynamic linker and dynamic libraries installed (e.g. a 64bit system without 32bit support installed). In your case, it's because you used a bad link command and made a dynamic executable with a bad interpreter path.


I'm on Ubuntu 15.10, where GNU file version 5.22 reports:

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

There is no /lib/ld64.so.1 on my system. ldd output is confusing, because ldd uses its default ELF interpreter, not the one specified by the binary.

$ 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)

So it assumes that the runtime interpreter in the binary resolved to the one ldd used itself, I guess.

Your ldd output is probably from an old version too, since it just shows /lib64/ld-linux-x86-64.so.2 for that line. Not taking a bad guess is probably better behaviour, for a weird case like this, but doesn't help you see that your binary has a weird interpreter path.

readelf -l a.out

will decode the ELF headers for you, including the interpreter path. (Thanks to @EmployedRussian's comment for pointing this out.)

Maples answered 29/11, 2015 at 5:3 Comment(1)
The standard way to see what the interpreter is for a given binary is readelf -l a.out | grep interpreterElectrotherapeutics
S
5

Use that:

  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
Skillful answered 28/11, 2015 at 11:4 Comment(3)
First, crt1.o, crti.o, and crtn.o are present in /usr/lib/x86_64-linux-gnu/ on my machine. After changing that and running ld and then the a.out, I get the error: bash: ./a.out: Accessing a corrupted shared libraryInformality
@pratyaksh You also need to adapt the linker to /lib64/ld-linux-x86-64.so.2 .Glade
@piggs_boson: That's a good reason to link dynamic executables with gcc c.o or gcc -nostartfiles c.o instead of ld manually - your distro will have configured it to use the right paths for your system. Use gcc -v to see what those paths are on your system, rather than someone else's on the Internet.Maples

© 2022 - 2024 — McMap. All rights reserved.