mirror of
https://github.com/golang/sys.git
synced 2026-01-29 15:12:09 +03:00
Change-Id: I1cc777822da1b557e69634faade8e24d06c00ce8
GitHub-Last-Rev: d09238ecd0
GitHub-Pull-Request: golang/sys#218
Reviewed-on: https://go-review.googlesource.com/c/sys/+/613855
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
209 lines
6.4 KiB
Go
209 lines
6.4 KiB
Go
// 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"
|
|
"bytes"
|
|
"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)
|
|
}
|
|
|
|
switch goarch {
|
|
case "riscv64", "loong64", "arm64":
|
|
// Kernel linux v6.11 removed some __NR_* macros that only
|
|
// existed on some architectures as an implementation detail. In
|
|
// order to keep backwards compatibility we add them back.
|
|
//
|
|
// See https://lkml.org/lkml/2024/8/5/1283.
|
|
if !bytes.Contains(cmd, []byte("#define __NR_arch_specific_syscall")) {
|
|
cmd = append(cmd, []byte("#define __NR_arch_specific_syscall 244\n")...)
|
|
}
|
|
}
|
|
|
|
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)`
|