#c #ассемблер #kernel #os #fasm
Доброе время суток! Недавно начал писать свою ос на FASM и C и застрял на создании
ядра. (пишу в Windows, mingw)
Вкратце, мне нужно залинковать загрузчик и ядро, но пока ничего не выходит.
Код загрузчика (bootloader.asm):
format binary
; preparation
org 0x7c00
use16
begin:
xor ax, ax
mov ds, ax
mov es, ax
mov [disk], dl ; save disk number for later
mov si, loading16 ; print welcome string
pchar:
.lp:
mov al, [si]
cmp al, 0
je .exit
.pch:
mov ah, 0eh
int 10h
add si, 1
jmp .lp
.exit:
mov ah, 0 ;reset disk
int 13h
; load kernel_entry and kernel from disk
mov ah, 2h ;read sectors
mov al, 1 ;sectors to read
mov ch, 0 ;cylinder idx
mov dh, 0 ;head idx
mov cl, 2 ;sector idx
mov dl, [disk] ;disk idx
mov bx, 0x1000 ;target pointer
int 13h
; get into pm and jump to KERNEL label
jmp switch_to_pm
jmp $ ; never executed
use32
KERNEL:
jmp 0x1000 ; jump to the actual kernel
jmp $ ; never executed
include './32/gdt.asm' ; gdt for protected mode
include './32/switch32.asm' ; code for entering protected mode
include './32/strout32.asm' ; protected mode print
;; data
disk: rb 1
use16
wc32: db "Welcome to x32!", 0
loading16: db "Loading OS"
osver: db "v.0.0", 0
;bootloader fill
times 510-($-$$) db 0
;magic number
dw 0xaa55
;;;;;;;;;;;;;
./32/gdt.asm:
use16
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
dd gdt_start ; address (32 bit)
; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
./32/strout32.asm:
use32
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f ; the color byte for each character
__print_string_label:
mov edx, VIDEO_MEMORY
.print_string_pm_loop:
mov al, [ebx] ; [ebx] is the address of our character
mov ah, WHITE_ON_BLACK
cmp al, 0 ; check if end of string
je .print_string_pm_done
mov [edx], ax ; store character + attribute in video memory
add ebx, 1 ; next char
add edx, 2 ; next video memory position
jmp .print_string_pm_loop
.print_string_pm_done:
ret
;PRINT MACRO
macro print _str* {
mov ebx, _str
call __print_string_label
}
./32/switch32.asm:
use16
switch_to_pm:
mov ax, 0x2401
int 15h ;A20
mov ax, 3h
int 10h
cli ; 1. disable interrupts
lgdt [gdt_descriptor] ; 2. load the GDT descriptor
mov eax, cr0
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
mov cr0, eax
jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
use32
init_pm: ; we are now using 32-bit instructions
mov ax, DATA_SEG ; 5. update the segment registers
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
mov esp, ebp
call KERNEL ; 7. Call a well-known label with useful code
use16 ;
Код загрузчика ядра (kernelentr.asm):
format elf
use32
extrn _start
call _start
jmp $
Ядро (kernel.c):
void start (void)
{
unsigned char* vga = (unsigned char*) 0xb8000;
vga[0] = 'X'; //need to make sure that this is a character
vga[1] = 0x09; //append the attribute byte
for(;;); //make sure our kernel never stops, with an infinite loop
}
Мой порядок действий:
Компиляция ядра
gcc -m32 -ffreestanding -nostdlib -c kernel.c -o kernel.o
Линковка получившегося обьекта и загрузчика ядра(?)
ld -T NUL -o kernel.tmp -Ttext 0x1000 kernelentr.o kernel.o
Извлечение бинарного кода из ядра с загрузчиком
objcopy -O binary -j .text kernel.tmp kernel.bin
Совмещение загрузчика и получившегося бинарника
copy /b bootloader.bin+kernel.bin os.bin
EDIT: Команды брал отсюда - https://stackoverflow.com/questions/25128579/ld-cannot-perform-pe-operations-on-non-pe-output-file-error
После всей процедуры получается файл который, увы, не запускается и вылетает из QEMU
с ошибкой 'Trying to execute code outside RAM or ROM at 0xc6fc458b'
Укажите пожалуйста на ошибку и и скажите как все таки заставить это штуку работать.
UPDATE: Вдруг бинарник таки заработал, но криво -
Теперь при запуске и правда высвечиваеся синяя буква Х, но экран постоянно меняется
на до х32 (с надписью Loading OS v0.0) и обратно.
При этом эмулятор не крашится...
Ответы
Ответ 1
На шаге ld -T NUL -o kernel.tmp -Ttext 0x1000 kernelentr.o kernel.o получается файл с двумя исполняемыми секциями: .flat ("загрузчик ядра") и .text (сишный код): .flat:00000000 ; Segment type: Pure code .flat:00000000 ; Segment permissions: Read/Write/Execute .flat:00000000 _flat segment dword public 'CODE' use32 .flat:00000000 assume cs:_flat .flat:00000000 assume es:nothing, ss:nothing, ds:_flat, fs:nothing, gs:nothing .flat:00000000 call _start .flat:00000005 .flat:00000005 loc_5: ; CODE XREF: .flat:loc_5j .flat:00000005 jmp short loc_5 .flat:00000005 ; --------------------------------------------------------------------------- .flat:00000007 align 200h .flat:00000200 dd 380h dup(?) .flat:00000200 _flat ends .flat:00000200 .text:00001000 ; Section 2. (virtual address FFC01000) .text:00001000 ; Virtual size : 00000020 ( 32.) .text:00001000 ; Section size in file : 00000200 ( 512.) .text:00001000 ; Offset to raw data for section: 00000600 .text:00001000 ; Flags 60300020: Text Executable Readable .text:00001000 ; Alignment : 4 bytes .text:00001000 ; =========================================================================== .text:00001000 .text:00001000 ; Segment type: Pure code .text:00001000 ; Segment permissions: Read/Execute .text:00001000 _text segment dword public 'CODE' use32 .text:00001000 assume cs:_text .text:00001000 ;org 1000h .text:00001000 assume es:nothing, ss:nothing, ds:_flat, fs:nothing, gs:nothing .text:00001000 .text:00001000 ; =============== S U B R O U T I N E ======================================= .text:00001000 .text:00001000 ; Attributes: bp-based frame .text:00001000 .text:00001000 public _start .text:00001000 _start proc near ; CODE XREF: .flat:00000000p .text:00001000 .text:00001000 var_4 = dword ptr -4 .text:00001000 .text:00001000 push ebp .text:00001001 mov ebp, esp .text:00001003 sub esp, 10h .text:00001006 mov [ebp+var_4], 0B8000h .text:0000100D mov eax, [ebp+var_4] .text:00001010 mov byte ptr [eax], 58h ; 'X' .text:00001013 mov eax, [ebp+var_4] .text:00001016 add eax, 1 .text:00001019 mov byte ptr [eax], 9 .text:0000101C nop .text:0000101D leave .text:0000101E retn .text:0000101E _start endp .text:0000101E .text:0000101E ; --------------------------------------------------------------------------- .text:0000101F align 200h .text:00001200 dd 380h dup(?) .text:00001200 _text ends На следующем шаге (objcopy) из этого исполняемого файла извлекается содержимое только секции .text, а код загрузчика ядра по факту никак не используется. Можно убедиться в этом, если с помощью дизассемблера посмотреть содержимое kernel.bin, полученного на шаге objcopy: seg000:00000000 ; Segment type: Pure code seg000:00000000 seg000 segment byte public 'CODE' use32 seg000:00000000 assume cs:seg000 seg000:00000000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing seg000:00000000 push ebp seg000:00000001 mov ebp, esp seg000:00000003 sub esp, 10h seg000:00000006 mov dword ptr [ebp-4], 0B8000h seg000:0000000D mov eax, [ebp-4] seg000:00000010 mov byte ptr [eax], 58h seg000:00000013 mov eax, [ebp-4] seg000:00000016 add eax, 1 seg000:00000019 mov byte ptr [eax], 9 seg000:0000001C nop seg000:0000001D leave seg000:0000001E retn seg000:0000001E ; --------------------------------------------------------------------------- seg000:0000001F align 10h seg000:0000001F seg000 ends Видим только функцию, без кода ее вызова и бесконечного цикла после вызова. Нужно сделать, чтобы при линковке все попало в одну секцию. Можно просто явно указать имя секции в kernelentr.asm: format elf use32 extrn _start section '.text' call _start jmp $ Результат линковки: .text:00001000 ; Segment type: Pure code .text:00001000 ; Segment permissions: Read/Execute .text:00001000 _text segment dword public 'CODE' use32 .text:00001000 assume cs:_text .text:00001000 ;org 1000h .text:00001000 assume es:nothing, ss:nothing, ds:_text, fs:nothing, gs:nothing .text:00001000 .text:00001000 ; =============== S U B R O U T I N E ======================================= .text:00001000 .text:00001000 ; Attributes: noreturn .text:00001000 .text:00001000 public start .text:00001000 start proc near .text:00001000 call _start .text:00001005 .text:00001005 loc_1005: ; CODE XREF: start:loc_1005j .text:00001005 jmp short loc_1005 .text:00001005 start endp .text:00001005 .text:00001005 ; --------------------------------------------------------------------------- .text:00001007 align 4 .text:00001008 .text:00001008 ; =============== S U B R O U T I N E ======================================= .text:00001008 .text:00001008 ; Attributes: bp-based frame .text:00001008 .text:00001008 public _start .text:00001008 _start proc near ; CODE XREF: startp .text:00001008 .text:00001008 var_4 = dword ptr -4 .text:00001008 .text:00001008 push ebp .text:00001009 mov ebp, esp .text:0000100B sub esp, 10h .text:0000100E mov [ebp+var_4], 0B8000h .text:00001015 mov eax, [ebp+var_4] .text:00001018 mov byte ptr [eax], 58h .text:0000101B mov eax, [ebp+var_4] .text:0000101E add eax, 1 .text:00001021 mov byte ptr [eax], 9 .text:00001024 nop .text:00001025 leave .text:00001026 retn .text:00001026 _start endp .text:00001026 .text:00001026 ; --------------------------------------------------------------------------- .text:00001027 align 200h .text:00001200 dd 380h dup(?) .text:00001200 _text ends Листинг содержимого kernel.bin: seg000:00000000 call sub_8 seg000:00000005 seg000:00000005 loc_5: ; CODE XREF: seg000:loc_5j seg000:00000005 jmp short loc_5 seg000:00000005 ; --------------------------------------------------------------------------- seg000:00000007 align 4 seg000:00000008 seg000:00000008 ; =============== S U B R O U T I N E ======================================= seg000:00000008 seg000:00000008 ; Attributes: bp-based frame seg000:00000008 seg000:00000008 sub_8 proc near ; CODE XREF: seg000:00000000p seg000:00000008 seg000:00000008 var_4 = dword ptr -4 seg000:00000008 seg000:00000008 push ebp seg000:00000009 mov ebp, esp seg000:0000000B sub esp, 10h seg000:0000000E mov [ebp+var_4], 0B8000h seg000:00000015 mov eax, [ebp+var_4] seg000:00000018 mov byte ptr [eax], 58h seg000:0000001B mov eax, [ebp+var_4] seg000:0000001E add eax, 1 seg000:00000021 mov byte ptr [eax], 9 seg000:00000024 nop seg000:00000025 leave seg000:00000026 retn seg000:00000026 sub_8 endp Дальше проходим по пунктам из вопроса, в конце запускаем os.bin с помощью QEMU: Стабильно показывает X, картинка не меняется (если не считать мигающего курсора под Х).
Комментариев нет:
Отправить комментарий