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