我已经找到了有关创建操作系统的教程,并且正在尝试将make文件中的链接部分转换为链接脚本。
以下是教程:https://github.com/ghaiklor/ghaiklor-os-gcc
以下是制作文件:
SOURCES = $(shell find cpu drivers include kernel libc -name '*.c')
HEADERS = $(shell find cpu drivers include kernel libc -name '*.h')
OBJ = ${SOURCES:.c=.o cpu/interrupt.o}
ASM = nasm
CC = gcc
LD = ld -m elf_i386
CFLAGS = -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -fno-pie
ifeq ($(shell uname -s),Darwin)
CC = i386-elf-gcc
LD = i386-elf-ld
endif
all: os-image.bin
run: all
qemu-system-i386 os-image.bin
clean:
rm -rf *.dis *.o *.elf
rm -rf *.bin os-image.bin boot/*.bin kernel/*.bin
rm -rf boot/*.o cpu/*.o drivers/*.o kernel/*.o libc/*.o
os-image.bin: boot/boot.bin kernel/kernel.bin
cat $^ > os-image.bin
boot/boot.bin: boot/boot.asm
${ASM} $< -f bin -o $@
kernel/kernel.bin: boot/kernel_entry.o ${OBJ}
${LD} -o $@ -Ttext 0x1000 $^ --oformat binary
# ${LD} -o $@ -Tlinker.ld
# ${LD} -o $@ -Ttext 0x1000 $^ --oformat binary
%.o: %.c ${HEADERS} ${CC} ${CFLAGS} -c $< -o $@
%.o: %.asm ${ASM} $< -f elf -o $@
%.bin: %.asm ${ASM} $< -f bin -o $@
这是我尝试为该make文件的链接阶段创建一个链接描述文件:
ENTRY(_start)
OUTPUT_FORMAT(binary)
INPUT(boot/kernel_entry.o cpu/idt.o cpu/interrupt.o cpu/isr.o cpu/ports.o cpu/timer.o drivers/keyboard.o drivers/screen.o libc/mem.o libc/string.o libc/common.o kernel/kernel.o)
OUTPUT(kernel/kernel.bin)
SECTIONS
{
. = 0x1000;
.text : { *(.text) }
end = .; _end = .; __end = .;
}
boot / kernel_entry.o没有.data或.bss,这就是为什么我没有将它们包含在链接脚本中的原因。我知道-Ttext 0x1000是应该加载.text节的位置,因此我将计数器设置为从0x1000的地址开始。当我使用Makefile ${LD} -o $@ -Tlinker.ld
中的新链接命令运行系统时,系统无法正常运行,因此我做错了事。我尝试添加简单的.data和.bss部分以及所有其他种类的内容,但是仍然无法通过链接脚本使该内容正常工作。任何帮助都会很棒。
谢谢。
答案 0 :(得分:2)
您链接到的教程是针对64位代码的。您的Makefile
和随后的注释建议您尝试对其进行修改,以将其汇编/编译/运行为32位内核。我已将下面讨论的修订后的项目的副本放置在wesbite上。可以从here下载压缩的tarball。
在将内核加载到内存中时,您所拥有的教程相当愚蠢。它要求您知道要读取多少扇区并对值进行硬编码。弄错了会导致异常的行为。无需对值进行硬编码,您可以使NASM包含kernel.bin
boot.bin
,以便引导加载程序可以计算在组装时要读取的扇区数。并非所有的仿真器和真实机器都支持多磁道读取,因此我将修改引导加载程序以使用LBA寻址一次读取一个扇区。要了解有关CHS到LBA转换计算的更多信息,请参阅本主题的其他Stackoveflow answer。将boot/boot.asm
修改为:
STAGE2_ABS_ADDR equ 0x01000
STAGE2_RUN_SEG equ 0x0000
STAGE2_RUN_OFS equ STAGE2_ABS_ADDR
; Run stage2 with segment of 0x0000 and offset of 0x1000
STAGE2_LOAD_SEG equ STAGE2_ABS_ADDR>>4
; Segment to start reading Stage2 into
; right after bootloader
STAGE2_LBA_START equ 1 ; Logical Block Address(LBA) Stage2 starts on
; LBA 1 = sector after boot sector
STAGE2_LBA_END equ STAGE2_LBA_START + NUM_STAGE2_SECTORS
; Logical Block Address(LBA) Stage2 ends at
DISK_RETRIES equ 3 ; Number of times to retry on disk error
bits 16
ORG 0x7c00
; Include a BPB (1.44MB floppy with FAT12) to be more comaptible with USB floppy media
; %include "bpb.inc"
boot_start:
xor ax, ax ; DS=SS=ES=0 for stage2 loading
mov ds, ax
mov ss, ax ; Stack at 0x0000:0x0000
; (grows down fromtopof1st 64KiB segment)
mov sp, 0x0000
cld ; Set string instructions to use forward movement
; Read Stage2 1 sector at a time until stage2 is completely loaded
load_stage2:
mov [bootDevice], dl ; Save boot drive
mov bx, MSG_LOAD_KERNEL
call print_string
mov di, STAGE2_LOAD_SEG ; DI = Current segment to read into
mov si, STAGE2_LBA_START ; SI = LBA that stage2 starts at
jmp .chk_for_last_lba ; Check to see if we are last sector in stage2
.read_sector_loop:
mov bp, DISK_RETRIES ; Set disk retry count
call lba_to_chs ; Convert current LBA to CHS
mov es, di ; Set ES to current segment number to read into
xor bx, bx ; Offset zero in segment
.retry:
mov ax, 0x0201 ; Call function 0x02 of int 13h (read sectors)
; AL = 1 = Sectors to read
int 0x13 ; BIOS Disk interrupt call
jc .disk_error ; If CF set then disk error
.success:
add di, 512>>4 ; Advance to next 512 byte segment (0x20*16=512)
inc si ; Next LBA
.chk_for_last_lba:
cmp si, STAGE2_LBA_END ; Have we reached the last stage2 sector?
jl .read_sector_loop ; If we haven't then read next sector
.stage2_loaded:
call switch_to_pm
.disk_error:
xor ah, ah ; Int13h/AH=0 is drive reset
int 0x13
dec bp ; Decrease retry count
jge .retry ; If retry count not exceeded then try again
error_end:
; Unrecoverable error; print drive error; enter infinite loop
mov bx, diskErrorMsg ; Display disk error message
call print_string
cli
.error_loop:
hlt
jmp .error_loop
; Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
; Works for all valid FAT12 compatible disk geometries.
;
; Resources: http://www.ctyme.com/intr/rb-0607.htm
; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
; https://stackoverflow.com/q/45434899/3857942
; Sector = (LBA mod SPT) + 1
; Head = (LBA / SPT) mod HEADS
; Cylinder = (LBA / SPT) / HEADS
;
; Inputs: SI = LBA
; Outputs: DL = Boot Drive Number
; DH = Head
; CH = Cylinder (lower 8 bits of 10-bit cylinder)
; CL = Sector/Cylinder
; Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
; Sector in lower 6 bits of CL
;
; Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
push ax ; Preserve AX
mov ax, si ; Copy LBA to AX
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [sectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT
mov cl, dl ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [numHeads] ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
mov dh, dl ; DH = H = (LBA / SPT) mod HEADS
mov dl, [bootDevice] ; boot device, not necessary to set but convenient
mov ch, al ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
shl ah, 6 ; Store upper 2 bits of 10-bit Cylinder into
or cl, ah ; upper 2 bits of Sector (CL)
pop ax ; Restore scratch registers
ret
%include "boot/print/print_string.asm"
%include "boot/pm/switch_to_pm.asm"
%include "boot/pm/gdt.asm"
bits 32
begin_pm:
jmp 0x1000
; Uncomment these lines if not using a BPB (via bpb.inc)
%ifndef WITH_BPB
numHeads: dw 2 ; 1.44MB Floppy has 2 heads & 18 sector per track
sectorsPerTrack: dw 18
%endif
bootDevice: db 0x00
diskErrorMsg: db "Unrecoverable disk error!", 0
MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0
MSG_LOAD_KERNEL db "Loading kernel into memory", 0
; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db 0
dw 0xaa55
; Beginning of stage2. This is at 0x1000 and will allow your stage2 to be 32.5KiB
; before running into problems. DL will be set to the drive number originally
; passed to us by the BIOS.
NUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512
; Number of 512 byte sectors stage2 uses.
stage2_start:
; Insert stage2 binary here. It is done this way since we
; can determine the size(and number of sectors) to load since
; Size = stage2_end-stage2_start
incbin "kernel/kernel.bin"
; End of stage2. Make sure this label is LAST in this file!
stage2_end:
您的Makefile
可以进行一些清理。我注意到您手动将interrupts.o
添加到OBJ
(因为它是一个ASM文件)。我建议收集所有与内核相关的.asm
文件,并将它们添加到OBJ
列表中。我建议将其更改为此:
SOURCES = $(shell find cpu drivers include kernel libc -name '*.c')
KERN_ASM = $(shell find cpu drivers include kernel libc -name '*.asm')
HEADERS = $(shell find cpu drivers include kernel libc -name '*.h')
OBJ = ${SOURCES:.c=.o} ${KERN_ASM:.asm=.o}
ASM = nasm
CC = gcc
LD = ld -m elf_i386
OBJCOPY = objcopy
CFLAGS = -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -fno-pic \
-fno-asynchronous-unwind-tables
ifeq ($(shell uname -s),Darwin)
CC = i386-elf-gcc
LD = i386-elf-ld
OBJCOPY = i386-elf-objcopy
endif
all: os-image.bin
run: all
qemu-system-i386 os-image.bin
clean:
rm -rf *.dis *.o *.elf
rm -rf *.bin os-image.bin boot/*.bin kernel/*.bin
rm -rf boot/*.o cpu/*.o drivers/*.o kernel/*.o libc/*.o
# Make a 1.44MiB disk image. Can work for HDA and FDA booting
os-image.bin: kernel/kernel.bin boot/boot.bin
dd if=/dev/zero of=$@ bs=1024 count=1440
dd if=$(word 2,$^) of=$@ conv=notrunc
boot/boot.bin: boot/boot.asm
${ASM} $< -f bin -o $@
kernel/kernel.bin: kernel/kernel.elf
${OBJCOPY} -O binary $^ $@
kernel/kernel.elf: ${OBJ}
${LD} -o $@ -Tlinker.ld $^
%.o: %.c ${HEADERS}
${CC} ${CFLAGS} -c $< -o $@
%.o: %.asm
${ASM} $< -f elf -o $@
%.bin: %.asm
${ASM} $< -f bin -o $@
此make文件使用DD创建一个1.44MiB软盘映像,该映像可用作软盘或硬盘驱动器映像。您会注意到我已经从显式依赖项列表中删除了kernel_entry.asm
。为了使新的Makefile
工作,您必须将boot/kernel_entry.asm
移动到kernel/kernel_entry.asm
。确保您删除boot / kernel_entry.asm 。
修改kernel/kernel_entry.asm
以使用.text.entry
部分,并将BSS归零。它看起来可能像这样:
global _start
bits 32
extern kernel_main
extern __bss_start
extern __bss_sizel
section .text.entry
_start:
; Zero out the BSS memory area a DWORD at a time
; since the memory isn't guaranteed to already be zero
xor eax, eax
mov ecx, __bss_sizel
mov edi, __bss_start
rep stosd
; Call C entry point of kernel
call kernel_main
jmp $
适用于这些更改的链接脚本linker.ld
如下:
OUTPUT_FORMAT(elf32-i386)
SECTIONS {
. = 0x1000;
.text : SUBALIGN(4)
{
*(.text.entry) /* Ensure .text.entry appears first */
*(.text*)
*(.rodata*)
*(.data)
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all BSS sections from all files */
}
. = ALIGN(4);
__bss_end = .;
__bss_sizeb = __bss_end - __bss_start; /* BSS size in bytes */
__bss_sizel = (__bss_end - __bss_start) / 4; /* BSS size in longs/DWORDs */
/DISCARD/ : { /* Remove Unneeded sections */
*(.eh_frame);
*(.comment);
}
end = .; _end = .; __end = .;
}
它处理您在基本OS的ELF文件中通常看到的所有常规段。它还使用特殊的.entry.text
部分来确保kernel/kernel_entry.asm
中的代码优先。