github.com/gidoBOSSftw5731/go/src@v0.0.0-20210226122457-d24b0edbf019/syscall/mksyscall_libc.pl (about) 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;