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