consider the following gas (GNU assembler) file for 64 bit linux
go.s
.global _start
.text
_start:
mov $60, %rax # system call 60 is exit
xor %rdi, %rdi # we want return code 0
syscall
Now to compile:
rm -f go go.o
as -o go.o go.s
ld -o go -s -nostartfiles go.o
ls -l go # -> 344 bytes
We get a super-small size of 344 bytes.
Great! With gcc and separate ld
command:
# gcc with separate link
rm -f go go.o
gcc -xassembler -c go.s
ld -s -nostartfiles -o go go.o
ls -l go # -> 344 bytes
But how does one get that small size, using only a single gcc command?
# single gcc command
rm -f go go.o
gcc -xassembler -s -static -nostartfiles -o go go.s
ls -l go # -> 4400 bytes too big!!!
Damn 4400 bytes! Which single line gcc invocation will give 344 bytes?
Running the above single line with the -v
flag...
gcc -v -xassembler -s -static -nostartfiles -o go go.s
... shows that
-nostartfiles
is not passed to the collect2
link command.
Hmmm...
So task: show me the single-line gcc invocation giving the minimal size!
Thanks.
Experimenting with the -v
flag:
gcc -v -xassembler -s -static -nostartfiles -o go go.s
does the following:
as -v --64 -o go.o go.s
# will give 4400 bytes
/usr/lib/gcc/x86_64-linux-gnu/8/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/8/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/8/lto-wrapper -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --build-id -m elf_x86_64 --hash-style=gnu -static -o go -s -L/usr/lib/gcc/x86_64-linux-gnu/8 -L/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/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/8/../../.. go.o --start-group -lgcc -lgcc_eh -lc --end-group
Manually adding -nostartfiles
(directly after collect2
) in the /usr/lib/gcc/x86_64-linux-gnu/8/collect2
command (above) gives a size of 520 bytes.
Manually adding -nostartfiles
(directly after collect2
) and removing --build-id
gives a size of 344 bytes. As we can see here, -Wl,--build-id=none
should remove the build-id.
But how does one instruct gcc to pass -nostartfiles
to ld
or collect2
?
In response to Jester's comment:
Doing the following (-Wl,-nostartfiles
):
gcc -Wl,-nostartfiles,--build-id=none -v -xassembler -s -static -nostartfiles -o go go.s
will add -nostartfiles
too far at the end of the collect2
command, with the result that the output binary go
is not even created. How does one give the command, so that -nostartfiles
occurs earlier in the collect2 ...
command: I know it works, if I manually construct -nostartfiles
to be a flag right at the beginning.
Looking here, there is a claim that -Wl,-nostartfiles
will not be handled correctly. But if I just use gcc -nostartflags ...
, then it is not passed to the linker: Maby this is a bug in gcc??
Maby all code gets optimized away... leaving nothing at all?? See here