Reputation: 7065
I have two C source files
foo1.c
:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
puts("hello world");
return 0;
}
and foo2.c
:
#include <stdlib.h>
#include <stdio.h>
void _start(void)
{
puts("hello world");
exit(0);
}
and I compile them like so on my i386 GNU/Linux platform:
$ #compile foo1
$ cc -o foo1 foo1.c
$ #compile foo2
$ cc -S foo2.c
$ as -o foo2.o foo2.s
$ ld -o foo2 -dynamic-linker /lib/i386-linux-gnu/ld-linux.so.2 -lc foo2.o
$ #notice that crt1.o and others are missing
The outputted executables do the same thing from a user's perspective.
$ ./foo1
hello world
$ ./foo2
hello world
But they are different:
$ wc -c foo1
5000
$ wc -c foo2
2208
$ objdump -d foo1 | wc -l
238
$ objdump -d foo2 | wc -l
35
Even when I enable gcc's -Os
option to optimize size,
$ #compile foo1
$ gcc -o foo1 foo1.c -Os
it is not much smaller:
$ wc -c foo1
4908
$ objdump -d foo1 | wc -l
229
Is there any way to get GCC to optimize out the parts of crt1.o
and friends which I suspect contribute to this bloated filesize without resorting to nonstandard code and weird (and likely harmful in some cases) compilation? My GCC's version string is "gcc (Debian 4.9.2-10) 4.9.2
".
Upvotes: 3
Views: 2527
Reputation: 7802
With gcc/clang you can use -nostartfiles
, but the c library you are using may depend on its own _start()
implementation for dynamic linking. Since you are on Linux, I would recommend using a static build of musl-libc.
Alternatively you could just implement the write()
and exit()
systemcalls and add the '\n' to your string to avoid the c library altogether by using _start()
instead of main()
. If you need access to argc, argv and envp, you will need some inline assembly to access the stack (Linux passes these on the stack for all ELF binaries regardless of the architecture).
Upvotes: 8