我目前正在使用自定义链接描述文件构建玩具操作系统来创建二进制文件:
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的实现以来,这个对齐错误始终存在,但之前很好.
谢谢你的回答.