diff options
Diffstat (limited to '')
-rw-r--r-- | dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/linux/mkall.go | 992 |
1 files changed, 992 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/linux/mkall.go b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/linux/mkall.go new file mode 100644 index 0000000..8c773d9 --- /dev/null +++ b/dependencies/pkg/mod/golang.org/x/sys@v0.1.0/unix/linux/mkall.go @@ -0,0 +1,992 @@ +// 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 +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "debug/elf" + "encoding/binary" + "errors" + "fmt" + "go/build/constraint" + "io" + "io/ioutil" + "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") + glibcConf := t.makeCommand(confScript, "--prefix="+filepath.Join(TempDir, t.GoArch), "--host="+t.GNUArch, "--enable-kernel="+MinKernel) + 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 only supports single-line constraints such as the //go:build + // convention used in Go 1.17+. Because the old //+build convention + // (which may have multiple lines of build tags) is being deprecated, + // we don't bother looking for multi-line 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, "// +build linux\n") + fmt.Fprintf(buf, "// +build %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 ptrace(PTRACE_GETREGSET, pid, uintptr(addr), uintptr(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 ptrace(PTRACE_SETREGSET, pid, uintptr(addr), uintptr(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 := ioutil.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 ptrace(PTRACE_GETREGS, pid, 0, uintptr(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 ptrace(PTRACE_SETREGS, pid, 0, uintptr(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; +} + +` |