如何解决为什么我的 hello world 二进制文件大多为零?
我已经编译
#include <stdio.h>
int main() {
printf("Hello world");
return 0;
}
在 Mac 上,大小为 48k。但是,当我查看带有 xxd
的二进制文件时,大部分内容如下所示:
...
0000b990: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000b9a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000b9b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
...
为什么会这样?
otool
告诉我:
otool -L hello
hello:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0,current version 1292.0.0)
很好,它再次动态链接到 libSystem,它 printf
所在的位置。
那为什么都是零呢?
解决方法
因为对齐。
XNU 强制要求映射部分二进制文件的每个段都与平台的页面大小对齐。在 x86_64 上,这是 0x1000 字节,在 arm64 上是 0x4000 字节(即使硬件支持 0x1000)。如果某些段的数据必须与某个偏移量对齐,那么文件中必须有某些东西来填补它们之间的空白——通常是零。
现在,如果你的二进制文件是 48KB,那么它的段可能看起来像这样:
LC 00: LC_SEGMENT_64 Mem: 0x000000000-0x100000000 File: Not Mapped ---/--- __PAGEZERO
LC 01: LC_SEGMENT_64 Mem: 0x100000000-0x100004000 File: 0x0-0x4000 r-x/r-x __TEXT
LC 02: LC_SEGMENT_64 Mem: 0x100004000-0x100008000 File: 0x4000-0x8000 rw-/rw- __DATA_CONST
LC 03: LC_SEGMENT_64 Mem: 0x100008000-0x10000c000 File: 0x8000-0xc000 rw-/rw- __DATA
LC 04: LC_SEGMENT_64 Mem: 0x10000c000-0x100010000 File: 0xc000-0xc110 r--/r-- __LINKEDIT
对于 0x4000 的对齐,这已经是最小的布局。但是,如果您使用的是 Intel,则可以通过将 -Wl,-segalign,0x1000
传递给编译器来强制链接器使用 0x1000。这应该会产生一个只有大约 12KB 的二进制文件:
LC 00: LC_SEGMENT_64 Mem: 0x000000000-0x100000000 File: Not Mapped ---/--- __PAGEZERO
LC 01: LC_SEGMENT_64 Mem: 0x100000000-0x100001000 File: 0x0-0x1000 r-x/r-x __TEXT
LC 02: LC_SEGMENT_64 Mem: 0x100001000-0x100002000 File: 0x1000-0x2000 rw-/rw- __DATA_CONST
LC 03: LC_SEGMENT_64 Mem: 0x100002000-0x100003000 File: 0x2000-0x3000 rw-/rw- __DATA
LC 04: LC_SEGMENT_64 Mem: 0x100003000-0x100004000 File: 0x3000-0x3110 r--/r-- __LINKEDIT
如果你想进一步优化你的二进制文件,你需要去掉段。通过导入和链接,您唯一可以真正摆脱的是 __DATA_CONST
,您可以通过使用 -mmacosx-version-min=10.14
定位 macOS Mojave(或更早版本)来实现这一点。这将给您留下超过 8KB 的空间:
LC 00: LC_SEGMENT_64 Mem: 0x000000000-0x100000000 File: Not Mapped ---/--- __PAGEZERO
LC 01: LC_SEGMENT_64 Mem: 0x100000000-0x100001000 File: 0x0-0x1000 r-x/r-x __TEXT
LC 02: LC_SEGMENT_64 Mem: 0x100001000-0x100002000 File: 0x1000-0x2000 rw-/rw- __DATA
LC 03: LC_SEGMENT_64 Mem: 0x100002000-0x100003000 File: 0x2000-0x20f0 r--/r-- __LINKEDIT
如果您正在争取尽可能小的可执行文件,您可以进一步放弃 __DATA
甚至 __LINKEDIT
,但您必须大幅更改代码以仅发出原始系统调用,而不是使用动态链接器等
对于任何实际应用程序,我还要说这些零实际上无关紧要。给定四个映射段,它们使用的空间永远不会超过 48KB。二进制越大,零组成的百分比就越小。
至于分发,答案显而易见:xz
。
使用该结果压缩上述二进制文件:
- 48KB 二进制文件为 776 字节。
- 12KB 二进制文件为 736 字节。
- 8KB 二进制文件为 684 字节。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。