我正在为我的某个项目编写一些汇编代码,我看到了一些有趣的东西。链接时二进制文件的大小太大。因此我进行了测试,即使使用最小的代码行,输出的Elf二进制文件也是如此。例如:
.section .text
.global _start
_start:
movl $1,%eax
movl $0,%ebx
int $0x80
在汇编并链接以上代码后,结果二进制文件超过4kb !
有趣的是,大多数二进制文件都用零填充。
我尝试了很多事情以找出导致失败的原因。
有人可以给我解释一下这是什么问题吗?
我只是汇编并链接文件:
as -o <OBJ_NAME> <SOURCE NAME>
ld -o <ELF_NAME> <OBJ_NAME>
推荐任何形式的资源以供进一步阅读会很好。
您可能会猜到,我使用的是64位GNU / Linux
谢谢。
答案 0 :(得分:4)
这与对齐有关。参见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。
如果查看程序头文件,则显示相同的图片。填充零的空间在ELF标头的末尾和0x1000之间。
这是为什么?
首先,因为ELF标准规定
可加载过程段的p_vaddr和p_offset必须具有一致的值,以页面大小为模。
(请参阅man elf
)。系统上的页面大小(也是我的页面大小)为4K。这是您在p_align
中看到的值。
第二,链接程序分配给“文本”段开头的虚拟地址(与此处的.text
相同,因为这就是该段所包含的全部地址)是0x0000000000401000
。因此,文件中“文本”段的偏移量的十六进制表示必须以000
结尾。但是包含ELF标头(文件的最开始)的只读段已经采用了0。第二个选择是0x1000
。
为什么链接器选择0x401000作为文本部分的虚拟地址?我不知道。我认为,只要稍微调整链接描述文件,就可以使用较小的可重编译可执行文件。
正如Peter和其他人指出的那样,可以使用-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,您可以使用链接器脚本精细控制链接器输出文件的布局。如果用户未指定默认链接描述文件,则ld.bfd
(通常也称为ld
)将解释默认的链接描述文件。可以使用ld --verbose
获得。然后,您可以对其进行编辑并提供您的版本,而不是使用-T <your-script>
提供默认版本。
我删除了
的第一次出现. = ALIGN(CONSTANT (MAXPAGESIZE));
(在.text
之前),得到720个字节(strip
时为400个)字节。这与使用-n
选项的结果不同。您仍然会获得2个可加载的段,而它们的p_align
仍然是0x1000
。
我不完全了解p_align
<MAX_PAGE_SIZE
对效率的影响。 (由于地址计算难度较大,因此页面加载速度不会很快?我认为应该有一个更好的解释。)如果您对此答案有更多了解或在何处进行解释,请随时编辑答案。