[英]How do I compile C without anything but my code in the binary?
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.我正在查看有关 hello world 二进制大小的页面,我想知道如果我没有 lib c,我的二进制文件能有多小。 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.
如您所见,我没有运气,5ish 指令仍然是 13K 的二进制文件。 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
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现在让我们看一下我们的精灵: $ readelf -h static_output ELF 头:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 类:ELF64 数据:2 的补码,little endian 版本:1(当前)操作系统/ABI:UNIX - System V ABI 版本:0 类型:EXEC(可执行文件) 机器:Advanced Micro Devices X86-64 版本:0x1 入口点地址:0x401000 程序头开始:64(字节到文件) 节头开始: 8368(字节到文件中)标志:0x0 此头的大小:64(字节)程序头的大小:56(字节)程序头的数量:7 节头的大小:64(字节) 节头的数量:7 节头字符串表索引:6
Looks like there is more than 8kn before the start of sections header!看起来在节标题开始之前有超过 8kn ! 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.这很奇怪,因为我们调用了 strip 函数,它应该从我们的精灵中删除这一部分。 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).
如果我们查看https://unix.stackexchange.com/questions/267070/why-doesnt-strip-remove-section-headers-from-elf-executables中的响应,我们可以看到虽然没有具体提及,但 strip 并没有从我们的二进制文件中删除这些部分,但只删除它们的内容(这对我们的情况没有帮助)。
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”段(我们的情况不需要它,查看为什么GCC编译的C程序需要.eh_frame段?查看它)。
$ 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 在我们不希望的情况下插入了这些部分:
$ 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
中有一个标志来删除我们的部分之间的对齐(这几乎是我们所有的大小)。
$ 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).这是一个巨大的改进(尽管当然可以做更多的工作)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.