whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
# Disable interactive prompts on package installation
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
# Dependencies to get the git sources and go binaries
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
rsync \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Get the git sources. If not cached, this takes O(5 minutes).
|
||||
WORKDIR /git
|
||||
RUN git config --global advice.detachedHead false
|
||||
# Linux Kernel: Released 07 January 2024
|
||||
RUN git clone --branch v6.7 --depth 1 https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
|
||||
# GNU C library: Released 1 Feb 2023
|
||||
RUN git clone --branch release/2.37/master --depth 1 https://sourceware.org/git/glibc.git
|
||||
|
||||
# Get Go
|
||||
ENV GOLANG_VERSION 1.21.0
|
||||
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
|
||||
ENV GOLANG_DOWNLOAD_SHA256 d0398903a16ba2232b389fb31032ddf57cac34efda306a0eebac34f0965a0742
|
||||
|
||||
RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
|
||||
&& echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
|
||||
&& tar -C /usr/local -xzf golang.tar.gz \
|
||||
&& rm golang.tar.gz
|
||||
|
||||
ENV PATH /usr/local/go/bin:$PATH
|
||||
|
||||
# Linux and Glibc build dependencies and emulator
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
bison gawk make python3 \
|
||||
gcc gcc-multilib \
|
||||
gettext texinfo \
|
||||
qemu-user \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
# Cross compilers (install recommended packages to get cross libc-dev)
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi \
|
||||
gcc-mips-linux-gnu gcc-mips64-linux-gnuabi64 \
|
||||
gcc-mips64el-linux-gnuabi64 gcc-mipsel-linux-gnu \
|
||||
gcc-powerpc-linux-gnu gcc-powerpc64-linux-gnu \
|
||||
gcc-powerpc64le-linux-gnu gcc-riscv64-linux-gnu \
|
||||
gcc-s390x-linux-gnu gcc-sparc64-linux-gnu \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Only for loong64, getting tools of qemu-user and gcc-cross-compiler
|
||||
ENV LOONG64_BASE_URL https://github.com/loongson/build-tools/releases/download/2023.08.08
|
||||
ENV LOONG64_GCC CLFS-loongarch64-8.1-x86_64-cross-tools-gcc-glibc.tar.xz
|
||||
ENV LOONG64_QEMU qemu-loongarch64
|
||||
ENV LOONG64_GCC_DOWNLOAD_URL $LOONG64_BASE_URL/$LOONG64_GCC
|
||||
ENV LOONG64_QEMU_DOWNLOAD_URL $LOONG64_BASE_URL/$LOONG64_QEMU
|
||||
|
||||
RUN apt-get update && apt-get install xz-utils -y && mkdir /loong64 && cd /loong64 \
|
||||
&& curl -fsSL "$LOONG64_QEMU_DOWNLOAD_URL" -o /usr/bin/"$LOONG64_QEMU" \
|
||||
&& chmod +x /usr/bin/"$LOONG64_QEMU" \
|
||||
&& curl -fsSL "$LOONG64_GCC_DOWNLOAD_URL" -o "$LOONG64_GCC" \
|
||||
&& tar xf "$LOONG64_GCC" -C /usr/local/ \
|
||||
&& ln -s /usr/local/cross-tools/bin/loongarch64-unknown-linux-gnu-gcc /usr/bin/loongarch64-linux-gnu-gcc \
|
||||
&& rm -rf /loong64
|
||||
|
||||
# Let the scripts know they are in the docker environment
|
||||
ENV GOLANG_SYS_BUILD docker
|
||||
WORKDIR /build/unix
|
||||
ENTRYPOINT ["go", "run", "linux/mkall.go", "/git/linux", "/git/glibc"]
|
||||
@@ -0,0 +1,993 @@
|
||||
// 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 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>
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build/constraint"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// These will be paths to the appropriate source directories.
|
||||
var LinuxDir string
|
||||
var GlibcDir string
|
||||
|
||||
const TempDir = "/tmp"
|
||||
|
||||
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
|
||||
env []string
|
||||
stderrBuf bytes.Buffer
|
||||
compiler string
|
||||
}
|
||||
|
||||
// List of all Linux targets supported by the go compiler. Currently, sparc64 is
|
||||
// not fully supported, but there is enough support already to generate Go type
|
||||
// and error definitions.
|
||||
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: "loong64",
|
||||
LinuxArch: "loongarch",
|
||||
GNUArch: "loongarch64-linux-gnu",
|
||||
Bits: 64,
|
||||
},
|
||||
{
|
||||
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: "ppc",
|
||||
LinuxArch: "powerpc",
|
||||
GNUArch: "powerpc-linux-gnu",
|
||||
BigEndian: true,
|
||||
Bits: 32,
|
||||
},
|
||||
{
|
||||
GoArch: "ppc64",
|
||||
LinuxArch: "powerpc",
|
||||
GNUArch: "powerpc64-linux-gnu",
|
||||
BigEndian: true,
|
||||
Bits: 64,
|
||||
},
|
||||
{
|
||||
GoArch: "ppc64le",
|
||||
LinuxArch: "powerpc",
|
||||
GNUArch: "powerpc64le-linux-gnu",
|
||||
Bits: 64,
|
||||
},
|
||||
{
|
||||
GoArch: "riscv64",
|
||||
LinuxArch: "riscv",
|
||||
GNUArch: "riscv64-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,
|
||||
},
|
||||
}
|
||||
|
||||
// ptracePairs is a list of pairs of targets that can, in some cases,
|
||||
// run each other's binaries. 'archName' is the combined name of 'a1'
|
||||
// and 'a2', which is used in the file name. Generally we use an 'x'
|
||||
// suffix in the file name to indicate that the file works for both
|
||||
// big-endian and little-endian, here we use 'nn' to indicate that this
|
||||
// file is suitable for 32-bit and 64-bit.
|
||||
var ptracePairs = []struct{ a1, a2, archName string }{
|
||||
{"386", "amd64", "x86"},
|
||||
{"arm", "arm64", "armnn"},
|
||||
{"mips", "mips64", "mipsnn"},
|
||||
{"mipsle", "mips64le", "mipsnnle"},
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
for _, t := range targets {
|
||||
fmt.Printf("arch %s: GENERATING\n", t.GoArch)
|
||||
if err := t.setupEnvironment(); err != nil {
|
||||
fmt.Printf("arch %s: could not setup environment: %v\n", t.GoArch, err)
|
||||
break
|
||||
}
|
||||
includeDir := filepath.Join(TempDir, t.GoArch, "include")
|
||||
// Make the include directory and fill it with headers
|
||||
if err := os.MkdirAll(includeDir, os.ModePerm); err != nil {
|
||||
fmt.Printf("arch %s: could not make directory: %v\n", t.GoArch, err)
|
||||
break
|
||||
}
|
||||
// During header generation "/git/linux/scripts/basic/fixdep" is created by "basic/Makefile" for each
|
||||
// instance of "make headers_install". This leads to a "text file is busy" error from any running
|
||||
// "make headers_install" after the first one's target. Workaround is to serialize header generation
|
||||
if err := t.makeHeaders(); err != nil {
|
||||
fmt.Printf("arch %s: could not make header files: %v\n", t.GoArch, err)
|
||||
break
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(t target) {
|
||||
defer wg.Done()
|
||||
fmt.Printf("arch %s: header files generated\n", t.GoArch)
|
||||
if err := t.generateFiles(); err != nil {
|
||||
fmt.Printf("%v\n***** FAILURE: %s *****\n\n", err, t.GoArch)
|
||||
} else {
|
||||
fmt.Printf("arch %s: SUCCESS\n", t.GoArch)
|
||||
}
|
||||
}(t)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
fmt.Printf("----- GENERATING: merging generated files -----\n")
|
||||
if err := mergeFiles(); err != nil {
|
||||
fmt.Printf("%v\n***** FAILURE: merging generated files *****\n\n", err)
|
||||
} else {
|
||||
fmt.Printf("----- SUCCESS: merging generated files -----\n\n")
|
||||
}
|
||||
|
||||
fmt.Printf("----- GENERATING ptrace pairs -----\n")
|
||||
ok := true
|
||||
for _, p := range ptracePairs {
|
||||
if err := generatePtracePair(p.a1, p.a2, p.archName); err != nil {
|
||||
fmt.Printf("%v\n***** FAILURE: %s/%s *****\n\n", err, p.a1, p.a2)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
// generate functions PtraceGetRegSetArm64 and PtraceSetRegSetArm64.
|
||||
if err := generatePtraceRegSet("arm64"); err != nil {
|
||||
fmt.Printf("%v\n***** FAILURE: generatePtraceRegSet(%q) *****\n\n", err, "arm64")
|
||||
ok = false
|
||||
}
|
||||
if ok {
|
||||
fmt.Printf("----- SUCCESS ptrace pairs -----\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (t *target) printAndResetBuilder() {
|
||||
if t.stderrBuf.Len() > 0 {
|
||||
for _, l := range bytes.Split(t.stderrBuf.Bytes(), []byte{'\n'}) {
|
||||
fmt.Printf("arch %s: stderr: %s\n", t.GoArch, l)
|
||||
}
|
||||
t.stderrBuf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// Makes an exec.Cmd with Stderr attached to the target string Builder, and target environment
|
||||
func (t *target) makeCommand(name string, args ...string) *exec.Cmd {
|
||||
cmd := exec.Command(name, args...)
|
||||
cmd.Env = t.env
|
||||
cmd.Stderr = &t.stderrBuf
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Set GOARCH for target and build environments.
|
||||
func (t *target) setTargetBuildArch(cmd *exec.Cmd) {
|
||||
// Set GOARCH_TARGET so command knows what GOARCH is..
|
||||
var env []string
|
||||
env = append(env, t.env...)
|
||||
cmd.Env = append(env, "GOARCH_TARGET="+t.GoArch)
|
||||
// Set GOARCH to host arch for command, so it can run natively.
|
||||
for i, s := range cmd.Env {
|
||||
if strings.HasPrefix(s, "GOARCH=") {
|
||||
cmd.Env[i] = "GOARCH=" + BuildArch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 := t.makeCommand(name, args...)
|
||||
if name == "mksyscall" {
|
||||
args = append([]string{"run", "mksyscall.go"}, args...)
|
||||
mainCmd = t.makeCommand("go", args...)
|
||||
t.setTargetBuildArch(mainCmd)
|
||||
} else if name == "mksysnum" {
|
||||
args = append([]string{"run", "linux/mksysnum.go"}, args...)
|
||||
mainCmd = t.makeCommand("go", args...)
|
||||
t.setTargetBuildArch(mainCmd)
|
||||
}
|
||||
|
||||
fmtCmd := t.makeCommand(formatter)
|
||||
if formatter == "mkpost" {
|
||||
fmtCmd = t.makeCommand("go", "run", "mkpost.go")
|
||||
t.setTargetBuildArch(fmtCmd)
|
||||
} else if formatter == "gofmt2" {
|
||||
fmtCmd = t.makeCommand("gofmt")
|
||||
mainCmd.Dir = filepath.Join(TempDir, t.GoArch, "mkerrors")
|
||||
if err = os.MkdirAll(mainCmd.Dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer t.printAndResetBuilder()
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
func (t *target) setupEnvironment() error {
|
||||
// Setup environment variables
|
||||
t.env = append(os.Environ(), fmt.Sprintf("%s=%s", "GOOS", GOOS))
|
||||
t.env = append(t.env, fmt.Sprintf("%s=%s", "GOARCH", t.GoArch))
|
||||
|
||||
// Get appropriate compiler and emulator (unless on x86)
|
||||
if t.LinuxArch != "x86" {
|
||||
// Check/Setup cross compiler
|
||||
t.compiler = t.GNUArch + "-gcc"
|
||||
if _, err := exec.LookPath(t.compiler); err != nil {
|
||||
return err
|
||||
}
|
||||
t.env = append(t.env, fmt.Sprintf("%s=%s", "CC", t.compiler))
|
||||
|
||||
// Check/Setup emulator (usually first component of GNUArch)
|
||||
qemuArchName := t.GNUArch[:strings.Index(t.GNUArch, "-")]
|
||||
if t.LinuxArch == "powerpc" {
|
||||
qemuArchName = t.GoArch
|
||||
}
|
||||
// Fake uname for QEMU to allow running on Host kernel version < 4.15
|
||||
if t.LinuxArch == "riscv" {
|
||||
t.env = append(t.env, fmt.Sprintf("%s=%s", "QEMU_UNAME", "4.15"))
|
||||
}
|
||||
t.env = append(t.env, fmt.Sprintf("%s=%s", "GORUN", "qemu-"+qemuArchName))
|
||||
} else {
|
||||
t.compiler = "gcc"
|
||||
t.env = append(t.env, fmt.Sprintf("%s=%s", "CC", "gcc"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generates all the files for a Linux target
|
||||
func (t *target) generateFiles() error {
|
||||
// Make each of the four files
|
||||
if err := t.makeZSysnumFile(); err != nil {
|
||||
return fmt.Errorf("could not make zsysnum file: %v", err)
|
||||
}
|
||||
fmt.Printf("arch %s: zsysnum file generated\n", t.GoArch)
|
||||
|
||||
if err := t.makeZSyscallFile(); err != nil {
|
||||
return fmt.Errorf("could not make zsyscall file: %v", err)
|
||||
}
|
||||
fmt.Printf("arch %s: zsyscall file generated\n", t.GoArch)
|
||||
|
||||
if err := t.makeZTypesFile(); err != nil {
|
||||
return fmt.Errorf("could not make ztypes file: %v", err)
|
||||
}
|
||||
fmt.Printf("arch %s: ztypes file generated\n", t.GoArch)
|
||||
|
||||
if err := t.makeZErrorsFile(); err != nil {
|
||||
return fmt.Errorf("could not make zerrors file: %v", err)
|
||||
}
|
||||
fmt.Printf("arch %s: zerrors file generated\n", t.GoArch)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create the Linux, glibc and ABI (C compiler convention) headers in the include directory.
|
||||
func (t *target) makeHeaders() error {
|
||||
defer t.printAndResetBuilder()
|
||||
|
||||
// Make the Linux headers we need for this architecture
|
||||
linuxMake := t.makeCommand("make", "headers_install", "ARCH="+t.LinuxArch, "INSTALL_HDR_PATH="+filepath.Join(TempDir, t.GoArch))
|
||||
linuxMake.Dir = LinuxDir
|
||||
if err := linuxMake.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildDir := filepath.Join(TempDir, t.GoArch, "build")
|
||||
// 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")
|
||||
glibcArgs := []string{"--prefix=" + filepath.Join(TempDir, t.GoArch), "--host=" + t.GNUArch}
|
||||
if t.LinuxArch == "loongarch" {
|
||||
// The minimum version requirement of the Loongarch for the kernel in glibc
|
||||
// is 5.19, if --enable-kernel is less than 5.19, glibc handles errors
|
||||
glibcArgs = append(glibcArgs, "--enable-kernel=5.19.0")
|
||||
} else {
|
||||
glibcArgs = append(glibcArgs, "--enable-kernel="+MinKernel)
|
||||
}
|
||||
glibcConf := t.makeCommand(confScript, glibcArgs...)
|
||||
|
||||
glibcConf.Dir = buildDir
|
||||
if err := glibcConf.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
glibcMake := t.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(TempDir, t.GoArch, "include", "gnu", "stubs.h")
|
||||
if file, err := os.Create(stubsFile); err != nil {
|
||||
return err
|
||||
} else {
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// ABI headers will specify C compiler behavior for the target platform.
|
||||
return t.makeABIHeaders()
|
||||
}
|
||||
|
||||
// makeABIHeaders generates C header files based on the platform's calling convention.
|
||||
// While many platforms have formal Application Binary Interfaces, in practice, whatever the
|
||||
// dominant C compilers generate is the de-facto calling convention.
|
||||
//
|
||||
// We generate C headers instead of a Go file, so as to enable references to the ABI from Cgo.
|
||||
func (t *target) makeABIHeaders() (err error) {
|
||||
abiDir := filepath.Join(TempDir, t.GoArch, "include", "abi")
|
||||
if err = os.Mkdir(abiDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.compiler == "" {
|
||||
return errors.New("CC (compiler) env var not set")
|
||||
}
|
||||
|
||||
// Build a sacrificial ELF file, to mine for C compiler behavior.
|
||||
binPath := filepath.Join(TempDir, t.GoArch, "tmp_abi.o")
|
||||
bin, err := t.buildELF(t.compiler, cCode, binPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot build ELF to analyze: %v", err)
|
||||
}
|
||||
defer bin.Close()
|
||||
defer os.Remove(binPath)
|
||||
|
||||
// Right now, we put everything in abi.h, but we may change this later.
|
||||
abiFile, err := os.Create(filepath.Join(abiDir, "abi.h"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if cerr := abiFile.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
|
||||
if err = t.writeBitFieldMasks(bin, abiFile); err != nil {
|
||||
return fmt.Errorf("cannot write bitfield masks: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *target) buildELF(cc, src, path string) (*elf.File, error) {
|
||||
// Compile the cCode source using the set compiler - we will need its .data section.
|
||||
// Do not link the binary, so that we can find .data section offsets from the symbol values.
|
||||
ccCmd := t.makeCommand(cc, "-o", path, "-gdwarf", "-x", "c", "-c", "-")
|
||||
ccCmd.Stdin = strings.NewReader(src)
|
||||
ccCmd.Stdout = os.Stdout
|
||||
defer t.printAndResetBuilder()
|
||||
if err := ccCmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("compiler error: %v", err)
|
||||
}
|
||||
|
||||
bin, err := elf.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read ELF file %s: %v", path, err)
|
||||
}
|
||||
|
||||
return bin, nil
|
||||
}
|
||||
|
||||
func (t *target) writeBitFieldMasks(bin *elf.File, out io.Writer) error {
|
||||
symbols, err := bin.Symbols()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting ELF symbols: %v", err)
|
||||
}
|
||||
var masksSym *elf.Symbol
|
||||
|
||||
for _, sym := range symbols {
|
||||
if sym.Name == "masks" {
|
||||
masksSym = &sym
|
||||
}
|
||||
}
|
||||
|
||||
if masksSym == nil {
|
||||
return errors.New("could not find the 'masks' symbol in ELF symtab")
|
||||
}
|
||||
|
||||
dataSection := bin.Section(".data")
|
||||
if dataSection == nil {
|
||||
return errors.New("ELF file has no .data section")
|
||||
}
|
||||
|
||||
data, err := dataSection.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read .data section: %v\n", err)
|
||||
}
|
||||
|
||||
var bo binary.ByteOrder
|
||||
if t.BigEndian {
|
||||
bo = binary.BigEndian
|
||||
} else {
|
||||
bo = binary.LittleEndian
|
||||
}
|
||||
|
||||
// 64 bit masks of type uint64 are stored in the data section starting at masks.Value.
|
||||
// Here we are running on AMD64, but these values may be big endian or little endian,
|
||||
// depending on target architecture.
|
||||
for i := uint64(0); i < 64; i++ {
|
||||
off := masksSym.Value + i*8
|
||||
// Define each mask in native by order, so as to match target endian.
|
||||
fmt.Fprintf(out, "#define BITFIELD_MASK_%d %dULL\n", i, bo.Uint64(data[off:off+8]))
|
||||
}
|
||||
|
||||
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(TempDir, t.GoArch, "include", "asm", "unistd.h")
|
||||
|
||||
args := append(t.cFlags(), unistdFile)
|
||||
return t.commandFormatOutput("gofmt", zsysnumFile, "mksysnum", 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,
|
||||
)
|
||||
|
||||
files, err := t.archMksyscallFiles()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check GOARCH-specific mksyscall files: %v", err)
|
||||
}
|
||||
args = append(args, files...)
|
||||
|
||||
return t.commandFormatOutput("gofmt", zsyscallFile, "mksyscall", args...)
|
||||
}
|
||||
|
||||
// archMksyscallFiles produces additional file arguments to mksyscall if the
|
||||
// build constraints in those files match those defined for target.
|
||||
func (t *target) archMksyscallFiles() ([]string, error) {
|
||||
// These input files don't fit the typical GOOS/GOARCH file name conventions
|
||||
// but are included conditionally in the arguments to mksyscall based on
|
||||
// whether or not the target matches the build constraints defined in each
|
||||
// file.
|
||||
//
|
||||
// TODO(mdlayher): it should be possible to generalize this approach to work
|
||||
// over all of syscall_linux_* rather than hard-coding a few special files.
|
||||
// Investigate this.
|
||||
inputs := []string{
|
||||
// GOARCH: all except arm* and riscv.
|
||||
"syscall_linux_alarm.go",
|
||||
}
|
||||
|
||||
var outputs []string
|
||||
for _, in := range inputs {
|
||||
ok, err := t.matchesMksyscallFile(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse file %q: %v", in, err)
|
||||
}
|
||||
if ok {
|
||||
// Constraints match, use for this target's code generation.
|
||||
outputs = append(outputs, in)
|
||||
}
|
||||
}
|
||||
|
||||
return outputs, nil
|
||||
}
|
||||
|
||||
// matchesMksyscallFile reports whether the input file contains constraints
|
||||
// which match those defined for target.
|
||||
func (t *target) matchesMksyscallFile(file string) (bool, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var (
|
||||
expr constraint.Expr
|
||||
found bool
|
||||
)
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
// Keep scanning until a valid constraint is found or we hit EOF.
|
||||
// This is sufficient for the single-line //go:build constraints.
|
||||
if expr, err = constraint.Parse(s.Text()); err == nil {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !found {
|
||||
return false, errors.New("no build constraints found")
|
||||
}
|
||||
|
||||
// Do the defined constraints match target's GOOS/GOARCH?
|
||||
ok := expr.Eval(func(tag string) bool {
|
||||
return tag == GOOS || tag == t.GoArch
|
||||
})
|
||||
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
// makes the zerrors_linux_$GOARCH.go file
|
||||
func (t *target) makeZErrorsFile() error {
|
||||
zerrorsFile := fmt.Sprintf("zerrors_linux_%s.go", t.GoArch)
|
||||
return t.commandFormatOutput("gofmt2", zerrorsFile, "/"+filepath.Join("build", "unix", "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)
|
||||
|
||||
cgoDir := filepath.Join(TempDir, t.GoArch, "cgo")
|
||||
if err := os.MkdirAll(cgoDir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := []string{"tool", "cgo", "-godefs", "-objdir=" + cgoDir, "--"}
|
||||
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" + filepath.Join(TempDir, t.GoArch, "include")}
|
||||
|
||||
// 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 means a 64-bit value should use (even, odd)-pair.
|
||||
if t.GoArch == "arm" || (t.LinuxArch == "mips" && t.Bits == 32) {
|
||||
flags = append(flags, "-arm")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Merge all the generated files for Linux targets
|
||||
func mergeFiles() error {
|
||||
// Setup environment variables
|
||||
os.Setenv("GOOS", runtime.GOOS)
|
||||
os.Setenv("GOARCH", runtime.GOARCH)
|
||||
|
||||
// Merge each of the four type of files
|
||||
for _, ztyp := range []string{"zerrors", "zsyscall", "zsysnum", "ztypes"} {
|
||||
cmd := exec.Command("go", "run", "./internal/mkmerge", "-out", fmt.Sprintf("%s_%s.go", ztyp, GOOS), fmt.Sprintf("%s_%s_*.go", ztyp, GOOS))
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not merge %s files: %w", ztyp, err)
|
||||
}
|
||||
fmt.Printf("%s files merged\n", ztyp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generatePtracePair takes a pair of GOARCH values that can run each
|
||||
// other's binaries, such as 386 and amd64. It extracts the PtraceRegs
|
||||
// type for each one. It writes a new file defining the types
|
||||
// PtraceRegsArch1 and PtraceRegsArch2 and the corresponding functions
|
||||
// Ptrace{Get,Set}Regs{arch1,arch2}. This permits debugging the other
|
||||
// binary on a native system. 'archName' is the combined name of 'arch1'
|
||||
// and 'arch2', which is used in the file name.
|
||||
func generatePtracePair(arch1, arch2, archName string) error {
|
||||
def1, err := ptraceDef(arch1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
def2, err := ptraceDef(arch2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(fmt.Sprintf("zptrace_%s_linux.go", archName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := bufio.NewWriter(f)
|
||||
fmt.Fprintf(buf, "// Code generated by linux/mkall.go generatePtracePair(%q, %q). DO NOT EDIT.\n", arch1, arch2)
|
||||
fmt.Fprintf(buf, "\n")
|
||||
fmt.Fprintf(buf, "//go:build linux && (%s || %s)\n", arch1, arch2)
|
||||
fmt.Fprintf(buf, "\n")
|
||||
fmt.Fprintf(buf, "package unix\n")
|
||||
fmt.Fprintf(buf, "\n")
|
||||
fmt.Fprintf(buf, "%s\n", `import "unsafe"`)
|
||||
fmt.Fprintf(buf, "\n")
|
||||
writeOnePtrace(buf, arch1, def1)
|
||||
fmt.Fprintf(buf, "\n")
|
||||
writeOnePtrace(buf, arch2, def2)
|
||||
if err := buf.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generatePtraceRegSet takes a GOARCH value to generate a file zptrace_linux_{arch}.go
|
||||
// containing functions PtraceGetRegSet{arch} and PtraceSetRegSet{arch}.
|
||||
func generatePtraceRegSet(arch string) error {
|
||||
f, err := os.Create(fmt.Sprintf("zptrace_linux_%s.go", arch))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := bufio.NewWriter(f)
|
||||
fmt.Fprintf(buf, "// Code generated by linux/mkall.go generatePtraceRegSet(%q). DO NOT EDIT.\n", arch)
|
||||
fmt.Fprintf(buf, "\n")
|
||||
fmt.Fprintf(buf, "package unix\n")
|
||||
fmt.Fprintf(buf, "\n")
|
||||
fmt.Fprintf(buf, "%s\n", `import "unsafe"`)
|
||||
fmt.Fprintf(buf, "\n")
|
||||
uarch := string(unicode.ToUpper(rune(arch[0]))) + arch[1:]
|
||||
fmt.Fprintf(buf, "// PtraceGetRegSet%s fetches the registers used by %s binaries.\n", uarch, arch)
|
||||
fmt.Fprintf(buf, "func PtraceGetRegSet%s(pid, addr int, regsout *PtraceRegs%s) error {\n", uarch, uarch)
|
||||
fmt.Fprintf(buf, "\tiovec := Iovec{(*byte)(unsafe.Pointer(regsout)), uint64(unsafe.Sizeof(*regsout))}\n")
|
||||
fmt.Fprintf(buf, "\treturn ptracePtr(PTRACE_GETREGSET, pid, uintptr(addr), unsafe.Pointer(&iovec))\n")
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
fmt.Fprintf(buf, "\n")
|
||||
fmt.Fprintf(buf, "// PtraceSetRegSet%s sets the registers used by %s binaries.\n", uarch, arch)
|
||||
fmt.Fprintf(buf, "func PtraceSetRegSet%s(pid, addr int, regs *PtraceRegs%s) error {\n", uarch, uarch)
|
||||
fmt.Fprintf(buf, "\tiovec := Iovec{(*byte)(unsafe.Pointer(regs)), uint64(unsafe.Sizeof(*regs))}\n")
|
||||
fmt.Fprintf(buf, "\treturn ptracePtr(PTRACE_SETREGSET, pid, uintptr(addr), unsafe.Pointer(&iovec))\n")
|
||||
fmt.Fprintf(buf, "}\n")
|
||||
if err := buf.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ptraceDef returns the definition of PtraceRegs for arch.
|
||||
func ptraceDef(arch string) (string, error) {
|
||||
filename := fmt.Sprintf("ztypes_linux_%s.go", arch)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("reading %s: %v", filename, err)
|
||||
}
|
||||
start := bytes.Index(data, []byte("type PtraceRegs struct"))
|
||||
if start < 0 {
|
||||
return "", fmt.Errorf("%s: no definition of PtraceRegs", filename)
|
||||
}
|
||||
data = data[start:]
|
||||
end := bytes.Index(data, []byte("\n}\n"))
|
||||
if end < 0 {
|
||||
return "", fmt.Errorf("%s: can't find end of PtraceRegs definition", filename)
|
||||
}
|
||||
return string(data[:end+2]), nil
|
||||
}
|
||||
|
||||
// writeOnePtrace writes out the ptrace definitions for arch.
|
||||
func writeOnePtrace(w io.Writer, arch, def string) {
|
||||
uarch := string(unicode.ToUpper(rune(arch[0]))) + arch[1:]
|
||||
fmt.Fprintf(w, "// PtraceRegs%s is the registers used by %s binaries.\n", uarch, arch)
|
||||
fmt.Fprintf(w, "%s\n", strings.Replace(def, "PtraceRegs", "PtraceRegs"+uarch, 1))
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "// PtraceGetRegs%s fetches the registers used by %s binaries.\n", uarch, arch)
|
||||
fmt.Fprintf(w, "func PtraceGetRegs%s(pid int, regsout *PtraceRegs%s) error {\n", uarch, uarch)
|
||||
fmt.Fprintf(w, "\treturn ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout))\n")
|
||||
fmt.Fprintf(w, "}\n")
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "// PtraceSetRegs%s sets the registers used by %s binaries.\n", uarch, arch)
|
||||
fmt.Fprintf(w, "func PtraceSetRegs%s(pid int, regs *PtraceRegs%s) error {\n", uarch, uarch)
|
||||
fmt.Fprintf(w, "\treturn ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs))\n")
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
||||
|
||||
// cCode is compiled for the target architecture, and the resulting data section is carved for
|
||||
// the statically initialized bit masks.
|
||||
const cCode = `
|
||||
// Bit fields are used in some system calls and other ABIs, but their memory layout is
|
||||
// implementation-defined [1]. Even with formal ABIs, bit fields are a source of subtle bugs [2].
|
||||
// Here we generate the offsets for all 64 bits in an uint64.
|
||||
// 1: http://en.cppreference.com/w/c/language/bit_field
|
||||
// 2: https://lwn.net/Articles/478657/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct bitfield {
|
||||
union {
|
||||
uint64_t val;
|
||||
struct {
|
||||
uint64_t u64_bit_0 : 1;
|
||||
uint64_t u64_bit_1 : 1;
|
||||
uint64_t u64_bit_2 : 1;
|
||||
uint64_t u64_bit_3 : 1;
|
||||
uint64_t u64_bit_4 : 1;
|
||||
uint64_t u64_bit_5 : 1;
|
||||
uint64_t u64_bit_6 : 1;
|
||||
uint64_t u64_bit_7 : 1;
|
||||
uint64_t u64_bit_8 : 1;
|
||||
uint64_t u64_bit_9 : 1;
|
||||
uint64_t u64_bit_10 : 1;
|
||||
uint64_t u64_bit_11 : 1;
|
||||
uint64_t u64_bit_12 : 1;
|
||||
uint64_t u64_bit_13 : 1;
|
||||
uint64_t u64_bit_14 : 1;
|
||||
uint64_t u64_bit_15 : 1;
|
||||
uint64_t u64_bit_16 : 1;
|
||||
uint64_t u64_bit_17 : 1;
|
||||
uint64_t u64_bit_18 : 1;
|
||||
uint64_t u64_bit_19 : 1;
|
||||
uint64_t u64_bit_20 : 1;
|
||||
uint64_t u64_bit_21 : 1;
|
||||
uint64_t u64_bit_22 : 1;
|
||||
uint64_t u64_bit_23 : 1;
|
||||
uint64_t u64_bit_24 : 1;
|
||||
uint64_t u64_bit_25 : 1;
|
||||
uint64_t u64_bit_26 : 1;
|
||||
uint64_t u64_bit_27 : 1;
|
||||
uint64_t u64_bit_28 : 1;
|
||||
uint64_t u64_bit_29 : 1;
|
||||
uint64_t u64_bit_30 : 1;
|
||||
uint64_t u64_bit_31 : 1;
|
||||
uint64_t u64_bit_32 : 1;
|
||||
uint64_t u64_bit_33 : 1;
|
||||
uint64_t u64_bit_34 : 1;
|
||||
uint64_t u64_bit_35 : 1;
|
||||
uint64_t u64_bit_36 : 1;
|
||||
uint64_t u64_bit_37 : 1;
|
||||
uint64_t u64_bit_38 : 1;
|
||||
uint64_t u64_bit_39 : 1;
|
||||
uint64_t u64_bit_40 : 1;
|
||||
uint64_t u64_bit_41 : 1;
|
||||
uint64_t u64_bit_42 : 1;
|
||||
uint64_t u64_bit_43 : 1;
|
||||
uint64_t u64_bit_44 : 1;
|
||||
uint64_t u64_bit_45 : 1;
|
||||
uint64_t u64_bit_46 : 1;
|
||||
uint64_t u64_bit_47 : 1;
|
||||
uint64_t u64_bit_48 : 1;
|
||||
uint64_t u64_bit_49 : 1;
|
||||
uint64_t u64_bit_50 : 1;
|
||||
uint64_t u64_bit_51 : 1;
|
||||
uint64_t u64_bit_52 : 1;
|
||||
uint64_t u64_bit_53 : 1;
|
||||
uint64_t u64_bit_54 : 1;
|
||||
uint64_t u64_bit_55 : 1;
|
||||
uint64_t u64_bit_56 : 1;
|
||||
uint64_t u64_bit_57 : 1;
|
||||
uint64_t u64_bit_58 : 1;
|
||||
uint64_t u64_bit_59 : 1;
|
||||
uint64_t u64_bit_60 : 1;
|
||||
uint64_t u64_bit_61 : 1;
|
||||
uint64_t u64_bit_62 : 1;
|
||||
uint64_t u64_bit_63 : 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct bitfield masks[] = {
|
||||
{.u64_bit_0 = 1},
|
||||
{.u64_bit_1 = 1},
|
||||
{.u64_bit_2 = 1},
|
||||
{.u64_bit_3 = 1},
|
||||
{.u64_bit_4 = 1},
|
||||
{.u64_bit_5 = 1},
|
||||
{.u64_bit_6 = 1},
|
||||
{.u64_bit_7 = 1},
|
||||
{.u64_bit_8 = 1},
|
||||
{.u64_bit_9 = 1},
|
||||
{.u64_bit_10 = 1},
|
||||
{.u64_bit_11 = 1},
|
||||
{.u64_bit_12 = 1},
|
||||
{.u64_bit_13 = 1},
|
||||
{.u64_bit_14 = 1},
|
||||
{.u64_bit_15 = 1},
|
||||
{.u64_bit_16 = 1},
|
||||
{.u64_bit_17 = 1},
|
||||
{.u64_bit_18 = 1},
|
||||
{.u64_bit_19 = 1},
|
||||
{.u64_bit_20 = 1},
|
||||
{.u64_bit_21 = 1},
|
||||
{.u64_bit_22 = 1},
|
||||
{.u64_bit_23 = 1},
|
||||
{.u64_bit_24 = 1},
|
||||
{.u64_bit_25 = 1},
|
||||
{.u64_bit_26 = 1},
|
||||
{.u64_bit_27 = 1},
|
||||
{.u64_bit_28 = 1},
|
||||
{.u64_bit_29 = 1},
|
||||
{.u64_bit_30 = 1},
|
||||
{.u64_bit_31 = 1},
|
||||
{.u64_bit_32 = 1},
|
||||
{.u64_bit_33 = 1},
|
||||
{.u64_bit_34 = 1},
|
||||
{.u64_bit_35 = 1},
|
||||
{.u64_bit_36 = 1},
|
||||
{.u64_bit_37 = 1},
|
||||
{.u64_bit_38 = 1},
|
||||
{.u64_bit_39 = 1},
|
||||
{.u64_bit_40 = 1},
|
||||
{.u64_bit_41 = 1},
|
||||
{.u64_bit_42 = 1},
|
||||
{.u64_bit_43 = 1},
|
||||
{.u64_bit_44 = 1},
|
||||
{.u64_bit_45 = 1},
|
||||
{.u64_bit_46 = 1},
|
||||
{.u64_bit_47 = 1},
|
||||
{.u64_bit_48 = 1},
|
||||
{.u64_bit_49 = 1},
|
||||
{.u64_bit_50 = 1},
|
||||
{.u64_bit_51 = 1},
|
||||
{.u64_bit_52 = 1},
|
||||
{.u64_bit_53 = 1},
|
||||
{.u64_bit_54 = 1},
|
||||
{.u64_bit_55 = 1},
|
||||
{.u64_bit_56 = 1},
|
||||
{.u64_bit_57 = 1},
|
||||
{.u64_bit_58 = 1},
|
||||
{.u64_bit_59 = 1},
|
||||
{.u64_bit_60 = 1},
|
||||
{.u64_bit_61 = 1},
|
||||
{.u64_bit_62 = 1},
|
||||
{.u64_bit_63 = 1}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct bitfield *mask_ptr = &masks[0];
|
||||
return mask_ptr->val;
|
||||
}
|
||||
|
||||
`
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
goos, goarch string
|
||||
)
|
||||
|
||||
// cmdLine returns this programs's commandline arguments
|
||||
func cmdLine() string {
|
||||
return "go run linux/mksysnum.go " + strings.Join(os.Args[1:], " ")
|
||||
}
|
||||
|
||||
// goBuildTags returns build tags in the go:build format.
|
||||
func goBuildTags() string {
|
||||
return fmt.Sprintf("%s && %s", goarch, goos)
|
||||
}
|
||||
|
||||
func format(name string, num int, offset int) (int, string) {
|
||||
if num > 999 {
|
||||
// ignore deprecated syscalls that are no longer implemented
|
||||
// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
|
||||
return 0, ""
|
||||
}
|
||||
name = strings.ToUpper(name)
|
||||
num = num + offset
|
||||
return num, fmt.Sprintf(" SYS_%s = %d;\n", name, num)
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// source string and substring slice for regexp
|
||||
type re struct {
|
||||
str string // source string
|
||||
sub []string // matched sub-string
|
||||
}
|
||||
|
||||
// Match performs regular expression match
|
||||
func (r *re) Match(exp string) bool {
|
||||
r.sub = regexp.MustCompile(exp).FindStringSubmatch(r.str)
|
||||
if r.sub != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// syscallNum holds the syscall number and the string
|
||||
// we will write to the generated file.
|
||||
type syscallNum struct {
|
||||
num int
|
||||
declaration string
|
||||
}
|
||||
|
||||
// syscallNums is a slice of syscallNum sorted by the syscall number in ascending order.
|
||||
type syscallNums []syscallNum
|
||||
|
||||
// addSyscallNum adds the syscall declaration to syscallNums.
|
||||
func (nums *syscallNums) addSyscallNum(num int, declaration string) {
|
||||
if declaration == "" {
|
||||
return
|
||||
}
|
||||
if len(*nums) == 0 || (*nums)[len(*nums)-1].num <= num {
|
||||
// This is the most common case as the syscall declarations output by the preprocessor
|
||||
// are almost always sorted.
|
||||
*nums = append(*nums, syscallNum{num, declaration})
|
||||
return
|
||||
}
|
||||
i := sort.Search(len(*nums), func(i int) bool { return (*nums)[i].num >= num })
|
||||
|
||||
// Maintain the ordering in the preprocessor output when we have multiple definitions with
|
||||
// the same value. i cannot be > len(nums) - 1 as nums[len(nums)-1].num > num.
|
||||
for ; (*nums)[i].num == num; i++ {
|
||||
}
|
||||
*nums = append((*nums)[:i], append([]syscallNum{{num, declaration}}, (*nums)[i:]...)...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Get the OS and architecture (using GOARCH_TARGET if it exists)
|
||||
goos = os.Getenv("GOOS")
|
||||
goarch = os.Getenv("GOARCH_TARGET")
|
||||
if goarch == "" {
|
||||
goarch = os.Getenv("GOARCH")
|
||||
}
|
||||
// Check if GOOS and GOARCH environment variables are defined
|
||||
if goarch == "" || goos == "" {
|
||||
fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
// Check that we are using the new build system if we should
|
||||
if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
|
||||
fmt.Fprintf(os.Stderr, "In the new build system, mksysnum should not be called directly.\n")
|
||||
fmt.Fprintf(os.Stderr, "See README.md\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cc := os.Getenv("CC")
|
||||
if cc == "" {
|
||||
fmt.Fprintf(os.Stderr, "CC is not defined in environment\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
args := os.Args[1:]
|
||||
args = append([]string{"-E", "-dD"}, args...)
|
||||
cmd, err := exec.Command(cc, args...).Output() // execute command and capture output
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't run %s", cc)
|
||||
os.Exit(1)
|
||||
}
|
||||
s := bufio.NewScanner(strings.NewReader(string(cmd)))
|
||||
var offset, prev, asOffset int
|
||||
var nums syscallNums
|
||||
for s.Scan() {
|
||||
t := re{str: s.Text()}
|
||||
|
||||
// The generated zsysnum_linux_*.go files for some platforms (arm64, loong64, riscv64)
|
||||
// treat SYS_ARCH_SPECIFIC_SYSCALL as if it's a syscall which it isn't. It's an offset.
|
||||
// However, as this constant is already part of the public API we leave it in place.
|
||||
// Lines of type SYS_ARCH_SPECIFIC_SYSCALL = 244 are thus processed twice, once to extract
|
||||
// the offset and once to add the constant.
|
||||
|
||||
if t.Match(`^#define __NR_arch_specific_syscall\s+([0-9]+)`) {
|
||||
// riscv: extract arch specific offset
|
||||
asOffset, _ = strconv.Atoi(t.sub[1]) // Make asOffset=0 if empty or non-numeric
|
||||
}
|
||||
|
||||
if t.Match(`^#define __NR_Linux\s+([0-9]+)`) {
|
||||
// mips/mips64: extract offset
|
||||
offset, _ = strconv.Atoi(t.sub[1]) // Make offset=0 if empty or non-numeric
|
||||
} else if t.Match(`^#define __NR(\w*)_SYSCALL_BASE\s+([0-9]+)`) {
|
||||
// arm: extract offset
|
||||
offset, _ = strconv.Atoi(t.sub[1]) // Make offset=0 if empty or non-numeric
|
||||
} else if t.Match(`^#define __NR_syscalls\s+`) {
|
||||
// ignore redefinitions of __NR_syscalls
|
||||
} else if t.Match(`^#define __NR_(\w*)Linux_syscalls\s+`) {
|
||||
// mips/mips64: ignore definitions about the number of syscalls
|
||||
} else if t.Match(`^#define __NR_(\w+)\s+([0-9]+)`) {
|
||||
prev, err = strconv.Atoi(t.sub[2])
|
||||
checkErr(err)
|
||||
nums.addSyscallNum(format(t.sub[1], prev, offset))
|
||||
} else if t.Match(`^#define __NR3264_(\w+)\s+([0-9]+)`) {
|
||||
prev, err = strconv.Atoi(t.sub[2])
|
||||
checkErr(err)
|
||||
nums.addSyscallNum(format(t.sub[1], prev, offset))
|
||||
} else if t.Match(`^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)`) {
|
||||
r2, err := strconv.Atoi(t.sub[2])
|
||||
checkErr(err)
|
||||
nums.addSyscallNum(format(t.sub[1], prev+r2, offset))
|
||||
} else if t.Match(`^#define __NR_(\w+)\s+\(__NR_(?:SYSCALL_BASE|Linux) \+ ([0-9]+)`) {
|
||||
r2, err := strconv.Atoi(t.sub[2])
|
||||
checkErr(err)
|
||||
nums.addSyscallNum(format(t.sub[1], r2, offset))
|
||||
} else if asOffset != 0 && t.Match(`^#define __NR_(\w+)\s+\(__NR_arch_specific_syscall \+ ([0-9]+)`) {
|
||||
r2, err := strconv.Atoi(t.sub[2])
|
||||
checkErr(err)
|
||||
nums.addSyscallNum(format(t.sub[1], r2, asOffset))
|
||||
}
|
||||
}
|
||||
err = s.Err()
|
||||
checkErr(err)
|
||||
var text strings.Builder
|
||||
for _, num := range nums {
|
||||
text.WriteString(num.declaration)
|
||||
}
|
||||
fmt.Printf(template, cmdLine(), goBuildTags(), text.String())
|
||||
}
|
||||
|
||||
const template = `// %s
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build %s
|
||||
|
||||
package unix
|
||||
|
||||
const(
|
||||
%s)`
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user