问题
I've found this tutorial on creating an operating system, and I'm trying to convert the linking part in the make file into a linker script.
Here is the tutorial: https://github.com/ghaiklor/ghaiklor-os-gcc
Here is the make file:
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 $@
Here is my attempt at creating a linker script for the linking phase of this make file:
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 = .;
}
There is no .data or .bss for the boot/kernel_entry.o and this is why I did not include them into the linker script. I know that -Ttext 0x1000 is where .text section is suppose to be loaded and thats why I set the counter to start at the address of 0x1000. When I run the system with the new linking command in the makefile ${LD} -o $@ -Tlinker.ld
the system isn't working like normal, so I'm doing something wrong. I've tried adding simple .data and .bss sections and all other sorts of things but still can't manage to get the thing to work correctly with a linking script. Any help would be great.
Thanks.
回答1:
The tutorial you linked to was for a 64-bit code.Your Makefile
and subsequent comments suggest you are trying to modify it to assemble/compile/run as a 32-bit kernel. I have placed a copy of the revised project discussed below on my wesbite. A compressed tarball can be downloaded from here.
The tutorial you have is rather dumb when it comes to loading the kernel into memory. It requires you to know how many sectors to read and hard code the value. Getting this wrong can cause unusual behaviour. Rather than hard coding the value you can get NASM to include kernel.bin
inside boot.bin
so that the bootloader can compute the number of sectors to read at assembly time. Not all emulators and real machines support multi-track reads so I'd modify the bootloader to read one sector at a time using LBA addressing. To learn more about CHS to LBA conversion calculations you can see my other Stackoveflow answer on the topic. Modify boot/boot.asm
to be:
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:
Your Makefile
can use some cleaning up. I noticed you added interrupts.o
to OBJ
manually (since it is an ASM file). I'd recommend collecting all the kernel related .asm
files and adding them to the OBJ
list. I recommend changing it to this:
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 $@
This make file uses DD to create a 1.44MiB floppy image that can be used as a floppy or hard drive disk image. You will notice I have removed kernel_entry.asm
from the explicit dependency list. For this new Makefile
to work You must MOVE boot/kernel_entry.asm
to kernel/kernel_entry.asm
. Ensure you REMOVE boot/kernel_entry.asm.
Modify kernel/kernel_entry.asm
to use section .text.entry
and zero out the BSS. It can look like this:
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 $
The linker script linker.ld
that works with these changes is as follows:
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 = .;
}
It handles all the normal segments you'd generally see in an ELF file for a basic OS. It also uses a special .entry.text
section to ensure the code in kernel/kernel_entry.asm
comes first.
来源:https://stackoverflow.com/questions/55085455/how-to-convert-ld-ttext-option-to-a-linker-script