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;