| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // linux/mkall.go - Generates all Linux zsysnum, zsyscall, zerror, and ztype |
| // files for all 11 linux architectures supported by the go compiler. See |
| // README.md for more information about the build system. |
| |
| // To run it you must have a git checkout of the Linux kernel and glibc. Once |
| // the appropriate sources are ready, the program is run as: |
| // go run linux/mkall.go <linux_dir> <glibc_dir> |
| |
| // +build ignore |
| |
| package main |
| |
| import ( |
| "fmt" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "runtime" |
| "strings" |
| ) |
| |
| // These will be paths to the appropriate source directories. |
| var LinuxDir string |
| var GlibcDir string |
| |
| const TempDir = "/tmp" |
| const IncludeDir = TempDir + "/include" // To hold our C headers |
| const BuildDir = TempDir + "/build" // To hold intermediate build files |
| |
| const GOOS = "linux" // Only for Linux targets |
| const BuildArch = "amd64" // Must be built on this architecture |
| const MinKernel = "2.6.23" // https://golang.org/doc/install#requirements |
| |
| type target struct { |
| GoArch string // Architecture name according to Go |
| LinuxArch string // Architecture name according to the Linux Kernel |
| GNUArch string // Architecture name according to GNU tools (https://wiki.debian.org/Multiarch/Tuples) |
| BigEndian bool // Default Little Endian |
| SignedChar bool // Is -fsigned-char needed (default no) |
| Bits int |
| } |
| |
| // List of the 11 Linux targets supported by the go compiler. sparc64 is not |
| // currently supported, though a port is in progress. |
| var targets = []target{ |
| { |
| GoArch: "386", |
| LinuxArch: "x86", |
| GNUArch: "i686-linux-gnu", // Note "i686" not "i386" |
| Bits: 32, |
| }, |
| { |
| GoArch: "amd64", |
| LinuxArch: "x86", |
| GNUArch: "x86_64-linux-gnu", |
| Bits: 64, |
| }, |
| { |
| GoArch: "arm64", |
| LinuxArch: "arm64", |
| GNUArch: "aarch64-linux-gnu", |
| SignedChar: true, |
| Bits: 64, |
| }, |
| { |
| GoArch: "arm", |
| LinuxArch: "arm", |
| GNUArch: "arm-linux-gnueabi", |
| Bits: 32, |
| }, |
| { |
| GoArch: "mips", |
| LinuxArch: "mips", |
| GNUArch: "mips-linux-gnu", |
| BigEndian: true, |
| Bits: 32, |
| }, |
| { |
| GoArch: "mipsle", |
| LinuxArch: "mips", |
| GNUArch: "mipsel-linux-gnu", |
| Bits: 32, |
| }, |
| { |
| GoArch: "mips64", |
| LinuxArch: "mips", |
| GNUArch: "mips64-linux-gnuabi64", |
| BigEndian: true, |
| Bits: 64, |
| }, |
| { |
| GoArch: "mips64le", |
| LinuxArch: "mips", |
| GNUArch: "mips64el-linux-gnuabi64", |
| Bits: 64, |
| }, |
| { |
| GoArch: "ppc64", |
| LinuxArch: "powerpc", |
| GNUArch: "powerpc64-linux-gnu", |
| BigEndian: true, |
| Bits: 64, |
| }, |
| { |
| GoArch: "ppc64le", |
| LinuxArch: "powerpc", |
| GNUArch: "powerpc64le-linux-gnu", |
| Bits: 64, |
| }, |
| { |
| GoArch: "s390x", |
| LinuxArch: "s390", |
| GNUArch: "s390x-linux-gnu", |
| BigEndian: true, |
| SignedChar: true, |
| Bits: 64, |
| }, |
| // { |
| // GoArch: "sparc64", |
| // LinuxArch: "sparc", |
| // GNUArch: "sparc64-linux-gnu", |
| // BigEndian: true, |
| // Bits: 64, |
| // }, |
| } |
| |
| func main() { |
| if runtime.GOOS != GOOS || runtime.GOARCH != BuildArch { |
| fmt.Printf("Build system has GOOS_GOARCH = %s_%s, need %s_%s\n", |
| runtime.GOOS, runtime.GOARCH, GOOS, BuildArch) |
| return |
| } |
| |
| // Check that we are using the new build system if we should |
| if os.Getenv("GOLANG_SYS_BUILD") != "docker" { |
| fmt.Println("In the new build system, mkall.go should not be called directly.") |
| fmt.Println("See README.md") |
| return |
| } |
| |
| // Parse the command line options |
| if len(os.Args) != 3 { |
| fmt.Println("USAGE: go run linux/mkall.go <linux_dir> <glibc_dir>") |
| return |
| } |
| LinuxDir = os.Args[1] |
| GlibcDir = os.Args[2] |
| |
| for _, t := range targets { |
| fmt.Printf("----- GENERATING: %s -----\n", t.GoArch) |
| if err := t.generateFiles(); err != nil { |
| fmt.Printf("%v\n***** FAILURE: %s *****\n\n", err, t.GoArch) |
| } else { |
| fmt.Printf("----- SUCCESS: %s -----\n\n", t.GoArch) |
| } |
| } |
| } |
| |
| // Makes an exec.Cmd with Stderr attached to os.Stderr |
| func makeCommand(name string, args ...string) *exec.Cmd { |
| cmd := exec.Command(name, args...) |
| cmd.Stderr = os.Stderr |
| return cmd |
| } |
| |
| // Runs the command, pipes output to a formatter, pipes that to an output file. |
| func (t *target) commandFormatOutput(formatter string, outputFile string, |
| name string, args ...string) (err error) { |
| mainCmd := makeCommand(name, args...) |
| |
| fmtCmd := makeCommand(formatter) |
| if formatter == "mkpost" { |
| fmtCmd = makeCommand("go", "run", "mkpost.go") |
| // Set GOARCH_TARGET so mkpost knows what GOARCH is.. |
| fmtCmd.Env = append(os.Environ(), "GOARCH_TARGET="+t.GoArch) |
| // Set GOARCH to host arch for mkpost, so it can run natively. |
| for i, s := range fmtCmd.Env { |
| if strings.HasPrefix(s, "GOARCH=") { |
| fmtCmd.Env[i] = "GOARCH=" + BuildArch |
| } |
| } |
| } |
| |
| // mainCmd | fmtCmd > outputFile |
| if fmtCmd.Stdin, err = mainCmd.StdoutPipe(); err != nil { |
| return |
| } |
| if fmtCmd.Stdout, err = os.Create(outputFile); err != nil { |
| return |
| } |
| |
| // Make sure the formatter eventually closes |
| if err = fmtCmd.Start(); err != nil { |
| return |
| } |
| defer func() { |
| fmtErr := fmtCmd.Wait() |
| if err == nil { |
| err = fmtErr |
| } |
| }() |
| |
| return mainCmd.Run() |
| } |
| |
| // Generates all the files for a Linux target |
| func (t *target) generateFiles() error { |
| // Setup environment variables |
| os.Setenv("GOOS", GOOS) |
| os.Setenv("GOARCH", t.GoArch) |
| |
| // Get appropriate compiler and emulator (unless on x86) |
| if t.LinuxArch != "x86" { |
| // Check/Setup cross compiler |
| compiler := t.GNUArch + "-gcc" |
| if _, err := exec.LookPath(compiler); err != nil { |
| return err |
| } |
| os.Setenv("CC", compiler) |
| |
| // Check/Setup emulator (usually first component of GNUArch) |
| qemuArchName := t.GNUArch[:strings.Index(t.GNUArch, "-")] |
| if t.LinuxArch == "powerpc" { |
| qemuArchName = t.GoArch |
| } |
| os.Setenv("GORUN", "qemu-"+qemuArchName) |
| } else { |
| os.Setenv("CC", "gcc") |
| } |
| |
| // Make the include directory and fill it with headers |
| if err := os.MkdirAll(IncludeDir, os.ModePerm); err != nil { |
| return err |
| } |
| defer os.RemoveAll(IncludeDir) |
| if err := t.makeHeaders(); err != nil { |
| return fmt.Errorf("could not make header files: %v", err) |
| } |
| fmt.Println("header files generated") |
| |
| // Make each of the four files |
| if err := t.makeZSysnumFile(); err != nil { |
| return fmt.Errorf("could not make zsysnum file: %v", err) |
| } |
| fmt.Println("zsysnum file generated") |
| |
| if err := t.makeZSyscallFile(); err != nil { |
| return fmt.Errorf("could not make zsyscall file: %v", err) |
| } |
| fmt.Println("zsyscall file generated") |
| |
| if err := t.makeZTypesFile(); err != nil { |
| return fmt.Errorf("could not make ztypes file: %v", err) |
| } |
| fmt.Println("ztypes file generated") |
| |
| if err := t.makeZErrorsFile(); err != nil { |
| return fmt.Errorf("could not make zerrors file: %v", err) |
| } |
| fmt.Println("zerrors file generated") |
| |
| return nil |
| } |
| |
| // Create the Linux and glibc headers in the include directory. |
| func (t *target) makeHeaders() error { |
| // Make the Linux headers we need for this architecture |
| linuxMake := makeCommand("make", "headers_install", "ARCH="+t.LinuxArch, "INSTALL_HDR_PATH="+TempDir) |
| linuxMake.Dir = LinuxDir |
| if err := linuxMake.Run(); err != nil { |
| return err |
| } |
| |
| // A Temporary build directory for glibc |
| if err := os.MkdirAll(BuildDir, os.ModePerm); err != nil { |
| return err |
| } |
| defer os.RemoveAll(BuildDir) |
| |
| // Make the glibc headers we need for this architecture |
| confScript := filepath.Join(GlibcDir, "configure") |
| glibcConf := makeCommand(confScript, "--prefix="+TempDir, "--host="+t.GNUArch, "--enable-kernel="+MinKernel) |
| glibcConf.Dir = BuildDir |
| if err := glibcConf.Run(); err != nil { |
| return err |
| } |
| glibcMake := makeCommand("make", "install-headers") |
| glibcMake.Dir = BuildDir |
| if err := glibcMake.Run(); err != nil { |
| return err |
| } |
| // We only need an empty stubs file |
| stubsFile := filepath.Join(IncludeDir, "gnu/stubs.h") |
| if file, err := os.Create(stubsFile); err != nil { |
| return err |
| } else { |
| file.Close() |
| } |
| |
| return nil |
| } |
| |
| // makes the zsysnum_linux_$GOARCH.go file |
| func (t *target) makeZSysnumFile() error { |
| zsysnumFile := fmt.Sprintf("zsysnum_linux_%s.go", t.GoArch) |
| unistdFile := filepath.Join(IncludeDir, "asm/unistd.h") |
| |
| args := append(t.cFlags(), unistdFile) |
| return t.commandFormatOutput("gofmt", zsysnumFile, "linux/mksysnum.pl", args...) |
| } |
| |
| // makes the zsyscall_linux_$GOARCH.go file |
| func (t *target) makeZSyscallFile() error { |
| zsyscallFile := fmt.Sprintf("zsyscall_linux_%s.go", t.GoArch) |
| // Find the correct architecture syscall file (might end with x.go) |
| archSyscallFile := fmt.Sprintf("syscall_linux_%s.go", t.GoArch) |
| if _, err := os.Stat(archSyscallFile); os.IsNotExist(err) { |
| shortArch := strings.TrimSuffix(t.GoArch, "le") |
| archSyscallFile = fmt.Sprintf("syscall_linux_%sx.go", shortArch) |
| } |
| |
| args := append(t.mksyscallFlags(), "-tags", "linux,"+t.GoArch, |
| "syscall_linux.go", archSyscallFile) |
| return t.commandFormatOutput("gofmt", zsyscallFile, "./mksyscall.pl", args...) |
| } |
| |
| // makes the zerrors_linux_$GOARCH.go file |
| func (t *target) makeZErrorsFile() error { |
| zerrorsFile := fmt.Sprintf("zerrors_linux_%s.go", t.GoArch) |
| |
| return t.commandFormatOutput("gofmt", zerrorsFile, "./mkerrors.sh", t.cFlags()...) |
| } |
| |
| // makes the ztypes_linux_$GOARCH.go file |
| func (t *target) makeZTypesFile() error { |
| ztypesFile := fmt.Sprintf("ztypes_linux_%s.go", t.GoArch) |
| |
| args := []string{"tool", "cgo", "-godefs", "--"} |
| args = append(args, t.cFlags()...) |
| args = append(args, "linux/types.go") |
| return t.commandFormatOutput("mkpost", ztypesFile, "go", args...) |
| } |
| |
| // Flags that should be given to gcc and cgo for this target |
| func (t *target) cFlags() []string { |
| // Compile statically to avoid cross-architecture dynamic linking. |
| flags := []string{"-Wall", "-Werror", "-static", "-I" + IncludeDir} |
| |
| // Architecture-specific flags |
| if t.SignedChar { |
| flags = append(flags, "-fsigned-char") |
| } |
| if t.LinuxArch == "x86" { |
| flags = append(flags, fmt.Sprintf("-m%d", t.Bits)) |
| } |
| |
| return flags |
| } |
| |
| // Flags that should be given to mksyscall for this target |
| func (t *target) mksyscallFlags() (flags []string) { |
| if t.Bits == 32 { |
| if t.BigEndian { |
| flags = append(flags, "-b32") |
| } else { |
| flags = append(flags, "-l32") |
| } |
| } |
| |
| // This flag menas a 64-bit value should use (even, odd)-pair. |
| if t.GoArch == "arm" || (t.LinuxArch == "mips" && t.Bits == 32) { |
| flags = append(flags, "-arm") |
| } |
| return |
| } |