mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Merge pull request #34 from achilleasa/remove-hardcoded-g-offsets-from-rt0-code
Remove hardcoded g offsets from rt0 code and use runtime.g0/m0
This commit is contained in:
commit
df6e4314ca
15
Makefile
15
Makefile
@ -22,7 +22,7 @@ GOROOT := $(shell $(GO) env GOROOT)
|
|||||||
|
|
||||||
GC_FLAGS ?=
|
GC_FLAGS ?=
|
||||||
LD_FLAGS := -n -T $(BUILD_DIR)/linker.ld -static --no-ld-generated-unwind-info
|
LD_FLAGS := -n -T $(BUILD_DIR)/linker.ld -static --no-ld-generated-unwind-info
|
||||||
AS_FLAGS := -g -f elf64 -F dwarf -I arch/$(ARCH)/asm/ -dNUM_REDIRECTS=$(shell $(GO) run tools/redirects/redirects.go count)
|
AS_FLAGS := -g -f elf64 -F dwarf -I $(BUILD_DIR)/ -I arch/$(ARCH)/asm/ -dNUM_REDIRECTS=$(shell $(GO) run tools/redirects/redirects.go count)
|
||||||
|
|
||||||
MIN_OBJCOPY_VERSION := 2.26.0
|
MIN_OBJCOPY_VERSION := 2.26.0
|
||||||
HAVE_VALID_OBJCOPY := $(shell objcopy -V | head -1 | awk -F ' ' '{print "$(MIN_OBJCOPY_VERSION)\n" $$NF}' | sort -ct. -k1,1n -k2,2n && echo "y")
|
HAVE_VALID_OBJCOPY := $(shell objcopy -V | head -1 | awk -F ' ' '{print "$(MIN_OBJCOPY_VERSION)\n" $$NF}' | sort -ct. -k1,1n -k2,2n && echo "y")
|
||||||
@ -38,7 +38,7 @@ kernel_image: $(kernel_target)
|
|||||||
@echo "[tools:redirects] populating kernel image redirect table"
|
@echo "[tools:redirects] populating kernel image redirect table"
|
||||||
@$(GO) run tools/redirects/redirects.go populate-table $(kernel_target)
|
@$(GO) run tools/redirects/redirects.go populate-table $(kernel_target)
|
||||||
|
|
||||||
$(kernel_target): $(asm_obj_files) linker_script go.o
|
$(kernel_target): asm_files linker_script go.o
|
||||||
@echo "[$(LD)] linking kernel-$(ARCH).bin"
|
@echo "[$(LD)] linking kernel-$(ARCH).bin"
|
||||||
@$(LD) $(LD_FLAGS) -o $(kernel_target) $(asm_obj_files) $(BUILD_DIR)/go.o
|
@$(LD) $(LD_FLAGS) -o $(kernel_target) $(asm_obj_files) $(BUILD_DIR)/go.o
|
||||||
|
|
||||||
@ -65,6 +65,9 @@ go.o:
|
|||||||
@objcopy \
|
@objcopy \
|
||||||
--add-symbol kernel.Kmain=.text:0x`nm $(BUILD_DIR)/go.o | grep "kmain.Kmain$$" | cut -d' ' -f1` \
|
--add-symbol kernel.Kmain=.text:0x`nm $(BUILD_DIR)/go.o | grep "kmain.Kmain$$" | cut -d' ' -f1` \
|
||||||
--globalize-symbol _rt0_interrupt_handlers \
|
--globalize-symbol _rt0_interrupt_handlers \
|
||||||
|
--globalize-symbol runtime.g0 \
|
||||||
|
--globalize-symbol runtime.m0 \
|
||||||
|
--globalize-symbol runtime.physPageSize \
|
||||||
$(BUILD_DIR)/go.o $(BUILD_DIR)/go.o
|
$(BUILD_DIR)/go.o $(BUILD_DIR)/go.o
|
||||||
|
|
||||||
binutils_version_check:
|
binutils_version_check:
|
||||||
@ -86,11 +89,19 @@ linker_script:
|
|||||||
-E -x \
|
-E -x \
|
||||||
c arch/$(ARCH)/script/linker.ld.in | grep -v "^#" > $(BUILD_DIR)/linker.ld
|
c arch/$(ARCH)/script/linker.ld.in | grep -v "^#" > $(BUILD_DIR)/linker.ld
|
||||||
|
|
||||||
|
$(BUILD_DIR)/go_asm_offsets.inc:
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
|
||||||
|
@echo "[tools:offsets] calculating OS/arch-specific offsets for g, m and stack structs"
|
||||||
|
@$(GO) run tools/offsets/offsets.go -target-os $(GOOS) -target-arch $(GOARCH) -go-binary $(GO) -out $@
|
||||||
|
|
||||||
$(BUILD_DIR)/arch/$(ARCH)/asm/%.o: arch/$(ARCH)/asm/%.s
|
$(BUILD_DIR)/arch/$(ARCH)/asm/%.o: arch/$(ARCH)/asm/%.s
|
||||||
@mkdir -p $(shell dirname $@)
|
@mkdir -p $(shell dirname $@)
|
||||||
@echo "[$(AS)] $<"
|
@echo "[$(AS)] $<"
|
||||||
@$(AS) $(AS_FLAGS) $< -o $@
|
@$(AS) $(AS_FLAGS) $< -o $@
|
||||||
|
|
||||||
|
asm_files: $(BUILD_DIR)/go_asm_offsets.inc $(asm_obj_files)
|
||||||
|
|
||||||
iso: $(iso_target)
|
iso: $(iso_target)
|
||||||
|
|
||||||
$(iso_target): iso_prereq kernel_image
|
$(iso_target): iso_prereq kernel_image
|
||||||
|
@ -20,12 +20,9 @@ _rt0_idt_desc:
|
|||||||
; Allocates space for the IRQ handlers pointers registered by the IRQ package
|
; Allocates space for the IRQ handlers pointers registered by the IRQ package
|
||||||
_rt0_irq_handlers resq IDT_ENTRIES
|
_rt0_irq_handlers resq IDT_ENTRIES
|
||||||
|
|
||||||
r0_g_ptr: resq 1 ; fs:0x00 is a pointer to the current g struct
|
; The FS register is loaded with the address of r0_g_ptr. fs:0x00 should contain
|
||||||
r0_g:
|
; a pointer to the currently active g struct (in this case runtime.g0)
|
||||||
r0_g_stack_lo: resq 1
|
r0_g_ptr: resq 1
|
||||||
r0_g_stack_hi: resq 1
|
|
||||||
r0_g_stackguard0: resq 1 ; rsp compared to this value in go stack growth prologue
|
|
||||||
r0_g_stackguard1: resq 1 ; rsp compared to this value in C stack growth prologue
|
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
|
|
||||||
@ -42,34 +39,7 @@ global _rt0_64_entry
|
|||||||
_rt0_64_entry:
|
_rt0_64_entry:
|
||||||
call _rt0_install_redirect_trampolines
|
call _rt0_install_redirect_trampolines
|
||||||
call _rt0_64_load_idt
|
call _rt0_64_load_idt
|
||||||
|
call _rt0_64_setup_go_runtime_structs
|
||||||
; According to the x86_64 ABI, the fs:0 should point to the address of
|
|
||||||
; the user-space thread structure. The actual TLS structure is located
|
|
||||||
; just before that (aligned). Go code tries to fetch the address to the
|
|
||||||
; active go-routine's g struct by accessing fs:-8. What we need to do
|
|
||||||
; is to setup a mock g0 struct, populate its stack_lo/hi/guard fields
|
|
||||||
; and then use wrmsr to update the FS register
|
|
||||||
extern stack_top
|
|
||||||
extern stack_bottom
|
|
||||||
|
|
||||||
; Setup r0_g
|
|
||||||
mov rax, stack_bottom
|
|
||||||
mov rbx, stack_top
|
|
||||||
mov rsi, r0_g
|
|
||||||
mov qword [rsi+0], rax ; stack_lo
|
|
||||||
mov qword [rsi+8], rbx ; stack_hi
|
|
||||||
mov qword [rsi+16], rax ; stackguard0
|
|
||||||
mov rax, r0_g_ptr
|
|
||||||
mov qword [rax], rsi
|
|
||||||
|
|
||||||
; Load 64-bit FS register address
|
|
||||||
; rax -> lower 32 bits
|
|
||||||
; rdx -> upper 32 bits
|
|
||||||
mov ecx, 0xc0000100 ; fs_base
|
|
||||||
mov rax, rsi ; lower 32 bits
|
|
||||||
shr rsi, 32
|
|
||||||
mov rdx, rsi ; high 32 bits
|
|
||||||
wrmsr
|
|
||||||
|
|
||||||
; Call the kernel entry point passing a pointer to the multiboot data
|
; Call the kernel entry point passing a pointer to the multiboot data
|
||||||
; copied by the 32-bit entry code
|
; copied by the 32-bit entry code
|
||||||
@ -93,6 +63,58 @@ _rt0_64_entry:
|
|||||||
cli
|
cli
|
||||||
hlt
|
hlt
|
||||||
|
|
||||||
|
;------------------------------------------------------------------------------
|
||||||
|
; Setup m0, g0 and other symbols required for bootstrapping the Go runtime.
|
||||||
|
; For the definitions of g and m see the Go runtime src: src/runtime/runtime2.go
|
||||||
|
;------------------------------------------------------------------------------
|
||||||
|
_rt0_64_setup_go_runtime_structs:
|
||||||
|
%include "go_asm_offsets.inc" ; generated by tools/offsets
|
||||||
|
|
||||||
|
; The Go allocator expects this symbol to be set to the system page size
|
||||||
|
; As the kernel bypass osinit() this needs to be set here.
|
||||||
|
extern runtime.physPageSize
|
||||||
|
mov rax, runtime.physPageSize
|
||||||
|
mov qword [rax], 0x1000 ; 4096
|
||||||
|
|
||||||
|
; Setup r0_g stack limits using the reserved stack
|
||||||
|
extern stack_top
|
||||||
|
extern stack_bottom
|
||||||
|
extern runtime.g0
|
||||||
|
mov rax, stack_bottom
|
||||||
|
mov rbx, stack_top
|
||||||
|
mov rsi, runtime.g0
|
||||||
|
mov qword [rsi+GO_G_STACK+GO_STACK_LO], rax ; g.stack.lo
|
||||||
|
mov qword [rsi+GO_G_STACK+GO_STACK_HI], rbx ; g.stack.hi
|
||||||
|
mov qword [rsi+GO_G_STACKGUARD0], rax ; g.stackguard0
|
||||||
|
|
||||||
|
; Link m0 to the g0
|
||||||
|
extern runtime.m0
|
||||||
|
mov rbx, runtime.m0
|
||||||
|
mov qword [rbx+GO_M_G0], rsi ; m.g0 = g0
|
||||||
|
mov qword [rsi+GO_G_M], rbx ; g.m = m
|
||||||
|
|
||||||
|
; Store the address of g0 in r0_g_ptr
|
||||||
|
mov rax, r0_g_ptr
|
||||||
|
mov qword [rax], rsi
|
||||||
|
|
||||||
|
; According to the x86_64 ABI, the fs register should contain the
|
||||||
|
; address after the pointer to the pointer to the user-space thread
|
||||||
|
; structure. This allows the Go runtime to retrieve the address of
|
||||||
|
; the currently active g structure by accessing fs:-0x8.
|
||||||
|
;
|
||||||
|
; Load 64-bit FS register address
|
||||||
|
; eax -> lower 32 bits
|
||||||
|
; edx -> upper 32 bits
|
||||||
|
mov ecx, 0xc0000100 ; fs_base
|
||||||
|
mov rsi, r0_g_ptr
|
||||||
|
add rsi, 8 ; fs -> r0_g_ptr + 0x8
|
||||||
|
mov rax, rsi ; lower 32 bits
|
||||||
|
shr rsi, 32
|
||||||
|
mov rdx, rsi ; high 32 bits
|
||||||
|
wrmsr
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
|
||||||
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
||||||
; Setup and load IDT. We preload each IDT entry with a pointer to a gate handler
|
; Setup and load IDT. We preload each IDT entry with a pointer to a gate handler
|
||||||
|
192
tools/offsets/offsets.go
Normal file
192
tools/offsets/offsets.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func exit(err error) {
|
||||||
|
fmt.Fprintf(os.Stderr, "[offsets] error: %s\n", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genBuildScript(targetOS, targetArch, goBinary, workDir string) ([]byte, error) {
|
||||||
|
// Write a dummy program in workDir so "go build" does not complain
|
||||||
|
dummyGoProgram := []byte("package main\n func main(){}")
|
||||||
|
err := ioutil.WriteFile(fmt.Sprintf("%s/main.go", workDir), dummyGoProgram, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run "go build -a -n" in workDir and capture the output. The -a flag
|
||||||
|
// ensures that the generated build script includes steps to always
|
||||||
|
// rebuild the runtime packages.
|
||||||
|
cmd := exec.Command(goBinary, "build", "-a", "-n")
|
||||||
|
cmd.Dir = workDir
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GOOS=%s", targetOS))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GOARCH=%s", targetArch))
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate build script\nMore info:\n%s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func patchBuildScript(script []byte, workDir, targetOS, targetArch, goBinary string) ([]byte, error) {
|
||||||
|
lines := strings.Split(string(script), "\n")
|
||||||
|
|
||||||
|
// Inject os/arch and workdir to the top of the build file
|
||||||
|
header := []string{
|
||||||
|
fmt.Sprintf("export GOOS=%s", targetOS),
|
||||||
|
fmt.Sprintf("export GOARCH=%s", targetArch),
|
||||||
|
fmt.Sprintf("WORK=%q", workDir),
|
||||||
|
fmt.Sprintf("alias pack='%s tool pack'", goBinary),
|
||||||
|
}
|
||||||
|
lines = append(header, lines...)
|
||||||
|
|
||||||
|
// We are only interested in building the runtime as this block generates
|
||||||
|
// the asm headers. Scan the lines till we find "# runtime" comment and
|
||||||
|
// stop at next comment
|
||||||
|
var stopOnNextComment bool
|
||||||
|
for lineIndex := 0; lineIndex < len(lines); lineIndex++ {
|
||||||
|
// Ignore empty comments
|
||||||
|
if lines[lineIndex] == "#" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if stopOnNextComment && strings.HasPrefix(lines[lineIndex], "#") {
|
||||||
|
return []byte(strings.Join(lines[:lineIndex], "\n")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if lines[lineIndex] == "# runtime" {
|
||||||
|
stopOnNextComment = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("generated build file does not specify -asmhdr when building the runtime")
|
||||||
|
}
|
||||||
|
|
||||||
|
func execBuildScript(script []byte, workDir string) error {
|
||||||
|
f, err := os.Create(fmt.Sprintf("%s/build.sh", workDir))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = f.Write(script)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
cmd := exec.Command("sh", f.Name())
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute build script\nMore info:\n%s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func genAsmIncludes(workDir string) ([]byte, error) {
|
||||||
|
headers, err := ioutil.ReadFile(fmt.Sprintf("%s/runtime/_obj/go_asm.h", workDir))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var includes []string
|
||||||
|
includes = append(includes, "; vim: set ft=nasm :\n")
|
||||||
|
includes = append(includes, fmt.Sprintf("; generated by tools/offsets at %v\n", time.Now()))
|
||||||
|
|
||||||
|
for _, line := range strings.Split(string(headers), "\n") {
|
||||||
|
line = strings.TrimPrefix(line, "#define ")
|
||||||
|
|
||||||
|
// We are only interested in the offsets for the g, m and stack structures
|
||||||
|
if strings.HasPrefix(line, "g_") || strings.HasPrefix(line, "m_") || strings.HasPrefix(line, "stack_") {
|
||||||
|
tokens := strings.Fields(line)
|
||||||
|
if len(tokens) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
offset, err := strconv.ParseInt(tokens[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
includes = append(includes,
|
||||||
|
fmt.Sprintf("GO_%s equ 0x%x ; %d",
|
||||||
|
strings.ToUpper(tokens[0]),
|
||||||
|
offset,
|
||||||
|
offset,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(strings.Join(includes, "\n")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTool() error {
|
||||||
|
targetOS := flag.String("target-os", "", "a valid GOOS value for generating the asm offsets")
|
||||||
|
targetArch := flag.String("target-arch", "", "a valid GOARCH value for generating the asm offsets")
|
||||||
|
goBinary := flag.String("go-binary", "go", "the Go binary to use")
|
||||||
|
asmOutput := flag.String("out", "-", "a file to write the asm headers or - to output to STDOUT")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case *targetOS == "":
|
||||||
|
exit(errors.New("-target-os parameter missing"))
|
||||||
|
case *targetArch == "":
|
||||||
|
exit(errors.New("-target-arch parameter missing"))
|
||||||
|
}
|
||||||
|
|
||||||
|
workDir, err := ioutil.TempDir("", "offsets-tool")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(workDir)
|
||||||
|
|
||||||
|
buildScript, err := genBuildScript(*targetOS, *targetArch, *goBinary, workDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buildScript, err = patchBuildScript(buildScript, workDir, *targetOS, *targetArch, *goBinary)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = execBuildScript(buildScript, workDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
asmIncludes, err := genAsmIncludes(workDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch *asmOutput {
|
||||||
|
case "-":
|
||||||
|
fmt.Printf("%s\n", string(asmIncludes))
|
||||||
|
default:
|
||||||
|
if err = ioutil.WriteFile(*asmOutput, asmIncludes, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := runTool(); err != nil {
|
||||||
|
exit(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user