What's the Difference between linking by GCC and LD? - gcc

Recently I was creating a loadable module and found that both
gcc -fPIC --shared -o foo.so.1 foo.c
and
gcc -fPIC --shared -c foo.c
ld --shared -o foo.so.2 foo.o
can achieve the same effect.
I also discovered that foo.so.1 is larger than foo.so.2 by about 3KB, and
gcc -### -fPIC --shared -o foo.so.1 foo.c
revealed that GCC added stuffs other than foo.c into foo.so.1 (e.g, crtendS.o and crtn.o):
/usr/lib/gcc/x86_64-linux-gnu/4.7/collect2 "--sysroot=/" --build-id --no-add-needed --eh-frame-hdr -m elf_x86_64 "--hash-style=both" -shared -o foo.so.1 /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.7/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/4.7 -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.7/../../.. /tmp/cc3JBdCJ.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/4.7/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crtn.o
Since both foo.so.1 and foo.so.2 can be loaded via dlopen, I was wondering:
What's the difference between these 2 linking methods?
Do crtendS.o and crtn.o make any difference to functions in created libraries?

There's no difference in principle. When you "link by gcc" it actually calls ld. If you get a message at the linking stage when "linking by gcc" you'll immediately see that it is actually from ld. If you want to pass some ld-specific command-line options to ld, gcc's command-line interface has features intended specifically for that purpose (-Xlinker and -Wl options).
As for the additional objects files... they probably contain global load-time library initialization/de-initialization code implicitly added by the compiler. (Requested by the standard library?) You can find some information about it here: https://gcc.gnu.org/onlinedocs/gccint/Initialization.html

Related

GCC: include math.h function in bare-metal software on ARM (arm-none-eabi-gcc)

I am working on a bare-metal free standing software on a STM32H753. I'm not using neither the libc nor the crt.
Here is the link command line:
arm-none-eabi-gcc -T"xxx.ld" -mfpu=fpv5-d16 -mfloat-abi=hard -mthumb -ffreestanding -nostdlib -nostartfiles --specs=nosys.specs -Wl,--start-group -lc -lm -Wl,--end-group -Wl,-Map=xxx.map -o xxx.elf <list of .o>
Now I need to include math library since I am using sqrt function. i thought the link command line would be sufficient but I get a "sqrt undefined" error.
I tried to add the path to the libm.a: (also tried without -Wl)
arm-none-eabi-gcc -T"xxx.ld" -Wl,-L/opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/ -mfpu=fpv5-d16 -mfloat-abi=hard -mthumb -ffreestanding -nostdlib -nostartfiles --specs=nosys.specs -Wl,--start-group -lc -lm -Wl,--end-group -Wl,-Map=build_uP1/base_gen_uP1.map -o build_uP1/base_gen_uP1.elf <list of .o>
But I still get the same error.
I don't understand what options to choose to link with the correct library
Sorry for this self answer but I think I 've found the solution.
my first mistake is that the library must be put at the end of the command line. the order of arguments does matter.
then there are many versions of libm.a in the gcc install so I had to pick the right one
The following line is working:
arm-none-eabi-gcc -T"xxx.ld" -L/opt/gcc-arm-none-eabi-10-2020-q4-major/arm-none-eabi/lib/thumb/v7+fp/hard/ -mfpu=fpv5-d16 -mfloat-abi=hard -mthumb -ffreestanding -nostdlib -nostartfiles --specs=nosys.specs -Wl,-Map=xxx.map -o xxx.elf <list of .o> -lm -lc
I've noticed that one symbol and some data from libc.a are needed: __errno, and impure_data

Selecting correct multilib version for linker automatically

