mirror of
https://github.com/golang/sys.git
synced 2026-02-08 11:46:04 +03:00
Right now the process for adding in new constants, errors, or syscalls for Linux is a pain and unreliable. The scripts are designed to be run on the target architecture and use the header files installed on the user's system. This makes it hard to generate files for all the architectures or to have consistency between users. See golang/go#15282. This CL fixes this issue by making all of the files for the 11 supported architectures directly from source checkouts of Linux, glibc, and bluez. This is done using Docker, the gcc cross-compilers, and qemu emulation. Previously discussed here: https://go-review.googlesource.com/c/37589/ A README.md file is also added to explain how all the parts of the build system work. In order to get the build working for all the architectures, I made some changes to the other scripts called from mkall_linux.go: - Files only used for generating linux code, moved to linux/ - linux/mksysnum.pl supports a specified CC compiler. - The generated C code in mkerrors.sh changed to avoid a warning - mkerrors.sh headers changed to fix powerpc64 bug in sys/ioctl.h - linux/types.go no longer needs to export Ptrace structs in lowercase Build instructions: - Host system needs to be x86-64 Linux - Install Docker (https://docs.docker.com/engine/installation/) - ./mkall.sh (That's it!!!) Change-Id: I87067c14442ba12f8d51991349a43a9d73f38ae0 Reviewed-on: https://go-review.googlesource.com/37943 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
380 lines
10 KiB
Go
380 lines
10 KiB
Go
// 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
|
|
}
|