mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
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
235 lines
6.0 KiB
Go
235 lines
6.0 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"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("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()
|
|
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) {
|
|
// 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("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 strings.TrimSpace(lines[lineIndex]) == "#" || strings.Contains(lines[lineIndex], "# import") {
|
|
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) {
|
|
// 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
|
|
}
|
|
|
|
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(allHeaders), "\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,
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|