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

tools: update offsets tool to work with go versions 1.7 - 1.10

Older go versions (1.7.x) specify a fixed page size (_PageSize const) as
part of their runtime whereas newer go versions populate the page size at
runtime.

The kernel asm bootstrap code was written with go 1.8 in mind. As a
result it attempts to populate the page size manually which obviously
breaks compilation in go 1.7.

The offsets tool has been updated to emit the special def
"SKIP_PAGESIZE_SETUP" when running under go 1.7 which allows us to
perform conditional compilation of the page setup code inside the
bootstrap asm code.

fixup
This commit is contained in:
Achilleas Anagnostopoulos 2018-03-19 09:01:34 +00:00
parent c0b9f62f78
commit 4e3567f8a1
4 changed files with 116 additions and 73 deletions

View File

@ -22,7 +22,6 @@ FUZZ_PKG_LIST := src/gopheros/device/acpi/aml
ifeq ($(OS), Linux)
export SHELL := /bin/bash -o pipefail
LD := ld
AS := nasm
@ -105,7 +104,7 @@ $(BUILD_DIR)/go_asm_offsets.inc:
@mkdir -p $(BUILD_DIR)
@echo "[tools:offsets] calculating OS/arch-specific offsets for g, m and stack structs"
@GOPATH=$(GOPATH) $(GO) run tools/offsets/offsets.go -target-os $(GOOS) -target-arch $(GOARCH) -go-binary $(GO) -out $@
@GOROOT=$(GOROOT) GOPATH=$(GOPATH) $(GO) run tools/offsets/offsets.go -target-os $(GOOS) -target-arch $(GOARCH) -go-binary $(GO) -out $@
$(BUILD_DIR)/arch/$(ARCH)/asm/%.o: src/arch/$(ARCH)/asm/%.s
@mkdir -p $(shell dirname $@)

View File

