1// Copyright 2019 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/*
9This program reads a file containing function prototypes
10(like syscall_aix.go) and generates system call bodies.
11The prototypes are marked by lines beginning with "//sys"
12and 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 err.
18	* If go func name needs to be different than its libc name,
19	* or the function is not in libc, name could be specified
20	* at the end, after "=" sign, like
21	  //sys	getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
22*/
23package main
24
25import (
26	"bufio"
27	"flag"
28	"fmt"
29	"os"
30	"regexp"
31	"strings"
32)
33
34var (
35	b32  = flag.Bool("b32", false, "32bit big-endian")
36	l32  = flag.Bool("l32", false, "32bit little-endian")
37	aix  = flag.Bool("aix", false, "aix")
38	tags = flag.String("tags", "", "build tags")
39)
40
41// cmdLine returns this programs's commandline arguments
42func cmdLine() string {
43	return "go run mksyscall_aix_ppc.go " + strings.Join(os.Args[1:], " ")
44}
45
46// buildTags returns build tags
47func buildTags() string {
48	return *tags
49}
50
51// Param is function parameter
52type Param struct {
53	Name string
54	Type string
55}
56
57// usage prints the program usage
58func usage() {
59	fmt.Fprintf(os.Stderr, "usage: go run mksyscall_aix_ppc.go [-b32 | -l32] [-tags x,y] [file ...]\n")
60	os.Exit(1)
61}
62
63// parseParamList parses parameter list and returns a slice of parameters
64func parseParamList(list string) []string {
65	list = strings.TrimSpace(list)
66	if list == "" {
67		return []string{}
68	}
69	return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
70}
71
72// parseParam splits a parameter into name and type
73func parseParam(p string) Param {
74	ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
75	if ps == nil {
76		fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
77		os.Exit(1)
78	}
79	return Param{ps[1], ps[2]}
80}
81
82func main() {
83	flag.Usage = usage
84	flag.Parse()
85	if len(flag.Args()) <= 0 {
86		fmt.Fprintf(os.Stderr, "no files to parse provided\n")
87		usage()
88	}
89
90	endianness := ""
91	if *b32 {
92		endianness = "big-endian"
93	} else if *l32 {
94		endianness = "little-endian"
95	}
96
97	pack := ""
98	text := ""
99	cExtern := "/*\n#include <stdint.h>\n#include <stddef.h>\n"
100	for _, path := range flag.Args() {
101		file, err := os.Open(path)
102		if err != nil {
103			fmt.Fprintf(os.Stderr, err.Error())
104			os.Exit(1)
105		}
106		s := bufio.NewScanner(file)
107		for s.Scan() {
108			t := s.Text()
109			if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" {
110				pack = p[1]
111			}
112			nonblock := regexp.MustCompile(`^\/\/sysnb\t`).FindStringSubmatch(t)
113			if regexp.MustCompile(`^\/\/sys\t`).FindStringSubmatch(t) == nil && nonblock == nil {
114				continue
115			}
116
117			// Line must be of the form
118			//	func Open(path string, mode int, perm int) (fd int, err error)
119			// Split into name, in params, out params.
120			f := regexp.MustCompile(`^\/\/sys(nb)?\t(\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t)
121			if f == nil {
122				fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
123				os.Exit(1)
124			}
125			funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6]
126
127			// Split argument lists on comma.
128			in := parseParamList(inps)
129			out := parseParamList(outps)
130
131			inps = strings.Join(in, ", ")
132			outps = strings.Join(out, ", ")
133
134			// Try in vain to keep people from editing this file.
135			// The theory is that they jump into the middle of the file
136			// without reading the header.
137			text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
138
139			// Check if value return, err return available
140			errvar := ""
141			retvar := ""
142			rettype := ""
143			for _, param := range out {
144				p := parseParam(param)
145				if p.Type == "error" {
146					errvar = p.Name
147				} else {
148					retvar = p.Name
149					rettype = p.Type
150				}
151			}
152
153			// System call name.
154			if sysname == "" {
155				sysname = funct
156			}
157			sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
158			sysname = strings.ToLower(sysname) // All libc functions are lowercase.
159
160			cRettype := ""
161			if rettype == "unsafe.Pointer" {
162				cRettype = "uintptr_t"
163			} else if rettype == "uintptr" {
164				cRettype = "uintptr_t"
165			} else if regexp.MustCompile(`^_`).FindStringSubmatch(rettype) != nil {
166				cRettype = "uintptr_t"
167			} else if rettype == "int" {
168				cRettype = "int"
169			} else if rettype == "int32" {
170				cRettype = "int"
171			} else if rettype == "int64" {
172				cRettype = "long long"
173			} else if rettype == "uint32" {
174				cRettype = "unsigned int"
175			} else if rettype == "uint64" {
176				cRettype = "unsigned long long"
177			} else {
178				cRettype = "int"
179			}
180			if sysname == "exit" {
181				cRettype = "void"
182			}
183
184			// Change p.Types to c
185			var cIn []string
186			for _, param := range in {
187				p := parseParam(param)
188				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
189					cIn = append(cIn, "uintptr_t")
190				} else if p.Type == "string" {
191					cIn = append(cIn, "uintptr_t")
192				} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
193					cIn = append(cIn, "uintptr_t", "size_t")
194				} else if p.Type == "unsafe.Pointer" {
195					cIn = append(cIn, "uintptr_t")
196				} else if p.Type == "uintptr" {
197					cIn = append(cIn, "uintptr_t")
198				} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil {
199					cIn = append(cIn, "uintptr_t")
200				} else if p.Type == "int" {
201					cIn = append(cIn, "int")
202				} else if p.Type == "int32" {
203					cIn = append(cIn, "int")
204				} else if p.Type == "int64" {
205					cIn = append(cIn, "long long")
206				} else if p.Type == "uint32" {
207					cIn = append(cIn, "unsigned int")
208				} else if p.Type == "uint64" {
209					cIn = append(cIn, "unsigned long long")
210				} else {
211					cIn = append(cIn, "int")
212				}
213			}
214
215			if funct != "fcntl" && funct != "FcntlInt" && funct != "readlen" && funct != "writelen" {
216				if sysname == "select" {
217					// select is a keyword of Go. Its name is
218					// changed to c_select.
219					cExtern += "#define c_select select\n"
220				}
221				// Imports of system calls from libc
222				cExtern += fmt.Sprintf("%s %s", cRettype, sysname)
223				cIn := strings.Join(cIn, ", ")
224				cExtern += fmt.Sprintf("(%s);\n", cIn)
225			}
226
227			// So file name.
228			if *aix {
229				if modname == "" {
230					modname = "libc.a/shr_64.o"
231				} else {
232					fmt.Fprintf(os.Stderr, "%s: only syscall using libc are available\n", funct)
233					os.Exit(1)
234				}
235			}
236
237			strconvfunc := "C.CString"
238
239			// Go function header.
240			if outps != "" {
241				outps = fmt.Sprintf(" (%s)", outps)
242			}
243			if text != "" {
244				text += "\n"
245			}
246
247			text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outps)
248
249			// Prepare arguments to Syscall.
250			var args []string
251			n := 0
252			argN := 0
253			for _, param := range in {
254				p := parseParam(param)
255				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
256					args = append(args, "C.uintptr_t(uintptr(unsafe.Pointer("+p.Name+")))")
257				} else if p.Type == "string" && errvar != "" {
258					text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name)
259					args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n))
260					n++
261				} else if p.Type == "string" {
262					fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
263					text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name)
264					args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n))
265					n++
266				} else if m := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); m != nil {
267					// Convert slice into pointer, length.
268					// Have to be careful not to take address of &a[0] if len == 0:
269					// pass nil in that case.
270					text += fmt.Sprintf("\tvar _p%d *%s\n", n, m[1])
271					text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name)
272					args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(unsafe.Pointer(_p%d)))", n))
273					n++
274					text += fmt.Sprintf("\tvar _p%d int\n", n)
275					text += fmt.Sprintf("\t_p%d = len(%s)\n", n, p.Name)
276					args = append(args, fmt.Sprintf("C.size_t(_p%d)", n))
277					n++
278				} else if p.Type == "int64" && endianness != "" {
279					if endianness == "big-endian" {
280						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
281					} else {
282						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
283					}
284					n++
285				} else if p.Type == "bool" {
286					text += fmt.Sprintf("\tvar _p%d uint32\n", n)
287					text += fmt.Sprintf("\tif %s {\n\t\t_p%d = 1\n\t} else {\n\t\t_p%d = 0\n\t}\n", p.Name, n, n)
288					args = append(args, fmt.Sprintf("_p%d", n))
289				} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil {
290					args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name))
291				} else if p.Type == "unsafe.Pointer" {
292					args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name))
293				} else if p.Type == "int" {
294					if (argN == 2) && ((funct == "readlen") || (funct == "writelen")) {
295						args = append(args, fmt.Sprintf("C.size_t(%s)", p.Name))
296					} else if argN == 0 && funct == "fcntl" {
297						args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
298					} else if (argN == 2) && ((funct == "fcntl") || (funct == "FcntlInt")) {
299						args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
300					} else {
301						args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
302					}
303				} else if p.Type == "int32" {
304					args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
305				} else if p.Type == "int64" {
306					args = append(args, fmt.Sprintf("C.longlong(%s)", p.Name))
307				} else if p.Type == "uint32" {
308					args = append(args, fmt.Sprintf("C.uint(%s)", p.Name))
309				} else if p.Type == "uint64" {
310					args = append(args, fmt.Sprintf("C.ulonglong(%s)", p.Name))
311				} else if p.Type == "uintptr" {
312					args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
313				} else {
314					args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
315				}
316				argN++
317			}
318
319			// Actual call.
320			arglist := strings.Join(args, ", ")
321			call := ""
322			if sysname == "exit" {
323				if errvar != "" {
324					call += "er :="
325				} else {
326					call += ""
327				}
328			} else if errvar != "" {
329				call += "r0,er :="
330			} else if retvar != "" {
331				call += "r0,_ :="
332			} else {
333				call += ""
334			}
335			if sysname == "select" {
336				// select is a keyword of Go. Its name is
337				// changed to c_select.
338				call += fmt.Sprintf("C.c_%s(%s)", sysname, arglist)
339			} else {
340				call += fmt.Sprintf("C.%s(%s)", sysname, arglist)
341			}
342
343			// Assign return values.
344			body := ""
345			for i := 0; i < len(out); i++ {
346				p := parseParam(out[i])
347				reg := ""
348				if p.Name == "err" {
349					reg = "e1"
350				} else {
351					reg = "r0"
352				}
353				if reg != "e1" {
354					body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
355				}
356			}
357
358			// verify return
359			if sysname != "exit" && errvar != "" {
360				if regexp.MustCompile(`^uintptr`).FindStringSubmatch(cRettype) != nil {
361					body += "\tif (uintptr(r0) ==^uintptr(0) && er != nil) {\n"
362					body += fmt.Sprintf("\t\t%s = er\n", errvar)
363					body += "\t}\n"
364				} else {
365					body += "\tif (r0 ==-1 && er != nil) {\n"
366					body += fmt.Sprintf("\t\t%s = er\n", errvar)
367					body += "\t}\n"
368				}
369			} else if errvar != "" {
370				body += "\tif (er != nil) {\n"
371				body += fmt.Sprintf("\t\t%s = er\n", errvar)
372				body += "\t}\n"
373			}
374
375			text += fmt.Sprintf("\t%s\n", call)
376			text += body
377
378			text += "\treturn\n"
379			text += "}\n"
380		}
381		if err := s.Err(); err != nil {
382			fmt.Fprintf(os.Stderr, err.Error())
383			os.Exit(1)
384		}
385		file.Close()
386	}
387	imp := ""
388	if pack != "unix" {
389		imp = "import \"golang.org/x/sys/unix\"\n"
390
391	}
392	fmt.Printf(srcTemplate, cmdLine(), buildTags(), pack, cExtern, imp, text)
393}
394
395const srcTemplate = `// %s
396// Code generated by the command above; see README.md. DO NOT EDIT.
397
398// +build %s
399
400package %s
401
402
403%s
404*/
405import "C"
406import (
407	"unsafe"
408)
409
410
411%s
412
413%s
414`
415