微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ELF 可执行文件:许多零字节

如何解决ELF 可执行文件:许多零字节

简介

我正在编译一个简单的汇编代码(Intel 语法、x86、Linux),打印“Hello World!”。这是:

SECTION .rodata
    msg:        db 'Hello World!',0xA
    msg_len:    equ $ - msg

SECTION .text
    global _start

_start:
    mov eax,4  ; `write` system call
    mov ebx,1  ; to stdout
    mov ecx,msg
    mov edx,msg_len
    int 0x80

    mov eax,1  ; `exit` system call
    xor ebx,ebx    ; exit code 0
    int 0x80

我使用以下命令编译它:

nasm -f elf32 -o hello_world.o hello_world.s
ld -m elf_i386 -o hello_world hello_world.o

代码运行良好,但我担心的是文件大小:

-rwxrwxr-x 1 nikolay nikolay 8704 Apr 27 15:20 hello_world
-rw-rw-r-- 1 nikolay nikolay  243 Apr 26 22:16 hello_world.s
-rw-rw-r-- 1 nikolay nikolay  640 Apr 27 15:20 hello_world.o

问题

目标文件比源代码稍大,但看起来合理,因为ELF文件中应该有一些元数据或其他东西,源代码不包含,对吧?但是可执行文件比目标文件还要大10倍以上!

此外,目标文件中有一些零字节,但我不会说它们太多。但是,可执行文件中有很多零(请参阅 Additional info 部分中两者的屏幕截图)。

调查

我曾尝试阅读一些关于 ELF 的文章包括维基百科和手册页。我没有仔细阅读它们,所以我可能错过了一些东西,但我发现有用的是 dumpelf 实用程序(来自 pax-utils 包,可通过 apt 安装),使用它我转储了我的精灵文件并发现了可能是这些零流的原因:

在可执行文件的所有三个标头中,都有 p_align 字段集:

.p_align  = 4096,/* (min mem alignment in bytes) */

这应该意味着每个部分都应该用零字节填充,以便其长度是 4096 的倍数。并且由于以下每个部分的大小相对较小,因此需要添加很多零字节,这就是这些零的来源。

问题

