如何使用汇编制作小型二进制文件?

how to make small binaries using assembly?

我正在为我的某个项目编写一些汇编代码,我看到了一些有趣的东西。 linked 时二进制文件的大小太大了。所以我测试了又测试,即使使用尽可能少的代码行,输出的 Elf 二进制文件也很大。例如:

.section .text
.global _start
_start:
    movl ,%eax
    movl [=10=],%ebx
    int [=10=]x80

在汇编和 link 上面的代码之后,结果二进制文件超过 4kb! 有趣的是,大多数二进制文件都用零填充。
我尝试了很多事情来找出没有成功的原因。
有人可以向我解释这里的问题是什么吗?

我只是 assemble 和 link 文件:

as -o <OBJ_NAME> <SOURCE NAME>
ld -o <ELF_NAME> <OBJ_NAME>

推荐任何形式的资源以供进一步阅读会很好。

你可能猜到了,我用的是64位GNU/Linux

谢谢。

这与对齐有关。参见 readelf -eW <ELF_NAME>。有趣的一点是

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        0000000000401000 001000 00000c 00  AX  0   0  1

注意 Off 列。这是文件中的偏移量,.text段以0x1000开始,也就是4K.

如果您查看 程序 headers,则图片相同。用零填充的space在ELF末尾header和0x1000之间。

这是为什么?

首先,因为 ELF 标准规定

Loadable process segments must have congruent values for p_vaddr and p_offset, modulo the page size.

(参见 man elf)。你系统(我的也是)的页面大小是 4K。这是您在 p_align.

中看到的值

其次,链接器分配给 "text" 段开头的虚拟地址 — 与此处的 .text 部分相同,因为这是该段包含的全部内容 — 是 0x0000000000401000.因此 "text" 段在文件中的偏移量的十六进制表示必须以 000 结尾。但是 0 已经被包含 ELF header 的只读段占用(文件的最开头)。第二个选择是0x1000.

为什么链接器选择 0x401000 作为文本部分的虚拟地址?我不知道。我认为,如果您稍微调整一下链接描述文件,您将能够获得更小的结果可执行文件。


正如 Peter 和其他人指出的那样,page-size 对齐可以使用 -n 链接器选项禁用:

'-n'
'--nmagic'
    Turn off page alignment of sections, and disable linking against
    shared libraries[…]

这样我就得到了

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 1] .text             PROGBITS        0000000000400078 000078 00000c 00  AX  0   0  1

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  LOAD           0x000078 0x0000000000400078 0x0000000000400078 0x00000c 0x00000c R E 0x1

并且可执行文件的大小降至 664 字节(stripping 后为 344)。


使用 GNU ld,您可以使用链接器脚本 来fine-control 链接器输出文件的布局。如果用户未指定,ld.bfd(通常也称为 ld)解释默认链接描述文件。可以用ld --verbose获得。然后你可以编辑它并提供你的版本而不是默认的 -T <your-script>.

我删除了第一次出现的

. = ALIGN(CONSTANT (MAXPAGESIZE));

(在 .text 之前)并获得 720(在 stripped 时为 400)字节。这与使用 -n 选项的结果不同。你仍然得到 2 个可加载的段,它们的 p_align 仍然是 0x1000.

p_align < MAX_PAGE_SIZE 对效率的影响我不完全理解。 (由于更难的地址计算,页面加载速度不会那么快?我认为应该有更好的解释。)如果您对此有更多了解或在何处进行了解释,请随时编辑答案。