简体   繁体   English

除了二进制文件中的代码之外,我如何编译 C?

[英]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

Shortly:不久:

  1. strip -s does not remove the sections but only overrides them with 0 (and thus the file size remains the same. strip -s 不会删除这些部分,而只会用 0 覆盖它们(因此文件大小保持不变。
  2. There are a lot of program headers that we do not need in this case (in order to handle exceptions etc.)在这种情况下,我们不需要很多程序头(为了处理异常等)
  3. There is a default alignment in the binary, which makes the start of it be at least 4000 (and we do not need it).二进制文件中有一个默认对齐方式,这使得它的开头至少为 4000(我们不需要它)。

Detailed详细的

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.

相关问题 如何在我的代码目录中编译 all.c 文件? - How do I compile all .c files in my code directory? 如何在Windows上交叉编译C代码,以便在Unix(Solaris / HPUX / Linux)上运行二进制文件? - How do I cross-compile C code on Windows for a binary to also be run on Unix (Solaris/HPUX/Linux)? 如何将C代码编译为原始的无操作系统二进制文件? - How do I compile C code to a raw os-less binary? 如何编译和链接示例代码以获取二进制文件? - How do I compile and link the sample code to get a binary? 我如何用C将二进制结果分成4组。代码的目的是将十进制数字转换为二进制表示法 - How do I split my binary results into groups of four with C. The aim of the code is convert and decimal figure into binary notation 在我的C ++程序中使用C代码需要做些什么特别的事情吗? - Is there anything special I need to do to use C code in my C++ program? 如何编译C程序以在没有命令箱的情况下运行? - How Do I Compile a C Program to Run Without a Command Box? 我如何在没有所有膨胀的情况下编译AC程序? - How do i compile a c program without all the bloat? 如何创建Makefile正确编译我的C代码? - How can I create a Makefile to compile my C code correctly? 如何获取我的代码以正确打印二进制文件? - How do I get My Code to print out Binary Properly?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM