1 #!/usr/bin/env perl |
|
2 # Copyright 2009 The Go Authors. All rights reserved. |
|
3 # Use of this source code is governed by a BSD-style |
|
4 # license that can be found in the LICENSE file. |
|
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 use strict; |
|
21 |
|
22 my $cmdline = "mksyscall_solaris.pl " . join(' ', @ARGV); |
|
23 my $errors = 0; |
|
24 my $_32bit = ""; |
|
25 my $tags = ""; # build tags |
|
26 |
|
27 binmode STDOUT; |
|
28 |
|
29 if($ARGV[0] eq "-b32") { |
|
30 $_32bit = "big-endian"; |
|
31 shift; |
|
32 } elsif($ARGV[0] eq "-l32") { |
|
33 $_32bit = "little-endian"; |
|
34 shift; |
|
35 } |
|
36 if($ARGV[0] eq "-tags") { |
|
37 shift; |
|
38 $tags = $ARGV[0]; |
|
39 shift; |
|
40 } |
|
41 |
|
42 if($ARGV[0] =~ /^-/) { |
|
43 print STDERR "usage: mksyscall_solaris.pl [-b32 | -l32] [-tags x,y] [file ...]\n"; |
|
44 exit 1; |
|
45 } |
|
46 |
|
47 sub parseparamlist($) { |
|
48 my ($list) = @_; |
|
49 $list =~ s/^\s*//; |
|
50 $list =~ s/\s*$//; |
|
51 if($list eq "") { |
|
52 return (); |
|
53 } |
|
54 return split(/\s*,\s*/, $list); |
|
55 } |
|
56 |
|
57 sub parseparam($) { |
|
58 my ($p) = @_; |
|
59 if($p !~ /^(\S*) (\S*)$/) { |
|
60 print STDERR "$ARGV:$.: malformed parameter: $p\n"; |
|
61 $errors = 1; |
|
62 return ("xx", "int"); |
|
63 } |
|
64 return ($1, $2); |
|
65 } |
|
66 |
|
67 my $package = ""; |
|
68 my $text = ""; |
|
69 my $dynimports = ""; |
|
70 my $linknames = ""; |
|
71 my @vars = (); |
|
72 while(<>) { |
|
73 chomp; |
|
74 s/\s+/ /g; |
|
75 s/^\s+//; |
|
76 s/\s+$//; |
|
77 $package = $1 if !$package && /^package (\S+)$/; |
|
78 my $nonblock = /^\/\/sysnb /; |
|
79 next if !/^\/\/sys / && !$nonblock; |
|
80 |
|
81 # Line must be of the form |
|
82 # func Open(path string, mode int, perm int) (fd int, err error) |
|
83 # Split into name, in params, out params. |
|
84 if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) { |
|
85 print STDERR "$ARGV:$.: malformed //sys declaration\n"; |
|
86 $errors = 1; |
|
87 next; |
|
88 } |
|
89 my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6); |
|
90 |
|
91 # Split argument lists on comma. |
|
92 my @in = parseparamlist($in); |
|
93 my @out = parseparamlist($out); |
|
94 |
|
95 # So file name. |
|
96 if($modname eq "") { |
|
97 $modname = "libc"; |
|
98 } |
|
99 |
|
100 # System call name. |
|
101 if($sysname eq "") { |
|
102 $sysname = "$func"; |
|
103 } |
|
104 |
|
105 # System call pointer variable name. |
|
106 my $sysvarname = "proc$sysname"; |
|
107 |
|
108 my $strconvfunc = "BytePtrFromString"; |
|
109 my $strconvtype = "*byte"; |
|
110 |
|
111 $sysname =~ y/A-Z/a-z/; # All libc functions are lowercase. |
|
112 |
|
113 # Runtime import of function to allow cross-platform builds. |
|
114 $dynimports .= "//go:cgo_import_dynamic libc_${sysname} ${sysname} \"$modname.so\"\n"; |
|
115 # Link symbol to proc address variable. |
|
116 $linknames .= "//go:linkname ${sysvarname} libc_${sysname}\n"; |
|
117 # Library proc address variable. |
|
118 push @vars, $sysvarname; |
|
119 |
|
120 # Go function header. |
|
121 $out = join(', ', @out); |
|
122 if($out ne "") { |
|
123 $out = " ($out)"; |
|
124 } |
|
125 if($text ne "") { |
|
126 $text .= "\n" |
|
127 } |
|
128 $text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out; |
|
129 |
|
130 # Check if err return available |
|
131 my $errvar = ""; |
|
132 foreach my $p (@out) { |
|
133 my ($name, $type) = parseparam($p); |
|
134 if($type eq "error") { |
|
135 $errvar = $name; |
|
136 last; |
|
137 } |
|
138 } |
|
139 |
|
140 # Prepare arguments to Syscall. |
|
141 my @args = (); |
|
142 my $n = 0; |
|
143 foreach my $p (@in) { |
|
144 my ($name, $type) = parseparam($p); |
|
145 if($type =~ /^\*/) { |
|
146 push @args, "uintptr(unsafe.Pointer($name))"; |
|
147 } elsif($type eq "string" && $errvar ne "") { |
|
148 $text .= "\tvar _p$n $strconvtype\n"; |
|
149 $text .= "\t_p$n, $errvar = $strconvfunc($name)\n"; |
|
150 $text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n"; |
|
151 push @args, "uintptr(unsafe.Pointer(_p$n))"; |
|
152 $n++; |
|
153 } elsif($type eq "string") { |
|
154 print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n"; |
|
155 $text .= "\tvar _p$n $strconvtype\n"; |
|
156 $text .= "\t_p$n, _ = $strconvfunc($name)\n"; |
|
157 push @args, "uintptr(unsafe.Pointer(_p$n))"; |
|
158 $n++; |
|
159 } elsif($type =~ /^\[\](.*)/) { |
|
160 # Convert slice into pointer, length. |
|
161 # Have to be careful not to take address of &a[0] if len == 0: |
|
162 # pass nil in that case. |
|
163 $text .= "\tvar _p$n *$1\n"; |
|
164 $text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n"; |
|
165 push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))"; |
|
166 $n++; |
|
167 } elsif($type eq "int64" && $_32bit ne "") { |
|
168 if($_32bit eq "big-endian") { |
|
169 push @args, "uintptr($name >> 32)", "uintptr($name)"; |
|
170 } else { |
|
171 push @args, "uintptr($name)", "uintptr($name >> 32)"; |
|
172 } |
|
173 } elsif($type eq "bool") { |
|
174 $text .= "\tvar _p$n uint32\n"; |
|
175 $text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n"; |
|
176 push @args, "uintptr(_p$n)"; |
|
177 $n++; |
|
178 } else { |
|
179 push @args, "uintptr($name)"; |
|
180 } |
|
181 } |
|
182 my $nargs = @args; |
|
183 |
|
184 # Determine which form to use; pad args with zeros. |
|
185 my $asm = "sysvicall6"; |
|
186 if ($nonblock) { |
|
187 $asm = "rawSysvicall6"; |
|
188 } |
|
189 if(@args <= 6) { |
|
190 while(@args < 6) { |
|
191 push @args, "0"; |
|
192 } |
|
193 } else { |
|
194 print STDERR "$ARGV:$.: too many arguments to system call\n"; |
|
195 } |
|
196 |
|
197 # Actual call. |
|
198 my $args = join(', ', @args); |
|
199 my $call = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $args)"; |
|
200 |
|
201 # Assign return values. |
|
202 my $body = ""; |
|
203 my $failexpr = ""; |
|
204 my @ret = ("_", "_", "_"); |
|
205 my @pout= (); |
|
206 my $do_errno = 0; |
|
207 for(my $i=0; $i<@out; $i++) { |
|
208 my $p = $out[$i]; |
|
209 my ($name, $type) = parseparam($p); |
|
210 my $reg = ""; |
|
211 if($name eq "err") { |
|
212 $reg = "e1"; |
|
213 $ret[2] = $reg; |
|
214 $do_errno = 1; |
|
215 } else { |
|
216 $reg = sprintf("r%d", $i); |
|
217 $ret[$i] = $reg; |
|
218 } |
|
219 if($type eq "bool") { |
|
220 $reg = "$reg != 0"; |
|
221 } |
|
222 if($type eq "int64" && $_32bit ne "") { |
|
223 # 64-bit number in r1:r0 or r0:r1. |
|
224 if($i+2 > @out) { |
|
225 print STDERR "$ARGV:$.: not enough registers for int64 return\n"; |
|
226 } |
|
227 if($_32bit eq "big-endian") { |
|
228 $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1); |
|
229 } else { |
|
230 $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i); |
|
231 } |
|
232 $ret[$i] = sprintf("r%d", $i); |
|
233 $ret[$i+1] = sprintf("r%d", $i+1); |
|
234 } |
|
235 if($reg ne "e1") { |
|
236 $body .= "\t$name = $type($reg)\n"; |
|
237 } |
|
238 } |
|
239 if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") { |
|
240 $text .= "\t$call\n"; |
|
241 } else { |
|
242 $text .= "\t$ret[0], $ret[1], $ret[2] := $call\n"; |
|
243 } |
|
244 $text .= $body; |
|
245 |
|
246 if ($do_errno) { |
|
247 $text .= "\tif e1 != 0 {\n"; |
|
248 $text .= "\t\terr = e1\n"; |
|
249 $text .= "\t}\n"; |
|
250 } |
|
251 $text .= "\treturn\n"; |
|
252 $text .= "}\n"; |
|
253 } |
|
254 |
|
255 if($errors) { |
|
256 exit 1; |
|
257 } |
|
258 |
|
259 print <<EOF; |
|
260 // $cmdline |
|
261 // Code generated by the command above; see README.md. DO NOT EDIT. |
|
262 |
|
263 // +build $tags |
|
264 |
|
265 package $package |
|
266 |
|
267 import ( |
|
268 "syscall" |
|
269 "unsafe" |
|
270 ) |
|
271 EOF |
|
272 |
|
273 print "import \"golang.org/x/sys/unix\"\n" if $package ne "unix"; |
|
274 |
|
275 my $vardecls = "\t" . join(",\n\t", @vars); |
|
276 $vardecls .= " syscallFunc"; |
|
277 |
|
278 chomp($_=<<EOF); |
|
279 |
|
280 $dynimports |
|
281 $linknames |
|
282 var ( |
|
283 $vardecls |
|
284 ) |
|
285 |
|
286 $text |
|
287 EOF |
|
288 print $_; |
|
289 exit 0; |
|