Hi I made a simple hello world C program and I am compiling it like this :
gcc -c hello.c
ld hello.o -lc -o out
I get a warning from ld : ld : _start not found defaulting to ....
I do an objdump -D hello.o and I cannot find the _start routine in the output.
What am I missing here ?
You're missing the crt* stuff which you will see if you link with gcc -v: crt1.o, crtend.o, crtn.o. Look at how gcc invokes collect2 (it's visible with gcc -v) and use the same options for ld.
main function is not the executable entry point: some initialization for standard library is done before main (because it's either impossible or illogical to do otherwise). Real entry point, which is _start by default, is in crt1.o which is always linked into your executable.
It's because you don't have a main function, also you can try
gcc -v hello.c -o hello
See if it compiles successfully.
On my system (Angstrom Linux, gcc 4.3.3), it happened due me installing libgcc-s-dev instead of libgcc-dev. No binary on the system contained _start string, I checked that. Installing libgcc-dev helped.
Related
I'm trying to understand C compilation
Given this simple C code in main.c:
int main() {
int a;
a = 42;
return 0;
}
I performed the following operations:
cpp main.c main.i
/usr/lib/gcc/x86_64-linux-gnu/9/cc1 main.i -o main.s
as -o main.o main.s
ld -o main.exe main.o
When executing main.exe, I get a Segmentation Fault.
How can I get a good memory addressing in this example?
When I try the sequence of commands from your question on an x86_64 Ubuntu 19.10 system, I get a warning from ld:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
This is an indication that something is wrong.
The error means that the linker did not find a symbol _start and used a default address instead. When running your program it will try to execute code at this address which apparently is invalid.
An executable program compiled from C code doesn't contain only your code. The compiler instructs the linker to add C run-time library and startup code. The startup code is responsible for initialization and for calling your main function.
Run e.g.
gcc -v -o main.exe main.o
to see what other files get added to your program. On my system this shows a few files with names starting with crt which means "C runtime".
If you don't use gcc to link your program but use ld directly, you have to manually add all necessary object files in a similar way as the compiler would do automatically.
I usually use gcc to compile my C program, it works ok, but when I tried to compile static library with -static parameter it always failed.
Although I tried some solutions on google, but it still didn't get fixed.
My command is as follows:
gcc mycode.c -static -L . -lurl -lcap -o mycode
The error message is:
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
but when I remove -static it works very well.
GCC's -static linkage option directs the linker to ignore shared libraries
during the linkage. So it must find static versions of all the libraries required
by the linkage, including those that are linked by default, such as libc.
You have not installed the static version of libc (which would be /usr/lib/???/libc.a), so:
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
libc.a is installed by the libc development package. The name of the libc
development package and how to install it depends on your distro. E.g. On Debian
or Ubuntu, the package to install is libc6-dev; on Fedora it is glibc-develop.
But before you go to do that, hang on a tick. You said:
I tried to compile static library with -static parameter it always failed.
gcc mycode.c -static -L . -lurl -lcap -o mycode
That sounds rather as if you just wanted to link your program with one or both
static libraries liburl.a, libcap.a, located in ./, and thought you should
do it by passing -static to the linkage.
There is no need to pass -static to link your program with ./liburl.a and/or
./libcap.a. The options:
-L . -lurl -lcap
will direct the linker to search in ./ for either of the files liburl.so (shared library)
or liburl.a (static library) and if it finds one or other of them it will link your
program with that library. If it finds both of them in ./, then it will choose the
shared library liburl.so. So unless you have ./liburl.so as well as ./liburl.a
then:
-L . -lurl
by itself will link your program against ./liburl.a.
And likewise for -lcap. No need for -static. The default shared library libc.so
will be linked automatically. The linker has no problem at all linking your program
with some static libraries and some shared ones. That is what is already happening
with your successful linkage:
gcc mycode.c -L . -lurl -lcap -o mycode
assuming that liburl.a and libcap.a are the only candidates for resolving
-lurl and -lcap in ./.
And even if you do have both ./liburl.a and ./liburl.so - and/or ./libcap.a and ./libcap.so - there is still no
need for a solution as drastic as a fully static linkage. You can just explicitly
tell the linker to find a particular static library if that's what you want, like:
gcc mycode.c -L . -l:liburl.a -l:libcap.a -o mycode
I have ASM code:
extern my_func
extern printf
extern exit
global _start
section .data
...
section .text
_start:
...
call printf
...
call my_func
...
call exit
and C code:
int my_func(int a, int b)
{
return a+b;
}
I'm using fedora on 64-bit machine. I want the executable be 32-bit.
For dynamic linking I do:
nasm -f elf32 asm.asm ; this gives me asm.o
gcc -m32 -Wall -c c_code.c ; this gives me c_code.o
ld c_code.o asm.o -melf_i386 -L /usr/lib/ -lc -I /lib/ld-linux.so.2 ; this gives me a.out which runs fine and weights 5601 bytes.
What I want to do is link libc statically. I do the following:
gcc -o a2.out -m32 -static -m32 asm.o c_code.o
And I get error:
asm.o: In function `_start':
asm.asm:(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-redhat-linux/4.8.3/../../../../lib64/crt1.o:(.text+0x0):
first defined here
collect2: error: ld returned 1 exit status
Then I change _start to main in ASM code and the whole thing links fine! ldd shows "not a dynamic executable". But the file created weights 721067 bytes! I think that it compiles statically a lot of unnecessary code.
So, my 1st question is:
1) How can I link statically only libc for the required printf and exit functions?
When I try
gcc -m32 -o a3.out -lc asm.o c_code.o ; ASM file has main instead of _start
I get a file that weights 7406 bytes. ldd shows the same dynamic libraries as for the a.out which weights 5601 bytes.
2) Why is that difference? Looks like some additional code that "connects" _start with main in my code...
3) What is the difference between linking with gcc and ld?
Thanks a lot for your attention!
1) How can I link statically only libc for the required printf
and exit functions?
Try compiling with -nostartfiles -static -nostdlib -lc which will avoid adding crt1.o and crtend.o. But keep in mind that this will disable all Glibc initialization code so some Glibc functions will fail to work.
2) Why is that difference? Looks like some additional code
that "connects" _start with main in my code...
GCC adds start files (crt*.o) which perform initialization. See the many online articles for details (e.g. this one).
3) What is the difference between linking with gcc and ld?
Already answered above but in general you can run gcc -v and inspect ld's (or collect2's) arguments.
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.
ld -lc a.o
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 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).
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.
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.)
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
I'm trying to compile a C program under Linux. However, out of curiosity, I'm trying to execute some steps by hand: I use:
the gcc frontend to produce assembler code
then run the GNU assembler to get an object file
and then link it with the C runtime to get a working executable.
Now I'm stuck with the linking part.
The program is a very basic "Hello world":
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
I use the following command to produce the assembly code:
gcc hello.c -S -masm=intel
I'm telling gcc to quit after compiling and dump the assembly code with Intel syntax.
Then I use th GNU assembler to produce the object file:
as -o hello.o hello.s
Then I try using ld to produce the final executable:
ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello
But I keep getting the following error message:
/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'
The symbols __libc_csu_fini/init seem to be a part of glibc, but I can't find them anywhere! I tried linking against libc statically (against /usr/lib/libc.a) with the same result.
What could the problem be?
/usr/lib/libc.so is a linker script which tells the linker to pull in the shared library /lib/libc.so.6, and a non-shared portion, /usr/lib/libc_nonshared.a.
__libc_csu_init and __libc_csu_fini come from /usr/lib/libc_nonshared.a. They're not being found because references to symbols in non-shared libraries need to appear before the archive that defines them on the linker line. In your case, /usr/lib/crt1.o (which references them) appears after /usr/lib/libc.so (which pulls them in), so it doesn't work.
Fixing the order on the link line will get you a bit further, but then you'll probably get a new problem, where __libc_csu_init and __libc_csu_fini (which are now found) can't find _init and _fini. In order to call C library functions, you should also link /usr/lib/crti.o (after crt1.o but before the C library) and /usr/lib/crtn.o (after the C library), which contain initialisation and finalisation code.
Adding those should give you a successfully linked executable. It still won't work, because it uses the dynamically linked C library without specifying what the dynamic linker is. You'll need to tell the linker that as well, with something like -dynamic-linker /lib/ld-linux.so.2 (for 32-bit x86 at least; the name of the standard dynamic linker varies across platforms).
If you do all that (essentially as per Rob's answer), you'll get something that works in simple cases. But you may come across further problems with more complex code, as GCC provides some of its own library routines which may be needed if your code uses certain features. These will be buried somewhere deep inside the GCC installation directories...
You can see what gcc is doing by running it with either the -v option (which will show you the commands it invokes as it runs), or the -### option (which just prints the commands it would run, with all of the arguments quotes, but doesn't actually run anything). The output will be confusing unless you know that it usually invokes ld indirectly via one of its own components, collect2 (which is used to glue in C++ constructor calls at the right point).
I found another post which contained a clue: -dynamic-linker /lib/ld-linux.so.2.
Try this:
$ 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
$
Assuming that a normal invocation of gcc -o hello hello.c produces a working build, run this command:
gcc --verbose -o hello hello.c
and gcc will tell you how it's linking things. That should give you a good idea of everything that you might need to account for in your link step.
In Ubuntu 14.04 (GCC 4.8), the minimal linking command is:
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
Although they may not be necessary, you should also link to -lgcc and -lgcc_s, since GCC may emit calls to functions present in those libraries for operations which your hardware does not implement natively, e.g. long long int operations on 32-bit. See also: Do I really need libgcc?
I had to add:
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
because the default linker script does not include that directory, and that is where libgcc.a was located.
As mentioned by Michael Burr, you can find the paths with gcc -v. More precisely, you need:
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
This is how I fixed it on ubuntu 11.10:
apt-get remove libc-dev
Say yes to remove all the packages but copy the list to reinstall after.
apt-get install libc-dev
If you're running a 64-bit OS, your glibc(-devel) may be broken. By looking at this and this you can find these 3 possible solutions:
add lib64 to LD_LIBRARY_PATH
use lc_noshared
reinstall glibc-devel
Since you are doing the link process by hand, you are forgetting to link the C run time initializer, or whatever it is called.
To not get into the specifics of where and what you should link for you platform, after getting your intel asm file, use gcc to generate (compile and link) your executable.
simply doing gcc hello.c -o hello should work.
Take it:
$ 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
Path to /usr/lib/crt*.o will when glibc configured with --prefix=/usr