1
2
3
4
5
6
7
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 libc = flag.Bool("libc", false, "libc system calls")
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")
44 tags = flag.String("tags", "", "build tags")
45 filename = flag.String("output", "", "output file name (standard output if omitted)")
46
47 libcPath = "libc.so"
48 )
49
50 var (
51 regexpComma = regexp.MustCompile(`\s*,\s*`)
52 regexpParamKV = regexp.MustCompile(`^(\S*) (\S*)$`)
53 regexpSys = regexp.MustCompile(`^\/\/sys\t`)
54 regexpSysNonblock = regexp.MustCompile(`^\/\/sysnb\t`)
55 regexpSysDeclaration = regexp.MustCompile(`^\/\/sys(nb)?\t(\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`)
56 regexpPointer = regexp.MustCompile(`^\*`)
57 regexpSlice = regexp.MustCompile(`^\[](.*)`)
58 regexpDragonflyExtp = regexp.MustCompile(`^(?i)extp(read|write)`)
59 regexpSyscallName = regexp.MustCompile(`([a-z])([A-Z])`)
60 )
61
62
63 func cmdLine() string {
64 return "go run mksyscall.go " + strings.Join(os.Args[1:], " ")
65 }
66
67
68 func goBuildTags() string {
69 return strings.ReplaceAll(*tags, ",", " && ")
70 }
71
72
73 type Param struct {
74 Name string
75 Type string
76 }
77
78
79 func usage() {
80 fmt.Fprintf(os.Stderr, "usage: go run mksyscall.go [-b32 | -l32] [-tags x,y] [file ...]\n")
81 os.Exit(1)
82 }
83
84
85 func parseParamList(list string) []string {
86 list = strings.TrimSpace(list)
87 if list == "" {
88 return []string{}
89 }
90 return regexpComma.Split(list, -1)
91 }
92
93
94 func parseParam(p string) Param {
95 ps := regexpParamKV.FindStringSubmatch(p)
96 if ps == nil {
97 fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
98 os.Exit(1)
99 }
100 return Param{ps[1], ps[2]}
101 }
102
103 func main() {
104 goos := os.Getenv("GOOS_TARGET")
105 if goos == "" {
106 goos = os.Getenv("GOOS")
107 }
108 if goos == "" {
109 fmt.Fprintln(os.Stderr, "GOOS not defined in environment")
110 os.Exit(1)
111 }
112
113
114 if goos == "linux" {
115 if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
116 fmt.Fprintf(os.Stderr, "In the Docker-based build system, mksyscall should not be called directly.\n")
117 fmt.Fprintf(os.Stderr, "See README.md\n")
118 os.Exit(1)
119 }
120 }
121
122 flag.Usage = usage
123 flag.Parse()
124 if len(flag.Args()) <= 0 {
125 fmt.Fprintf(os.Stderr, "no files to parse provided\n")
126 usage()
127 }
128
129 endianness := ""
130 if *b32 {
131 endianness = "big-endian"
132 } else if *l32 {
133 endianness = "little-endian"
134 }
135
136 if goos == "darwin" {
137 libcPath = "/usr/lib/libSystem.B.dylib"
138 *libc = true
139 }
140
141 trampolines := map[string]bool{}
142
143 text := ""
144 for _, path := range flag.Args() {
145 file, err := os.Open(path)
146 if err != nil {
147 fmt.Fprintf(os.Stderr, err.Error())
148 os.Exit(1)
149 }
150 s := bufio.NewScanner(file)
151 for s.Scan() {
152 t := s.Text()
153 nonblock := regexpSysNonblock.FindStringSubmatch(t)
154 if regexpSys.FindStringSubmatch(t) == nil && nonblock == nil {
155 continue
156 }
157
158
159
160
161 f := regexpSysDeclaration.FindStringSubmatch(t)
162 if f == nil {
163 fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
164 os.Exit(1)
165 }
166 funct, inps, outps, sysname := f[2], f[3], f[4], f[5]
167
168
169 in := parseParamList(inps)
170 out := parseParamList(outps)
171
172
173
174
175 text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
176
177
178 outDecl := ""
179 if len(out) > 0 {
180 outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", "))
181 }
182 text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
183
184
185 errvar := ""
186 for _, param := range out {
187 p := parseParam(param)
188 if p.Type == "error" {
189 errvar = p.Name
190 break
191 }
192 }
193
194
195 var args []string
196 n := 0
197 for _, param := range in {
198 p := parseParam(param)
199 if regexpPointer.FindStringSubmatch(p.Type) != nil {
200 args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
201 } else if p.Type == "string" && errvar != "" {
202 text += fmt.Sprintf("\tvar _p%d *byte\n", n)
203 text += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name)
204 text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
205 args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
206 n++
207 } else if p.Type == "string" {
208 fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
209 text += fmt.Sprintf("\tvar _p%d *byte\n", n)
210 text += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name)
211 args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
212 n++
213 } else if regexpSlice.FindStringSubmatch(p.Type) != nil {
214
215
216
217
218 text += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n)
219 text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name)
220 text += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n)
221 args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
222 n++
223 } else if p.Type == "int64" && ((*openbsd && !*libc) || *netbsd) {
224 args = append(args, "0")
225 if endianness == "big-endian" {
226 args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
227 } else if endianness == "little-endian" {
228 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
229 } else {
230 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
231 }
232 } else if p.Type == "int64" && *dragonfly {
233 if regexpDragonflyExtp.FindStringSubmatch(funct) == nil {
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 if endianness == "little-endian" {
239 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
240 } else {
241 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
242 }
243 } else if (p.Type == "int64" || p.Type == "uint64") && endianness != "" {
244 if len(args)%2 == 1 && *arm {
245
246
247 args = append(args, "0")
248 }
249 if endianness == "big-endian" {
250 args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
251 } else {
252 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
253 }
254 } else {
255 args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
256 }
257 }
258
259
260 asm := "Syscall"
261 if nonblock != nil {
262 if errvar == "" && goos == "linux" {
263 asm = "RawSyscallNoError"
264 } else {
265 asm = "RawSyscall"
266 }
267 } else {
268 if errvar == "" && goos == "linux" {
269 asm = "SyscallNoError"
270 }
271 }
272 if len(args) <= 3 {
273 for len(args) < 3 {
274 args = append(args, "0")
275 }
276 } else if len(args) <= 6 {
277 asm += "6"
278 for len(args) < 6 {
279 args = append(args, "0")
280 }
281 } else if len(args) <= 9 {
282 asm += "9"
283 for len(args) < 9 {
284 args = append(args, "0")
285 }
286 } else {
287 fmt.Fprintf(os.Stderr, "%s:%s too many arguments to system call\n", path, funct)
288 }
289
290
291 if sysname == "" {
292 sysname = "SYS_" + funct
293 sysname = regexpSyscallName.ReplaceAllString(sysname, `${1}_$2`)
294 sysname = strings.ToUpper(sysname)
295 }
296
297 var libcFn string
298 if *libc {
299 asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:]
300 sysname = strings.TrimPrefix(sysname, "SYS_")
301 sysname = strings.ToLower(sysname)
302 if *openbsd && *libc {
303 switch sysname {
304 case "__getcwd":
305 sysname = "getcwd"
306 case "__sysctl":
307 sysname = "sysctl"
308 }
309 }
310 libcFn = sysname
311 sysname = "libc_" + sysname + "_trampoline_addr"
312 }
313
314
315 arglist := strings.Join(args, ", ")
316 call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist)
317
318
319 body := ""
320 ret := []string{"_", "_", "_"}
321 doErrno := false
322 for i := 0; i < len(out); i++ {
323 p := parseParam(out[i])
324 reg := ""
325 if p.Name == "err" && !*plan9 {
326 reg = "e1"
327 ret[2] = reg
328 doErrno = true
329 } else if p.Name == "err" && *plan9 {
330 ret[0] = "r0"
331 ret[2] = "e1"
332 break
333 } else {
334 reg = fmt.Sprintf("r%d", i)
335 ret[i] = reg
336 }
337 if p.Type == "bool" {
338 reg = fmt.Sprintf("%s != 0", reg)
339 }
340 if p.Type == "int64" && endianness != "" {
341
342 if i+2 > len(out) {
343 fmt.Fprintf(os.Stderr, "%s:%s not enough registers for int64 return\n", path, funct)
344 }
345 if endianness == "big-endian" {
346 reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1)
347 } else {
348 reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i)
349 }
350 ret[i] = fmt.Sprintf("r%d", i)
351 ret[i+1] = fmt.Sprintf("r%d", i+1)
352 }
353 if reg != "e1" || *plan9 {
354 body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
355 }
356 }
357 if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
358 text += fmt.Sprintf("\t%s\n", call)
359 } else {
360 if errvar == "" && goos == "linux" {
361
362 text += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], call)
363 } else {
364 text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
365 }
366 }
367 text += body
368
369 if *plan9 && ret[2] == "e1" {
370 text += "\tif int32(r0) == -1 {\n"
371 text += "\t\terr = e1\n"
372 text += "\t}\n"
373 } else if doErrno {
374 text += "\tif e1 != 0 {\n"
375 text += "\t\terr = errnoErr(e1)\n"
376 text += "\t}\n"
377 }
378 text += "\treturn\n"
379 text += "}\n\n"
380
381 if *libc && !trampolines[libcFn] {
382
383 trampolines[libcFn] = true
384
385 text += fmt.Sprintf("var libc_%s_trampoline_addr uintptr\n\n", libcFn)
386
387
388 text += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s %q\n", libcFn, libcFn, libcPath)
389 text += "\n"
390 }
391 }
392 if err := s.Err(); err != nil {
393 fmt.Fprintf(os.Stderr, err.Error())
394 os.Exit(1)
395 }
396 file.Close()
397 }
398 fmt.Printf(srcTemplate, cmdLine(), goBuildTags(), text)
399 }
400
401 const srcTemplate = `// %s
402 // Code generated by the command above; see README.md. DO NOT EDIT.
403
404 //go:build %s
405
406 package unix
407
408 import (
409 "syscall"
410 "unsafe"
411 )
412
413 var _ syscall.Errno
414
415 %s
416 `
417
View as plain text