当前位置: 首页 > 知识库问答 >
问题:

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

羊舌庆
2023-03-14

对于大学课程,我喜欢比较使用gcc/clang与汇编编写和编译的功能相似程序的代码大小。在重新评估如何进一步缩小某些可执行文件的大小的过程中,当我两年前组装/链接的相同汇编代码现在已经增长时,我不敢相信自己的眼睛

$ 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程序,使用GNUGNU ldhtml" target="_blank">编译(总是使用32位汇编)当时是708字节,现在已经增长到8.5K。即使告诉链接器关闭页面对齐(ld -n),它仍然有近4.2K。stripping/sstripping 也没有得到回报。

readelve告诉我,节标题的开头在代码中要晚得多(字节468 vs 8464),但我不知道为什么。它在与2018年相同的arch系统上运行,Makefile是相同的,我没有链接到任何库(尤其是不是libc)。我想关于ld的一些事情已经发生了变化,因为目标文件仍然很小,但是什么以及为什么?

免责声明:我正在x86-64机器上构建32位可执行文件。

编辑:我正在使用GNU binutils(作为

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

更新:使用<code>ld时。黄金而非黄金 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

也许我之前只是在没有意识到的情况下使用了

共有1个答案

康文昌
2023-03-14

一般来说,这不是10倍,而是几个部分的页面对齐,正如Jester所说,出于安全原因,ld的默认链接器脚本发生了变化:

>

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

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

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

    额外的空间只是00个填充,并且可以在.tar.gz或其他任何东西中很好地压缩。

    因此,它的最坏情况上限是大约2倍4k额外的填充页,并且微小的可执行文件接近最坏情况。

    <code>gcc-Wl,-nmagic</code>将关闭节的页面对齐,如果出于某种原因需要。(请参阅<code>ld(1)

    条带ping对作为部分的填充没有帮助;我认为它只能删除整个部分。

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

    这个问题是关于<code>ld,它过去也默认为生成静态可执行文件。但现代Linux发行版配置GCC时默认使用<code>-pie

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

    还:

    >

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

    FASM可以制作非常小的可执行文件,使用它的模式直接输出一个静态的可执行文件(不是目标文件),没有ELF段元数据,只有程序头。(用GDB调试或者用objdump反汇编都很痛苦;大多数工具都假定会有节头,即使运行静态可执行文件并不需要它们。)

    一个小的C程序包括setup在内,合理的最小汇编指令数是多少?

    Linux ldd的“静态链接”和“不是动态可执行文件”有什么区别?(静态vs .静态-pie vs .(动态)pie恰好没有共享库。)

  •  类似资料:
    • 我在Windows 10,我有anaconda安装,但我想创建一个独立的可执行文件在一个新的,干净的最小环境使用python 3.5.所以我做了一些测试: 我创建了一个python脚本test1.py在文件夹testenv只有: 然后我创建了环境,安装了pyinstaller并创建了可执行文件 它创造了我大约6Mb的test1.exe 测试2:我修改了测试1。具体如下: 我在环境中安装了panda

    • 问题内容: 我在Windows 10上,安装了anaconda,但我想使用python 3.5在新的,干净的最小环境中独立创建可执行文件。所以我做了一些测试: TEST1:我在文件夹testenv中仅创建了一个python脚本test1.py: 然后创建环境,安装pyinstaller并创建可执行文件 它创建了大约6 Mb的我的test1.exe 测试2:我将test1.py修改如下: 我在环境中

    • 问题内容: 我只有一行用python编写的代码,并带有pyinstaller和option 。exe文件为4577 kB,几乎是5Mb。如何减小其大小或排除某些自动捆绑的图书馆? 问题答案: 您使用pyinstaller创建的.exe文件包括python解释器和脚本中包含的所有模块。也许您正在使用的模块本身都有一个很大的库。但是,您可以尝试使用 py2exe, 但它可能不适用于所有项目。另一种缩小

    • 我在cassandra设置中有“commitlog_segment_size_in_mb: 32”,但下面的错误表示最大尺寸为16777216,约为16mb。我在看修复下面错误的正确设置吗? 我指的是基于 http://mail-archives.apache.org/mod_mbox/cassandra-user/201406.mbox/<53A40144.2020808@gmail.com>中

    • 问题内容: 我用Python打开了一个8 MB的文件,因为我想批量更改各种类型的文件名。我仔细检查并将文件加载到字符串中,然后使用字符串方法replace替换了所有内容。然后我注意到只有一半的文件被替换了。好像Python没有完全打开文件。 我必须在Python的范围内发挥某种字符串大小限制或最大文件大小限制吗? 请参阅Python搜索中的代码并替换未正确替换的代码。 我已更改为建议的代码。缓冲区

    • 我正在下载一个歌曲文件。下面的代码(好吧,原始代码,这只是我正在做的一个示例)在Asha 310设备上运行良好。然而,在较新的Asha 501设备上,下载的文件比实际文件大得多。如果我使用512缓冲区,一个2.455.870字节的文件最终会下载2.505.215字节,而且它也不会加载。使用4096缓冲区,文件大小最终为3.342.335字节!! 发生这种情况的原因是什么?它在另一部手机上运行良好,