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