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