linux - ELF 可执行文件 : many zero bytes

标签 linux executable elf executable-format

介绍
我正在编译一个简单的汇编代码(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 安装),我使用它转储了我的 elf 文件并发现了一些可能是这些零流的原因:
在可执行文件的所有三个头文件中,都有 p_align字段集:
.p_align  = 4096       , /* (min mem alignment in bytes) */
这应该意味着每个部分都应该用零字节填充,以便其长度是 4096 的倍数。并且因为以下每个部分的大小相对较小,所以要添加很多零字节,那就是这些零来自哪里。
问题)
所以,我想知道:
  • 我对吗?是否添加了这些零字节以使这些部分足够长?
    我还注意到前三个部分( '''.rodata''.text' )从 0 开始, 40968192分别,但以下( '.symtab''.strtab''.shstrtab' )似乎不再对齐:它们开始于 8208 , 83688422 ... 为什么?这里发生了什么?
  • 我们需要这种对齐方式是为了什么?在编程头文件中,有 p_vaddrp_paddr字段设置为前三个部分开始的地址,那么如果我们已经知道 对齐部分的原因是什么?精确来自标题的部分的地址?它与内存页(在我的机器上大小为 4KiB)有关吗?
  • 我什么时候想要/需要,以及如何更改对齐值?看起来应该有一个链接器参数来更改此值。我找到了 --nmagic ld 中的参数的手册,它完全禁用了对齐(并且,万岁!,可执行文件没有与目标文件的大小相同),但我猜对齐是故意存在的,所以也许我只需要减少值,以便它更适合我的情况?

  • 如果您知道我遗漏了什么,我非常感谢您回答这些问题中的任何一个或任何其他细节。也请告诉我我是否在任何地方错了。先感谢您!
    附加信息
    我的目标文件的转储(带有 xxd hello_world.o | grep -E '0000|$' --color=always | less -R ):
    Hexadecimal dump of hello_world.o
    我的可执行文件转储的一部分(使用类似于上面的命令):
    一个新的部分从地址 0x1000 开始
    Hexadecimal dump of hello_worlddumpelf 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, 0x0, 0x0, 0x0, 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_addr      = 0x0        ,
        .sh_offset    = 352        , /* (bytes) */
        .sh_size      = 13         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 4          ,
        .sh_entsize   = 0         
    },
    /* Section Header #2 '.text' 0x90 */
    {
        .sh_name      = 9          ,
        .sh_type      = 1          , /* [SHT_PROGBITS] */
        .sh_flags     = 6          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 368        , /* (bytes) */
        .sh_size      = 31         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 16         ,
        .sh_entsize   = 0         
    },
    /* Section Header #3 '.shstrtab' 0xB8 */
    {
        .sh_name      = 15         ,
        .sh_type      = 3          , /* [SHT_STRTAB] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 400        , /* (bytes) */
        .sh_size      = 51         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 1          ,
        .sh_entsize   = 0         
    },
    /* Section Header #4 '.symtab' 0xE0 */
    {
        .sh_name      = 25         ,
        .sh_type      = 2          , /* [SHT_SYMTAB] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 464        , /* (bytes) */
        .sh_size      = 112        , /* (bytes) */
        .sh_link      = 5          ,
        .sh_info      = 6          ,
        .sh_addralign = 4          ,
        .sh_entsize   = 16        
    },
    /* Section Header #5 '.strtab' 0x108 */
    {
        .sh_name      = 33         ,
        .sh_type      = 3          , /* [SHT_STRTAB] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 576        , /* (bytes) */
        .sh_size      = 37         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 1          ,
        .sh_entsize   = 0         
    },
    /* Section Header #6 '.rel.text' 0x130 */
    {
        .sh_name      = 41         ,
        .sh_type      = 9          , /* [SHT_REL] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 624        , /* (bytes) */
        .sh_size      = 8          , /* (bytes) */
        .sh_link      = 4          ,
        .sh_info      = 2          ,
        .sh_addralign = 4          ,
        .sh_entsize   = 8         
    },
    },
    
    .dyns = dumpedelf_dyn_0,
    };
    Elf32_Dyn dumpedelf_dyn_0[] = {
     /* no dynamic tags ! */ };
    
    dumpelf hello_world 的输出:
    #include <elf.h>
    
    /*
     * ELF dump of 'hello_world'
     *     8704 (0x2200) bytes
     */
    
    Elf32_Dyn dumpedelf_dyn_0[];
    struct {
        Elf32_Ehdr ehdr;
        Elf32_Phdr phdrs[3];
        Elf32_Shdr shdrs[6];
        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, 0x0, 0x0, 0x0, 0x0, 0x0,
        },
        .e_type      = 2          , /* (ET_EXEC) */
        .e_machine   = 3          , /* (EM_386) */
        .e_version   = 1          , /* (EV_CURRENT) */
        .e_entry     = 0x8049000  , /* (start address at runtime) */
        .e_phoff     = 52         , /* (bytes into file) */
        .e_shoff     = 8464       , /* (bytes into file) */
        .e_flags     = 0x0        ,
        .e_ehsize    = 52         , /* (bytes) */
        .e_phentsize = 32         , /* (bytes) */
        .e_phnum     = 3          , /* (program headers) */
        .e_shentsize = 40         , /* (bytes) */
        .e_shnum     = 6          , /* (section headers) */
        .e_shstrndx  = 5         
    },
    
    .phdrs = {
    /* Program Header #0 0x34 */
    {
        .p_type   = 1          , /* [PT_LOAD] */
        .p_offset = 0          , /* (bytes into file) */
        .p_vaddr  = 0x8048000  , /* (virtual addr at runtime) */
        .p_paddr  = 0x8048000  , /* (physical addr at runtime) */
        .p_filesz = 148        , /* (bytes in file) */
        .p_memsz  = 148        , /* (bytes in mem at runtime) */
        .p_flags  = 0x4        , /* PF_R */
        .p_align  = 4096       , /* (min mem alignment in bytes) */
    },
    /* Program Header #1 0x54 */
    {
        .p_type   = 1          , /* [PT_LOAD] */
        .p_offset = 4096       , /* (bytes into file) */
        .p_vaddr  = 0x8049000  , /* (virtual addr at runtime) */
        .p_paddr  = 0x8049000  , /* (physical addr at runtime) */
        .p_filesz = 31         , /* (bytes in file) */
        .p_memsz  = 31         , /* (bytes in mem at runtime) */
        .p_flags  = 0x5        , /* PF_R | PF_X */
        .p_align  = 4096       , /* (min mem alignment in bytes) */
    },
    /* Program Header #2 0x74 */
    {
        .p_type   = 1          , /* [PT_LOAD] */
        .p_offset = 8192       , /* (bytes into file) */
        .p_vaddr  = 0x804A000  , /* (virtual addr at runtime) */
        .p_paddr  = 0x804A000  , /* (physical addr at runtime) */
        .p_filesz = 13         , /* (bytes in file) */
        .p_memsz  = 13         , /* (bytes in mem at runtime) */
        .p_flags  = 0x4        , /* PF_R */
        .p_align  = 4096       , /* (min mem alignment in bytes) */
    },
    },
    
    .shdrs = {
    /* Section Header #0 '' 0x2110 */
    {
        .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 '.text' 0x2138 */
    {
        .sh_name      = 27         ,
        .sh_type      = 1          , /* [SHT_PROGBITS] */
        .sh_flags     = 6          ,
        .sh_addr      = 0x8049000  ,
        .sh_offset    = 4096       , /* (bytes) */
        .sh_size      = 31         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 16         ,
        .sh_entsize   = 0         
    },
    /* Section Header #2 '.rodata' 0x2160 */
    {
        .sh_name      = 33         ,
        .sh_type      = 1          , /* [SHT_PROGBITS] */
        .sh_flags     = 2          ,
        .sh_addr      = 0x804A000  ,
        .sh_offset    = 8192       , /* (bytes) */
        .sh_size      = 13         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 4          ,
        .sh_entsize   = 0         
    },
    /* Section Header #3 '.symtab' 0x2188 */
    {
        .sh_name      = 1          ,
        .sh_type      = 2          , /* [SHT_SYMTAB] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 8208       , /* (bytes) */
        .sh_size      = 160        , /* (bytes) */
        .sh_link      = 4          ,
        .sh_info      = 6          ,
        .sh_addralign = 4          ,
        .sh_entsize   = 16        
    },
    /* Section Header #4 '.strtab' 0x21B0 */
    {
        .sh_name      = 9          ,
        .sh_type      = 3          , /* [SHT_STRTAB] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 8368       , /* (bytes) */
        .sh_size      = 54         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 1          ,
        .sh_entsize   = 0         
    },
    /* Section Header #5 '.shstrtab' 0x21D8 */
    {
        .sh_name      = 17         ,
        .sh_type      = 3          , /* [SHT_STRTAB] */
        .sh_flags     = 0          ,
        .sh_addr      = 0x0        ,
        .sh_offset    = 8422       , /* (bytes) */
        .sh_size      = 41         , /* (bytes) */
        .sh_link      = 0          ,
        .sh_info      = 0          ,
        .sh_addralign = 1          ,
        .sh_entsize   = 0         
    },
    },
    
    .dyns = dumpedelf_dyn_0,
    };
    Elf32_Dyn dumpedelf_dyn_0[] = {
     /* no dynamic tags ! */ };
    

    最佳答案

    对齐是 4096 字节,这是此架构上的页面大小。这并非巧合,因为 man page说到 nmagic:“关闭部分的页面对齐”。
    通过正常(非 nmagic)二进制文件的大小,您可以猜测链接器布局了三个页面,大概具有不同的访问权限(代码 = 不可写,数据 = 不可执行,rodata = 只读),这些权限只能设置为-页。运行时,磁盘布局与 RAM 中的布局相匹配。
    这对于需求分页很重要。程序启动时,整个可执行文件基本上都被mmaped了,并根据需要通过页面错误从磁盘加载页面。此外,页面可以在其其他运行实例之间共享(这对于动态库更重要),并且可以在需要时由于内存压力而从 RAM 中删除。
    nmagic 可执行文件在运行时仍会加载到三个页面中,但由于那些不再与磁盘上的内容匹配,因此它不是按需分页的。我不建议在更大的东西上使用该选项。
    注意:如果您制作了一个运行时间更长的可执行文件(可能添加了对输入的读取),您可以通过查看/proc/[pid]/maps 和 smaps 来检查正在运行的进程的内存布局详细信息。

    关于linux - ELF 可执行文件 : many zero bytes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67288459/

    相关文章:

    linux - 如何在linux中路由

    c - SECCOMP:如何模拟 malloc、realloc 和 free?

    linux - 挂载 cif 的问题

    common-lisp - SBCL 内置可执行文件 : "When attempting to set the slot' s value to XXX (SETF of SLOT-VALUE), 对象中缺少插槽 YYY”?

    java - jar 文件打不开

    c - 使用 C 程序获取程序标志

    c - 从elf获取动态符号表信息

    windows - 如何修复 Linux 测试文件中的 Windows 字符?

    c - Xcode 错误 "Conflicting types"为一个非常简单的函数运行 C 代码

    android - 是否可以在 Android 上运行静态链接的 ELF 二进制文件?