#c #operating-system #clang
#c #операционная система #лязг
Вопрос:
Это странно.
Итак, я пытаюсь создать небольшое ядро, и я решил использовать C для этого. Я сделал все, и теперь у меня есть (почти) рабочий драйвер текстового режима VGA. Почему почти? Потому что всякий раз, когда я передаю write
метод a const char*
, заголовок multiboot буквально исчезает.
И после небольшой возни я понял, что ЛЮБОЕ const char*
использование сводит его с ума. Даже просто переменная.
Странно то, что если я никогда не создаю a const char*
, это просто работает. Я тоже могу печатать отдельные символы.
Примечание: я основывался на простом обучении на OSDev.
Вот соответствующий код:
# Main.asm
MBALIGN equ 1 << 0
MEMINFO equ 1 << 1
FLAGS equ MBALIGN | MEMINFO
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC FLAGS)
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
section .bss
align 16
stack_bottom:
resb 16384
stack_top:
section .text
global _start:function (_start.end - _start)
_start:
mov esp, stack_top
extern kernel_main
call kernel_main
cli
.hang: hlt
jmp .hang
.end:
// Main.cpp
void init() {
Drivers::VGA vga;
vga.putc('h');
vga.write("hello", 5);
}
extern "C" void kernel_main() {
init();
}
// Part of VGA.cpp
void VGA::write(const char* data, size_t size) {
for (size_t i = 0; i < size; i = 1) {
s_buffer[i] = vga_entry(data[i], _color);
}
}
[...]
u16 VGA::vga_entry(unsigned char c, u8 color) {
return (u16)c | (u16)color << 8;
}
# Linker.ld
ENTRY(_start)
SECTIONS {
. = 1M;
.text : ALIGN(4K) {
KEEP(*(.multiboot))
*(.text)
}
.rodata : ALIGN(4K) {
*(.rodata)
}
.data : ALIGN(4K) {
*(.data)
}
.bss : ALIGN(4K) {
*(COMMON)
*(.bss)
}
}
Параметры компилятора: -target i686-pc-elf -c -IKernel -ffreestanding -nostdlib -fno-exceptions -fno-rtti -fno-stack-protector -m32 -fno-use-cxa-atexit
Набор инструментов: Clang, Nasm и ld.lld
Комментарии:
1. Простите за мое невежество, но какая часть «соответствующего кода» на самом деле реализуется
write
? Какой код выполняется приvga.write("hello", 5)
вызове? Каково определение классаDrivers::VGA
?2. @IgorTandetnik Я только что добавил его. Сначала я не добавлял его, так как думал, что это не очень актуально, поскольку даже простое создание переменной типа
const char*
все портит3. Хм. Единственное, что необычно в строковых литералах, это то, что они входят в
.rodata
раздел. Может быть, каким-то образом этот раздел не попадает в окончательный двоичный файл? Похоже, что так и должно быть. Извините, это предел моих знаний.4. @IgorTandetnik Хорошо, используя readelf, я увидел, что .rodata будет предшествовать .text (который содержит .multiboot). Это не так, как должно быть, верно? Без какого-либо строкового литерала в коде .rodata исчезает
5. @IgorTandetnik неважно, я опубликовал ответ
Ответ №1:
Проблема в том, что ld.lld
есть ошибка (?) Или что-то в этом роде. Он поместил раздел rodata перед текстом, поэтому заголовок multiboot не будет виден.
Вот результат readelf -S Kernel
с ld.lld
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .rodata.str1.1 PROGBITS 00100000 001000 000001 01 AMS 0 0 1
[ 2] .text PROGBITS 00101000 002000 0002fe 00 AX 0 0 4096
[ 3] .data PROGBITS 00102000 003000 000004 00 WA 0 0 4096
[ 4] .bss NOBITS 00103000 003004 004000 00 WA 0 0 4096
[ 5] .comment PROGBITS 00000000 003004 000029 01 MS 0 0 1
[ 6] .symtab SYMTAB 00000000 003030 0001a0 10 8 14 4
[ 7] .shstrtab STRTAB 00000000 0031d0 000044 00 0 0 1
[ 8] .strtab STRTAB 00000000 003214 00018f 00 0 0 1
И вот результат с системным ld
(GNU)
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00100000 001000 0002fe 00 AX 0 0 4096
[ 2] .rodata.str1.1 PROGBITS 001002fe 0012fe 000001 01 AMS 0 0 1
[ 3] .data PROGBITS 00101000 002000 000004 00 WA 0 0 4096
[ 4] .bss NOBITS 00102000 002004 004000 00 WA 0 0 4096
[ 5] .comment PROGBITS 00000000 002004 000015 01 MS 0 0 1
[ 6] .symtab SYMTAB 00000000 00201c 0001f0 10 7 19 4
[ 7] .strtab STRTAB 00000000 00220c 00018f 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 00239b 000044 00 0 0 1