对于大学课程,我喜欢比较使用 gcc/clang 和汇编编写和编译的功能相似的程序的代码大小。在重新评估如何进一步缩小的过程中...
对于大学课程,我喜欢比较使用 gcc/clang 和汇编编写和编译的功能相似的程序的代码大小。在重新评估如何进一步缩小某些可执行文件的大小的过程中,当我 2 年前汇编/链接的相同汇编代码在再次构建后大小增长了 10 倍以上时,我简直不敢相信自己的眼睛(对于多个程序来说都是如此,不仅仅是 helloworld):
$ make
as -32 -o helloworld-asm-2020.o helloworld-asm-2020.s
ld -melf_i386 -o helloworld-asm-2020 helloworld-asm-2020.o
$ ls -l
-rwxr-xr-x 1 xxx users 708 Jul 18 2018 helloworld-asm-2018*
-rwxr-xr-x 1 xxx users 8704 Nov 25 15:00 helloworld-asm-2020*
-rwxr-xr-x 1 xxx users 4724 Nov 25 15:00 helloworld-asm-2020-n*
-rwxr-xr-x 1 xxx users 4228 Nov 25 15:00 helloworld-asm-2020-n-sstripped*
-rwxr-xr-x 1 xxx users 604 Nov 25 15:00 helloworld-asm-2020.o*
-rw-r--r-- 1 xxx users 498 Nov 25 14:44 helloworld-asm-2020.s
汇编代码为:
.code32
.section .data
msg: .ascii "Hello, world!\n"
len = . - msg
.section .text
.globl _start
_start:
movl $len, %edx # EDX = message length
movl $msg, %ecx # ECX = address of message
movl $1, %ebx # EBX = file descriptor (1 = stdout)
movl $4, %eax # EAX = syscall number (4 = write)
int $0x80 # call kernel by interrupt
# and exit
movl $0, %ebx # return code is zero
movl $1, %eax # exit syscall number (1 = exit)
int $0x80 # call kernel again
同样的 hello world 程序,使用 GNU as
和 GNU ld
(始终使用 32 位汇编),当时有 708 字节,现在已经增长到 8.5K。即使告诉链接器关闭页面对齐( ld -n
近 strip
4.2K。ping/ping sstrip
也无济于事。
readelf
告诉我节头的开始在代码中要靠后得多(字节 468 与 8464),但我不知道为什么。它运行在与 2018 年相同的 arch 系统上,Makefile 相同,我没有链接任何库(尤其是 libc)。我猜有些事情 ld
已经发生了变化,因为目标文件仍然很小,但是什么,为什么呢?
免责声明:我正在 x86-64 机器上构建 32 位可执行文件。
编辑:我正在使用 GNU binutils (as&ld) 版本 2.35.1 这是一个 base64 编码的档案,其中包含源代码和两个可执行文件(小的旧文件,大的新文件):
cat << EOF | base64 -d | tar xj
QlpoOTFBWSZTWVaGrEQABBp////xebj/7//Xf+a8RP/v3/rAAEVARARAeEADBAAAoCAI0AQ+NAam
ytMpCGmpDVPU0aNpGmh6Rpo9QAAeoBoADQaNAADQ09IAACSSGUwaJpTNQGE9QZGhoADQPUAA0AAA
AA0aA4AAAABoAAAAA0GgAAAAZAGgAHAAAAANAAAAAGg0AAAADIA0AASJCBIyE8hHpqPVPUPU/VAa
fqn6o0ep6BB6TQaNGj0j1ABobU00yeU9JYiuVVZKYE+dKNa3wls6x81yBpGAN71NoylDUvNryWiW
E4ER8XkfpaJcPb6ND12ULEqkQX3eaBHP70Apa5uFhWNDy+U3Ekj+OLx5MtDHxQHQLfMcgCHrGayE
Dc76F4ZC4rcRkvTW4S2EbJAsbBGbQxSbx5o48zkyk5iPBBhJowtCSwDBsQBc0koYRSO6SgJNL0Bg
EmCoxCDAs5QkEmTGmQUgqZNIoxsmwDmDQe0NIDI0KjQ64leOr1fVk6AaVhjOAJjLrEYkYy4cDbyS
iXSuILWohNh+PA9Izk0YUM4TQQGEYNgn4oEjGmAByO+kzmDIxEC3Txni6E1WdswBJLKYiANdiQ2K
00jU/zpMzuIhjTbgiBqE24dZWBcNBBAAioiEhCQEIfAR8Vir4zNQZFgvKZa67Jckh6EHZWAWuf6Q
kGy1lOtA2h9fsyD/uPPI2kjvoYL+w54IUKBEEYFBIWRNCNpuyY86v3pNiHEB7XyCX5wDjZUSF2tO
w0PVlY2FQNcLQcbZjmMhZdlCGkVHojuICHMMMB5kQQSZRwNJkYTKz6stT/MTWmozDCcj+UjtB9Cf
CUqAqqRlgJdREtMtSO4S4GpJE2I/P8vuO9ckqCM2+iSJCLRWx2Gi8VSR8BIkVX6stqIDmtG8xSVU
kk7BnC5caZXTIynyI0doXiFY1+/Csw2RUQJroC0lCNiIqVVUkTqTRMYqKNVGtCJ5yfo7e3ZpgECk
PYUEihPU0QVgfQ76JA8Eb16KCbSzP3WYiVApqmfDhUk0aVc+jyBJH13uKztUuva8F4YdbpmzomjG
kSJmP+vCFdKkHU384LdRoO0LdN7VJlywJ2xJdM+TMQ0KhMaicvRqfC5pHSu+gVDVjfiss+S00ikI
DeMgatVKKtcjsVDX09XU3SzowLWXXunnFZp/fP3eN9Rj1ubiLc0utMl3CUUkcYsmwbKKrWhaZiLO
u67kMSsW20jVBcZ5tZUKgdRtu0UleWOs1HK2QdMpyKMxTRHWhhHwMnVEsWIUEjIfFEbWhRTRMJXn
oIBSEa2Q0llTBfJV0LEYEQTBTFsDKIxhgqNwZB2dovl/kiW4TLp6aGXxmoIpVeWTEXqg1PnyKwux
caORGyBhTEPV2G7/O3y+KeAL9mUM4Zjl1DsDKyTZy8vgn31EDY08rY+64Z/LO5tcRJHttMYsz0Fh
CRN8LTYJL/I/4u5IpwoSCtDViIA=
EOF
更新: 当使用 ld.gold
而不是 ld.bfd
( /usr/bin/ld
默认情况下符号链接到)时,可执行文件的大小会变得像预期的那样小:
$ cat Makefile
TARGET=helloworld
all:
as -32 -o ${TARGET}-asm.o ${TARGET}-asm.s
ld.bfd -melf_i386 -o ${TARGET}-asm-bfd ${TARGET}-asm.o
ld.gold -melf_i386 -o ${TARGET}-asm-gold ${TARGET}-asm.o
rm ${TARGET}-asm.o
$ make -q
$ ls -l
total 68
-rw-r--r-- 1 eso eso 200 Dec 1 13:57 Makefile
-rwxrwxr-x 1 eso eso 8700 Dec 1 13:57 helloworld-asm-bfd
-rwxrwxr-x 1 eso eso 732 Dec 1 13:57 helloworld-asm-gold
-rw-r--r-- 1 eso eso 498 Dec 1 13:44 helloworld-asm.s
也许我只是 gold
之前使用过而没有意识到。
根据安全原因对默认链接器脚本 ld
的更改,它是几个部分的页面对齐
p1
p2
p3
额外的空间只是 00
填充,可以很好地压缩 .tar.gz
。
因此,它的最坏情况上限约为 2x 4k 额外填充页,而微型可执行文件接近最坏情况。
gcc -Wl,--nmagic
如果您出于某种原因想要关闭页面对齐功能,则将关闭该功能。(请参阅 ld(1)
man page )我不知道为什么它不能将所有内容压缩到旧大小。也许检查默认链接器脚本会有所帮助,但它太长了。运行 ld --verbose
它查看它。
strip
ping 对于作为部分内容的填充没有帮助;我认为它只能删除整个部分。
ld -z noseparate-code
使用旧布局,总共只有 2 个段来覆盖 .text
和 .rodata
部分以及 .data
和 .bss
部分。(以及动态链接想要访问的 ELF 元数据。)
gcc
而不是 ld
这个问题是关于的 ld
,但请注意,如果您使用的是 gcc -nostdlib
,它以前也默认生成静态可执行文件。但现代 Linux 发行版将 GCC 配置 -pie
为默认配置,并且 即使没有任何共享库被链接, 也不会默认生成静态文件 -no-pie
模式不同,在这种情况下它只会生成静态可执行文件。( 静态文件 仍然需要启动代码来应用任何绝对地址的重定位。)
因此直接的等价物 ld
是 gcc -nostdlib -static
(这意味着 -no-pie
)。或者 gcc -nostdlib -no-pie
应该让它默认为 -static
当没有共享库被链接时。你可以将它与 -Wl,--nmagic
and/or -Wl,-z -Wl,noseparate-code
.
还:
p12
p13
p14
第15页