Actual default linker script and settings gcc uses

前端 未结 1 734
隐瞒了意图╮
隐瞒了意图╮ 2021-02-07 10:07

Where can I find the actual linker script and settings gcc uses?


Things I\'ve tried:

For concreteness, let\'s consider a small program: empty.c

相关标签:
1条回答
  • 2021-02-07 10:36

    Well, I know that this is an old question, but I also found it frustrating that there is no precise info about options that are used during the linking process. This answer shows my journey to find them.

    First of all, I was looking into official docs https://gcc.gnu.org/onlinedocs/ - I searched the GCC Manual and GCC Internals Manual. The only meaningful information that I found is that gcc uses an internal tool called collect2 to invoke the linker. According to https://gcc.gnu.org/onlinedocs/gccint/Collect2.html "The program collect2 works by linking the program once and looking through the linker output file for symbols with particular names indicating they are constructor functions". So it's used to make linking possible.

    Next thing that I tried is getting through source code. You can browse code here https://code.woboq.org/gcc/gcc/collect2.c.html . The problem is that it wasn't really helpful. But I noticed that collect2 fork_execute function to invoke ld. You can deep dive into fork_execute to find out that it will fork (execute a new program in the forked program) and wait for it to finish. Because both forks and execs are system calls (to put it quickly - system calls are the way/functions the application uses to communicate with a system). I decided to give it a try.

    So I made the simple program that doesn't require any compilation (it's already compiled to object file - so everything that gcc have to do is linking).

    [Alex@Normandy tmp]$ gcc hello.c.s  -o hello_gcc
    [Alex@Normandy tmp]$ ./hello_gcc 
    Hello, World!
    

    Then I use strace with following options:

    • -o forked.log save the output to forked.log
    • -s 1024 variables shorter than 1024 chars are not truncated (default 32 was not enough)
    • -f - enables strace on forked processes
    • -e trace=/exec - filter system calls so only ones starting with exec are shown

    The final output was following.

    [Alex@Normandy tmp]$ strace -o forked.log -s 1024 -f -e trace=/exec gcc hello.c.s  -o hello_gcc
    [Alex@Normandy tmp]$ grep 'ld' forked.log 
    2153  execve("/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2", ["/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2", "--build-id", "--no-add-needed", "--eh-frame-hdr", "--hash-style=gnu", "-m", "elf_x86_64", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "hello_gcc", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64", "-L/lib/../lib64", "-L/usr/lib/../lib64", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../..", "/tmp/ccyl36jf.o", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o"], 0x17b9da0 /* 61 vars */) = 0
    2154  execve("/usr/bin/ld", ["/usr/bin/ld", "--build-id", "--no-add-needed", "--eh-frame-hdr", "--hash-style=gnu", "-m", "elf_x86_64", "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", "-o", "hello_gcc", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64", "-L/lib/../lib64", "-L/usr/lib/../lib64", "-L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../..", "/tmp/ccyl36jf.o", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "-lc", "-lgcc", "--as-needed", "-lgcc_s", "--no-as-needed", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o", "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o"], 0x7fff14226a98 /* 61 vars */) = 0
    

    So used ld command was

    /usr/bin/ld --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello_gcc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccyl36jf.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o
    

    So what the f*** was made? Well, all options can be found in the manual. Here is decomposed output.

    • /usr/bin/ld - linker program
    • --build-id - add build-id to binary. In my system default it is sha1.
    • --no-add-needed - it is depracaceted name for --no-copy-dt-needed-entries - it is connected with DT_NEEDED tags inside ELF, if I get that correctly it means that DT_NEEDED tag won't be copied from input libraries.
    • --eh-frame-hdr - "Request creation of ".eh_frame_hdr" section and ELF "PT_GNU_EH_FRAME" segment header." Whatever that means.
    • --hash-style=gnu - "Set the type of linker's hash table(s)." Default is sysv, but there is a newer format gnu. Binary can also have a hash table(s) in both formats.
    • -m elf_x86_64 - linkers emulates (makes elf type binary for x86_64)
    • -dynamic-linker /lib64/ld-linux-x86-64.so.2 - set name of expected dynamic linker
    • -o hello_gcc - set output binary
    • /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o - code that is run before main of actual program
    • /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o- code that is run before main of actual program
    • /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o - code that is run before main of actual program
    • -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 - additional library search path
    • -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 - additional library search path
    • -L/lib/../lib64 - additional library search path
    • -L/usr/lib/../lib64 - additional library search path
    • -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. - additional library search path
    • /tmp/ccyl36jf.o - this is actual program (binary object) with it's main function
    • -lgcc - -l option - "Add the archive or object file specified by namespec to the list of files to link." In that case it is gcc.
    • --as-needed - enable "as-needed" mode that checks if on particular point following library (namespace?) is needed
    • -lgcc_s - add gcc_s note that only if it's really needed at this moment.
    • --no-as-needed - disable "as-needed" mode that checks if on particular point following library (namespace?) is needed
    • -lc- standard C namespace/library
    • -lgcc - this lib should be already set. There might be something between this and previous usage of this option.
    • --as-needed - set "as-needed mode. There might be something between this and previous usage of this option.
    • -lgcc_s - already described. There might be something between this and previous usage of this option.
    • --no-as-needed -- disable "as-needed mode".
    • /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o - additional code that run when program finish
    • /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o - additional code that run when program finish.

    More about: crt1.o, crti.o, crtbegin.o, crtend.o, crtn.o - they are startup, initialization, constructor, destructor and finalization files (according to Building Embedded Linux Systems By Karim Yaghmour).

    Probably simpler way

    During writing this answer I also "discovered" that you can invoke gcc with -v option and it will return COLLECT_GCC_OPTIONS, that is identical to invoked ld

    COLLECT_GCC_OPTIONS='-v' '-o' 'hello_gcc' '-mtune=generic' '-march=x86-64'
     /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello_gcc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. hello_gcc.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o
    

    Still, if you want to be sure for 100% how ld was invoked - the strace is your best bet.

    Lastly, note that I used Enterprise Linux v7 and v8 system to check if I'm right. Both of them uses the x86_64 arch, and the results might be different on different architectures.

    0 讨论(0)
提交回复
热议问题