1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

Implement Makefile for building, running and debugging the kernel

The makefile provides the following targets:
- kernel
- iso
- run
- gdb

It sniffs the OS type and when running on non-linux hosts it uses
vagrant ssh and runs make with the above targets inside the vagrant box.

The kernel build process consists of the following steps:
1) Compile arch-specific (only x86 for now) assembly files.
2) Run go build -n to obtain the build commands for our kernel. The
makefile sets the build target to 386/linux so that our current rt0
implementation does not need to switch to long-mode.
3) The build commands are then patched to:
  - use build/ as the output directory
  - force the go linker to use external link mode and to place its output files
  (--tmpdir) to the build folder. By forcing external link mode, the go
  linker will emit a single go.o file which can be used by ld.
4) We run our own link step and use ld to link the rt0 .o files with the
go.o file and provide a custom linker script to ensure that our
multiboot record is located at the top of the kernel image so that grub
can find it.

The ISO build process sets up a minimal folder structure for building a
bootable ISO (basically the kernel image plus a grub configuration) and
runs grub-mkrescue to produce the ISO file.

Both the run and the gdb targets assume that qemu is installed. The gdb
target starts qemu, attaches the debugger and sets a breakpoint to the
rt0 entrypoint.
This commit is contained in:
Achilleas Anagnostopoulos 2017-03-23 07:10:49 +00:00
parent 865f46c467
commit 95569d2982
3 changed files with 141 additions and 0 deletions

99
Makefile Normal file
View File

@ -0,0 +1,99 @@
OS = $(shell uname -s)
ARCH := x86
BUILD_DIR := build
BUILD_ABS_DIR := $(CURDIR)/$(BUILD_DIR)
kernel_target :=$(BUILD_DIR)/kernel-$(ARCH).bin
iso_target := $(BUILD_DIR)/kernel-$(ARCH).iso
ifeq ($(OS), Linux)
export SHELL := /bin/bash
LD := ld
AS := nasm
GOOS := linux
GOARCH := 386
LD_FLAGS := -n -melf_i386 -T arch/$(ARCH)/script/linker.ld -static --no-ld-generated-unwind-info
AS_FLAGS := -g -f elf32 -F dwarf -I arch/$(ARCH)/asm/
asm_src_files := $(wildcard arch/$(ARCH)/asm/*.s)
asm_obj_files := $(patsubst arch/$(ARCH)/asm/%.s, $(BUILD_DIR)/arch/$(ARCH)/asm/%.o, $(asm_src_files))
.PHONY: kernel iso clean
kernel: $(kernel_target)
$(kernel_target): $(asm_obj_files) go.o
@echo "[$(LD)] linking kernel-$(ARCH).bin"
@$(LD) $(LD_FLAGS) -o $(kernel_target) $(asm_obj_files) $(BUILD_DIR)/go.o
go.o:
@mkdir -p $(BUILD_DIR)
@echo "[go] compiling go sources into a standalone .o file"
@GOARCH=386 GOOS=linux go build -n 2>&1 | sed \
-e "1s|^|set -e\n|" \
-e "1s|^|export GOOS=linux\n|" \
-e "1s|^|export GOARCH=386\n|" \
-e "1s|^|WORK='$(BUILD_ABS_DIR)'\n|" \
-e "1s|^|alias pack='go tool pack'\n|" \
-e "/^mv/d" \
-e "s|-extld|-tmpdir='$(BUILD_ABS_DIR)' -linkmode=external -extldflags='-nostdlib' -extld|g" \
| sh 2>&1 | sed -e "s/^/ | /g"
@# build/go.o is a elf32 object file but all go symbols are unexported. Our
@# asm entrypoint code needs to know the address to 'main.main' so we use
@# objcopy to make that symbol exportable
@echo "[objcopy] export 'main.main' symbol in go.o"
@objcopy --globalize-symbol='main.main' $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o
$(BUILD_DIR)/arch/$(ARCH)/asm/%.o: arch/$(ARCH)/asm/%.s
@mkdir -p $(shell dirname $@)
@echo "[$(AS)] $<"
@$(AS) $(AS_FLAGS) $< -o $@
iso: $(iso_target)
$(iso_target): $(kernel_target)
@echo "[grub] building ISO kernel-$(ARCH).iso"
@mkdir -p $(BUILD_DIR)/isofiles/boot/grub
@cp $(kernel_target) $(BUILD_DIR)/isofiles/boot/kernel.bin
@cp arch/$(ARCH)/script/grub.cfg $(BUILD_DIR)/isofiles/boot/grub
@grub-mkrescue -o $(iso_target) $(BUILD_DIR)/isofiles 2>&1 | sed -e "s/^/ | /g"
@rm -r $(BUILD_DIR)/isofiles
else
VAGRANT_SRC_FOLDER = /home/vagrant/workspace/src/github.com/achilleasa/gopher-os
.PHONY: kernel iso vagrant-up vagrant-down vagrant-ssh run gdb clean
kernel:
vagrant ssh -c 'cd $(VAGRANT_SRC_FOLDER); make kernel'
iso:
vagrant ssh -c 'cd $(VAGRANT_SRC_FOLDER); make iso'
run: iso
qemu-system-i386 -cdrom $(iso_target)
gdb: iso
qemu-system-i386 -s -S -cdrom $(iso_target) &
sleep 1
gdb \
-ex "add-auto-load-safe-path $(pwd)" \
-ex "file $(kernel_target)" \
-ex "set disassembly-flavor intel" \
-ex 'set arch i386:intel' \
-ex 'target remote localhost:1234' \
-ex 'layout asm' \
-ex 'b _rt0_entry' \
-ex 'continue' \
-ex 'disass'
@killall qemu-system-i386 || true
endif
clean:
@test -d $(BUILD_DIR) && rm -rf $(BUILD_DIR) || true

7
arch/x86/script/grub.cfg Normal file
View File

@ -0,0 +1,7 @@
set timeout=0
set default=0
menuentry "gopheros" {
multiboot2 /boot/kernel.bin
boot
}

35
arch/x86/script/linker.ld Normal file
View File

@ -0,0 +1,35 @@
ENTRY(_rt0_entry)
SECTIONS {
/* Kernel starts at 1M */
. = 1M;
/* ensure that the multiboot header is at the beginning */
.multiboot :
{
*(.multiboot_header)
}
.text BLOCK(4K) : ALIGN(4K)
{
*(.text)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
}