ELF部分的错误对齐值导致程序加载错误

 手机用户2702932807 发布于 2023-01-20 14:31

我目前正在使用自定义链接描述文件构建玩具操作系统来创建二进制文件:

ENTRY(entry_point)

/* base virtual address of the kernel */

VIRT_BASE = 0xFFFFFFFF80000000;

SECTIONS
{
    . = 0x100000;

    /*
     * Place multiboot header at 0x10000 as it is where Grub will be looking
     * for it.
     * Immediately followed by the boot code
     */

    .boot :
    {
        *(.mbhdr)
        _load_start = .;
        *(.boot)

        . = ALIGN(4096);

        /* reserve space for paging data structures */

        pml4 = .;
        . += 0x1000;
        pdpt = .;
        . += 0x1000;
        pagedir = .;
        . += 0x1000;
        . += 0x8000;

        /* stack segment for loader */

        stack = .;
    }

    /*
     * Kernel code section is placed at his virtual address
     */

    . += VIRT_BASE;

    .text ALIGN(0x1000) : AT(ADDR(.text) - VIRT_BASE)
    {
        *(.text)
        *(.gnu.linkonce.t*)
    }

    .data ALIGN(0x1000) : AT(ADDR(.data) - VIRT_BASE)
    {
        *(.data)
        *(.gnu.linkonce.d*)
    }

    .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - VIRT_BASE)
    {
        *(.rodata*)
        *(.gnu.linkonce.r*)
    }

    _load_end = . - VIRT_BASE;

    .bss ALIGN(0x1000) : AT(ADDR(.bss) - VIRT_BASE)
    {
        *(COMMON)
        *(.bss)
        *(.gnu.linkonce.b*)
    }

    _bss_end = . - VIRT_BASE;

    /DISCARD/ :
    {
        *(.comment)
        *(.eh_frame)
    }
}

由于我使用虚拟内存,因此我使用AT()指令来区分重定位地址(符号的值)和实际的物理加载地址,因为加载二进制文件时不启用虚拟内存.我给AT的地址对应于映射到虚拟重定位地址的物理地址.这很好用.

但在某些情况下,我注意到我的代码加载到内存后有一个奇怪的转变..text部分(不包括程序集引导代码,只有C++代码)比它应该的位置高8个字节.objdump显示二进制文件中的虚拟重定位地址以及加载地址是正确的.唯一可见的变化是在readelf -l中:

Elf file type is EXEC (Executable file)
Entry point 0x10003c
There are 3 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  LOAD           0x0000e8 0x0000000000100000 0x0000000000100000 0x00c000 0x00c000 R   0x8
  LOAD           0x00c0f0 0xffffffff8010c000 0x000000000010c000 0x004032 0x005002 RWE 0x10
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x8

 Section to Segment mapping:
  Segment Sections...
   00     .boot
   01     .text .text._ZN2io3outEth .data .rodata .bss
   02

我的文本部分的ALIGN在这里是0x10,而当一切正常时它是0x8.

为什么这种对齐只出现在某些情况下?例如,当我使用这种C++静态初始化时:

struct interrupt_desc
{
    uint16_t clbk_low  = 0x0;
    uint16_t selector  = 0x08;
    uint8_t  zero      = 0x0;
    uint8_t  flags     = 0xE;
    uint16_t clbk_mid  = 0x0;
    uint32_t clbk_high = 0x0;
    uint32_t zero2     = 0x0;
} __attribute__((packed));

interrupt_desc bar[32];

或者当我为IDT处理实现这些汇编结构时,如果我将它们放在.data部分中,一切都很好,但是如果它们转到.text,那么对齐就是:

[SECTION .data]
[GLOBAL _interrupt_table_register]
[GLOBAL _interrupt_vector_table]
[BITS 64]

align 8

_interrupt_table_register:
    DW  0xABCD
    DQ  _interrupt_vector_table

_interrupt_vector_table:
    %rep    256
        DW      0x0000
        DW      0x0008
        DB      0x00
        DB      0x0E
        DW      0x0000
        DD      0x00000000
        DD      0x00000000
    %endrep

最后,我真的不明白这个对齐值意味着什么,以及我如何调整它.此外,加载地址已经是0x10字节对齐,那么为什么这个对齐会改变有效加载地址?

也许我不理解这里重要的事情,所以对ELF内部的任何解释都是非常受欢迎的.

如果有人想知道,二进制文件是与GNU ld链接的ELF64,由Grub2使用Multiboot2启动.

PS:我的虚拟内存映射没有任何问题,因为当对齐为8时,它们可以正常工作.此外,当我转储物理内存时,转换也会出现.

此外,我曾经使用Rust作为主代码而不是C++.使用Rust,自编译器中LTO的实现以来,这个对齐错误始终存在,但之前很好.

谢谢你的回答.

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有