assembly 为什么我的hello world二进制大多数是0?

qaxu7uf2  于 4个月前  发布在  其他
关注(0)|答案(1)|浏览(82)

我整理了

#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中。
那为什么全是零

cfh9epnr

cfh9epnr1#

是因为对齐。
XNU强制Map二进制部分的每个段与平台的页面大小对齐。在x86_64上,为0x 1000字节,在arm 64上,为0x 4000字节(即使在硬件将支持0x 1000的情况下)。并且如果某些段的数据必须与某个偏移对齐,那么在文件中必须有一些东西来填补中间差距--通常是零。
现在,如果您的二进制文件是48 KB,那么它的段可能看起来像这样:

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

字符串
对于0x 4000的对齐,这已经是最小的布局。但是如果你使用的是Intel,你可以通过将-Wl,-segalign,0x1000传递给编译器来强制链接器使用0x 1000。这应该会产生一个只有大约12 KB的二进制文件:

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,你可以通过在macOS莫哈韦(或更早的版本)中使用-mmacosx-version-min=10.14来实现。这将给你留下8 KB多一点的空间:

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,但您必须对代码进行实质性的更改,以便仅发出原始syscalls,而不使用动态链接器等。
对于任何实际应用程序,我还要说这些零实际上并不重要。给定四个Map段,它们永远不会占用超过48 KB的空间。二进制文件越大,零所占的百分比就越小。
至于分布,答案显而易见:xz
使用压缩上述二进制档,会产生:

  • 48 KB二进制文件为776字节。
  • 12 KB二进制文件为736字节。
  • 8 KB二进制文件为684字节。

相关问题