...

Source file src/golang.org/x/sys/unix/linux/mksysnum.go

Documentation: golang.org/x/sys/unix/linux

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build ignore
     6  
     7  package main
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"fmt"
    13  	"os"
    14  	"os/exec"
    15  	"regexp"
    16  	"sort"
    17  	"strconv"
    18  	"strings"
    19  )
    20  
    21  var (
    22  	goos, goarch string
    23  )
    24  
    25  // cmdLine returns this programs's commandline arguments
    26  func cmdLine() string {
    27  	return "go run linux/mksysnum.go " + strings.Join(os.Args[1:], " ")
    28  }
    29  
    30  // goBuildTags returns build tags in the go:build format.
    31  func goBuildTags() string {
    32  	return fmt.Sprintf("%s && %s", goarch, goos)
    33  }
    34  
    35  func format(name string, num int, offset int) (int, string) {
    36  	if num > 999 {
    37  		// ignore deprecated syscalls that are no longer implemented
    38  		// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
    39  		return 0, ""
    40  	}
    41  	name = strings.ToUpper(name)
    42  	num = num + offset
    43  	return num, fmt.Sprintf("	SYS_%s = %d;\n", name, num)
    44  }
    45  
    46  func checkErr(err error) {
    47  	if err != nil {
    48  		fmt.Fprintf(os.Stderr, "%v\n", err)
    49  		os.Exit(1)
    50  	}
    51  }
    52  
    53  // source string and substring slice for regexp
    54  type re struct {
    55  	str string   // source string
    56  	sub []string // matched sub-string
    57  }
    58  
    59  // Match performs regular expression match
    60  func (r *re) Match(exp string) bool {
    61  	r.sub = regexp.MustCompile(exp).FindStringSubmatch(r.str)
    62  	if r.sub != nil {
    63  		return true
    64  	}
    65  	return false
    66  }
    67  
    68  // syscallNum holds the syscall number and the string
    69  // we will write to the generated file.
    70  type syscallNum struct {
    71  	num         int
    72  	declaration string
    73  }
    74  
    75  // syscallNums is a slice of syscallNum sorted by the syscall number in ascending order.
    76  type syscallNums []syscallNum
    77  
    78  // addSyscallNum adds the syscall declaration to syscallNums.
    79  func (nums *syscallNums) addSyscallNum(num int, declaration string) {
    80  	if declaration == "" {
    81  		return
    82  	}
    83  	if len(*nums) == 0 || (*nums)[len(*nums)-1].num <= num {
    84  		// This is the most common case as the syscall declarations output by the preprocessor
    85  		// are almost always sorted.
    86  		*nums = append(*nums, syscallNum{num, declaration})
    87  		return
    88  	}
    89  	i := sort.Search(len(*nums), func(i int) bool { return (*nums)[i].num >= num })
    90  
    91  	// Maintain the ordering in the preprocessor output when we have multiple definitions with
    92  	// the same value. i cannot be > len(nums) - 1 as nums[len(nums)-1].num > num.
    93  	for ; (*nums)[i].num == num; i++ {
    94  	}
    95  	*nums = append((*nums)[:i], append([]syscallNum{{num, declaration}}, (*nums)[i:]...)...)
    96  }
    97  
    98  func main() {
    99  	// Get the OS and architecture (using GOARCH_TARGET if it exists)
   100  	goos = os.Getenv("GOOS")
   101  	goarch = os.Getenv("GOARCH_TARGET")
   102  	if goarch == "" {
   103  		goarch = os.Getenv("GOARCH")
   104  	}
   105  	// Check if GOOS and GOARCH environment variables are defined
   106  	if goarch == "" || goos == "" {
   107  		fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n")
   108  		os.Exit(1)
   109  	}
   110  	// Check that we are using the new build system if we should
   111  	if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
   112  		fmt.Fprintf(os.Stderr, "In the new build system, mksysnum should not be called directly.\n")
   113  		fmt.Fprintf(os.Stderr, "See README.md\n")
   114  		os.Exit(1)
   115  	}
   116  
   117  	cc := os.Getenv("CC")
   118  	if cc == "" {
   119  		fmt.Fprintf(os.Stderr, "CC is not defined in environment\n")
   120  		os.Exit(1)
   121  	}
   122  	args := os.Args[1:]
   123  	args = append([]string{"-E", "-dD"}, args...)
   124  	cmd, err := exec.Command(cc, args...).Output() // execute command and capture output
   125  	if err != nil {
   126  		fmt.Fprintf(os.Stderr, "can't run %s", cc)
   127  		os.Exit(1)
   128  	}
   129  
   130  	switch goarch {
   131  	case "riscv64", "loong64", "arm64":
   132  		// Kernel linux v6.11 removed some __NR_* macros that only
   133  		// existed on some architectures as an implementation detail. In
   134  		// order to keep backwards compatibility we add them back.
   135  		//
   136  		// See https://lkml.org/lkml/2024/8/5/1283.
   137  		if !bytes.Contains(cmd, []byte("#define __NR_arch_specific_syscall")) {
   138  			cmd = append(cmd, []byte("#define __NR_arch_specific_syscall 244\n")...)
   139  		}
   140  	}
   141  
   142  	s := bufio.NewScanner(strings.NewReader(string(cmd)))
   143  	var offset, prev, asOffset int
   144  	var nums syscallNums
   145  	for s.Scan() {
   146  		t := re{str: s.Text()}
   147  
   148  		// The generated zsysnum_linux_*.go files for some platforms (arm64, loong64, riscv64)
   149  		// treat SYS_ARCH_SPECIFIC_SYSCALL as if it's a syscall which it isn't.  It's an offset.
   150  		// However, as this constant is already part of the public API we leave it in place.
   151  		// Lines of type SYS_ARCH_SPECIFIC_SYSCALL = 244 are thus processed twice, once to extract
   152  		// the offset and once to add the constant.
   153  
   154  		if t.Match(`^#define __NR_arch_specific_syscall\s+([0-9]+)`) {
   155  			// riscv: extract arch specific offset
   156  			asOffset, _ = strconv.Atoi(t.sub[1]) // Make asOffset=0 if empty or non-numeric
   157  		}
   158  
   159  		if t.Match(`^#define __NR_Linux\s+([0-9]+)`) {
   160  			// mips/mips64: extract offset
   161  			offset, _ = strconv.Atoi(t.sub[1]) // Make offset=0 if empty or non-numeric
   162  		} else if t.Match(`^#define __NR(\w*)_SYSCALL_BASE\s+([0-9]+)`) {
   163  			// arm: extract offset
   164  			offset, _ = strconv.Atoi(t.sub[1]) // Make offset=0 if empty or non-numeric
   165  		} else if t.Match(`^#define __NR_syscalls\s+`) {
   166  			// ignore redefinitions of __NR_syscalls
   167  		} else if t.Match(`^#define __NR_(\w*)Linux_syscalls\s+`) {
   168  			// mips/mips64: ignore definitions about the number of syscalls
   169  		} else if t.Match(`^#define __NR_(\w+)\s+([0-9]+)`) {
   170  			prev, err = strconv.Atoi(t.sub[2])
   171  			checkErr(err)
   172  			nums.addSyscallNum(format(t.sub[1], prev, offset))
   173  		} else if t.Match(`^#define __NR3264_(\w+)\s+([0-9]+)`) {
   174  			prev, err = strconv.Atoi(t.sub[2])
   175  			checkErr(err)
   176  			nums.addSyscallNum(format(t.sub[1], prev, offset))
   177  		} else if t.Match(`^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)`) {
   178  			r2, err := strconv.Atoi(t.sub[2])
   179  			checkErr(err)
   180  			nums.addSyscallNum(format(t.sub[1], prev+r2, offset))
   181  		} else if t.Match(`^#define __NR_(\w+)\s+\(__NR_(?:SYSCALL_BASE|Linux) \+ ([0-9]+)`) {
   182  			r2, err := strconv.Atoi(t.sub[2])
   183  			checkErr(err)
   184  			nums.addSyscallNum(format(t.sub[1], r2, offset))
   185  		} else if asOffset != 0 && t.Match(`^#define __NR_(\w+)\s+\(__NR_arch_specific_syscall \+ ([0-9]+)`) {
   186  			r2, err := strconv.Atoi(t.sub[2])
   187  			checkErr(err)
   188  			nums.addSyscallNum(format(t.sub[1], r2, asOffset))
   189  		}
   190  	}
   191  	err = s.Err()
   192  	checkErr(err)
   193  	var text strings.Builder
   194  	for _, num := range nums {
   195  		text.WriteString(num.declaration)
   196  	}
   197  	fmt.Printf(template, cmdLine(), goBuildTags(), text.String())
   198  }
   199  
   200  const template = `// %s
   201  // Code generated by the command above; see README.md. DO NOT EDIT.
   202  
   203  //go:build %s
   204  
   205  package unix
   206  
   207  const(
   208  %s)`
   209  

View as plain text