I am using GCC to compile a program for an Atmel Cortex M4 SAM4S Processor. I need to link the standard libraries libgcc.a and libc.a, and to do so I am currently using the following makefile commands
LIBDIR1=C:/Program\ Files\ \(x86\)/GNU\ Arm\ Embedded\ Toolchain/10\ 2020-q4-major/arm-none-eabi/lib/thumb/v7e-m/nofp
LIBDIR2=C:/Program\ Files\ \(x86\)/GNU\ Arm\ Embedded\ Toolchain/10\ 2020-q4-major/lib/gcc/arm-none-eabi/10.2.1/thumb/v7e-m/nofp
ld_input=linker.ld startup.o main.o syscalls.o
ld_flags=-L$(LIBDIR1) -L$(LIBDIR2) -lm -lc -lgcc
out.elf: $(ld_input)
arm-none-eabi-ld -o out.elf -T $(ld_input) $(ld_flags)
However, to link to the correct multilib, I need to specify the subdirectories /thumb/v7e-m/nofp explicitly, which I determined via
arm-none-eabi-gcc -mcpu=cortex-m4 --print-multi-dir
Is there a way to link to the correct subdirectories automatically, based on the -mcpu=cortex-m4 option?
It seems everything works by using gcc instead of ld for linking. gcc then calls the linker with all the correct options, including all the standard librarier automatically.
The code shown in the question can be replaced by
ld_input=linker.ld startup.o main.o syscalls.o
out.elf: $(ld_input)
arm-none-eabi-gcc -mcpu=cortex-m4 -o out.elf -T $(ld_input)

Making relocatable object with gcc causes "cannot find -lgcc_s" error

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.

GCC Static Linking And Separate Loader

I'm trying to understand the process of static linking, loading of GCC:
I have the following toy program
#include "stdio.h"
int main() {
fprintf(stdout, "Hello World \n");
return 0 ;
}
I can compile it and run file as follows:
gcc -static -std=gnu99 -Wall -Wno-unused -g test.c -o test;
But as soon as I try to separate out the compile and linking process as follows:
gcc -static -std=gnu99 -Wall -Wno-unused -g test.c -c;
ld -o test -T link.lds test.o
where the link.lds is
SECTIONS
{
. = 0x10000;
.text : { *(.text) }
. = 0x8000000;
.data : { *(.data) }
.bss : { *(.bss) }
}
I get the error "undefined reference to stdouttest.o: In function `main':
test.c:(.text+0x7): undefined reference to `stdout'
test.c:(.text+0x1e): undefined reference to `fwrite'
If I try adding the flag -lc to ld, it tells me that it is not found. I've tried running gcc with -lc
and/or -static-libgcc but I have the same problem.
What am I doing wrong?
Do
gcc -v -static -std=gnu99 -Wall -Wno-unused -g test.c
and look for the collect2 tag.
In my case it is
collect2 --sysroot=/ --build-id -m elf_x86_64 --hash-style=gnu --as-needed -static -z relro -o test /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/crtbeginT.o -L/usr/lib/gcc/x86_64-linux-gnu/4.8 -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../.. /tmp/ccoR98Xr.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o
You have to replace the temporary object file. In my case I replaced /tmp/ccoR98Xr.o with test.o. Then do
gcc -c -std=gnu99 -Wall -Wno-unused -g test.c
ld --sysroot=/ --build-id -m elf_x86_64 --hash-style=gnu --as-needed -static -z relro -o test /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/crtbeginT.o -L/usr/lib/gcc/x86_64-linux-gnu/4.8 -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.8/../../.. test.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o
It links to the object files : crt1.o, crti.o, crtbeginT.o, crtend.o, and crtn.o.
It links to the libraires: libgcc.a, libgcc_eh.a, and libc.a.
You can replace --start-group -lgcc -lgcc_eh -lc --end-group with -lgcc -lc -lgcc_eh -lc if you like.
Knowing this we can simply this to
ln -s `gcc -print-file-name=crt1.o`
ln -s `gcc -print-file-name=crti.o`
ln -s `gcc -print-file-name=crtn.o`
ln -s `gcc -print-file-name=libgcc_eh.a`
ln -s `gcc -print-file-name=libc.a`
gcc -c -std=gnu99 -Wall -Wno-unused -g test.c
ld -m elf_x86_64 -o test crt1.o crti.o test.o libc.a libgcc_eh.a libc.a crtn.o
I did not use crtbeginT.o, crtend.o, and libgcc.a because it worked without them.
Take a look at this, strace does the job (show you the secrets) but you will soon realize there are tons of options in it... You need to link a few stuff together (from GNU C lib) to get your executable, not only your object... You can add grep 'exec' in the end to make it cleaner.
Uhhh, also you need to do this:
as obj.s -o obj.o
Use GNU assembler to convert your .s to .o then link with ld.

GCC suppress flags

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

Resources