github.com/fletavendor/sys@v0.0.0-20181107165924-66b7b1311ac8/unix/mksyscall_aix_ppc64.pl (about)

     1  #!/usr/bin/env perl
     2  # Copyright 2018 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_aix.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  # This program will generate three files and handle both gc and gccgo implementation:
    21  #   - zsyscall_aix_ppc64.go: the common part of each implementation (error handler, pointer creation)
    22  #   - zsyscall_aix_ppc64_gc.go: gc part with //go_cgo_import_dynamic and a call to syscall6
    23  #   - zsyscall_aix_ppc64_gccgo.go: gccgo part with C function and conversion to C type.
    24  
    25  # The generated code looks like this
    26  #
    27  # zsyscall_aix_ppc64.go
    28  # func asyscall(...) (n int, err error) {
    29  #	  // Pointer Creation
    30  #	  r1, e1 := callasyscall(...)
    31  #	  // Type Conversion
    32  #	  // Error Handler
    33  #	  return
    34  # }
    35  #
    36  # zsyscall_aix_ppc64_gc.go
    37  # //go:cgo_import_dynamic libc_asyscall asyscall "libc.a/shr_64.o"
    38  # //go:linkname libc_asyscall libc_asyscall
    39  # var asyscall syscallFunc
    40  #
    41  # func callasyscall(...) (r1 uintptr, e1 Errno) {
    42  #	  r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_asyscall)), "nb_args", ... )
    43  #	  return
    44  # }
    45  #
    46  # zsyscall_aix_ppc64_ggcgo.go
    47  # /*
    48  #  int asyscall(...)
    49  #
    50  # */
    51  # import "C"
    52  #
    53  # func callasyscall(...) (r1 uintptr, e1 Errno) {
    54  #	  r1 = uintptr(C.asyscall(...))
    55  #	  e1 = syscall.GetErrno()
    56  #	  return
    57  # }
    58  
    59  
    60  
    61  use strict;
    62  
    63  my $cmdline = "mksyscall_aix_ppc64.pl " . join(' ', @ARGV);
    64  my $errors = 0;
    65  my $_32bit = "";
    66  my $tags = "";  # build tags
    67  my $aix = 0;
    68  my $solaris = 0;
    69  
    70  binmode STDOUT;
    71  
    72  if($ARGV[0] eq "-b32") {
    73  	$_32bit = "big-endian";
    74  	shift;
    75  } elsif($ARGV[0] eq "-l32") {
    76  	$_32bit = "little-endian";
    77  	shift;
    78  }
    79  if($ARGV[0] eq "-aix") {
    80  	$aix = 1;
    81  	shift;
    82  }
    83  if($ARGV[0] eq "-tags") {
    84  	shift;
    85  	$tags = $ARGV[0];
    86  	shift;
    87  }
    88  
    89  if($ARGV[0] =~ /^-/) {
    90  	print STDERR "usage: mksyscall_aix.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
    91  	exit 1;
    92  }
    93  
    94  sub parseparamlist($) {
    95  	my ($list) = @_;
    96  	$list =~ s/^\s*//;
    97  	$list =~ s/\s*$//;
    98  	if($list eq "") {
    99  		return ();
   100  	}
   101  	return split(/\s*,\s*/, $list);
   102  }
   103  
   104  sub parseparam($) {
   105  	my ($p) = @_;
   106  	if($p !~ /^(\S*) (\S*)$/) {
   107  		print STDERR "$ARGV:$.: malformed parameter: $p\n";
   108  		$errors = 1;
   109  		return ("xx", "int");
   110  	}
   111  	return ($1, $2);
   112  }
   113  
   114  my $package = "";
   115  # GCCGO
   116  my $textgccgo = "";
   117  my $c_extern = "/*\n#include <stdint.h>\n";
   118  # GC
   119  my $textgc = "";
   120  my $dynimports = "";
   121  my $linknames = "";
   122  my @vars = ();
   123  # COMMUN
   124  my $textcommon = "";
   125  
   126  while(<>) {
   127  	chomp;
   128  	s/\s+/ /g;
   129  	s/^\s+//;
   130  	s/\s+$//;
   131  	$package = $1 if !$package && /^package (\S+)$/;
   132  	my $nonblock = /^\/\/sysnb /;
   133  	next if !/^\/\/sys / && !$nonblock;
   134  
   135  	# Line must be of the form
   136  	# func Open(path string, mode int, perm int) (fd int, err error)
   137  	# Split into name, in params, out params.
   138  	if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
   139  		print STDERR "$ARGV:$.: malformed //sys declaration\n";
   140  		$errors = 1;
   141  		next;
   142  	}
   143  	my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
   144  
   145  	# Split argument lists on comma.
   146  	my @in = parseparamlist($in);
   147  	my @out = parseparamlist($out);
   148  
   149  	$in = join(', ', @in);
   150  	$out = join(', ', @out);
   151  
   152  	if($sysname eq "") {
   153  		$sysname = "$func";
   154  	}
   155  
   156  	my $onlyCommon = 0;
   157  	if ($func eq "readlen" || $func eq "writelen" || $func eq "FcntlInt" || $func eq "FcntlFlock") {
   158  		# This function call another syscall which is already implemented.
   159  		# Therefore, the gc and gccgo part must not be generated.
   160  		$onlyCommon = 1
   161  	}
   162  
   163  	# Try in vain to keep people from editing this file.
   164  	# The theory is that they jump into the middle of the file
   165  	# without reading the header.
   166  
   167  	$textcommon .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
   168  	if (!$onlyCommon) {
   169  		$textgccgo .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
   170  		$textgc .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
   171  	}
   172  
   173  
   174  	# Check if value return, err return available
   175  	my $errvar = "";
   176  	my $retvar = "";
   177  	my $rettype = "";
   178  	foreach my $p (@out) {
   179  		my ($name, $type) = parseparam($p);
   180  		if($type eq "error") {
   181  			$errvar = $name;
   182  		} else {
   183  			$retvar = $name;
   184  			$rettype = $type;
   185  		}
   186  	}
   187  
   188  
   189  	$sysname =~ s/([a-z])([A-Z])/${1}_$2/g;
   190  	$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
   191  
   192  	# GCCGO Prototype return type
   193  	my $C_rettype = "";
   194  	if($rettype eq "unsafe.Pointer") {
   195  		$C_rettype = "uintptr_t";
   196  	} elsif($rettype eq "uintptr") {
   197  		$C_rettype = "uintptr_t";
   198  	} elsif($rettype =~ /^_/) {
   199  		$C_rettype = "uintptr_t";
   200  	} elsif($rettype eq "int") {
   201  		$C_rettype = "int";
   202  	} elsif($rettype eq "int32") {
   203  		$C_rettype = "int";
   204  	} elsif($rettype eq "int64") {
   205  		$C_rettype = "long long";
   206  	} elsif($rettype eq "uint32") {
   207  		$C_rettype = "unsigned int";
   208  	} elsif($rettype eq "uint64") {
   209  		$C_rettype = "unsigned long long";
   210  	} else {
   211  		$C_rettype = "int";
   212  	}
   213  	if($sysname eq "exit") {
   214  		$C_rettype = "void";
   215  	}
   216  
   217  	# GCCGO Prototype arguments type
   218  	my @c_in = ();
   219  	foreach my $i (0 .. $#in) {
   220  		my ($name, $type) = parseparam($in[$i]);
   221  		if($type =~ /^\*/) {
   222  			push @c_in, "uintptr_t";
   223  			} elsif($type eq "string") {
   224  			push @c_in, "uintptr_t";
   225  		} elsif($type =~ /^\[\](.*)/) {
   226  			push @c_in, "uintptr_t", "size_t";
   227  		} elsif($type eq "unsafe.Pointer") {
   228  			push @c_in, "uintptr_t";
   229  		} elsif($type eq "uintptr") {
   230  			push @c_in, "uintptr_t";
   231  		} elsif($type =~ /^_/) {
   232  			push @c_in, "uintptr_t";
   233  		} elsif($type eq "int") {
   234  			if (($i == 0 || $i == 2) && $func eq "fcntl"){
   235  				# These fcntl arguments needs to be uintptr to be able to call FcntlInt and FcntlFlock
   236  				push @c_in, "uintptr_t";
   237  			} else {
   238  				push @c_in, "int";
   239  			}
   240  		} elsif($type eq "int32") {
   241  			push @c_in, "int";
   242  		} elsif($type eq "int64") {
   243  			push @c_in, "long long";
   244  		} elsif($type eq "uint32") {
   245  			push @c_in, "unsigned int";
   246  		} elsif($type eq "uint64") {
   247  			push @c_in, "unsigned long long";
   248  		} else {
   249  			push @c_in, "int";
   250  		}
   251  	}
   252  
   253  	if (!$onlyCommon){
   254  		# GCCGO Prototype Generation
   255  		# Imports of system calls from libc
   256  		$c_extern .= "$C_rettype $sysname";
   257  		my $c_in = join(', ', @c_in);
   258  		$c_extern .= "($c_in);\n";
   259  	}
   260  
   261  	# GC Library name
   262  	if($modname eq "") {
   263  		$modname = "libc.a/shr_64.o";
   264  	} else {
   265  		print STDERR "$func: only syscall using libc are available\n";
   266  		$errors = 1;
   267  		next;
   268  	}
   269  	my $sysvarname = "libc_${sysname}";
   270  
   271  	if (!$onlyCommon){
   272  		# GC Runtime import of function to allow cross-platform builds.
   273  		$dynimports .= "//go:cgo_import_dynamic ${sysvarname} ${sysname} \"$modname\"\n";
   274  		# GC Link symbol to proc address variable.
   275  		$linknames .= "//go:linkname ${sysvarname} ${sysvarname}\n";
   276  		# GC Library proc address variable.
   277  		push @vars, $sysvarname;
   278  	}
   279  
   280  	my $strconvfunc ="BytePtrFromString";
   281  	my $strconvtype = "*byte";
   282  
   283  	# Go function header.
   284  	if($out ne "") {
   285  		$out = " ($out)";
   286  	}
   287  	if($textcommon ne "") {
   288  		$textcommon .= "\n"
   289  	}
   290  
   291  	$textcommon .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out ;
   292  
   293  	# Prepare arguments to call.
   294  	my @argscommun = (); # Arguments in the commun part
   295  	my @argscall = ();   # Arguments for call prototype
   296  	my @argsgc = ();     # Arguments for gc call (with syscall6)
   297  	my @argsgccgo = ();  # Arguments for gccgo call (with C.name_of_syscall)
   298  	my $n = 0;
   299  	my $arg_n = 0;
   300  	foreach my $p (@in) {
   301  		my ($name, $type) = parseparam($p);
   302  		if($type =~ /^\*/) {
   303  			push @argscommun, "uintptr(unsafe.Pointer($name))";
   304  			push @argscall, "$name uintptr";
   305  			push @argsgc, "$name";
   306  			push @argsgccgo, "C.uintptr_t($name)";
   307  		} elsif($type eq "string" && $errvar ne "") {
   308  			$textcommon .= "\tvar _p$n $strconvtype\n";
   309  			$textcommon .= "\t_p$n, $errvar = $strconvfunc($name)\n";
   310  			$textcommon .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
   311  
   312  			push @argscommun, "uintptr(unsafe.Pointer(_p$n))";
   313  			push @argscall, "_p$n uintptr ";
   314  			push @argsgc, "_p$n";
   315  			push @argsgccgo, "C.uintptr_t(_p$n)";
   316  			$n++;
   317  		} elsif($type eq "string") {
   318  			print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
   319  			$textcommon .= "\tvar _p$n $strconvtype\n";
   320  			$textcommon .= "\t_p$n, $errvar = $strconvfunc($name)\n";
   321  			$textcommon .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
   322  
   323  			push @argscommun, "uintptr(unsafe.Pointer(_p$n))";
   324  			push @argscall, "_p$n uintptr";
   325  			push @argsgc, "_p$n";
   326  			push @argsgccgo, "C.uintptr_t(_p$n)";
   327  			$n++;
   328  		} elsif($type =~ /^\[\](.*)/) {
   329  			# Convert slice into pointer, length.
   330  			# Have to be careful not to take address of &a[0] if len == 0:
   331  			# pass nil in that case.
   332  			$textcommon .= "\tvar _p$n *$1\n";
   333  			$textcommon .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
   334  			push @argscommun, "uintptr(unsafe.Pointer(_p$n))", "len($name)";
   335  			push @argscall, "_p$n uintptr", "_lenp$n int";
   336  			push @argsgc, "_p$n", "uintptr(_lenp$n)";
   337  			push @argsgccgo, "C.uintptr_t(_p$n)", "C.size_t(_lenp$n)";
   338  			$n++;
   339  		} elsif($type eq "int64" && $_32bit ne "") {
   340  			print STDERR "$ARGV:$.: $func uses int64 with 32 bits mode. Case not yet implemented\n";
   341  			# if($_32bit eq "big-endian") {
   342  			# 	push @args, "uintptr($name >> 32)", "uintptr($name)";
   343  			# } else {
   344  			# 	push @args, "uintptr($name)", "uintptr($name >> 32)";
   345  			# }
   346  			# $n++;
   347  		} elsif($type eq "bool") {
   348  			print STDERR "$ARGV:$.: $func uses bool. Case not yet implemented\n";
   349  			# $text .= "\tvar _p$n uint32\n";
   350  			# $text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
   351  			# push @args, "_p$n";
   352  			# $n++;
   353  		} elsif($type =~ /^_/ ||$type eq "unsafe.Pointer") {
   354  			push @argscommun, "uintptr($name)";
   355  			push @argscall, "$name uintptr";
   356  			push @argsgc, "$name";
   357  			push @argsgccgo, "C.uintptr_t($name)";
   358  		} elsif($type eq "int") {
   359  			if (($arg_n == 0 || $arg_n == 2) && ($func eq "fcntl" || $func eq "FcntlInt" || $func eq "FcntlFlock")) {
   360  				# These fcntl arguments need to be uintptr to be able to call FcntlInt and FcntlFlock
   361  				push @argscommun, "uintptr($name)";
   362  				push @argscall, "$name uintptr";
   363  				push @argsgc, "$name";
   364  				push @argsgccgo, "C.uintptr_t($name)";
   365  			} else {
   366  				push @argscommun, "$name";
   367  				push @argscall, "$name int";
   368  				push @argsgc, "uintptr($name)";
   369  				push @argsgccgo, "C.int($name)";
   370  			}
   371  		} elsif($type eq "int32") {
   372  			push @argscommun, "$name";
   373  			push @argscall, "$name int32";
   374  			push @argsgc, "uintptr($name)";
   375  			push @argsgccgo, "C.int($name)";
   376  		} elsif($type eq "int64") {
   377  			push @argscommun, "$name";
   378  			push @argscall, "$name int64";
   379  			push @argsgc, "uintptr($name)";
   380  			push @argsgccgo, "C.longlong($name)";
   381  		} elsif($type eq "uint32") {
   382  			push @argscommun, "$name";
   383  			push @argscall, "$name uint32";
   384  			push @argsgc, "uintptr($name)";
   385  			push @argsgccgo, "C.uint($name)";
   386  		} elsif($type eq "uint64") {
   387  			push @argscommun, "$name";
   388  			push @argscall, "$name uint64";
   389  			push @argsgc, "uintptr($name)";
   390  			push @argsgccgo, "C.ulonglong($name)";
   391  		} elsif($type eq "uintptr") {
   392  			push @argscommun, "$name";
   393  			push @argscall, "$name uintptr";
   394  			push @argsgc, "$name";
   395  			push @argsgccgo, "C.uintptr_t($name)";
   396  		} else {
   397  			push @argscommun, "int($name)";
   398  			push @argscall, "$name int";
   399  			push @argsgc, "uintptr($name)";
   400  			push @argsgccgo, "C.int($name)";
   401  		}
   402  		$arg_n++;
   403  	}
   404  	my $nargs = @argsgc;
   405  
   406  	# COMMUN function generation
   407  	my $argscommun = join(', ', @argscommun);
   408  	my $callcommun = "call$sysname($argscommun)";
   409  	my @ret = ("_", "_");
   410  	my $body = "";
   411  	my $do_errno = 0;
   412  	for(my $i=0; $i<@out; $i++) {
   413  		my $p = $out[$i];
   414  		my ($name, $type) = parseparam($p);
   415  		my $reg = "";
   416  		if($name eq "err") {
   417  			$reg = "e1";
   418  			$ret[1] = $reg;
   419  			$do_errno = 1;
   420  		} else {
   421  			$reg = "r0";
   422  			$ret[0] = $reg;
   423  		}
   424  		if($type eq "bool") {
   425  			$reg = "$reg != 0";
   426  		}
   427  		if($reg ne "e1") {
   428  			$body .= "\t$name = $type($reg)\n";
   429  		}
   430  	}
   431  	if ($ret[0] eq "_"  && $ret[1] eq "_") {
   432  		$textcommon .= "\t$callcommun\n";
   433  	} else {
   434  		$textcommon .= "\t$ret[0], $ret[1] := $callcommun\n";
   435  	}
   436  	$textcommon .= $body;
   437  
   438  	if ($do_errno) {
   439  		$textcommon .= "\tif e1 != 0 {\n";
   440  		$textcommon .= "\t\terr = errnoErr(e1)\n";
   441  		$textcommon .= "\t}\n";
   442  	}
   443  	$textcommon .= "\treturn\n";
   444  	$textcommon .= "}\n";
   445  
   446  	if ($onlyCommon){
   447  		next
   448  	}
   449  	# CALL Prototype
   450  	my $callProto = sprintf "func call%s(%s) (r1 uintptr, e1 Errno) {\n", $sysname, join(', ', @argscall);
   451  
   452  	# GC function generation
   453  	my $asm = "syscall6";
   454  	if ($nonblock) {
   455  		$asm = "rawSyscall6";
   456  	}
   457  
   458  	if(@argsgc <= 6) {
   459  		while(@argsgc < 6) {
   460  			push @argsgc, "0";
   461  		}
   462  	} else {
   463  		print STDERR "$ARGV:$.: too many arguments to system call\n";
   464  	}
   465  	my $argsgc = join(', ', @argsgc);
   466  	my $callgc = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $argsgc)";
   467  
   468  	$textgc .= $callProto;
   469  	$textgc .= "\tr1, _, e1 = $callgc\n";
   470  	$textgc .= "\treturn\n}\n";
   471  
   472  	# GCCGO function generation
   473  	my $argsgccgo = join(', ', @argsgccgo);
   474  	my $callgccgo = "C.$sysname($argsgccgo)";
   475  	$textgccgo .= $callProto;
   476  	$textgccgo .= "\tr1 = uintptr($callgccgo)\n";
   477  	$textgccgo .= "\te1 = syscall.GetErrno()\n";
   478  	$textgccgo .= "\treturn\n}\n";
   479  }
   480  
   481  if($errors) {
   482  	exit 1;
   483  }
   484  
   485  # Print zsyscall_aix_ppc64.go
   486  open(my $fcommun, '>', 'zsyscall_aix_ppc64.go');
   487  my $tofcommun = <<EOF;
   488  // $cmdline
   489  // Code generated by the command above; see README.md. DO NOT EDIT.
   490  
   491  // +build $tags
   492  
   493  package $package
   494  
   495  import (
   496  	"unsafe"
   497  )
   498  
   499  EOF
   500  
   501  $tofcommun .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
   502  
   503  $tofcommun .=<<EOF;
   504  
   505  $textcommon
   506  EOF
   507  print $fcommun $tofcommun;
   508  
   509  
   510  # Print zsyscall_aix_ppc64_gc.go
   511  open(my $fgc, '>', 'zsyscall_aix_ppc64_gc.go');
   512  my $tofgc = <<EOF;
   513  // $cmdline
   514  // Code generated by the command above; see README.md. DO NOT EDIT.
   515  
   516  // +build $tags
   517  // +build !gccgo
   518  
   519  package $package
   520  
   521  import (
   522  	"unsafe"
   523  )
   524  
   525  
   526  EOF
   527  
   528  $tofgc .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
   529  
   530  my $vardecls = "\t" . join(",\n\t", @vars);
   531  $vardecls .= " syscallFunc";
   532  
   533  $tofgc .=<<EOF;
   534  $dynimports
   535  $linknames
   536  type syscallFunc uintptr
   537  
   538  var (
   539  $vardecls
   540  )
   541  
   542  // Implemented in runtime/syscall_aix.go.
   543  func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
   544  func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
   545  
   546  $textgc
   547  EOF
   548  print $fgc $tofgc;
   549  
   550  # Print zsyscall_aix_ppc64_gc.go
   551  open(my $fgccgo, '>', 'zsyscall_aix_ppc64_gccgo.go');
   552  my $tofgccgo = <<EOF;
   553  // $cmdline
   554  // Code generated by the command above; see README.md. DO NOT EDIT.
   555  
   556  // +build $tags
   557  // +build gccgo
   558  
   559  package $package
   560  
   561  
   562  $c_extern
   563  */
   564  import "C"
   565  import (
   566  	"syscall"
   567  )
   568  
   569  
   570  EOF
   571  
   572  $tofgccgo .= "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
   573  
   574  $tofgccgo .=<<EOF;
   575  
   576  $textgccgo
   577  EOF
   578  print $fgccgo $tofgccgo;
   579  exit 0;