对于小型程序来说,链接后的最小可执行文件大小比 2 年前大 10 倍?

2024-10-10 08:38:00
admin
原创
74
摘要:问题描述:对于大学课程,我喜欢比较使用 gcc/clang 和汇编编写和编译的功能相似的程序的代码大小。在重新评估如何进一步缩小某些可执行文件的大小的过程中,当我 2 年前汇编/链接的相同汇编代码在再次构建后大小增长了 10 倍以上时,我简直不敢相信自己的眼睛(对于多个程序来说都是如此,不仅仅是 hellow...

问题描述:

对于大学课程,我喜欢比较使用 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!
"
         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 程序,使用 GNUas和 GNU编译ld(始终使用 32 位汇编),当时有 708 字节,现在已经增长到 8.5K。即使告诉链接器关闭页面对齐(ld -n),它仍然有近 4.2K。ping/pingstripsstrip无济于事。

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之前使用过而没有意识到。


解决方案 1:

ld一般来说它不是 10x,而是像 Jester 所说的那样,根据安全原因对默认链接器脚本的更改,它是几个部分的页面对齐:

  • 第一个更改:确保 的数据.data不存在于 的任何映射中.text,因此可执行页面中的 ROP / Spectre 小工具均无法使用任何静态数据。(在较旧的 中ld,这意味着程序头将相同的磁盘块映射两次,也映射到实际 .data 部分的 RW-without-exec 段中。可执行映射仍然是只读的。)

  • 最近的更改:将.rodata其分成.text单独的段,这样静态数据就不会映射到可执行页面中。以前,如果您想以这种方式测试 shellcodeconst char code[]= {...},可以将其转换为函数指针并调用,而无需 mprotect 或其他技巧。(单独的 Linux 内核更改仅适用于实际堆栈,而不是 READ_IMPLIES_EXEC。)gcc -z execstack`-z execstack`

  • 请参阅为什么 ELF 可执行文件可以有 4 个 LOAD 段?了解此历史记录,包括一个奇怪的事实,即它.rodata位于与用于访问 ELF 元数据的只读映射不同的段中。

额外的空间只是00填充,可以很好地压缩.tar.gz

因此,它的最坏情况上限约为 2x 4k 额外填充页,而微型可执行文件接近最坏情况。

gcc -Wl,--nmagic如果您出于某种原因想要关闭页面对齐功能,则会关闭页面对齐功能。(请参阅手册ld(1)页)我不知道为什么它不能将所有内容压缩到旧大小。也许检查默认链接器脚本会有所帮助,但它太长了。运行ld --verbose它查看它。

stripping 对于作为部分内容的填充没有帮助;我认为它只能删除整个部分。

ld -z noseparate-code使用旧布局,总共只有 2 个段来覆盖.text.rodata部分以及.data.bss部分。(以及动态链接想要访问的 ELF 元数据。)


有关的:

链接为gcc而不是ld

这个问题是关于的ld,但请注意,如果您使用的是gcc -nostdlib,它以前也默认生成静态可执行文件。但现代 Linux 发行版将 GCC 配置为-pie默认配置,并且即使没有任何共享库被链接, GCC也不会默认生成静态文件-no-pie。与模式不同,在这种情况下它只会生成静态可执行文件。(静态文件仍然需要启动代码来应用任何绝对地址的重定位。)

ld因此直接的等价物是gcc -nostdlib -static(这意味着-no-pie)。当没有共享库被链接时,或者gcc -nostdlib -no-pie应该让它默认为。您可以将其与and/or结合使用。-static`-Wl,--nmagic`-Wl,-z -Wl,noseparate-code

还:

  • 关于为 Linux 创建真正小型 ELF 可执行文件的旋风教程- 最终制作一个 45 字节的可执行文件,将系统调用的机器代码_exit塞入 ELF 程序头本身。

  • FASM 可以使用其模式生成非常小的可执行文件,其中它直接输出静态可执行文件(不是目标文件),没有 ELF 节元数据,只有程序头。(使用 GDB 进行调试或使用 objdump 进行反汇编很麻烦;大多数工具都假设会有节头,即使运行静态可执行文件不需要它们。)

  • 对于包括设置在内的小型 C 程序,合理的最少汇编指令数量是多少?

  • Linux ldd 中的“静态链接”和“非动态可执行文件”之间有什么区别?(静态、静态 pie 和恰好没有共享库的(动态)PIE。)

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用