mksyscall_solaris.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. // +build ignore
  5. /*
  6. This program reads a file containing function prototypes
  7. (like syscall_solaris.go) and generates system call bodies.
  8. The prototypes are marked by lines beginning with "//sys"
  9. and read like func declarations if //sys is replaced by func, but:
  10. * The parameter lists must give a name for each argument.
  11. This includes return parameters.
  12. * The parameter lists must give a type for each argument:
  13. the (x, y, z int) shorthand is not allowed.
  14. * If the return parameter is an error number, it must be named err.
  15. * If go func name needs to be different than its libc name,
  16. * or the function is not in libc, name could be specified
  17. * at the end, after "=" sign, like
  18. //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
  19. */
  20. package main
  21. import (
  22. "bufio"
  23. "flag"
  24. "fmt"
  25. "os"
  26. "regexp"
  27. "strings"
  28. )
  29. var (
  30. b32 = flag.Bool("b32", false, "32bit big-endian")
  31. l32 = flag.Bool("l32", false, "32bit little-endian")
  32. tags = flag.String("tags", "", "build tags")
  33. )
  34. // cmdLine returns this programs's commandline arguments
  35. func cmdLine() string {
  36. return "go run mksyscall_solaris.go " + strings.Join(os.Args[1:], " ")
  37. }
  38. // buildTags returns build tags
  39. func buildTags() string {
  40. return *tags
  41. }
  42. // Param is function parameter
  43. type Param struct {
  44. Name string
  45. Type string
  46. }
  47. // usage prints the program usage
  48. func usage() {
  49. fmt.Fprintf(os.Stderr, "usage: go run mksyscall_solaris.go [-b32 | -l32] [-tags x,y] [file ...]\n")
  50. os.Exit(1)
  51. }
  52. // parseParamList parses parameter list and returns a slice of parameters
  53. func parseParamList(list string) []string {
  54. list = strings.TrimSpace(list)
  55. if list == "" {
  56. return []string{}
  57. }
  58. return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
  59. }
  60. // parseParam splits a parameter into name and type
  61. func parseParam(p string) Param {
  62. ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
  63. if ps == nil {
  64. fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
  65. os.Exit(1)
  66. }
  67. return Param{ps[1], ps[2]}
  68. }
  69. func main() {
  70. flag.Usage = usage
  71. flag.Parse()
  72. if len(flag.Args()) <= 0 {
  73. fmt.Fprintf(os.Stderr, "no files to parse provided\n")
  74. usage()
  75. }
  76. endianness := ""
  77. if *b32 {
  78. endianness = "big-endian"
  79. } else if *l32 {
  80. endianness = "little-endian"
  81. }
  82. pack := ""
  83. text := ""
  84. dynimports := ""
  85. linknames := ""
  86. var vars []string
  87. for _, path := range flag.Args() {
  88. file, err := os.Open(path)
  89. if err != nil {
  90. fmt.Fprintf(os.Stderr, err.Error())
  91. os.Exit(1)
  92. }
  93. s := bufio.NewScanner(file)
  94. for s.Scan() {
  95. t := s.Text()
  96. t = strings.TrimSpace(t)
  97. t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
  98. if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" {
  99. pack = p[1]
  100. }
  101. nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
  102. if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
  103. continue
  104. }
  105. // Line must be of the form
  106. // func Open(path string, mode int, perm int) (fd int, err error)
  107. // Split into name, in params, out params.
  108. f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t)
  109. if f == nil {
  110. fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
  111. os.Exit(1)
  112. }
  113. funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6]
  114. // Split argument lists on comma.
  115. in := parseParamList(inps)
  116. out := parseParamList(outps)
  117. inps = strings.Join(in, ", ")
  118. outps = strings.Join(out, ", ")
  119. // Try in vain to keep people from editing this file.
  120. // The theory is that they jump into the middle of the file
  121. // without reading the header.
  122. text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
  123. // So file name.
  124. if modname == "" {
  125. modname = "libc"
  126. }
  127. // System call name.
  128. if sysname == "" {
  129. sysname = funct
  130. }
  131. // System call pointer variable name.
  132. sysvarname := fmt.Sprintf("proc%s", sysname)
  133. strconvfunc := "BytePtrFromString"
  134. strconvtype := "*byte"
  135. sysname = strings.ToLower(sysname) // All libc functions are lowercase.
  136. // Runtime import of function to allow cross-platform builds.
  137. dynimports += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"%s.so\"\n", sysname, sysname, modname)
  138. // Link symbol to proc address variable.
  139. linknames += fmt.Sprintf("//go:linkname %s libc_%s\n", sysvarname, sysname)
  140. // Library proc address variable.
  141. vars = append(vars, sysvarname)
  142. // Go function header.
  143. outlist := strings.Join(out, ", ")
  144. if outlist != "" {
  145. outlist = fmt.Sprintf(" (%s)", outlist)
  146. }
  147. if text != "" {
  148. text += "\n"
  149. }
  150. text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outlist)
  151. // Check if err return available
  152. errvar := ""
  153. for _, param := range out {
  154. p := parseParam(param)
  155. if p.Type == "error" {
  156. errvar = p.Name
  157. continue
  158. }
  159. }
  160. // Prepare arguments to Syscall.
  161. var args []string
  162. n := 0
  163. for _, param := range in {
  164. p := parseParam(param)
  165. if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
  166. args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
  167. } else if p.Type == "string" && errvar != "" {
  168. text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
  169. text += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name)
  170. text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
  171. args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
  172. n++
  173. } else if p.Type == "string" {
  174. fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
  175. text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
  176. text += fmt.Sprintf("\t_p%d, _ = %s(%s)\n", n, strconvfunc, p.Name)
  177. args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
  178. n++
  179. } else if s := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); s != nil {
  180. // Convert slice into pointer, length.
  181. // Have to be careful not to take address of &a[0] if len == 0:
  182. // pass nil in that case.
  183. text += fmt.Sprintf("\tvar _p%d *%s\n", n, s[1])
  184. text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name)
  185. args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
  186. n++
  187. } else if p.Type == "int64" && endianness != "" {
  188. if endianness == "big-endian" {
  189. args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
  190. } else {
  191. args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
  192. }
  193. } else if p.Type == "bool" {
  194. text += fmt.Sprintf("\tvar _p%d uint32\n", n)
  195. 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)
  196. args = append(args, fmt.Sprintf("uintptr(_p%d)", n))
  197. n++
  198. } else {
  199. args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
  200. }
  201. }
  202. nargs := len(args)
  203. // Determine which form to use; pad args with zeros.
  204. asm := "sysvicall6"
  205. if nonblock != nil {
  206. asm = "rawSysvicall6"
  207. }
  208. if len(args) <= 6 {
  209. for len(args) < 6 {
  210. args = append(args, "0")
  211. }
  212. } else {
  213. fmt.Fprintf(os.Stderr, "%s: too many arguments to system call\n", path)
  214. os.Exit(1)
  215. }
  216. // Actual call.
  217. arglist := strings.Join(args, ", ")
  218. call := fmt.Sprintf("%s(uintptr(unsafe.Pointer(&%s)), %d, %s)", asm, sysvarname, nargs, arglist)
  219. // Assign return values.
  220. body := ""
  221. ret := []string{"_", "_", "_"}
  222. doErrno := false
  223. for i := 0; i < len(out); i++ {
  224. p := parseParam(out[i])
  225. reg := ""
  226. if p.Name == "err" {
  227. reg = "e1"
  228. ret[2] = reg
  229. doErrno = true
  230. } else {
  231. reg = fmt.Sprintf("r%d", i)
  232. ret[i] = reg
  233. }
  234. if p.Type == "bool" {
  235. reg = fmt.Sprintf("%d != 0", reg)
  236. }
  237. if p.Type == "int64" && endianness != "" {
  238. // 64-bit number in r1:r0 or r0:r1.
  239. if i+2 > len(out) {
  240. fmt.Fprintf(os.Stderr, "%s: not enough registers for int64 return\n", path)
  241. os.Exit(1)
  242. }
  243. if endianness == "big-endian" {
  244. reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1)
  245. } else {
  246. reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i)
  247. }
  248. ret[i] = fmt.Sprintf("r%d", i)
  249. ret[i+1] = fmt.Sprintf("r%d", i+1)
  250. }
  251. if reg != "e1" {
  252. body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
  253. }
  254. }
  255. if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
  256. text += fmt.Sprintf("\t%s\n", call)
  257. } else {
  258. text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
  259. }
  260. text += body
  261. if doErrno {
  262. text += "\tif e1 != 0 {\n"
  263. text += "\t\terr = e1\n"
  264. text += "\t}\n"
  265. }
  266. text += "\treturn\n"
  267. text += "}\n"
  268. }
  269. if err := s.Err(); err != nil {
  270. fmt.Fprintf(os.Stderr, err.Error())
  271. os.Exit(1)
  272. }
  273. file.Close()
  274. }
  275. imp := ""
  276. if pack != "unix" {
  277. imp = "import \"golang.org/x/sys/unix\"\n"
  278. }
  279. vardecls := "\t" + strings.Join(vars, ",\n\t")
  280. vardecls += " syscallFunc"
  281. fmt.Printf(srcTemplate, cmdLine(), buildTags(), pack, imp, dynimports, linknames, vardecls, text)
  282. }
  283. const srcTemplate = `// %s
  284. // Code generated by the command above; see README.md. DO NOT EDIT.
  285. // +build %s
  286. package %s
  287. import (
  288. "syscall"
  289. "unsafe"
  290. )
  291. %s
  292. %s
  293. %s
  294. var (
  295. %s
  296. )
  297. %s
  298. `