Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
7.3 KiB

  1. # Assembler.pm
  2. #
  3. # Copyright (c) 1996 Malcolm Beattie
  4. #
  5. # You may distribute under the terms of either the GNU General Public
  6. # License or the Artistic License, as specified in the README file.
  7. package B::Assembler;
  8. use Exporter;
  9. use B qw(ppname);
  10. use B::Asmdata qw(%insn_data @insn_name);
  11. use Config qw(%Config);
  12. require ByteLoader; # we just need its $VERSIOM
  13. @ISA = qw(Exporter);
  14. @EXPORT_OK = qw(assemble_fh newasm endasm assemble);
  15. $VERSION = 0.02;
  16. use strict;
  17. my %opnumber;
  18. my ($i, $opname);
  19. for ($i = 0; defined($opname = ppname($i)); $i++) {
  20. $opnumber{$opname} = $i;
  21. }
  22. my($linenum, $errors, $out); # global state, set up by newasm
  23. sub error {
  24. my $str = shift;
  25. warn "$linenum: $str\n";
  26. $errors++;
  27. }
  28. my $debug = 0;
  29. sub debug { $debug = shift }
  30. #
  31. # First define all the data conversion subs to which Asmdata will refer
  32. #
  33. sub B::Asmdata::PUT_U8 {
  34. my $arg = shift;
  35. my $c = uncstring($arg);
  36. if (defined($c)) {
  37. if (length($c) != 1) {
  38. error "argument for U8 is too long: $c";
  39. $c = substr($c, 0, 1);
  40. }
  41. } else {
  42. $c = chr($arg);
  43. }
  44. return $c;
  45. }
  46. sub B::Asmdata::PUT_U16 { pack("S", $_[0]) }
  47. sub B::Asmdata::PUT_U32 { pack("L", $_[0]) }
  48. sub B::Asmdata::PUT_I32 { pack("L", $_[0]) }
  49. sub B::Asmdata::PUT_NV { sprintf("%s\0", $_[0]) } # "%lf" looses precision and pack('d',...)
  50. # may not even be portable between compilers
  51. sub B::Asmdata::PUT_objindex { pack("L", $_[0]) } # could allow names here
  52. sub B::Asmdata::PUT_svindex { &B::Asmdata::PUT_objindex }
  53. sub B::Asmdata::PUT_opindex { &B::Asmdata::PUT_objindex }
  54. sub B::Asmdata::PUT_pvindex { &B::Asmdata::PUT_objindex }
  55. sub B::Asmdata::PUT_strconst {
  56. my $arg = shift;
  57. $arg = uncstring($arg);
  58. if (!defined($arg)) {
  59. error "bad string constant: $arg";
  60. return "";
  61. }
  62. if ($arg =~ s/\0//g) {
  63. error "string constant argument contains NUL: $arg";
  64. }
  65. return $arg . "\0";
  66. }
  67. sub B::Asmdata::PUT_pvcontents {
  68. my $arg = shift;
  69. error "extraneous argument: $arg" if defined $arg;
  70. return "";
  71. }
  72. sub B::Asmdata::PUT_PV {
  73. my $arg = shift;
  74. $arg = uncstring($arg);
  75. error "bad string argument: $arg" unless defined($arg);
  76. return pack("L", length($arg)) . $arg;
  77. }
  78. sub B::Asmdata::PUT_comment_t {
  79. my $arg = shift;
  80. $arg = uncstring($arg);
  81. error "bad string argument: $arg" unless defined($arg);
  82. if ($arg =~ s/\n//g) {
  83. error "comment argument contains linefeed: $arg";
  84. }
  85. return $arg . "\n";
  86. }
  87. sub B::Asmdata::PUT_double { sprintf("%s\0", $_[0]) } # see PUT_NV above
  88. sub B::Asmdata::PUT_none {
  89. my $arg = shift;
  90. error "extraneous argument: $arg" if defined $arg;
  91. return "";
  92. }
  93. sub B::Asmdata::PUT_op_tr_array {
  94. my $arg = shift;
  95. my @ary = split(/\s*,\s*/, $arg);
  96. if (@ary != 256) {
  97. error "wrong number of arguments to op_tr_array";
  98. @ary = (0) x 256;
  99. }
  100. return pack("S256", @ary);
  101. }
  102. # XXX Check this works
  103. sub B::Asmdata::PUT_IV64 {
  104. my $arg = shift;
  105. return pack("LL", $arg >> 32, $arg & 0xffffffff);
  106. }
  107. my %unesc = (n => "\n", r => "\r", t => "\t", a => "\a",
  108. b => "\b", f => "\f", v => "\013");
  109. sub uncstring {
  110. my $s = shift;
  111. $s =~ s/^"// and $s =~ s/"$// or return undef;
  112. $s =~ s/\\(\d\d\d|.)/length($1) == 3 ? chr(oct($1)) : ($unesc{$1}||$1)/eg;
  113. return $s;
  114. }
  115. sub strip_comments {
  116. my $stmt = shift;
  117. # Comments only allowed in instructions which don't take string arguments
  118. $stmt =~ s{
  119. (?sx) # Snazzy extended regexp coming up. Also, treat
  120. # string as a single line so .* eats \n characters.
  121. ^\s* # Ignore leading whitespace
  122. (
  123. [^"]* # A double quote '"' indicates a string argument. If we
  124. # find a double quote, the match fails and we strip nothing.
  125. )
  126. \s*\# # Any amount of whitespace plus the comment marker...
  127. .*$ # ...which carries on to end-of-string.
  128. }{$1}; # Keep only the instruction and optional argument.
  129. return $stmt;
  130. }
  131. # create the ByteCode header: magic, archname, ByteLoader $VERSION, ivsize,
  132. # ptrsize, byteorder
  133. # nvtype is irrelevant (floats are stored as strings)
  134. # byteorder is strconst not U32 because of varying size issues
  135. sub gen_header {
  136. my $header = "";
  137. $header .= B::Asmdata::PUT_U32(0x43424c50); # 'PLBC'
  138. $header .= B::Asmdata::PUT_strconst('"' . $Config{archname}. '"');
  139. $header .= B::Asmdata::PUT_strconst(qq["$ByteLoader::VERSION"]);
  140. $header .= B::Asmdata::PUT_U32($Config{ivsize});
  141. $header .= B::Asmdata::PUT_U32($Config{ptrsize});
  142. $header .= B::Asmdata::PUT_strconst(sprintf(qq["0x%s"], $Config{byteorder}));
  143. $header;
  144. }
  145. sub parse_statement {
  146. my $stmt = shift;
  147. my ($insn, $arg) = $stmt =~ m{
  148. (?sx)
  149. ^\s* # allow (but ignore) leading whitespace
  150. (.*?) # Instruction continues up until...
  151. (?: # ...an optional whitespace+argument group
  152. \s+ # first whitespace.
  153. (.*) # The argument is all the rest (newlines included).
  154. )?$ # anchor at end-of-line
  155. };
  156. if (defined($arg)) {
  157. if ($arg =~ s/^0x(?=[0-9a-fA-F]+$)//) {
  158. $arg = hex($arg);
  159. } elsif ($arg =~ s/^0(?=[0-7]+$)//) {
  160. $arg = oct($arg);
  161. } elsif ($arg =~ /^pp_/) {
  162. $arg =~ s/\s*$//; # strip trailing whitespace
  163. my $opnum = $opnumber{$arg};
  164. if (defined($opnum)) {
  165. $arg = $opnum;
  166. } else {
  167. error qq(No such op type "$arg");
  168. $arg = 0;
  169. }
  170. }
  171. }
  172. return ($insn, $arg);
  173. }
  174. sub assemble_insn {
  175. my ($insn, $arg) = @_;
  176. my $data = $insn_data{$insn};
  177. if (defined($data)) {
  178. my ($bytecode, $putsub) = @{$data}[0, 1];
  179. my $argcode = &$putsub($arg);
  180. return chr($bytecode).$argcode;
  181. } else {
  182. error qq(no such instruction "$insn");
  183. return "";
  184. }
  185. }
  186. sub assemble_fh {
  187. my ($fh, $out) = @_;
  188. my $line;
  189. my $asm = newasm($out);
  190. while ($line = <$fh>) {
  191. assemble($line);
  192. }
  193. endasm();
  194. }
  195. sub newasm {
  196. my($outsub) = @_;
  197. die "Invalid printing routine for B::Assembler\n" unless ref $outsub eq 'CODE';
  198. die <<EOD if ref $out;
  199. Can't have multiple byteassembly sessions at once!
  200. (perhaps you forgot an endasm()?)
  201. EOD
  202. $linenum = $errors = 0;
  203. $out = $outsub;
  204. $out->(gen_header());
  205. }
  206. sub endasm {
  207. if ($errors) {
  208. die "There were $errors assembly errors\n";
  209. }
  210. $linenum = $errors = $out = 0;
  211. }
  212. sub assemble {
  213. my($line) = @_;
  214. my ($insn, $arg);
  215. $linenum++;
  216. chomp $line;
  217. if ($debug) {
  218. my $quotedline = $line;
  219. $quotedline =~ s/\\/\\\\/g;
  220. $quotedline =~ s/"/\\"/g;
  221. $out->(assemble_insn("comment", qq("$quotedline")));
  222. }
  223. $line = strip_comments($line) or next;
  224. ($insn, $arg) = parse_statement($line);
  225. $out->(assemble_insn($insn, $arg));
  226. if ($debug) {
  227. $out->(assemble_insn("nop", undef));
  228. }
  229. }
  230. 1;
  231. __END__
  232. =head1 NAME
  233. B::Assembler - Assemble Perl bytecode
  234. =head1 SYNOPSIS
  235. use B::Assembler qw(newasm endasm assemble);
  236. newasm(\&printsub); # sets up for assembly
  237. assemble($buf); # assembles one line
  238. endasm(); # closes down
  239. use B::Assembler qw(assemble_fh);
  240. assemble_fh($fh, \&printsub); # assemble everything in $fh
  241. =head1 DESCRIPTION
  242. See F<ext/B/B/Assembler.pm>.
  243. =head1 AUTHORS
  244. Malcolm Beattie, C<[email protected]>
  245. Per-statement interface by Benjamin Stuhl, C<[email protected]>
  246. =cut