vendor/golang.org/x/sys/unix/mksyscall.pl
changeset 251 1c52a0eeb952
parent 250 c040f992052f
child 252 8399cd48111b
equal deleted inserted replaced
250:c040f992052f 251:1c52a0eeb952
     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_darwin.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 errno.
       
    15 
       
    16 # A line beginning with //sysnb is like //sys, except that the
       
    17 # goroutine will not be suspended during the execution of the system
       
    18 # call.  This must only be used for system calls which can never
       
    19 # block, as otherwise the system call could cause all goroutines to
       
    20 # hang.
       
    21 
       
    22 use strict;
       
    23 
       
    24 my $cmdline = "mksyscall.pl " . join(' ', @ARGV);
       
    25 my $errors = 0;
       
    26 my $_32bit = "";
       
    27 my $plan9 = 0;
       
    28 my $openbsd = 0;
       
    29 my $netbsd = 0;
       
    30 my $dragonfly = 0;
       
    31 my $arm = 0; # 64-bit value should use (even, odd)-pair
       
    32 my $tags = "";  # build tags
       
    33 
       
    34 if($ARGV[0] eq "-b32") {
       
    35 	$_32bit = "big-endian";
       
    36 	shift;
       
    37 } elsif($ARGV[0] eq "-l32") {
       
    38 	$_32bit = "little-endian";
       
    39 	shift;
       
    40 }
       
    41 if($ARGV[0] eq "-plan9") {
       
    42 	$plan9 = 1;
       
    43 	shift;
       
    44 }
       
    45 if($ARGV[0] eq "-openbsd") {
       
    46 	$openbsd = 1;
       
    47 	shift;
       
    48 }
       
    49 if($ARGV[0] eq "-netbsd") {
       
    50 	$netbsd = 1;
       
    51 	shift;
       
    52 }
       
    53 if($ARGV[0] eq "-dragonfly") {
       
    54 	$dragonfly = 1;
       
    55 	shift;
       
    56 }
       
    57 if($ARGV[0] eq "-arm") {
       
    58 	$arm = 1;
       
    59 	shift;
       
    60 }
       
    61 if($ARGV[0] eq "-tags") {
       
    62 	shift;
       
    63 	$tags = $ARGV[0];
       
    64 	shift;
       
    65 }
       
    66 
       
    67 if($ARGV[0] =~ /^-/) {
       
    68 	print STDERR "usage: mksyscall.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
       
    69 	exit 1;
       
    70 }
       
    71 
       
    72 # Check that we are using the new build system if we should
       
    73 if($ENV{'GOOS'} eq "linux" && $ENV{'GOARCH'} ne "sparc64") {
       
    74 	if($ENV{'GOLANG_SYS_BUILD'} ne "docker") {
       
    75 		print STDERR "In the new build system, mksyscall should not be called directly.\n";
       
    76 		print STDERR "See README.md\n";
       
    77 		exit 1;
       
    78 	}
       
    79 }
       
    80 
       
    81 
       
    82 sub parseparamlist($) {
       
    83 	my ($list) = @_;
       
    84 	$list =~ s/^\s*//;
       
    85 	$list =~ s/\s*$//;
       
    86 	if($list eq "") {
       
    87 		return ();
       
    88 	}
       
    89 	return split(/\s*,\s*/, $list);
       
    90 }
       
    91 
       
    92 sub parseparam($) {
       
    93 	my ($p) = @_;
       
    94 	if($p !~ /^(\S*) (\S*)$/) {
       
    95 		print STDERR "$ARGV:$.: malformed parameter: $p\n";
       
    96 		$errors = 1;
       
    97 		return ("xx", "int");
       
    98 	}
       
    99 	return ($1, $2);
       
   100 }
       
   101 
       
   102 my $text = "";
       
   103 while(<>) {
       
   104 	chomp;
       
   105 	s/\s+/ /g;
       
   106 	s/^\s+//;
       
   107 	s/\s+$//;
       
   108 	my $nonblock = /^\/\/sysnb /;
       
   109 	next if !/^\/\/sys / && !$nonblock;
       
   110 
       
   111 	# Line must be of the form
       
   112 	#	func Open(path string, mode int, perm int) (fd int, errno error)
       
   113 	# Split into name, in params, out params.
       
   114 	if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$/) {
       
   115 		print STDERR "$ARGV:$.: malformed //sys declaration\n";
       
   116 		$errors = 1;
       
   117 		next;
       
   118 	}
       
   119 	my ($func, $in, $out, $sysname) = ($2, $3, $4, $5);
       
   120 
       
   121 	# Split argument lists on comma.
       
   122 	my @in = parseparamlist($in);
       
   123 	my @out = parseparamlist($out);
       
   124 
       
   125 	# Try in vain to keep people from editing this file.
       
   126 	# The theory is that they jump into the middle of the file
       
   127 	# without reading the header.
       
   128 	$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
       
   129 
       
   130 	# Go function header.
       
   131 	my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : "";
       
   132 	$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl;
       
   133 
       
   134 	# Check if err return available
       
   135 	my $errvar = "";
       
   136 	foreach my $p (@out) {
       
   137 		my ($name, $type) = parseparam($p);
       
   138 		if($type eq "error") {
       
   139 			$errvar = $name;
       
   140 			last;
       
   141 		}
       
   142 	}
       
   143 
       
   144 	# Prepare arguments to Syscall.
       
   145 	my @args = ();
       
   146 	my $n = 0;
       
   147 	foreach my $p (@in) {
       
   148 		my ($name, $type) = parseparam($p);
       
   149 		if($type =~ /^\*/) {
       
   150 			push @args, "uintptr(unsafe.Pointer($name))";
       
   151 		} elsif($type eq "string" && $errvar ne "") {
       
   152 			$text .= "\tvar _p$n *byte\n";
       
   153 			$text .= "\t_p$n, $errvar = BytePtrFromString($name)\n";
       
   154 			$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
       
   155 			push @args, "uintptr(unsafe.Pointer(_p$n))";
       
   156 			$n++;
       
   157 		} elsif($type eq "string") {
       
   158 			print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
       
   159 			$text .= "\tvar _p$n *byte\n";
       
   160 			$text .= "\t_p$n, _ = BytePtrFromString($name)\n";
       
   161 			push @args, "uintptr(unsafe.Pointer(_p$n))";
       
   162 			$n++;
       
   163 		} elsif($type =~ /^\[\](.*)/) {
       
   164 			# Convert slice into pointer, length.
       
   165 			# Have to be careful not to take address of &a[0] if len == 0:
       
   166 			# pass dummy pointer in that case.
       
   167 			# Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
       
   168 			$text .= "\tvar _p$n unsafe.Pointer\n";
       
   169 			$text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}";
       
   170 			$text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}";
       
   171 			$text .= "\n";
       
   172 			push @args, "uintptr(_p$n)", "uintptr(len($name))";
       
   173 			$n++;
       
   174 		} elsif($type eq "int64" && ($openbsd || $netbsd)) {
       
   175 			push @args, "0";
       
   176 			if($_32bit eq "big-endian") {
       
   177 				push @args, "uintptr($name>>32)", "uintptr($name)";
       
   178 			} elsif($_32bit eq "little-endian") {
       
   179 				push @args, "uintptr($name)", "uintptr($name>>32)";
       
   180 			} else {
       
   181 				push @args, "uintptr($name)";
       
   182 			}
       
   183 		} elsif($type eq "int64" && $dragonfly) {
       
   184 			if ($func !~ /^extp(read|write)/i) {
       
   185 				push @args, "0";
       
   186 			}
       
   187 			if($_32bit eq "big-endian") {
       
   188 				push @args, "uintptr($name>>32)", "uintptr($name)";
       
   189 			} elsif($_32bit eq "little-endian") {
       
   190 				push @args, "uintptr($name)", "uintptr($name>>32)";
       
   191 			} else {
       
   192 				push @args, "uintptr($name)";
       
   193 			}
       
   194 		} elsif($type eq "int64" && $_32bit ne "") {
       
   195 			if(@args % 2 && $arm) {
       
   196 				# arm abi specifies 64-bit argument uses
       
   197 				# (even, odd) pair
       
   198 				push @args, "0"
       
   199 			}
       
   200 			if($_32bit eq "big-endian") {
       
   201 				push @args, "uintptr($name>>32)", "uintptr($name)";
       
   202 			} else {
       
   203 				push @args, "uintptr($name)", "uintptr($name>>32)";
       
   204 			}
       
   205 		} else {
       
   206 			push @args, "uintptr($name)";
       
   207 		}
       
   208 	}
       
   209 
       
   210 	# Determine which form to use; pad args with zeros.
       
   211 	my $asm = "Syscall";
       
   212 	if ($nonblock) {
       
   213 		if ($errvar eq "" && $ENV{'GOOS'} eq "linux") {
       
   214 			$asm = "RawSyscallNoError";
       
   215 		} else {
       
   216 			$asm = "RawSyscall";
       
   217 		}
       
   218 	} else {
       
   219 		if ($errvar eq "" && $ENV{'GOOS'} eq "linux") {
       
   220 			$asm = "SyscallNoError";
       
   221 		}
       
   222 	}
       
   223 	if(@args <= 3) {
       
   224 		while(@args < 3) {
       
   225 			push @args, "0";
       
   226 		}
       
   227 	} elsif(@args <= 6) {
       
   228 		$asm .= "6";
       
   229 		while(@args < 6) {
       
   230 			push @args, "0";
       
   231 		}
       
   232 	} elsif(@args <= 9) {
       
   233 		$asm .= "9";
       
   234 		while(@args < 9) {
       
   235 			push @args, "0";
       
   236 		}
       
   237 	} else {
       
   238 		print STDERR "$ARGV:$.: too many arguments to system call\n";
       
   239 	}
       
   240 
       
   241 	# System call number.
       
   242 	if($sysname eq "") {
       
   243 		$sysname = "SYS_$func";
       
   244 		$sysname =~ s/([a-z])([A-Z])/${1}_$2/g;	# turn FooBar into Foo_Bar
       
   245 		$sysname =~ y/a-z/A-Z/;
       
   246 	}
       
   247 
       
   248 	# Actual call.
       
   249 	my $args = join(', ', @args);
       
   250 	my $call = "$asm($sysname, $args)";
       
   251 
       
   252 	# Assign return values.
       
   253 	my $body = "";
       
   254 	my @ret = ("_", "_", "_");
       
   255 	my $do_errno = 0;
       
   256 	for(my $i=0; $i<@out; $i++) {
       
   257 		my $p = $out[$i];
       
   258 		my ($name, $type) = parseparam($p);
       
   259 		my $reg = "";
       
   260 		if($name eq "err" && !$plan9) {
       
   261 			$reg = "e1";
       
   262 			$ret[2] = $reg;
       
   263 			$do_errno = 1;
       
   264 		} elsif($name eq "err" && $plan9) {
       
   265 			$ret[0] = "r0";
       
   266 			$ret[2] = "e1";
       
   267 			next;
       
   268 		} else {
       
   269 			$reg = sprintf("r%d", $i);
       
   270 			$ret[$i] = $reg;
       
   271 		}
       
   272 		if($type eq "bool") {
       
   273 			$reg = "$reg != 0";
       
   274 		}
       
   275 		if($type eq "int64" && $_32bit ne "") {
       
   276 			# 64-bit number in r1:r0 or r0:r1.
       
   277 			if($i+2 > @out) {
       
   278 				print STDERR "$ARGV:$.: not enough registers for int64 return\n";
       
   279 			}
       
   280 			if($_32bit eq "big-endian") {
       
   281 				$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
       
   282 			} else {
       
   283 				$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
       
   284 			}
       
   285 			$ret[$i] = sprintf("r%d", $i);
       
   286 			$ret[$i+1] = sprintf("r%d", $i+1);
       
   287 		}
       
   288 		if($reg ne "e1" || $plan9) {
       
   289 			$body .= "\t$name = $type($reg)\n";
       
   290 		}
       
   291 	}
       
   292 	if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
       
   293 		$text .= "\t$call\n";
       
   294 	} else {
       
   295 		if ($errvar eq "" && $ENV{'GOOS'} eq "linux") {
       
   296 			# raw syscall without error on Linux, see golang.org/issue/22924
       
   297 			$text .= "\t$ret[0], $ret[1] := $call\n";
       
   298 		} else {
       
   299 			$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
       
   300 		}
       
   301 	}
       
   302 	$text .= $body;
       
   303 
       
   304 	if ($plan9 && $ret[2] eq "e1") {
       
   305 		$text .= "\tif int32(r0) == -1 {\n";
       
   306 		$text .= "\t\terr = e1\n";
       
   307 		$text .= "\t}\n";
       
   308 	} elsif ($do_errno) {
       
   309 		$text .= "\tif e1 != 0 {\n";
       
   310 		$text .= "\t\terr = errnoErr(e1)\n";
       
   311 		$text .= "\t}\n";
       
   312 	}
       
   313 	$text .= "\treturn\n";
       
   314 	$text .= "}\n\n";
       
   315 }
       
   316 
       
   317 chomp $text;
       
   318 chomp $text;
       
   319 
       
   320 if($errors) {
       
   321 	exit 1;
       
   322 }
       
   323 
       
   324 print <<EOF;
       
   325 // $cmdline
       
   326 // Code generated by the command above; see README.md. DO NOT EDIT.
       
   327 
       
   328 // +build $tags
       
   329 
       
   330 package unix
       
   331 
       
   332 import (
       
   333 	"syscall"
       
   334 	"unsafe"
       
   335 )
       
   336 
       
   337 var _ syscall.Errno
       
   338 
       
   339 $text
       
   340 EOF
       
   341 exit 0;