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