所以,我想知道:

  1. 我说得对吗?添加这些零字节是为了使部分足够长吗?

    我还注意到前三个部分('''.rodata''.text')从 040968192 开始分别,但以下 ('.symtab','.strtab','.shstrtab') 似乎不再对齐:它们开始于 82088368 和 {{1 }}... 为什么?这里发生了什么?

  2. 我们需要这种对齐方式有什么用?在编程头中,有 8422p_vaddr 字段被设置为前三个部分开始的地址,那么如果我们已经知道 exact 来自标题的部分的地址?它与内存页(在我的机器上大小为 4KiB)有关吗?

  3. 我什么时候想要/需要,以及如何更改对齐值?看起来应该有一个链接器参数来更改此值。我在 p_paddr 的手册中找到了 --nmagic 参数,它完全禁用了对齐(并且,万岁!,可执行文件与目标文件的大小相同),但我猜对齐是故意存在的,所以也许我只需要减少该值以使其更适合我的情况?

如果您知道我遗漏了什么,我非常感谢您回答这些问题中的任何一个或任何其他细节。也请告诉我我是否在任何地方错了。提前致谢!

其他信息

我的目标文件的转储(带有 ld):

Hexadecimal dump of hello_world.o

我的可执行文件转储的一部分(使用类似于上面的命令): 一个新的部分从地址 0x1000

开始

Hexadecimal dump of hello_world

xxd hello_world.o | grep -E '0000|$' --color=always | less -R输出

dumpelf hello_world.o

#include <elf.h> /* * ELF dump of 'hello_world.o' * 640 (0x280) bytes */ Elf32_Dyn dumpedelf_dyn_0[]; struct { Elf32_Ehdr ehdr; Elf32_Phdr phdrs[0]; Elf32_Shdr shdrs[7]; Elf32_Dyn *dyns; } dumpedelf_0 = { .ehdr = { .e_ident = { /* (EI_NIDENT bytes) */ /* [0] EI_MAG: */ 0x7F,'E','L','F',/* [4] EI_CLASS: */ 1,/* (ELFCLASS32) */ /* [5] EI_DATA: */ 1,/* (ELFDATA2LSB) */ /* [6] EI_VERSION: */ 1,/* (EV_CURRENT) */ /* [7] EI_OSABI: */ 0,/* (ELFOSABI_NONE) */ /* [8] EI_ABIVERSION: */ 0,/* [9-15] EI_PAD: */ 0x0,0x0,},.e_type = 1,/* (ET_REL) */ .e_machine = 3,/* (EM_386) */ .e_version = 1,/* (EV_CURRENT) */ .e_entry = 0x0,/* (start address at runtime) */ .e_phoff = 0,/* (bytes into file) */ .e_shoff = 64,/* (bytes into file) */ .e_flags = 0x0,.e_ehsize = 52,/* (bytes) */ .e_phentsize = 0,/* (bytes) */ .e_phnum = 0,/* (program headers) */ .e_shentsize = 40,/* (bytes) */ .e_shnum = 7,/* (section headers) */ .e_shstrndx = 3 },.phdrs = { /* no program headers ! */ },.shdrs = { /* Section Header #0 '' 0x40 */ { .sh_name = 0,.sh_type = 0,/* [SHT_NULL] */ .sh_flags = 0,.sh_addr = 0x0,.sh_offset = 0,/* (bytes) */ .sh_size = 0,/* (bytes) */ .sh_link = 0,.sh_info = 0,.sh_addralign = 0,.sh_entsize = 0 },/* Section Header #1 '.rodata' 0x68 */ { .sh_name = 1,.sh_type = 1,/* [SHT_PROGBITS] */ .sh_flags = 2,.sh_offset = 352,/* (bytes) */ .sh_size = 13,.sh_addralign = 4,/* Section Header #2 '.text' 0x90 */ { .sh_name = 9,/* [SHT_PROGBITS] */ .sh_flags = 6,.sh_offset = 368,/* (bytes) */ .sh_size = 31,.sh_addralign = 16,/* Section Header #3 '.shstrtab' 0xB8 */ { .sh_name = 15,.sh_type = 3,/* [SHT_STRTAB] */ .sh_flags = 0,.sh_offset = 400,/* (bytes) */ .sh_size = 51,.sh_addralign = 1,/* Section Header #4 '.symtab' 0xE0 */ { .sh_name = 25,.sh_type = 2,/* [SHT_SYMTAB] */ .sh_flags = 0,.sh_offset = 464,/* (bytes) */ .sh_size = 112,/* (bytes) */ .sh_link = 5,.sh_info = 6,.sh_entsize = 16 },/* Section Header #5 '.strtab' 0x108 */ { .sh_name = 33,.sh_offset = 576,/* (bytes) */ .sh_size = 37,/* Section Header #6 '.rel.text' 0x130 */ { .sh_name = 41,.sh_type = 9,/* [SHT_REL] */ .sh_flags = 0,.sh_offset = 624,/* (bytes) */ .sh_size = 8,/* (bytes) */ .sh_link = 4,.sh_info = 2,.sh_entsize = 8 },.dyns = dumpedelf_dyn_0,}; Elf32_Dyn dumpedelf_dyn_0[] = { /* no dynamic tags ! */ }; 输出

dumpelf hello_world

解决方法

对齐是4096字节,这是这个架构上的页面大小。这并非巧合,正如 man page 关于 nmagic 所说:“关闭部分的页面对齐”。

通过正常(非 nmagic)二进制文件的大小,您可以猜测链接器布置了三个页面,大概具有不同的访问权限(代码 = 不可写,数据 = 不可执行,rodata = 只读),这些权限只能每页设置。运行时,磁盘布局与 RAM 中的布局匹配。

这对于需求分页很重要。程序启动时,整个可执行文件基本上都被mmaped了,并根据需要通过页面错误从磁盘加载页面。此外,页面可以在其其他运行实例之间共享(这对于动态库更重要),并且可以在需要时由于内存压力而从 RAM 中驱逐。

nmagic 可执行文件在运行时仍被加载到三个页面中,但由于那些不再与磁盘上的内容匹配,因此它不是按需分页的。我不建议在更大的物体上使用该选项。

注意:如果您制作了一个运行时间更长的可执行文件(可能添加了对输入的读取),您可以通过查看 /proc/[pid]/maps 和 smaps 来检查正在运行的进程的内存布局细节。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。