如何使用汇编制作小型二进制文件?
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 字节(strip
ping 后为 344)。
使用 GNU ld,您可以使用链接器脚本 来fine-control 链接器输出文件的布局。如果用户未指定,ld.bfd
(通常也称为 ld
)解释默认链接描述文件。可以用ld --verbose
获得。然后你可以编辑它并提供你的版本而不是默认的 -T <your-script>
.
我删除了第一次出现的
. = ALIGN(CONSTANT (MAXPAGESIZE));
(在 .text
之前)并获得 720(在 strip
ped 时为 400)字节。这与使用 -n
选项的结果不同。你仍然得到 2 个可加载的段,它们的 p_align
仍然是 0x1000
.
p_align
< MAX_PAGE_SIZE
对效率的影响我不完全理解。 (由于更难的地址计算,页面加载速度不会那么快?我认为应该有更好的解释。)如果您对此有更多了解或在何处进行了解释,请随时编辑答案。
我正在为我的某个项目编写一些汇编代码,我看到了一些有趣的东西。 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 字节(strip
ping 后为 344)。
使用 GNU ld,您可以使用链接器脚本 来fine-control 链接器输出文件的布局。如果用户未指定,ld.bfd
(通常也称为 ld
)解释默认链接描述文件。可以用ld --verbose
获得。然后你可以编辑它并提供你的版本而不是默认的 -T <your-script>
.
我删除了第一次出现的
. = ALIGN(CONSTANT (MAXPAGESIZE));
(在 .text
之前)并获得 720(在 strip
ped 时为 400)字节。这与使用 -n
选项的结果不同。你仍然得到 2 个可加载的段,它们的 p_align
仍然是 0x1000
.
p_align
< MAX_PAGE_SIZE
对效率的影响我不完全理解。 (由于更难的地址计算,页面加载速度不会那么快?我认为应该有更好的解释。)如果您对此有更多了解或在何处进行了解释,请随时编辑答案。