mksyscall_aix_ppc64.pl 15 KB

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