I'm trying to get my head around how the linking process works when producing an executable. To do that I'm reading Ian Taylor's blog series about it, but a lot of it is beyond me at the moment - so I'd like to see how it works in practice.
At the moment I produce some object files and link them via gcc with:
gcc -m32 -o test.o -c test.c
gcc -m32 -o main.o -c main.c
gcc -m32 -o test main.o test.o
How do I replicate the gcc -m32 -o test main.o test.o stage using ld?
I've tried a very naive: ld -A i386 ./test.o ./main.o
But that returns me these errors:
ld: i386 architecture of input file `./test.o' is incompatible with i386:x86-64 output
ld: i386 architecture of input file `./main.o' is incompatible with i386:x86-64 output
ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
./test.o: In function `print_hello':
test.c:(.text+0xd): undefined reference to `_GLOBAL_OFFSET_TABLE_'
test.c:(.text+0x1e): undefined reference to `puts'
./main.o: In function `main':
main.c:(.text+0x15): undefined reference to `_GLOBAL_OFFSET_TABLE_
I'm most confused by _start and _GLOBAL_OFFSET_TABLE_ being missing - what additional info does gcc give to ld to add them?
Here are the files:
main.c
#include "test.h"
void main()
{
print_hello();
}
test.h
void print_hello();
test.c
#include <stdio.h>
void print_hello()
{
puts("Hello, world");
}
#sam : I am not the best people to answer your question because I am a beginner in compilation. I know how to compile programs but I do not really understand all the details (https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools)
So, I decided this year to try to understand how compilation works and I tried to do, more or less, the same things as you tried a few days ago. As nobody has answered, I am going to expose what I have done but I hope an expert will supplement my answer.
Short answer : It is recommended to not use ld directly but to use gcc directly instead. Nevertheless, it is, as you write, interesting to know how the linking process works. This command works on my computer :
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o /usr/lib/crtn.o
Very Long answer :
How did I find the command above ?
As n.m suggested, run gcc with -v option.
gcc -v -m32 -o test main.o test.o
... /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 ... (many
options and parameters)....
If you run ld with these options and parameters (copy and paste), it should work.
Try your command with -m elf_i386 (cf. collect2 parameters)
ld -m elf_i386 test.o main.o
ld: warning: cannot find entry symbol _start; ....
Look for symbol _start in object files used in the full ld command.
readelf -s /usr/lib/crt1.o (or objdump -t)
Symbol table '.symtab' contains 18 entries: Num: Value Size
Type Bind Vis Ndx Name... 11: 00000000 0 FUNC
GLOBAL DEFAULT 2 _start
Add this object to your ld command :ld -m elf_i386 test.o main.o /usr/lib/crt1.o
... undefined reference to `__libc_csu_fini'...
Look for this new reference in object files. It is not so obvious to know which library/object files are used because of -L, -l options and some .so include other libraries. For example, cat /usr/lib/libc.so. But, ld with --trace option helps. Try this commandld --trace ... (collect2 parameters)At the end, you should findld -m elf_i386 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc_nonshared.a /lib/libc.so.6 /usr/lib/crti.oor shorter (cf. cat /usr/lib/libc.so) ld -m elf_i386 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o
It compiles but it does not run (Try to run ./test). It needs the right -dynamic-linker option because it is a dynamically linked ELF executable. (cf collect2 parameters to find it) ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o But, it does not run (Segmentation fault (core dumped)) because you need the epilogue of the _init and _fini functions (https://gcc.gnu.org/onlinedocs/gccint/Initialization.html). Add the ctrn.o object. ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test test.o main.o /usr/lib/crt1.o /usr/lib/libc.so /usr/lib/crti.o /usr/lib/crtn.o./test
Hello, world
Related
I just want to test -pg, the source file is very simple, my environment is
cygwin,
$ uname -a
CYGWIN_NT-10.0 SHA-LPLATOW 2.8.2(0.313/5/3) 2017-07-12 10:58 x86_64 Cygwin
$ vi pgtest.c
#include <stdio.h>
void main(void){
printf("hello, world\n");
}
no -pg compiling is OK.
$ gcc -c pgtest.c
$ gcc -o pgtest.exe pgtest.o
but -pg report error
$ gcc -pg -c pgtest.c
$ gcc -o pgtest.exe pgtest.o
pgtest.o:pgtest.c:(.text+0x1): undefined reference to `__fentry__'
pgtest.o:pgtest.c:(.text+0x1): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `__fentry__'
pgtest.o:pgtest.c:(.text+0xe): undefined reference to `_monstartup'
pgtest.o:pgtest.c:(.text+0xe): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `_monstartup'
collect2: error: ld returned 1 exit status
I have tried LDFLAGS, it is the same.
export LDFLAGS="-pg" ; gcc -o pgtest.exe pgtest.o
from the gcc info page
'-pg'
Generate extra code to write profile information suitable for the
analysis program 'gprof'. You must use this option when compiling
the source files you want data about, and you must also use it when
linking.
so if you want to do a separate compilation and linking you need to repeate the -pg
$ gcc -c pgtest.c -pg
$ gcc -o pgtest.exe pgtest.o -pg
I have a library defined in libadd.asm, it exposes one "function" _add. I have a .c source file that refers to add and I'm trying to get the two object files to link, but am encountering this error regardless of the order in which I link the object files:
Undefined symbols for architecture x86_64:
"_add", referenced from:
_main in prog.o
Here's the code:
// prog.c
#include <stdio.h>
int add();
int main() {
printf("%d\n", add(4, 5));
return 0;
}
And here's the assembly file. It almost certainly doesn't respect the appropriate calling convention. I don't really understand what I should be doing to shuffle the values between registers. (That's what I was trying to figure out originally.)
; libadd.asm
_add:
add eax, edx
ret
Here's what I'm using to the tiny project. I'm intentionally shadowing the implicit .c.o rule with one that does as little as possible and ignores *FLAGS. I'm using cc to drive the linker because that's the simplest way I know to link in the c runtime/standard library/whatever it's called. I've always tried linking with prog.o and libadd.o in the other order.
all: prog
prog: prog.o libadd.o
$(CC) -o prog $^
%.o: %.asm
nasm -f macho64 -o $# $<
%.o: %.c
$(CC) -c -o $# $<
clean:
$(RM) $(wildcard *.o)
running make produces the following output
cc -c -o prog.o prog.c
nasm -f macho64 -o libadd.o libadd.asm
cc -o prog prog.o libadd.o
Undefined symbols for architecture x86_64:
"_add", referenced from:
_main in prog.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [prog] Error 1
Exit 2
libadd.o gets assembled successfully and appears to have the right symbol in it.
% nm libadd.o
0000000000000000 t _add
Why is ld complaining that it can't find the symbol?
I'm trying to make a relocatable object file with gcc. I use solution from this post. The solution works fine with ld:
$ ld -r a.o b.o -o c.o
However when I try to use it with gcc, the following error happens:
$ gcc -r a.o b.o -o c.o
/usr/bin/ld: cannot find -lgcc_s
/usr/bin/ld: cannot find -lgcc_s
collect2: ld returned 1 exit status
Using the -Wl,-r and -Wl,--relocatable options gives the same result.
Is there any way to link relocatable object file with gcc or I'm forced to use ld for doing this?
To solve this problem, the -nostdlib option must also be passed to gcc:
$ gcc -r -nostdlib a.o b.o -o c.o
I don't know it for sure, but it seems without this option gcc tries to link standard libraries into output relocatable object.
I got a problem with link objective files on a Mac OS X. Tracing back the problem is,
here is my C hello world program
#include <stdio.h>
int main(){
printf("Hello, world!\n");
return 0;
}
//Compile with gcc (clang LLVM compiler on mac)
$ gcc -c hello.c
The output file is hello.o
link with gcc and run the executable is
$ gcc hello.o -o hello
$ ./hello
Now, I have to use the mac linker program ld or Ld to link the the objective files instead of gcc. In this case, what arguments should I pass into the ld program in order to get the program run? A simple pass in the object file name, i.e.
$ ld hello.o
resulting in
ld: warning: -macosx_version_min not specified, assuming 10.6
Undefined symbols for architecture x86_64:
"_printf", referenced from:
_main in hello.o
"start", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for inferred architecture x86_64
So what other files that i need to include to link or architecture information that I need to specify? Thanks.
For a reference, my complete linker options are
ld -demangle -dynamic -arch x86_64
-macosx_version_min 10.9.0
-o hello
-lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/5.0/lib/darwin/libclang_rt.osx.a
Okay, I had this question before too. Yes, the reason for the linker errors is because you need to feed it all the magic arguments that gcc does. And the easy way to discover those is to invoke the -v option on gcc to reveal all the commands executed in the compilation stages. In your case, run:
gcc hello.o -o hello -v
...the output of which, on my system, ends with the line:
/usr/libexec/gcc/i686-apple-darwin9/4.2.1/collect2 -dynamic -arch i386 -macosx_version_min 10.5.8 -weak_reference_mismatches non-weak -o test -lcrt1.10.5.o -L/usr/lib/i686-apple-darwin9/4.2.1 -L/usr/lib/gcc/i686-apple-darwin9/4.2.1 -L/usr/lib/gcc/i686-apple-darwin9/4.2.1 -L/usr/lib/gcc/i686-apple-darwin9/4.2.1/../../../i686-apple-darwin9/4.2.1 -L/usr/lib/gcc/i686-apple-darwin9/4.2.1/../../.. test.o -lgcc_s.10.5 -lgcc -lSystem
I don't know what the collect2 program is, but if you feed all those arguments to ld it should work just the same (at least it does on my system).
I'm trying to create a shared library with my gcc. It's a gcc for vxworks (thats probably the problem...).
I use the gcc as following:
./gcc -shared -B/path/to/gnutools/bin -o test.so test.c
Result:
/path/to/ld: -r and -shared may not be used together
collect2: ld returned 1 exit status
If I try the same with the linux gcc, there's no problem. So i guess the gcc for VxWorks automatically passes the -r (or -i, which is the same and results in the same) flag to the linker. Is there a way to suppress this?
Greetz
marty
PS: making it static is not really an alternative...
Try compile object file separately with -fPIC and then link:
gcc -Wall -fPIC -c -o test.o test.c
gcc -Wall -shared -o test.so test.o
Another suggestion is to use libtool (at least to figure out the correct flags).
A workaround may be to go directly with ld:
ld -shared -o test.so test.o -lc