@ -17,17 +17,17 @@ _rt0_idt_desc:
resw 1
resq 1
; 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
; According to the "ELF handling for TLS" document section 3.4.6
; (https://www.akkadia.org/drepper/tls.pdf) for the GNU variant for x86-64,
; fs:0x00 contains a pointer to the TCB. Variables in the TLS are stored
; fs:0x00 contains a pointer to the TCB. Variables in the TLS are stored
; before the TCB and are accessed using negative offsets from the TCB address.
r0_g_ptr: resq 1
tcb_ptr: resq 1
r0_g_ptr: resq 1
tcb_ptr: resq 1
section .text
section .text
;------------------------------------------------------------------------------
; Kernel 64-bit entry point
@ -36,7 +36,7 @@ section .text
; - it has entered long mode and enabled paging
; - it has loaded a 64bit GDT
; - it has set up identity paging for the physical 0-8M region and the
; PAGE_OFFSET to PAGE_OFFSET+8M region.
; PAGE_OFFSET to PAGE_OFFSET+8M region.
;------------------------------------------------------------------------------
global _rt0_64_entry
_rt0_64_entry:
@ -50,7 +50,7 @@ _rt0_64_entry:
extern _kernel_start
extern _kernel_end
extern kernel.Kmain
mov rax, PAGE_OFFSET
push rax
mov rax, _kernel_end - PAGE_OFFSET
@ -60,7 +60,7 @@ _rt0_64_entry:
mov rax, multiboot_data
push rax
call kernel.Kmain
; Main should never return; halt the CPU
mov rdi, err_kmain_returned
call write_string
@ -75,13 +75,15 @@ _rt0_64_entry:
_rt0_64_setup_go_runtime_structs:
%include "go_asm_offsets.inc" ; generated by tools/offsets
%ifndef SKIP_PAGESIZE_SETUP
; 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.
; As the kernel bypasses osinit() this needs to be manually set here.
extern runtime.physPageSize
mov rax, runtime.physPageSize
mov qword [rax], 0x1000 ; 4096
; Setup r0_g stack limits using the reserved stack
%endif
; Setup r0_g stack limits using the reserved stack
extern stack_top
extern stack_bottom
extern runtime.g0
@ -99,21 +101,21 @@ _rt0_64_setup_go_runtime_structs:
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
; Store the address of g0 in r0_g_ptr
mov rax, r0_g_ptr
mov qword [rax], rsi
; According to the x86-64 ABI requirements fs:0x0 should point to the
; According to the x86-64 ABI requirements fs:0x0 should point to the
; TCB.
mov rax, tcb_ptr
mov qword [rax], rax
; Load 64-bit FS register address
; eax -> lower 32 bits
; Load 64-bit FS register address
; eax -> lower 32 bits
; edx -> upper 32 bits
mov ecx, 0xc0000100 ; fs_base
mov rsi, tcb_ptr
mov rax, rsi ; lower 32 bits
mov rax, rsi ; lower 32 bits
shr rsi, 32
mov rdx, rsi ; high 32 bits
wrmsr
@ -122,26 +124,26 @@ _rt0_64_setup_go_runtime_structs:
;------------------------------------------------------------------------------
; Setup and load IDT. We preload each IDT entry with a pointer to a gate handler
; but set it as inactive. The code in irq_amd64 is responsible for enabling
; Setup and load IDT. We preload each IDT entry with a pointer to a gate handler
; but set it as inactive. The code in irq_amd64 is responsible for enabling
; individual IDT entries when handlers are installed.
;------------------------------------------------------------------------------
_rt0_64_load_idt:
mov rax, _rt0_idt_start
%assign gate_num 0
%assign gate_num 0
%rep IDT_ENTRIES
mov rbx, _rt0_64_gate_entry_%+ gate_num
mov word [rax], bx ; gate entry bits 0-15
mov word [rax+2], 0x8 ; GDT descriptor
mov byte [rax+5], 0x0 ; Mark the entry as NOT present
mov byte [rax+5], 0x0 ; Mark the entry as NOT present
shr rbx, 16
mov word [rax+6], bx ; gate entry bits 16-31
shr rbx, 16
mov dword [rax+8], ebx ; gate entry bits 32-63
add rax, 16 ; size of IDT entry
%assign gate_num gate_num+1
%assign gate_num gate_num+1
%endrep
mov rax, _rt0_idt_desc
mov word [rax], _rt0_idt_end - _rt0_idt_start - 1 ; similar to GDT this must be len(IDT) - 1
@ -152,15 +154,15 @@ ret
;------------------------------------------------------------------------------
; Generate gate entries. Each gate handler pushes the address of the registered
; Generate gate entries. Each gate handler pushes the address of the registered
; handler to the stack before jumping to a dispatcher function.
;
; Some exceptions push an error code to the stack after the stack frame. This
; code must be popped off the stack before calling iretq. The generated handlers
; are aware whether they need to deal with the code or not and jump to the
; code must be popped off the stack before calling iretq. The generated handlers
; are aware whether they need to deal with the code or not and jump to the
; appropriate get dispatcher.
;------------------------------------------------------------------------------
%assign gate_num 0
%assign gate_num 0
%rep IDT_ENTRIES
extern _rt0_interrupt_handlers
_rt0_64_gate_entry_%+ gate_num:
@ -177,13 +179,13 @@ _rt0_64_gate_entry_%+ gate_num:
%else
jmp _rt0_64_gate_dispatcher_without_code
%endif
%assign gate_num gate_num+1
%assign gate_num gate_num+1
%endrep
%macro save_regs 0
push r15
push r15
push r14
push r13
push r13
push r12
push r11
push r10
@ -191,10 +193,10 @@ _rt0_64_gate_entry_%+ gate_num:
push r8
push rbp
push rdi
push rsi
push rdx
push rcx
push rbx
push rsi
push rdx
push rcx
push rbx
push rax
%endmacro
@ -203,13 +205,13 @@ _rt0_64_gate_entry_%+ gate_num:
pop rbx
pop rcx
pop rdx
pop rsi
pop rsi
pop rdi
pop rbp
pop rbp
pop r8
pop r9
pop r10
pop r11
pop r11
pop r12
pop r13
pop r14
@ -217,14 +219,14 @@ _rt0_64_gate_entry_%+ gate_num:
%endmacro
;------------------------------------------------------------------------------
; This dispatcher is invoked by gate entries that expect a code to be pushed
; This dispatcher is invoked by gate entries that expect a code to be pushed
; by the CPU to the stack. It performs the following functions:
; - save registers
; - push pointer to saved regs
; - push pointer to stack frame
; - read and push exception code
; - push pointer to saved regs
; - push pointer to stack frame
; - read and push exception code
; - invoke handler(code, &frame, &regs)
; - restore registers
; - restore registers
; - pop exception code from stack so rsp points to the stack frame
;------------------------------------------------------------------------------
_rt0_64_gate_dispatcher_with_code:
@ -236,7 +238,7 @@ _rt0_64_gate_dispatcher_with_code:
;-----------------|
; Exception code | <- needs to be removed from stack before calling iretq
;-----------------|
; RIP | <- exception frame
; RIP | <- exception frame
; CS |
; RFLAGS |
; RSP |
@ -244,12 +246,12 @@ _rt0_64_gate_dispatcher_with_code:
;-----------------
cld
; save regs and push a pointer to them
; save regs and push a pointer to them
save_regs
mov rax, rsp ; rax points to saved rax
push rax ; push pointer to saved regs
; push pointer to exception stack frame (we have used 15 qwords for the
; push pointer to exception stack frame (we have used 15 qwords for the
; saved registers plus one qword for the data pushed by the gate entry
; plus one extra qword to jump over the exception code)
add rax, 17*8
@ -261,7 +263,7 @@ _rt0_64_gate_dispatcher_with_code:
call [rsp + 18*8] ; call registered irq handler
add rsp, 3 * 8 ; unshift the pushed arguments so rsp points to the saved regs
add rsp, 3 * 8 ; unshift the pushed arguments so rsp points to the saved regs
restore_regs
add rsp, 16 ; pop handler address and exception code off the stack before returning
@ -271,10 +273,10 @@ _rt0_64_gate_dispatcher_with_code:
; This dispatcher is invoked by gate entries that do not use exception codes.
; It performs the following functions:
; - save registers
; - push pointer to saved regs
; - push pointer to stack frame
; - push pointer to saved regs
; - push pointer to stack frame
; - invoke handler(&frame, &regs)
; - restore registers
; - restore registers
;------------------------------------------------------------------------------
_rt0_64_gate_dispatcher_without_code:
; This is how the stack looks like when entering this function:
@ -283,7 +285,7 @@ _rt0_64_gate_dispatcher_without_code:
;------------------
; handler address | <- pushed by gate_entry_xxx (RSP points here)
;-----------------|
; RIP | <- exception frame
; RIP | <- exception frame
; CS |
; RFLAGS |
; RSP |
@ -291,21 +293,21 @@ _rt0_64_gate_dispatcher_without_code:
;-----------------
cld
; save regs and push a pointer to them
; save regs and push a pointer to them
save_regs
mov rax, rsp ; rax points to saved rax
push rax ; push pointer to saved regs
; push pointer to exception stack frame (we have used 15 qwords for the
; push pointer to exception stack frame (we have used 15 qwords for the
; saved registers plus one qword for the data pushed by the gate entry)
add rax, 16*8
push rax
call [rsp + 17*8] ; call registered irq handler
add rsp, 2 * 8 ; unshift the pushed arguments so rsp points to the saved regs
add rsp, 2 * 8 ; unshift the pushed arguments so rsp points to the saved regs
restore_regs
add rsp, 8 ; pop handler address off the stack before returning
iretq
@ -340,13 +342,13 @@ write_string:
; runtime functions to the kernel's own implementation without the need to
; export/globalize any symbols. This works by first setting up a redirect table
; (populated by a post-link step) that contains the addresses of the symbol to
; hook and the address where calls to that symbol should be redirected.
; hook and the address where calls to that symbol should be redirected.
;
; This function iterates the redirect table entries and for each entry it
; sets up a trampoline to the dst symbol and overwrites the code in src with
; the 14-byte long _rt0_redirect_trampoline code.
; the 14-byte long _rt0_redirect_trampoline code.
;
; Note: this code modification is only possible because we are currently
; Note: this code modification is only possible because we are currently
; operating in supervisor mode with no memory protection enabled. Under normal
; conditions the .text section should be flagged as read-only.
;------------------------------------------------------------------------------
@ -355,7 +357,7 @@ _rt0_install_redirect_trampolines:
mov rdx, NUM_REDIRECTS
_rt0_install_redirect_rampolines.next:
mov rdi, [rax] ; the symbol address to hook
mov rdi, [rax] ; the symbol address to hook
mov rbx, [rax+8] ; the symbol to redirect to
; setup trampoline target and copy it to the hooked symbol
@ -364,15 +366,15 @@ _rt0_install_redirect_rampolines.next:
mov rcx, 14
rep movsb ; copy rcx bytes from rsi to rdi
add rax, 16
dec rdx
add rax, 16
dec rdx
jnz _rt0_install_redirect_rampolines.next
ret
;------------------------------------------------------------------------------
; This trampoline exploits rip-relative addressing to allow a jump to a
; 64-bit address without the need to touch any registers. The generated
; This trampoline exploits rip-relative addressing to allow a jump to a
; 64-bit address without the need to touch any registers. The generated
; code is equivalent to:
;
; jmp [rip+0]
@ -380,14 +382,14 @@ _rt0_install_redirect_rampolines.next:
;------------------------------------------------------------------------------
_rt0_redirect_trampoline:
db 0xff ; the first 6 bytes encode a "jmp [rip+0]" instruction
db 0x25
dd 0x00
db 0x25
dd 0x00
dq 0x00 ; the absolute address to jump to
;------------------------------------------------------------------------------
; The redirect table is placed in a dedicated section allowing us to easily
; The redirect table is placed in a dedicated section allowing us to easily
; find its offset in the kernel image file. As the VMA addresses of the src
; and target symbols for the redirect are now known in advance we just reserve
; and target symbols for the redirect are now known in advance we just reserve
; enough space space for the src and dst addresses using the NUM_REDIRECTS
; define which is calculated by the Makefile and passed to nasm.
;------------------------------------------------------------------------------
@ -395,7 +397,7 @@ section .goredirectstbl
_rt0_redirect_table:
%rep NUM_REDIRECTS
dq 0 ; src: address of the symbol we want to redirect
dq 0 ; src: address of the symbol we want to redirect
dq 0 ; dst: address of the symbol where calls to src are redirected to
%endrep

View File

@ -7,6 +7,8 @@ import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
@ -30,6 +32,7 @@ func genBuildScript(targetOS, targetArch, goBinary, workDir string) ([]byte, err
// rebuild the runtime packages.
cmd := exec.Command(goBinary, "build", "-a", "-n")
cmd.Dir = workDir
cmd.Env = append(cmd.Env, fmt.Sprintf("GOROOT=%s", os.Getenv("GOROOT")))
cmd.Env = append(cmd.Env, fmt.Sprintf("GOOS=%s", targetOS))
cmd.Env = append(cmd.Env, fmt.Sprintf("GOARCH=%s", targetArch))
out, err := cmd.CombinedOutput()
@ -41,13 +44,18 @@ func genBuildScript(targetOS, targetArch, goBinary, workDir string) ([]byte, err
}
func patchBuildScript(script []byte, workDir, targetOS, targetArch, goBinary string) ([]byte, error) {
lines := strings.Split(string(script), "\n")
// Replace $WORK with the workDir location. This is required for executing
// build scripts generated by go 1.10
lines := strings.Split(
strings.Replace(string(script), "$WORK", workDir, -1),
"\n",
)
// Inject os/arch and workdir to the top of the build file
header := []string{
fmt.Sprintf("export GOROOT=%s", os.Getenv("GOROOT")),
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...)
@ -58,7 +66,7 @@ func patchBuildScript(script []byte, workDir, targetOS, targetArch, goBinary str
var stopOnNextComment bool
for lineIndex := 0; lineIndex < len(lines); lineIndex++ {
// Ignore empty comments
if lines[lineIndex] == "#" {
if strings.TrimSpace(lines[lineIndex]) == "#" || strings.Contains(lines[lineIndex], "# import") {
continue
}
@ -97,8 +105,32 @@ func execBuildScript(script []byte, workDir string) error {
}
func genAsmIncludes(workDir string) ([]byte, error) {
headers, err := ioutil.ReadFile(fmt.Sprintf("%s/runtime/_obj/go_asm.h", workDir))
if err != nil {
// Find all generated go_asm.h files and concat their conentents
var (
allHeaders, headers []byte
)
if err := filepath.Walk(workDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if filepath.Base(path) != "go_asm.h" {
return nil
}
if headers, err = ioutil.ReadFile(path); err != nil {
return err
}
allHeaders = append(allHeaders, '\n')
allHeaders = append(allHeaders, headers...)
return nil
}); err != nil {
return nil, err
}
@ -106,7 +138,7 @@ func genAsmIncludes(workDir string) ([]byte, error) {
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") {
for _, line := range strings.Split(string(allHeaders), "\n") {
line = strings.TrimPrefix(line, "#define ")
// We are only interested in the offsets for the g, m and stack structures
@ -131,6 +163,16 @@ func genAsmIncludes(workDir string) ([]byte, error) {
}
}
// In go 1.7.x, the page size is given by the _PageSize constant whereas in
// newer go versions it is specified at runtime and needs to be manually set
// by our asm bootstrap code.
if strings.Contains(runtime.Version(), "go1.7") {
includes = append(includes,
"; go 1.7 runtime uses a fixed 4k page size for our target arch so our bootstrap code does not need to do any extra work to set it up",
"%define SKIP_PAGESIZE_SETUP 1",
)
}
return []byte(strings.Join(includes, "\n")), nil
}