【问题标题】:gcc: passing -nostartfiles to ld via gcc for minimal binary sizegcc:通过 gcc 将 -nostartfiles 传递给 ld 以获得最小的二进制大小
【发布时间】:2021-05-13 09:03:13
【问题描述】:

考虑以下用于 64 位 linux 的气体(GNU 汇编器)文件

go.s

        .global _start
        .text

_start:
        mov     $60, %rax               # system call 60 is exit
        xor     %rdi, %rdi              # we want return code 0
        syscall  

现在编译:

rm -f go go.o
as -o go.o go.s
ld -o go -s -nostartfiles go.o
ls -l go                                            # -> 344 bytes

我们得到了 344 字节的超小尺寸。

太棒了!使用 gcc 和单独的ld 命令:

# 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

但是如何只使用一个 gcc 命令来获得这么小的尺寸

# single gcc command                                                                                                                                                                     
rm -f go go.o
gcc -xassembler -s -static -nostartfiles -o go go.s
ls -l go                                            # -> 4400 bytes  too big!!!

该死的 4400 字节!哪个单行 gcc 调用将提供 344 个字节?

使用-v 标志运行上述单行...

gcc -v -xassembler -s -static -nostartfiles -o go go.s

... 表明 -nostartfiles 未传递给 collect2 链接命令。

嗯……

所以任务:向我展示给出最小尺寸的单行 gcc 调用!

谢谢。


-v 标志试验:

gcc -v -xassembler -s -static -nostartfiles -o go go.s

执行以下操作:

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

/usr/lib/gcc/x86_64-linux-gnu/8/collect2 命令(上图)中手动添加-nostartfiles(直接在collect2 之后)会得到520 字节的大小。

手动添加-nostartfiles(直接在collect2之后)并删除--build-id会得到344字节的大小。正如我们所见,here-Wl,--build-id=none 应该删除 build-id。

但是如何指示 gcc 将-nostartfiles 传递给ldcollect2


回应 Jester 的评论:

执行以下操作 (-Wl,-nostartfiles):

gcc -Wl,-nostartfiles,--build-id=none -v -xassembler -s -static -nostartfiles -o go go.s

将在collect2 命令的末尾添加-nostartfiles 太远,结果甚至没有创建输出二进制文件go。如何给出命令,以便-nostartfiles 出现在collect2 ... 命令的前面:我知道它有效,如果我手动将-nostartfiles 构造为在开头的标志。

查看here,有人声称-Wl,-nostartfiles 将无法正确处理。但是如果我只是使用gcc -nostartflags ...,那么它不会传递给链接器:Maby this is a bug in gcc??

可能所有代码都被优化掉了……什么都没有留下??见here

【问题讨论】:

  • 你试过-Wl,-nostartfiles吗?
  • 是的。它确实工作。 -v 标志显示 -nostartfiles 被添加到 collects2 链接命令 (/usr/lib/gcc/x86_64-linux-gnu/8/collect2 .... go.o --build-id=none -nostartfiles --start-group -lgcc -lgcc_eh -lc --end-group) 的末尾;结果是:输出二进制文件go 甚至没有被创建!!!!!

标签: assembly gcc ld gnu-assembler


【解决方案1】:

-nostartfiles 不是ld 选项。 解析为ld -n -o startfiles

我尝试了您的命令,但它们没有创建一个名为 go 的文件,而是创建了一个名为 startfiles 的可执行文件。

$ cat > go.s
  paste + control-d
$ as -o go.o go.s
$ ld -o go -s -nostartfiles go.o
$ ll go
ls: cannot access 'go': No such file or directory
$ ll -clrt
-rw-r--r-- 1 peter peter  193 May 13 11:33 go.s
-rw-r--r-- 1 peter peter  704 May 13 11:33 go.o
-rwxr-xr-x 1 peter peter  344 May 13 11:33 startfiles

您的go 必须是您的ld -s -nostartfiles -o go go.o 遗留下来的,其中-o go 是命令行上-o 的最后一个实例,未被-o startfiles 覆盖。


使您的二进制文件变小的选项是ld -n

-n
--nmagic
关闭部分的页面对齐,并禁用与共享库的链接。如果输出格式支持 Unix 风格的幻数,请标记 输出为“NMAGIC”。

相关:Minimal executable size now 10x larger after linking than 2 years ago, for tiny programs?


作为 GCC 选项,-nostartfiles 告诉gcc 前端链接crt*.o 文件。如果您手动运行ld,您只需省略提及它们。没有必要告诉ld 你没有链接什么,ld 命令本身不会链接任何它没有被明确告知的东西。链接 CRT 文件和 libc / libgcc 是 gcc 默认值,而不是 ld

$ gcc -s -nostdlib -static  -Wl,--nmagic,--build-id=none go.s
$ ll a.out 
-rwxr-xr-x 1 peter peter 344 May 13 12:35 a.out

您希望 -nostdlib 省略库以及 CRT 启动文件。
-nostartfiles 只是 -nostdlib 的一部分。
(尽管在静态链接时,ld 不会从libc.alibgcc.a 中提取任何代码,因为您的文件没有引用任何外部符号。所以您实际上仍然可以通过使用-nostartfiles 获得相同的344 字节文件如-nostdlib。但-nostdlib 更准确地复制了您的手动ld 命令,没有传递任何额外的文件。)

您需要 -static 不动态链接,在 -pie 是默认值的 GCC 上。 (这也意味着-no-pie-static-pie 默认不会启用。)如果您让 GCC 尝试动态链接 PIE 可执行文件(即使没有共享库)。

【讨论】:

  • OP已经提到可以使用-Wl,--build-id=none删除构建ID。
  • @Armali:谢谢,用gcc 重现了 344 字节大小。
  • 太棒了!谢谢!
猜你喜欢
  • 1970-01-01
  • 2015-12-31
  • 1970-01-01
  • 1970-01-01
  • 2017-03-22
  • 2021-12-13
  • 2019-05-01
  • 2021-06-21
  • 1970-01-01
相关资源
最近更新 更多