0

Let's say I run the following command to compile/run a basic C program which returns the result 48:

$ gcc -v -o file.o file.c && ./file.o; echo $?
# 48

The -v flag returns a lot of data, for example:

$ gcc -v -o file.o file.c && ./file.o; echo $?

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04.1' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.1)
COLLECT_GCC_OPTIONS='-v' '-o' 'file.o' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -quiet -v -imultiarch x86_64-linux-gnu file.c -quiet -dumpbase file.c -mtune=generic -march=x86-64 -auxbase file -version -fstack-protector -Wformat -Wformat-security -o /tmp/ccZ9MZvc.s
GNU C (Ubuntu 4.8.4-2ubuntu1~14.04.1) version 4.8.4 (x86_64-linux-gnu)
    compiled by GNU C version 4.8.4, GMP version 5.1.3, MPFR version 3.1.2-p3, MPC version 1.0.1
warning: MPFR header version 3.1.2-p3 differs from library version 3.1.3.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
GNU C (Ubuntu 4.8.4-2ubuntu1~14.04.1) version 4.8.4 (x86_64-linux-gnu)
    compiled by GNU C version 4.8.4, GMP version 5.1.3, MPFR version 3.1.2-p3, MPC version 1.0.1
warning: MPFR header version 3.1.2-p3 differs from library version 3.1.3.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 47b61fdffcea151b8f9b26c4df78cd3c
COLLECT_GCC_OPTIONS='-v' '-o' 'file.o' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/cc5PIIv9.o /tmp/ccZ9MZvc.s
GNU assembler version 2.24 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.24
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'file.o' '-mtune=generic' '-march=x86-64'
 /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o file.o /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/crtbegin.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/cc5PIIv9.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.8/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o

48

From what I gather (I think?) the three commands it is running are:

  1. /usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -quiet -v -imultiarch x86_64-linux-gnu file.c -quiet -dumpbase file.c -mtune=generic -march=x86-64 -auxbase file -version -fstack-protector -Wformat -Wformat-security -o /tmp/ccZ9MZvc.s
  2. as -v --64 -o /tmp/cc5PIIv9.o /tmp/ccZ9MZvc.s
  3. /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o file.o /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/crtbegin.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/cc5PIIv9.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.8/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o

Is that correct, or is it running other commands as well that I may have missed. I know this is very broad, but could someone explain basically what those three commands are doing / perhaps re-writing them to exclude any non-essential options?


After playing around with it a bit it seems like it can be boiled down to (roughly):

$ cc1 file.c -o file.s
$ as -o file.o file.s

But the collect2 / ld command has been a bit tricky.

David542
  • 104,438
  • 178
  • 489
  • 842
  • 2
    1 is the C compiler 2 is the assembler and 3 is the linker. You can just use `gcc foo.s` on an assembly file and have gcc do steps 2 and 3. Usually you don't care about the internals. – Jester Jul 29 '20 at 23:05
  • 1
    On GNU/Linux, I think `collect2` just passes along all its args to the system `ld`; AFAIK it doesn't have to do anything. – Peter Cordes Jul 29 '20 at 23:09
  • @PeterCordes yes it seems to be an alias for `ld` because I sometimes get: `collect2: error: ld returned 1 exit status` – David542 Jul 29 '20 at 23:21
  • 1
    No, that actually only makes sense if it's a wrapper. `collect2` is tell you that the `ld` command it ran failed. If it was just a wrapper, it would have simply exited with status=1 itself instead of printing anything. Also if it was a wrapper, it would either think of itself as `ld`, or use the command name it was invoked as (`collect2`) from its `argv[0]`. Seeing `collect2: some message about ld` makes it clear it's a wrapper. – Peter Cordes Jul 29 '20 at 23:23
  • @PeterCordes is this part the linker: `-dynamic-linker /lib64/ld-linux-x86-64.so.2` ? – David542 Jul 29 '20 at 23:24
  • 1
    No, `ld` does static linking, creating an executable with the right path to the ELF interpreter (dynamic linker). In the same way that the kernel runs bash on a `#!/bin/bash` script, the kernel runs the ELF-interpreter "on" a dynamically linked ELF executable. Use `readelf -a a.out` to see that path, or make a static executable without one. (see also [What's the difference between "statically linked" and "not a dynamic executable" from Linux ldd?](https://stackoverflow.com/q/61553723) for an example) – Peter Cordes Jul 29 '20 at 23:26
  • The linker command includes bits and pieces of the C and gcc libraries and may depend on versions and stuff. If you have standalone code that does not need anything you can omit linking those but otherwise better leave it to gcc to do whatever appropriate. Note that even terminating the program already needs something as you already found out in your previous question. You might get away with just `-lc -dynamic-linker /lib64/ld-linux-x86-64.so.2` if you use `exit(48);` (should be `42` obviously :)) – Jester Jul 29 '20 at 23:38
  • It may be worthy of a question as to why the default value for `-dynamic-linker` isn't usually correct and thus has to be overridden. – Jester Jul 29 '20 at 23:39
  • 1
    As someone who uses some Linux systems where binaries are installed in oddball locations and GCC may have a slightly modified default specs file, I'm **really** hoping you don't actually do that in any project that may sneak outside your computer; and are only trying to examine this particular build process to uncover something interesting, say perhaps a compiler/linker/library bug. – None Jul 29 '20 at 23:48
  • @PeterCordes I see -- so is `collect2` a sort of wrapper around `ld` or its a separate program? – David542 Jul 30 '20 at 03:40
  • 1
    It's a separate program that runs `ld`, passing on all its args. That's what "wrapper" means. – Peter Cordes Jul 30 '20 at 03:43
  • @Jester: I seem to recall writing an answer pointing out some history of GNU/Linux multilib systems and how toolchain defaults were appropriate for every AMD64 ports, but a re-org to make it easier and cleaner to have 32-bit libs installed on a 64-bit system changed the ld.so path. So if anyone is thinking of posting that question, have a look first because it might already be asked and answered. – Peter Cordes Jul 30 '20 at 03:46

0 Answers0