Reputation: 241
I was looking at this page about hello world binary sizes and I was wondering if I had no lib c how small can I get my binary. I started off with something very simple (code below). As you can see I had no luck and 5ish instructions are still 13K of binary. What am I doing wrong?
$ cat nolib.c
void _start() {
asm("mov $60,%rax; mov $1,%rdi; syscall");
}
$ gcc -nostdlib nolib.c
$ strip a.out
$ ls -lh
-rwxr-xr-x 1 eric eric 13K Nov 21 18:03 a.out
Upvotes: 5
Views: 1265
Reputation: 91
First, we can improve it slightly if we compile the binary statically:
$ gcc -nostdlib -static nolib.c -o static_output
$ strip -s static_output # strip -s in order to strip all (not helping here)
$ ls -lh static_output
-rwxrwxrwx 1 graul graul 8.7K Jan 17 22:59 static_output
Lets look over our elf now: $ readelf -h static_output ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x401000 Start of program headers: 64 (bytes into file) Start of section headers: 8368 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 7 Size of section headers: 64 (bytes) Number of section headers: 7 Section header string table index: 6
Looks like there is more than 8kn before the start of sections header! Let's look at what this is made of:
$ readelf -e static_output
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x401000
Start of program headers: 64 (bytes into file)
Start of section headers: 8368 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 7
Size of section headers: 64 (bytes)
Number of section headers: 7
Section header string table index: 6
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.gnu.propert NOTE 00000000004001c8 000001c8
0000000000000020 0000000000000000 A 0 0 8
[ 2] .note.gnu.build-i NOTE 00000000004001e8 000001e8
0000000000000024 0000000000000000 A 0 0 4
[ 3] .text PROGBITS 0000000000401000 00001000
000000000000001b 0000000000000000 AX 0 0 1
[ 4] .eh_frame PROGBITS 0000000000402000 00002000
0000000000000038 0000000000000000 A 0 0 8
[ 5] .comment PROGBITS 0000000000000000 00002038
000000000000002a 0000000000000001 MS 0 0 1
[ 6] .shstrtab STRTAB 0000000000000000 00002062
000000000000004a 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000020c 0x000000000000020c R 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x000000000000001b 0x000000000000001b R E 0x1000
LOAD 0x0000000000002000 0x0000000000402000 0x0000000000402000
0x0000000000000038 0x0000000000000038 R 0x1000
NOTE 0x00000000000001c8 0x00000000004001c8 0x00000000004001c8
0x0000000000000020 0x0000000000000020 R 0x8
NOTE 0x00000000000001e8 0x00000000004001e8 0x00000000004001e8
0x0000000000000024 0x0000000000000024 R 0x4
GNU_PROPERTY 0x00000000000001c8 0x00000000004001c8 0x00000000004001c8
0x0000000000000020 0x0000000000000020 R 0x8
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
Section to Segment mapping:
Segment Sections...
00 .note.gnu.property .note.gnu.build-id
01 .text
02 .eh_frame
03 .note.gnu.property
04 .note.gnu.build-id
05 .note.gnu.property
06
This is weird, as we called the strip function which was supposed to remove this section from our elf. If we look over the response in https://unix.stackexchange.com/questions/267070/why-doesnt-strip-remove-section-headers-from-elf-executables We can see that though not mentioned specifically, strip does not remove these parts from our binary, but only removes their content (which does not help for our case).
We can use strip -R in order to remove these sections completely, the biggest one here is the ".eh_frame" segment (which is not needed for our case, look over Why GCC compiled C program needs .eh_frame section? to look over it).
$ strip -R .eh_frame static_output
$ ls -lh static_output
-rwxrwxrwx 1 graul graul 4.6K Jan 17 23:22 static_output*
Just to be clear, there is no reason to not strip the rest of the unwanted sections as well:
$ strip -R .eh_frame -R .note.gnu.property -R .note.gnu.build-id -R .note.gnu.property static_output
-rwxrwxrwx 1 graul graul 4.4K Jan 17 23:31 static_output
Half the size! But still not good enough. looks like there is a big program header we need to remove.
looks like gcc inserts these sections without our desire:
$ gcc -c -nostdlib -static nolib.c -o nolib.o
$ ls -l nolib.o
-rwxrwxrwx 1 graul graul 1376 Jan 17 23:40 nolib.o
$ strip -R .data -R .bss -R .comment -R .note.GNU-stack -R .note.GNU-stack -R .note.gnu.propery -R .eh_frame -R .real.eh_frame -R .symtab -R.strtab -R.shstrtab nolib.o
$ ls -l nolib.o
-rwxrwxrwx 1 graul graul 424 Jan 17 23:41 nolib.o
But this is not an elf, if we run now
$ld nolib.o -o ld_output
$ls -l ld_output
-rwxrwxrwx 1 graul graul 4760 Jan 17 23:55 ld_output
In the program ld
there is a flag to remove the alignment between our sections (which is almost all of our size).
$ ld -n -static nolib.o -o ld_output
$ls -l ld_output
-rwxrwxrwx 1 graul graul 928 Jan 17 23:57 ld_output
$strip -R .note.gnu.property ld_output
$ls -l ld_output
-rwxrwxrwx 1 graul graul 472 Jan 17 23:58 ld_output
Which is a drastic improvement (though of course a lot of more work could be done).
Upvotes: 5