...
Run Format

Text file src/syscall/mksyscall_solaris.pl

Documentation: syscall

     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		my $syscalldot = "";
    82		$syscalldot = "syscall." if $package ne "syscall";
    83	
    84		# Line must be of the form
    85		#	func Open(path string, mode int, perm int) (fd int, err error)
    86		# Split into name, in params, out params.
    87		if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
    88			print STDERR "$ARGV:$.: malformed //sys declaration\n";
    89			$errors = 1;
    90			next;
    91		}
    92		my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
    93	
    94		# Split argument lists on comma.
    95		my @in = parseparamlist($in);
    96		my @out = parseparamlist($out);
    97	
    98		# So file name.
    99		if($modname eq "") {
   100			$modname = "libc";
   101		}
   102	
   103		# System call name.
   104		if($sysname eq "") {
   105			$sysname = "$func";
   106		}
   107	
   108		# System call pointer variable name.
   109		my $sysvarname = "libc_${sysname}";
   110	
   111		my $strconvfunc = "BytePtrFromString";
   112		my $strconvtype = "*byte";
   113	
   114		$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
   115	
   116		# Runtime import of function to allow cross-platform builds.
   117		$dynimports .= "//go:cgo_import_dynamic ${sysvarname} ${sysname} \"$modname.so\"\n";
   118		# Link symbol to proc address variable.
   119		$linknames .= "//go:linkname ${sysvarname} ${sysvarname}\n";
   120		# Library proc address variable.
   121		push @vars, $sysvarname;
   122	
   123		# Go function header.
   124		$out = join(', ', @out);
   125		if($out ne "") {
   126			$out = " ($out)";
   127		}
   128		if($text ne "") {
   129			$text .= "\n"
   130		}
   131		$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out;
   132	
   133		# Check if err return available
   134		my $errvar = "";
   135		foreach my $p (@out) {
   136			my ($name, $type) = parseparam($p);
   137			if($type eq "error") {
   138				$errvar = $name;
   139				last;
   140			}
   141		}
   142	
   143		# Prepare arguments to Syscall.
   144		my @args = ();
   145		my $n = 0;
   146		foreach my $p (@in) {
   147			my ($name, $type) = parseparam($p);
   148			if($type =~ /^\*/) {
   149				push @args, "uintptr(unsafe.Pointer($name))";
   150			} elsif($type eq "string" && $errvar ne "") {
   151				$text .= "\tvar _p$n $strconvtype\n";
   152				$text .= "\t_p$n, $errvar = $strconvfunc($name)\n";
   153				$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
   154				push @args, "uintptr(unsafe.Pointer(_p$n))";
   155				$n++;
   156			} elsif($type eq "string") {
   157				print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
   158				$text .= "\tvar _p$n $strconvtype\n";
   159				$text .= "\t_p$n, _ = $strconvfunc($name)\n";
   160				push @args, "uintptr(unsafe.Pointer(_p$n))";
   161				$n++;
   162			} elsif($type =~ /^\[\](.*)/) {
   163				# Convert slice into pointer, length.
   164				# Have to be careful not to take address of &a[0] if len == 0:
   165				# pass nil in that case.
   166				$text .= "\tvar _p$n *$1\n";
   167				$text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
   168				push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))";
   169				$n++;
   170			} elsif($type eq "int64" && $_32bit ne "") {
   171				if($_32bit eq "big-endian") {
   172					push @args, "uintptr($name >> 32)", "uintptr($name)";
   173				} else {
   174					push @args, "uintptr($name)", "uintptr($name >> 32)";
   175				}
   176			} elsif($type eq "bool") {
   177	 			$text .= "\tvar _p$n uint32\n";
   178				$text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
   179				push @args, "uintptr(_p$n)";
   180				$n++;
   181			} else {
   182				push @args, "uintptr($name)";
   183			}
   184		}
   185		my $nargs = @args;
   186	
   187		# Determine which form to use; pad args with zeros.
   188		my $asm = "${syscalldot}sysvicall6";
   189		if ($nonblock) {
   190			$asm = "${syscalldot}rawSysvicall6";
   191		}
   192		if(@args <= 6) {
   193			while(@args < 6) {
   194				push @args, "0";
   195			}
   196		} else {
   197			print STDERR "$ARGV:$.: too many arguments to system call\n";
   198		}
   199	
   200		# Actual call.
   201		my $args = join(', ', @args);
   202		my $call = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $args)";
   203	
   204		# Assign return values.
   205		my $body = "";
   206		my $failexpr = "";
   207		my @ret = ("_", "_", "_");
   208		my @pout= ();
   209		my $do_errno = 0;
   210		for(my $i=0; $i<@out; $i++) {
   211			my $p = $out[$i];
   212			my ($name, $type) = parseparam($p);
   213			my $reg = "";
   214			if($name eq "err") {
   215				$reg = "e1";
   216				$ret[2] = $reg;
   217				$do_errno = 1;
   218			} else {
   219				$reg = sprintf("r%d", $i);
   220				$ret[$i] = $reg;
   221			}
   222			if($type eq "bool") {
   223				$reg = "$reg != 0";
   224			}
   225			if($type eq "int64" && $_32bit ne "") {
   226				# 64-bit number in r1:r0 or r0:r1.
   227				if($i+2 > @out) {
   228					print STDERR "$ARGV:$.: not enough registers for int64 return\n";
   229				}
   230				if($_32bit eq "big-endian") {
   231					$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
   232				} else {
   233					$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
   234				}
   235				$ret[$i] = sprintf("r%d", $i);
   236				$ret[$i+1] = sprintf("r%d", $i+1);
   237			}
   238			if($reg ne "e1") {
   239				$body .= "\t$name = $type($reg)\n";
   240			}
   241		}
   242		if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
   243			$text .= "\t$call\n";
   244		} else {
   245			$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
   246		}
   247		$text .= $body;
   248	
   249		if ($do_errno) {
   250			$text .= "\tif e1 != 0 {\n";
   251			$text .= "\t\terr = errnoErr(e1)\n";
   252			$text .= "\t}\n";
   253		}
   254		$text .= "\treturn\n";
   255		$text .= "}\n";
   256	}
   257	
   258	if($errors) {
   259		exit 1;
   260	}
   261	
   262	print <<EOF;
   263	// $cmdline
   264	// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
   265	
   266	// +build $tags
   267	
   268	package $package
   269	
   270	import "unsafe"
   271	EOF
   272	
   273	print "import \"syscall\"\n" if $package ne "syscall";
   274	
   275	my $vardecls = "\t" . join(",\n\t", @vars);
   276	$vardecls .= " libcFunc";
   277	
   278	chomp($_=<<EOF);
   279	
   280	$dynimports
   281	$linknames
   282	type libcFunc uintptr
   283	
   284	var (
   285	$vardecls
   286	)
   287	
   288	$text
   289	EOF
   290	print $_;
   291	exit 0;

View as plain text