mirror of
				https://github.com/taigrr/gopher-os
				synced 2025-01-18 04:43:13 -08:00 
			
		
		
		
	Implement tool for calculating offsets into g, m and stack structs
The offsets tool is essentially a wrapper around "go build -a -n". It creates a temporary folder with a dummy go file and runs the above command using the target OS/ARCH for the kernel and captures the output. The use of the "-a" flag forces go build to generate a build script for rebuilding all packages including the runtime ones. As a by-product of building the runtime package, the compiler emits the "go_asm.h" file that contains (among other things) the offsets for each element of the g, m and stack structures (see src/runtime/runtime2.go). These offsets are used in Go assembly files instead of hardcoded offsets. For example the following snippet accesses the pointer to m in the g struct address stored at register CX: MOVQ TLS, CX MOVQ g_m(CX), BX The offsets tool modifies the captured output from the go build command so it only includes the steps up to building the runtime package, executes the build script and post-processes the generated go_asm.h file to retain the entries relevant to g, m and stack and then formats them so they are compatible with nasm definitions (name equ value). Depending on the value of the "-out" option, the tool outputs the generated definitions either to STDOUT (default value for -out) or to a file.
This commit is contained in:
		
							parent
							
								
									0c79af3f90
								
							
						
					
					
						commit
						62aca2f2de
					
				
							
								
								
									
										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