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.
There are several things wrong with this command line:
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 differentcrt1.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 useld
directly).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.Use that:
The other answers only address how to avoid this, not the actual question of what happened.
The
gcc -c a.c; ld -lc a.o
commands you gave produce a pretty obvious warning: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:
execve(2)
returns ENOENT because it can't find the interpreter (which I figured out fromfile
and so on, see below). You'd get the same error from trying to run a file that started withAs 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:There is no
/lib/ld64.so.1
on my system.ldd
output is confusing, becauseldd
uses its default ELF interpreter, not the one specified by the binary.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.will decode the ELF headers for you, including the interpreter path. (Thanks to @EmployedRussian's comment for pointing this out.)