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