Неверное значение выравнивания для раздела ELF приводит к неправильной загрузке программы

В настоящее время я создаю игрушечную ОС, используя собственный скрипт компоновщика для создания двоичного файла:

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 в компиляторе, но хорошо раньше.

Спасибо за ваши ответы.

5

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]