Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Как загрузить ядро системы загрузчиком на fasm?

#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, картинка не меняется (если не считать мигающего курсора под Х).

Комментариев нет:

Отправить комментарий