В настоящее время я создаю игрушечную ОС, используя собственный скрипт компоновщика для создания двоичного файла:
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 приветствуются.
В случае, если кому-то интересно, бинарный файл представляет собой ELF64, связанный с GNU ld, загружаемый Grub2 с использованием Multiboot2.
PS: нет ничего плохого в моих отображениях виртуальной памяти, так как они отлично работают, когда выровняйте 8. также, сдвиг присутствует, когда я сбрасываю физическую память.
Кроме того, ранее я использовал Rust для основного кода вместо C ++. В Rust эта ошибка выравнивания присутствовала всегда, начиная с реализации LTO в компиляторе, но хорошо раньше.
Спасибо за ваши ответы.
Задача ещё не решена.
Других решений пока нет …