github.com/giovannyortegon/go@v0.0.0-20220115155912-8890063f5bdd/src/pkg/mod/golang.org/x/sys@v0.0.0-20210927094055-39ccf1dd6fa6/unix/mksyscall.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 // +build ignore 7 8 /* 9 This program reads a file containing function prototypes 10 (like syscall_darwin.go) and generates system call bodies. 11 The prototypes are marked by lines beginning with "//sys" 12 and read like func declarations if //sys is replaced by func, but: 13 * The parameter lists must give a name for each argument. 14 This includes return parameters. 15 * The parameter lists must give a type for each argument: 16 the (x, y, z int) shorthand is not allowed. 17 * If the return parameter is an error number, it must be named errno. 18 19 A line beginning with //sysnb is like //sys, except that the 20 goroutine will not be suspended during the execution of the system 21 call. This must only be used for system calls which can never 22 block, as otherwise the system call could cause all goroutines to 23 hang. 24 */ 25 package main 26 27 import ( 28 "bufio" 29 "flag" 30 "fmt" 31 "os" 32 "regexp" 33 "strings" 34 ) 35 36 var ( 37 b32 = flag.Bool("b32", false, "32bit big-endian") 38 l32 = flag.Bool("l32", false, "32bit little-endian") 39 plan9 = flag.Bool("plan9", false, "plan9") 40 openbsd = flag.Bool("openbsd", false, "openbsd") 41 netbsd = flag.Bool("netbsd", false, "netbsd") 42 dragonfly = flag.Bool("dragonfly", false, "dragonfly") 43 arm = flag.Bool("arm", false, "arm") // 64-bit value should use (even, odd)-pair 44 tags = flag.String("tags", "", "build tags") 45 filename = flag.String("output", "", "output file name (standard output if omitted)") 46 ) 47 48 // cmdLine returns this programs's commandline arguments 49 func cmdLine() string { 50 return "go run mksyscall.go " + strings.Join(os.Args[1:], " ") 51 } 52 53 // goBuildTags returns build tags in the go:build format. 54 func goBuildTags() string { 55 return strings.ReplaceAll(*tags, ",", " && ") 56 } 57 58 // plusBuildTags returns build tags in the +build format. 59 func plusBuildTags() string { 60 return *tags 61 } 62 63 // Param is function parameter 64 type Param struct { 65 Name string 66 Type string 67 } 68 69 // usage prints the program usage 70 func usage() { 71 fmt.Fprintf(os.Stderr, "usage: go run mksyscall.go [-b32 | -l32] [-tags x,y] [file ...]\n") 72 os.Exit(1) 73 } 74 75 // parseParamList parses parameter list and returns a slice of parameters 76 func parseParamList(list string) []string { 77 list = strings.TrimSpace(list) 78 if list == "" { 79 return []string{} 80 } 81 return regexp.MustCompile(`\s*,\s*`).Split(list, -1) 82 } 83 84 // parseParam splits a parameter into name and type 85 func parseParam(p string) Param { 86 ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p) 87 if ps == nil { 88 fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p) 89 os.Exit(1) 90 } 91 return Param{ps[1], ps[2]} 92 } 93 94 func main() { 95 goos := os.Getenv("GOOS_TARGET") 96 if goos == "" { 97 goos = os.Getenv("GOOS") 98 } 99 if goos == "" { 100 fmt.Fprintln(os.Stderr, "GOOS not defined in environment") 101 os.Exit(1) 102 } 103 104 // Check that we are using the Docker-based build system if we should 105 if goos == "linux" { 106 if os.Getenv("GOLANG_SYS_BUILD") != "docker" { 107 fmt.Fprintf(os.Stderr, "In the Docker-based build system, mksyscall should not be called directly.\n") 108 fmt.Fprintf(os.Stderr, "See README.md\n") 109 os.Exit(1) 110 } 111 } 112 113 flag.Usage = usage 114 flag.Parse() 115 if len(flag.Args()) <= 0 { 116 fmt.Fprintf(os.Stderr, "no files to parse provided\n") 117 usage() 118 } 119 120 endianness := "" 121 if *b32 { 122 endianness = "big-endian" 123 } else if *l32 { 124 endianness = "little-endian" 125 } 126 127 libc := false 128 if goos == "darwin" { 129 libc = true 130 } 131 trampolines := map[string]bool{} 132 133 text := "" 134 for _, path := range flag.Args() { 135 file, err := os.Open(path) 136 if err != nil { 137 fmt.Fprintf(os.Stderr, err.Error()) 138 os.Exit(1) 139 } 140 s := bufio.NewScanner(file) 141 for s.Scan() { 142 t := s.Text() 143 nonblock := regexp.MustCompile(`^\/\/sysnb\t`).FindStringSubmatch(t) 144 if regexp.MustCompile(`^\/\/sys\t`).FindStringSubmatch(t) == nil && nonblock == nil { 145 continue 146 } 147 148 // Line must be of the form 149 // func Open(path string, mode int, perm int) (fd int, errno error) 150 // Split into name, in params, out params. 151 f := regexp.MustCompile(`^\/\/sys(nb)?\t(\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`).FindStringSubmatch(t) 152 if f == nil { 153 fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t) 154 os.Exit(1) 155 } 156 funct, inps, outps, sysname := f[2], f[3], f[4], f[5] 157 158 // Split argument lists on comma. 159 in := parseParamList(inps) 160 out := parseParamList(outps) 161 162 // Try in vain to keep people from editing this file. 163 // The theory is that they jump into the middle of the file 164 // without reading the header. 165 text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" 166 167 // Go function header. 168 outDecl := "" 169 if len(out) > 0 { 170 outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", ")) 171 } 172 text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl) 173 174 // Check if err return available 175 errvar := "" 176 for _, param := range out { 177 p := parseParam(param) 178 if p.Type == "error" { 179 errvar = p.Name 180 break 181 } 182 } 183 184 // Prepare arguments to Syscall. 185 var args []string 186 n := 0 187 for _, param := range in { 188 p := parseParam(param) 189 if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { 190 args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))") 191 } else if p.Type == "string" && errvar != "" { 192 text += fmt.Sprintf("\tvar _p%d *byte\n", n) 193 text += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name) 194 text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar) 195 args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) 196 n++ 197 } else if p.Type == "string" { 198 fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n") 199 text += fmt.Sprintf("\tvar _p%d *byte\n", n) 200 text += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name) 201 args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) 202 n++ 203 } else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil { 204 // Convert slice into pointer, length. 205 // Have to be careful not to take address of &a[0] if len == 0: 206 // pass dummy pointer in that case. 207 // Used to pass nil, but some OSes or simulators reject write(fd, nil, 0). 208 text += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n) 209 text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name) 210 text += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n) 211 args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name)) 212 n++ 213 } else if p.Type == "int64" && (*openbsd || *netbsd) { 214 args = append(args, "0") 215 if endianness == "big-endian" { 216 args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) 217 } else if endianness == "little-endian" { 218 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) 219 } else { 220 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) 221 } 222 } else if p.Type == "int64" && *dragonfly { 223 if regexp.MustCompile(`^(?i)extp(read|write)`).FindStringSubmatch(funct) == nil { 224 args = append(args, "0") 225 } 226 if endianness == "big-endian" { 227 args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) 228 } else if endianness == "little-endian" { 229 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) 230 } else { 231 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) 232 } 233 } else if (p.Type == "int64" || p.Type == "uint64") && endianness != "" { 234 if len(args)%2 == 1 && *arm { 235 // arm abi specifies 64-bit argument uses 236 // (even, odd) pair 237 args = append(args, "0") 238 } 239 if endianness == "big-endian" { 240 args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name)) 241 } else { 242 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name)) 243 } 244 } else { 245 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) 246 } 247 } 248 249 // Determine which form to use; pad args with zeros. 250 asm := "Syscall" 251 if nonblock != nil { 252 if errvar == "" && goos == "linux" { 253 asm = "RawSyscallNoError" 254 } else { 255 asm = "RawSyscall" 256 } 257 } else { 258 if errvar == "" && goos == "linux" { 259 asm = "SyscallNoError" 260 } 261 } 262 if len(args) <= 3 { 263 for len(args) < 3 { 264 args = append(args, "0") 265 } 266 } else if len(args) <= 6 { 267 asm += "6" 268 for len(args) < 6 { 269 args = append(args, "0") 270 } 271 } else if len(args) <= 9 { 272 asm += "9" 273 for len(args) < 9 { 274 args = append(args, "0") 275 } 276 } else { 277 fmt.Fprintf(os.Stderr, "%s:%s too many arguments to system call\n", path, funct) 278 } 279 280 // System call number. 281 if sysname == "" { 282 sysname = "SYS_" + funct 283 sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`) 284 sysname = strings.ToUpper(sysname) 285 } 286 287 var libcFn string 288 if libc { 289 asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:] // internal syscall call 290 sysname = strings.TrimPrefix(sysname, "SYS_") // remove SYS_ 291 sysname = strings.ToLower(sysname) // lowercase 292 libcFn = sysname 293 sysname = "libc_" + sysname + "_trampoline_addr" 294 } 295 296 // Actual call. 297 arglist := strings.Join(args, ", ") 298 call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist) 299 300 // Assign return values. 301 body := "" 302 ret := []string{"_", "_", "_"} 303 doErrno := false 304 for i := 0; i < len(out); i++ { 305 p := parseParam(out[i]) 306 reg := "" 307 if p.Name == "err" && !*plan9 { 308 reg = "e1" 309 ret[2] = reg 310 doErrno = true 311 } else if p.Name == "err" && *plan9 { 312 ret[0] = "r0" 313 ret[2] = "e1" 314 break 315 } else { 316 reg = fmt.Sprintf("r%d", i) 317 ret[i] = reg 318 } 319 if p.Type == "bool" { 320 reg = fmt.Sprintf("%s != 0", reg) 321 } 322 if p.Type == "int64" && endianness != "" { 323 // 64-bit number in r1:r0 or r0:r1. 324 if i+2 > len(out) { 325 fmt.Fprintf(os.Stderr, "%s:%s not enough registers for int64 return\n", path, funct) 326 } 327 if endianness == "big-endian" { 328 reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1) 329 } else { 330 reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i) 331 } 332 ret[i] = fmt.Sprintf("r%d", i) 333 ret[i+1] = fmt.Sprintf("r%d", i+1) 334 } 335 if reg != "e1" || *plan9 { 336 body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg) 337 } 338 } 339 if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" { 340 text += fmt.Sprintf("\t%s\n", call) 341 } else { 342 if errvar == "" && goos == "linux" { 343 // raw syscall without error on Linux, see golang.org/issue/22924 344 text += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], call) 345 } else { 346 text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call) 347 } 348 } 349 text += body 350 351 if *plan9 && ret[2] == "e1" { 352 text += "\tif int32(r0) == -1 {\n" 353 text += "\t\terr = e1\n" 354 text += "\t}\n" 355 } else if doErrno { 356 text += "\tif e1 != 0 {\n" 357 text += "\t\terr = errnoErr(e1)\n" 358 text += "\t}\n" 359 } 360 text += "\treturn\n" 361 text += "}\n\n" 362 363 if libc && !trampolines[libcFn] { 364 // some system calls share a trampoline, like read and readlen. 365 trampolines[libcFn] = true 366 // Declare assembly trampoline address. 367 text += fmt.Sprintf("var libc_%s_trampoline_addr uintptr\n\n", libcFn) 368 // Assembly trampoline calls the libc_* function, which this magic 369 // redirects to use the function from libSystem. 370 text += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"/usr/lib/libSystem.B.dylib\"\n", libcFn, libcFn) 371 text += "\n" 372 } 373 } 374 if err := s.Err(); err != nil { 375 fmt.Fprintf(os.Stderr, err.Error()) 376 os.Exit(1) 377 } 378 file.Close() 379 } 380 fmt.Printf(srcTemplate, cmdLine(), goBuildTags(), plusBuildTags(), text) 381 } 382 383 const srcTemplate = `// %s 384 // Code generated by the command above; see README.md. DO NOT EDIT. 385 386 //go:build %s 387 // +build %s 388 389 package unix 390 391 import ( 392 "syscall" 393 "unsafe" 394 ) 395 396 var _ syscall.Errno 397 398 %s 399 `