File Coverage

blib/lib/Nasm/X86.pm
Criterion Covered Total %
statement 99 911 10.8
branch 6 190 3.1
condition 0 8 0.0
subroutine 12 331 3.6
pod 59 303 19.4
total 176 1743 10.1


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib/ -I. -I/home/phil/perl/cpan/AsmC/lib/
2             #-------------------------------------------------------------------------------
3             # Generate Nasm X86 code from Perl.
4             # Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2021
5             #-------------------------------------------------------------------------------
6             # podDocumentation
7             # Indent opcodes by call depth, - replace push @text with a method call
8             package Nasm::X86;
9             our $VERSION = "202104013";
10 1     1   754 use warnings FATAL => qw(all);
  1         6  
  1         30  
11 1     1   4 use strict;
  1         2  
  1         29  
12 1     1   4 use Carp qw(confess cluck);
  1         2  
  1         96  
13 1     1   449 use Data::Dump qw(dump);
  1         6603  
  1         100  
14 1     1   3482 use Data::Table::Text qw(:all);
  1         124912  
  1         1583  
15 1     1   792 use Asm::C qw(:all);
  1         3574  
  1         232  
16 1     1   11 use feature qw(say current_sub);
  1         1  
  1         956  
17              
18             my $debug = -e q(/home/phil/); # Developing
19             my $sde = q(/var/isde/sde64); # Intel emulator
20             $sde = q(sde/sde64) unless $debug;
21              
22             binModeAllUtf8;
23              
24             my %rodata; # Read only data already written
25             my %rodatas; # Read only string already written
26             my %subroutines; # Subroutines generated
27             my @rodata; # Read only data
28             my @data; # Data
29             my @bss; # Block started by symbol
30             my @text; # Code
31              
32             my $sysout = 1; # File descriptor for output
33              
34             BEGIN{
35 1     1   5 my %r = ( map {$_=>'8'} qw(al bl cl dl r8b r9b r10b r11b r12b r13b r14b r15b sil dil spl bpl ah bh ch dh));
  20         41  
36 1         5 %r = (%r, map {$_=>'s'} qw(cs ds es fs gs ss));
  6         25  
37 1         6 %r = (%r, map {$_=>'16'} qw(ax bx cx dx r8w r9w r10w r11w r12w r13w r14w r15w si di sp bp));
  16         35  
38 1         5 %r = (%r, map {$_=>'32a'} qw(eax ebx ecx edx esi edi esp ebp));
  8         21  
39 1         7 %r = (%r, map {$_=>'32b'} qw(r8d r8l r9d r9l r10d r10l r11d r11l r12d r12l r13d r13l r14d r14l r15d r15l));
  16         50  
40 1         10 %r = (%r, map {$_=>'f'} qw(st0 st1 st2 st3 st4 st5 st6 st7));
  8         26  
41 1         9 %r = (%r, map {$_=>'64'} qw(rax rbx rcx rdx r8 r9 r10 r11 r12 r13 r14 r15 rsi rdi rsp rbp rip rflags));
  18         48  
42 1         12 %r = (%r, map {$_=>'64m'} qw(mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7));
  8         27  
43 1         11 %r = (%r, map {$_=>'128'} qw(xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31));
  32         68  
44 1         15 %r = (%r, map {$_=>'256'} qw(ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31));
  32         79  
45 1         18 %r = (%r, map {$_=>'512'} qw(zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31));
  32         76  
46 1         21 %r = (%r, map {$_=>'m'} qw(k0 k1 k2 k3 k4 k5 k6 k7));
  8         57  
47              
48 1         9 my @i0 = qw(pushfq rdtsc ret syscall); # Zero operand instructions
49 1         3 my @i1 = qw(call inc jge jmp jz pop push); # Single operand instructions
50 1         16 my @i2 = split /\s+/, <
51             add and cmp or lea mov shl shr sub test Vmovdqu8 vmovdqu32 vmovdqu64 vpxorq xor
52             END
53 1         3 my @i3 = split /\s+/, <
54             vprolq
55             END
56              
57 1         76 for my $r(sort keys %r)
58 204     0 0 7124 {eval "sub $r\{q($r)\}";
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0            
59 204 50       665 confess $@ if $@;
60             }
61              
62 1         33 my %v = map {$_=>1} values %r;
  204         246  
63 1         19 for my $v(sort keys %v) # Types of register
64 12         907 {my @r = grep {$r{$_} eq $v} sort keys %r;
  2448         3049  
65 12     0 0 114 eval "sub registers_$v\{".dump(\@r)."}";
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0     0 0    
  0            
66 12 50       13146 confess $@ if $@;
67             }
68              
69 1         5 if (1) # Instructions that take zero operands
70 1         3 {my $s = '';
71 1         3 for my $i(@i0)
72 4         9 {my $I = ucfirst $i;
73 4         9 $s .= <
74             sub $I()
75             {\@_ == 0 or confess "No arguments allowed";
76             push \@text, qq( $i\\n);
77             }
78             END
79             }
80 1 0   0 0 217 eval $s;
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0            
  0            
  0            
  0            
  0            
81 1 50       20 confess $@ if $@;
82             }
83              
84 1         2 if (1) # Instructions that take one operand
85 1         2 {my $s = '';
86 1         2 for my $i(@i1)
87 7         11 {my $I = ucfirst $i;
88 7         15 $s .= <
89             sub $I(\$)
90             {my (\$target) = \@_;
91             \@_ == 1 or confess "One argument required";
92             push \@text, qq( $i \$target\\n);
93             }
94             END
95             }
96 1 0   0 0 442 eval $s;
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
97 1 50       7 confess $@ if $@;
98             }
99              
100 1         3 if (1) # Instructions that take two operands
101 1         3 {my $s = '';
102 1         3 for my $i(@i2)
103 15         19 {my $I = ucfirst $i;
104 15         29 $s .= <
105             sub $I(\$\$)
106             {my (\$target, \$source) = \@_;
107             \@_ == 2 or confess "Two arguments required";
108             push \@text, qq( $i \$target, \$source\\n);
109             }
110             END
111             }
112 1 0   0 0 943 eval $s;
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0 0   0 0    
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
113 1 50       9 confess $@ if $@;
114             }
115              
116 1         2 if (1) # Instructions that take three operands
117 1         3 {my $s = '';
118 1         2 for my $i(@i3)
119 1         3 {my $I = ucfirst $i;
120 1         4 $s .= <
121             sub $I(\$\$\$)
122             {my (\$target, \$source, \$bits) = \@_;
123             \@_ == 3 or confess "Three arguments required";
124             push \@text, qq( $i \$target, \$source, \$bits\\n);
125             }
126             END
127             }
128 1 0   0 0 82 eval $s;
  0            
  0            
  0            
129 1 50       6420 confess $@ if $@;
130             }
131             }
132              
133             sub ClearRegisters(@); # Clear registers by setting them to zero
134             sub PrintOutRegisterInHex($); # Print any register as a hex string
135             sub Syscall(); # System call in linux 64 format per: https://filippo.io/linux-syscall-table/
136              
137             #D1 Generate Network Assembler Code # Generate assembler code that can be assembled with Nasm
138              
139             my $labels = 0;
140             sub label #P Create a unique label
141 0     0 1   {"l".++$labels; # Generate a label
142             }
143              
144             sub SetLabel($) # Set a label in the code section
145 0     0 1   {my ($l) = @_; # Label
146 0           push @text, <
147             $l:
148             END
149             }
150              
151             sub Start() # Initialize the assembler
152 0     0 1   {@bss = @data = @rodata = %rodata = %rodatas = %subroutines = @text = ();
153 0           $labels = 0;
154             }
155              
156             sub Ds(@) # Layout bytes in memory and return their label
157 0     0 1   {my (@d) = @_; # Data to be laid out
158 0           my $d = join '', @_;
159 0           $d =~ s(') (\')gs;
160 0           my $l = label;
161 0           push @data, <
162             $l: db '$d';
163             END
164 0           $l # Return label
165             }
166              
167             sub Rs(@) # Layout bytes in read only memory and return their label
168 0     0 1   {my (@d) = @_; # Data to be laid out
169 0           my $d = join '', @_;
170 0           $d =~ s(') (\')gs;
171 0 0         return $_ if $_ = $rodatas{$d}; # Data already exists so return it
172 0           my $l = label;
173 0           $rodatas{$d} = $l; # Record label
174 0           push @rodata, <
175             $l: db '$d',0;
176             END
177 0           $l # Return label
178             }
179              
180             sub Dbwdq($@) # Layout data
181 0     0 1   {my ($s, @d) = @_; # Element size, data to be laid out
182 0           my $d = join ', ', @d;
183 0           my $l = label;
184 0           push @data, <
185             $l: d$s $d
186             END
187 0           $l # Return label
188             }
189              
190             sub Db(@) # Layout bytes in the data segment and return their label
191 0     0 1   {my (@bytes) = @_; # Bytes to layout
192 0           Dbwdq 'b', @_;
193             }
194             sub Dw(@) # Layout words in the data segment and return their label
195 0     0 1   {my (@words) = @_; # Words to layout
196 0           Dbwdq 'w', @_;
197             }
198             sub Dd(@) # Layout double words in the data segment and return their label
199 0     0 1   {my (@dwords) = @_; # Double words to layout
200 0           Dbwdq 'd', @_;
201             }
202             sub Dq(@) # Layout quad words in the data segment and return their label
203 0     0 1   {my (@qwords) = @_; # Quad words to layout
204 0           Dbwdq 'q', @_;
205             }
206              
207             sub Rbwdq($@) # Layout data
208 0     0 1   {my ($s, @d) = @_; # Element size, data to be laid out
209 0           my $d = join ', ', @d; # Data to be laid out
210 0 0         return $_ if $_ = $rodata{$d}; # Data already exists so return it
211 0           my $l = label; # New data - create a label
212 0           push @rodata, <
213             $l: d$s $d
214             END
215 0           $rodata{$d} = $l; # Record label
216 0           $l # Return label
217             }
218              
219             sub Rb(@) # Layout bytes in the data segment and return their label
220 0     0 1   {my (@bytes) = @_; # Bytes to layout
221 0           Rbwdq 'b', @_;
222             }
223             sub Rw(@) # Layout words in the data segment and return their label
224 0     0 1   {my (@words) = @_; # Words to layout
225 0           Rbwdq 'w', @_;
226             }
227             sub Rd(@) # Layout double words in the data segment and return their label
228 0     0 1   {my (@dwords) = @_; # Double words to layout
229 0           Rbwdq 'd', @_;
230             }
231             sub Rq(@) # Layout quad words in the data segment and return their label
232 0     0 1   {my (@qwords) = @_; # Quad words to layout
233 0           Rbwdq 'q', @_;
234             }
235              
236             sub Comment(@) # Insert a comment into the assembly code
237 0     0 1   {my (@comment) = @_; # Text of comment
238 0           my $c = join "", @comment;
239 0           push @text, <
240             ; $c
241             END
242             }
243              
244             sub Exit(;$) # Exit with the specified return code or zero if no return code supplied
245 0     0 1   {my ($c) = @_; # Return code
246 0 0 0       if (@_ == 0 or $c == 0)
    0          
247 0           {Comment "Exit code: 0";
248 0           ClearRegisters rdi;
249             }
250             elsif (@_ == 1)
251 0           {Comment "Exit code: $c";
252 0           Mov rdi, $c;
253             }
254 0           Mov rax, 60;
255 0           Syscall;
256             }
257              
258             sub SaveFirstFour() # Save the first 4 parameter registers
259 0     0 1   {Push rax;
260 0           Push rdi;
261 0           Push rsi;
262 0           Push rdx;
263 0           4 * ®isterSize(rax); # Space occupied by push
264             }
265              
266             sub RestoreFirstFour() # Restore the first 4 parameter registers
267 0     0 1   {Pop rdx;
268 0           Pop rsi;
269 0           Pop rdi;
270 0           Pop rax;
271             }
272              
273             sub RestoreFirstFourExceptRax() # Restore the first 4 parameter registers except rax so it can return its value
274 0     0 1   {Pop rdx;
275 0           Pop rsi;
276 0           Pop rdi;
277 0           Add rsp, 8;
278             }
279              
280             sub SaveFirstSeven() # Save the first 7 parameter registers
281 0     0 1   {Push rax;
282 0           Push rdi;
283 0           Push rsi;
284 0           Push rdx;
285 0           Push r10;
286 0           Push r8;
287 0           Push r9;
288 0           7 * registerSize(rax); # Space occupied by push
289             }
290              
291             sub RestoreFirstSeven() # Restore the first 7 parameter registers
292 0     0 1   {Pop r9;
293 0           Pop r8;
294 0           Pop r10;
295 0           Pop rdx;
296 0           Pop rsi;
297 0           Pop rdi;
298 0           Pop rax;
299             }
300              
301             sub RestoreFirstSevenExceptRax() # Restore the first 7 parameter registers except rax which is being used to return the result
302 0     0 1   {Pop r9;
303 0           Pop r8;
304 0           Pop r10;
305 0           Pop rdx;
306 0           Pop rsi;
307 0           Pop rdi;
308 0           Add rsp, registerSize(rax); # Skip rax
309             }
310              
311             sub RestoreFirstSevenExceptRaxAndRdi() # Restore the first 7 parameter registers except rax and rdi which are being used to return the results
312 0     0 1   {Pop r9;
313 0           Pop r8;
314 0           Pop r10;
315 0           Pop rdx;
316 0           Pop rsi;
317 0           Add rsp, 2*registerSize(rax); # Skip rdi and rax
318             }
319              
320             sub If(&;&) # If
321 0     0 1   {my ($then, $else) = @_; # Then - required , else - optional
322 0 0         @_ >= 1 or confess;
323 0 0         if (@_ == 1) # No else
324 0           {Comment "if then";
325 0           my $end = label;
326 0           Jz $end;
327 0           &$then;
328 0           SetLabel $end;
329             }
330             else # With else
331 0           {Comment "if then else";
332 0           my $endIf = label;
333 0           my $startElse = label;
334 0           Jz $startElse;
335 0           &$then;
336 0           Jmp $endIf;
337 0           SetLabel $startElse;
338 0           &$else;
339 0           SetLabel $endIf;
340             }
341             }
342              
343             sub For(&$$$) # For
344 0     0 1   {my ($body, $register, $limit, $increment) = @_; # Body, register, limit on loop, increment
345 0 0         @_ == 4 or confess;
346 0           Comment "For $register $limit";
347 0           my $start = label;
348 0           my $end = label;
349 0           SetLabel $start;
350 0           Cmp $register, $limit;
351 0           Jge $end;
352              
353 0           &$body;
354              
355 0 0         if ($increment == 1)
356 0           {Inc $register;
357             }
358             else
359 0           {Add $register, $increment;
360             }
361 0           Jmp $start;
362 0           SetLabel $end;
363             }
364              
365             sub S(&%) # Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub
366 0     0 1   {my ($body, %options) = @_; # Body, options.
367 0 0         @_ >= 1 or confess;
368 0           my $name = $options{name}; # Optional name for subroutine reuse
369 0           my $comment = $options{comment}; # Optional comment
370 0   0       Comment "Subroutine " .($comment//'');
371              
372 0 0 0       if ($name and my $n = $subroutines{$name}) {return $n} # Return the label of a pre-existing copy of the code
  0            
373              
374 0           my $start = label;
375 0           my $end = label;
376 0           Jmp $end;
377 0           SetLabel $start;
378 0           &$body;
379 0           Ret;
380 0           SetLabel $end;
381 0 0         $subroutines{$name} = $start if $name; # Cache a reference to the generated code if a name was supplied
382              
383 0           $start
384             }
385              
386             sub registerSize($) # Return the size of a register
387 0     0 1   {my ($r) = @_; # Register
388 0 0         return 16 if $r =~ m(\Ax);
389 0 0         return 32 if $r =~ m(\Ay);
390 0 0         return 64 if $r =~ m(\Az);
391 0           8
392             }
393              
394             sub PushR(@) # Push registers onto the stack
395 0     0 1   {my (@r) = @_; # Register
396 0           for my $r(@r)
397 0           {my $size = registerSize $r;
398 0 0         if ($size > 8)
399 0           {Sub rsp, $size;
400 0           Vmovdqu32 "[rsp]", $r;
401             }
402             else
403 0           {Push $r;
404             }
405             }
406             }
407              
408             sub PopR(@) # Pop registers from the stack
409 0     0 1   {my (@r) = @_; # Register
410 0           for my $r(reverse @r) # Pop registers in reverse order
411 0           {my $size = registerSize $r;
412 0 0         if ($size > 8)
413 0           {Vmovdqu32 $r, "[rsp]";
414 0           Add(rsp, $size);
415             }
416             else
417 0           {Pop $r;
418             }
419             }
420             }
421              
422             sub PeekR($) # Peek at register on stack
423 0     0 1   {my ($r) = @_; # Register
424 0           my $size = registerSize $r;
425 0 0         if ($size > 8) # x|y|zmm*
426 0           {Vmovdqu32 $r, "[rsp]";
427             }
428             else # 8 byte register
429 0           {Mov $r, "[rsp]";
430             }
431             }
432              
433             sub PrintOutNl() # Write a new line
434 0 0   0 1   {@_ == 0 or confess;
435 0           my $a = Rb(10);
436 0           Comment "Write new line";
437 0           SaveFirstFour;
438 0           Mov rax, 1;
439 0           Mov rdi, 1;
440 0           Mov rsi, $a;
441 0           Mov rdx, 1;
442 0           Syscall;
443 0           RestoreFirstFour()
444             }
445              
446             sub PrintOutString($) # Write a constant string to sysout.
447 0     0 1   {my ($string) = @_; # String
448 0 0         @_ == 1 or confess;
449              
450 0           SaveFirstFour;
451 0           Comment "Write String: $string";
452 0           my ($c) = @_;
453 0           my $l = length($c);
454 0           my $a = Rs($c);
455 0           Mov rax, 1;
456 0           Mov rdi, $sysout;
457 0           Mov rsi, $a;
458 0           Mov rdx, $l;
459 0           Syscall;
460 0           RestoreFirstFour();
461             }
462              
463             sub PrintOutMemory # Print the memory addressed by rax for a length of rdi
464 0 0   0 1   {@_ == 0 or confess;
465 0           Comment "Print memory";
466 0           SaveFirstFour;
467 0           Mov rsi, rax;
468 0           Mov rdx, rdi;
469 0           Mov rax, 1;
470 0           Mov rdi, $sysout;
471 0           Syscall;
472 0           RestoreFirstFour();
473             }
474              
475             sub PrintOutRaxInHex # Write the content of register rax to stderr in hexadecimal in big endian notation
476 0 0   0 1   {@_ == 0 or confess;
477 0           Comment "Print Rax In Hex";
478              
479             my $hexTranslateTable = sub
480 0     0     {my $h = '0123456789ABCDEF';
481 0           my @t;
482 0           for my $i(split //, $h)
483 0           {for my $j(split //, $h)
484 0           {push @t, "$i$j";
485             }
486             }
487             Rs @t # Constant strings are only saved if they are unique, else a read only copy is returned.
488 0           }->();
  0            
489              
490             my $sub = S # Address conversion routine
491 0     0     {SaveFirstFour;
492 0           Mov rdx, rax; # Content to be printed
493 0           Mov rdi, 2; # Length of a byte in hex
494 0           for my $i(0..7)
495 0           {my $s = 8*$i;
496 0           Mov rax,rdx;
497 0           Shl rax,$s; # Push selected byte high
498 0           Shr rax,56; # Push select byte low
499 0           Shl rax,1; # Multiply by two because each entry in the translation table is two bytes long
500 0           Lea rax, "[$hexTranslateTable+rax]";
501 0           PrintOutMemory;
502 0 0         PrintOutString ' ' if $i % 2;
503             }
504 0           RestoreFirstFour;
505 0           } name => "PrintOutRaxInHex";
506              
507 0           Call $sub;
508             }
509              
510             sub ClearRegisters(@) # Clear registers by setting them to zero
511 0     0 1   {my (@registers) = @_; # Registers
512 0 0         @_ == 1 or confess;
513 0           for my $r(@registers)
514 0           {my $size = registerSize $r;
515 0 0         Xor $r, $r if $size == 8;
516 0 0         Vpxorq $r, $r if $size > 8;
517             }
518             }
519              
520             sub ReverseBytesInRax # Reverse the bytes in rax
521 0 0   0 1   {@_ == 0 or confess;
522 0           Comment "Reverse bytes in rax";
523              
524             my $sub = S # Reverse rax
525 0     0     {my $size = registerSize rax;
526 0           SaveFirstFour;
527 0           ClearRegisters rsi;
528 0           for(1..$size) # Reverse each byte
529 0           {Mov rdi,rax;
530 0           Shr rdi,($_-1)*8;
531 0           Shl rdi,($size-1)*8;
532 0           Shr rdi,($_-1)*8;
533 0           Or rsi,rdi;
534             }
535 0           Mov rax,rsi;
536 0           RestoreFirstFourExceptRax;
537 0           } name => "ReverseBytesInRax";
538              
539 0           Call $sub;
540             }
541              
542             sub PrintOutRaxInReverseInHex # Write the content of register rax to stderr in hexadecimal in little endian notation
543 0 0   0 1   {@_ == 0 or confess;
544 0           Comment "Print Rax In Reverse In Hex";
545 0           ReverseBytesInRax;
546 0           PrintOutRaxInHex;
547             }
548              
549             sub PrintOutRegisterInHex($) # Print any register as a hex string
550 0     0 1   {my ($r) = @_; # Name of the register to print
551 0           Comment "Print register $r in Hex";
552 0 0         @_ == 1 or confess;
553              
554             my $sub = S # Reverse rax
555 0           {PrintOutString sprintf("%6s: ", $r);
556              
557             my sub printReg(@) # Print the contents of a register
558 0           {my (@regs) = @_; # Size in bytes, work registers
559 0           my $s = registerSize $r; # Size of the register
560 0           PushR @regs; # Save work registers
561 0           PushR $r; # Place register contents on stack
562 0           PopR @regs; # Load work registers
563 0           for my $R(@regs) # Print work registers to print input register
564 0 0         {if ($R !~ m(\Arax))
565 0           {PrintOutString(" ");
566 0           Mov rax, $R
567             }
568 0           PrintOutRaxInHex; # Print work register
569             }
570 0           PopR @regs;
571             };
572 0 0         if ($r =~ m(\Ar)) {printReg qw(rax)} # 64 bit register requested
  0 0          
    0          
    0          
573 0           elsif ($r =~ m(\Ax)) {printReg qw(rax rbx)} # xmm*
574 0           elsif ($r =~ m(\Ay)) {printReg qw(rax rbx rcx rdx)} # ymm*
575 0           elsif ($r =~ m(\Az)) {printReg qw(rax rbx rcx rdx r8 r9 r10 r11)} # zmm*
576              
577 0           PrintOutNl;
578 0           } name => "PrintOutRegister${r}InHex"; # One routine per register printed
579              
580 0           Call $sub;
581             }
582              
583             sub PrintOutRipInHex # Print the instruction pointer in hex
584 0 0   0 1   {@_ == 0 or confess;
585 0           my @regs = qw(rax);
586             my $sub = S
587 0     0     {PushR @regs;
588 0           my $l = label;
589 0           push @text, <
590             $l:
591             END
592 0           Lea rax, "[$l]"; # Current instruction pointer
593 0           PrintOutString "rip: ";
594 0           PrintOutRaxInHex;
595 0           PrintOutNl;
596 0           PopR @regs;
597 0           } name=> "PrintOutRipInHex";
598              
599 0           Call $sub;
600             }
601              
602             sub PrintOutRflagsInHex # Print the flags register in hex
603 0 0   0 1   {@_ == 0 or confess;
604 0           my @regs = qw(rax);
605              
606             my $sub = S
607 0     0     {PushR @regs;
608 0           Pushfq;
609 0           Pop rax;
610 0           PrintOutString "rfl: ";
611 0           PrintOutRaxInHex;
612 0           PrintOutNl;
613 0           PopR @regs;
614 0           } name=> "PrintOutRflagsInHex";
615              
616 0           Call $sub;
617             }
618              
619             sub PrintOutRegistersInHex # Print the general purpose registers in hex
620 0 0   0 1   {@_ == 0 or confess;
621              
622             my $sub = S
623 0     0     {PrintOutRipInHex;
624 0           PrintOutRflagsInHex;
625              
626 0           my @regs = qw(rax);
627 0           PushR @regs;
628              
629 0           my $w = registers_64();
630 0           for my $r(sort @$w)
631 0 0         {next if $r =~ m(rip|rflags);
632 0 0         if ($r eq rax)
633 0           {Pop rax;
634 0           Push rax
635             }
636 0           PrintOutString reverse(pad(reverse($r), 3)).": ";
637 0           Mov rax, $r;
638 0           PrintOutRaxInHex;
639 0           PrintOutNl;
640             }
641 0           PopR @regs;
642 0           } name=> "PrintOutRegistersInHex";
643              
644 0           Call $sub;
645             }
646              
647             sub PrintOutMemoryInHex # Dump memory from the address in rax for the length in rdi
648 0 0   0 1   {@_ == 0 or confess;
649 0           Comment "Print out memory in hex";
650              
651             my $sub = S
652 0     0     {my $size = registerSize rax;
653 0           SaveFirstFour;
654 0           Mov rsi,rax; # Position in memory
655 0           Lea rdi,"[rax+rdi-$size]";
656             For # Print string in blocks
657 0           {Mov rax, "[rsi]";
658 0           ReverseBytesInRax;
659 0           PrintOutRaxInHex;
660 0           } rsi, rdi, $size;
661 0           RestoreFirstFour;
662 0           } name=> "PrintOutMemoryInHex";
663              
664 0           Call $sub;
665             }
666              
667             sub allocateMemory # Allocate the amount of memory specified in rax via mmap and return the address of the allocated memory in rax
668 0 0   0 1   {@_ == 0 or confess;
669 0           Comment "Allocate memory";
670              
671             my $sub = S
672 0     0     {SaveFirstSeven;
673 0           my $d = extractMacroDefinitionsFromCHeaderFile "linux/mman.h"; # mmap constants
674 0           my $pa = $$d{MAP_PRIVATE} | $$d{MAP_ANONYMOUS};
675 0           my $wr = $$d{PROT_WRITE} | $$d{PROT_READ};
676              
677 0           Mov rsi, rax; # Amount of memory
678 0           Mov rax, 9; # mmap
679 0           Xor rdi, rdi; # Anywhere
680 0           Mov rdx, $wr; # Read write protections
681 0           Mov r10, $pa; # Private and anonymous map
682 0           Mov r8, -1; # File descriptor for file backing memory if any
683 0           Mov r9, 0; # Offset into file
684 0           Syscall;
685 0           RestoreFirstSevenExceptRax;
686 0           } name=> "allocateMemory";
687              
688 0           Call $sub;
689             }
690              
691             sub freeMemory # Free memory via mmap. The address of the memory is in rax, the length to free is in rdi
692 0 0   0 1   {@_ == 0 or confess;
693 0           Comment "Free memory";
694             my $sub = S
695 0     0     {SaveFirstFour;
696 0           Mov rsi, rdi;
697 0           Mov rdi, rax;
698 0           Mov rax, 11;
699 0           Syscall;
700 0           RestoreFirstFourExceptRax;
701 0           } name=> "freeMemory";
702              
703 0           Call $sub;
704             }
705              
706             sub Fork() # Fork
707 0 0   0 1   {@_ == 0 or confess;
708 0           Comment "Fork";
709 0           Mov rax, 57;
710 0           Syscall
711             }
712              
713             sub GetPid() # Get process identifier
714 0 0   0 1   {@_ == 0 or confess;
715 0           Comment "Get Pid";
716              
717 0           Mov rax, 39;
718 0           Syscall
719             }
720              
721             sub GetPPid() # Get parent process identifier
722 0 0   0 1   {@_ == 0 or confess;
723 0           Comment "Get Parent Pid";
724              
725 0           Mov rax, 110;
726 0           Syscall
727             }
728              
729             sub GetUid() # Get userid of current process
730 0 0   0 1   {@_ == 0 or confess;
731 0           Comment "Get User id";
732              
733 0           Mov rax, 102;
734 0           Syscall
735             }
736              
737             sub WaitPid() # Wait for the pid in rax to complete
738 0 0   0 1   {@_ == 0 or confess;
739 0           Comment "WaitPid - wait for the pid in rax";
740 0           SaveFirstSeven;
741 0           Mov rdi,rax;
742 0           Mov rax, 61;
743 0           Mov rsi, 0;
744 0           Mov rdx, 0;
745 0           Mov r10, 0;
746 0           Syscall;
747 0           RestoreFirstSevenExceptRax;
748             }
749              
750             sub readTimeStampCounter() # Read the time stamp counter
751 0 0   0 1   {@_ == 0 or confess;
752 0           Comment "Read Time-Stamp Counter";
753 0           Push rdx;
754 0           Rdtsc;
755 0           Shl rdx,32; # Or upper half into rax
756 0           Or rax,rdx;
757 0           Pop rdx;
758 0           RestoreFirstFourExceptRax;
759             }
760              
761             sub OpenRead() # Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
762 0 0   0 1   {@_ == 0 or confess;
763 0           Comment "Open a file for read";
764              
765             my $sub = S
766 0     0     {my $S = extractMacroDefinitionsFromCHeaderFile "asm-generic/fcntl.h"; # Constants for reading a file
767 0           my $O_RDONLY = $$S{O_RDONLY};
768 0           SaveFirstFour;
769 0           Mov rdi,rax;
770 0           Mov rax,2;
771 0           Mov rsi,$O_RDONLY;
772 0           Xor rdx,rdx;
773 0           Syscall;
774 0           RestoreFirstFourExceptRax;
775 0           } name=> "OpenRead";
776              
777 0           Call $sub;
778             }
779              
780             sub Close($) # Close a file descriptor
781 0     0 1   {my ($fdes) = @_; # File descriptor
782 0 0         @_ == 1 or confess;
783 0           Comment "Close a file";
784 0           SaveFirstFour;
785 0           Mov rdi,$fdes;
786 0           Mov rax,3;
787 0           Syscall;
788 0           RestoreFirstFourExceptRax;
789             }
790              
791             sub localData() # Map local data
792 0 0   0 1   {@_ == 0 or confess;
793 0           my $local = genHash("LocalData",
794             size => 0,
795             variables => [],
796             );
797             }
798              
799             sub LocalData::start($) # Start a local data area on the stack
800 0     0     {my ($local) = @_; # Local data descriptor
801 0 0         @_ == 1 or confess;
802 0           my $size = $local->size; # Size of local data
803 0           Push rbp;
804 0           Mov rbp,rsp;
805 0           Sub rsp, $size;
806             }
807              
808             sub LocalData::free($) # Free a local data area on the stack
809 0     0     {my ($local) = @_; # Local data descriptor
810 0 0         @_ == 1 or confess;
811 0           Mov rsp,rbp;
812 0           Pop rbp;
813             }
814              
815             sub LocalData::variable($$;$) # Add a local variable
816 0     0     {my ($local, $length, $comment) = @_; # Local data descriptor, length of data, optional comment
817 0 0         @_ >= 2 or confess;
818 0           my $variable = genHash("LocalVariable",
819             loc => $local->size,
820             size => $length,
821             comment => $comment
822             );
823 0           $local->size += $length; # Update size of local data
824 0           $variable
825             }
826              
827             sub LocalVariable::stack($) # Address a local variable on the stack
828 0     0     {my ($variable) = @_; # Variable
829 0 0         @_ == 1 or confess;
830 0           my $loc = $variable->loc; # Location of variable on stack
831 0           "[$loc+rbp]" # Address variable
832             }
833              
834             sub LocalData::allocate8($@) # Add some 8 byte local variables and return an array of variable definitions
835 0     0     {my ($local, @comments) = @_; # Local data descriptor, optional comment
836 0           my @v;
837 0           for my $c(@comments)
838 0           {push @v, LocalData::variable($local, 8, $c);
839             }
840 0 0         wantarray ? @v : $v[-1]; # Avoid returning the number of elements accidently
841             }
842              
843             sub AllocateAll8($) # Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions...)
844 0     0 1   {my ($N) = @_; # Number of variables required
845 0           my $local = localData; # Create local data descriptor
846 0           my @v;
847 0           for(1..$N) # Create the variables
848 0           {my $v = $local->variable(registerSize(rax));
849 0           push @v, $v->stack;
850             }
851 0           $local->start; # Create the local data area on the stack
852 0           ($local, @v)
853             }
854              
855             sub MemoryClear() # Clear memory - the address of the memory is in rax, the length in rdi
856 0 0   0 1   {@_ == 0 or confess;
857 0           Comment "Clear memory";
858              
859 0           my $size = registerSize zmm0;
860 0           my $saveSize = SaveFirstFour; # Generated code
861 0           PushR zmm0; # Pump zeros with this register
862 0           Lea rdi, "[rax+rdi-$size]"; # Address of upper limit of buffer
863 0           ClearRegisters zmm0; # Clear the register that will be written into memory
864              
865             For # Clear memory
866 0     0     {Vmovdqu64 "[rax]", zmm0;
867 0           } rax, rdi, registerSize zmm0;
868              
869 0           PopR zmm0;
870 0           RestoreFirstFour;
871             }
872              
873             sub StatSize() # Stat a file whose name is addressed by rax to get its size in rax
874 0 0   0 1   {@_ == 0 or confess;
875 0           Comment "Stat a file for size";
876 0           my $S = extractCStructure "#include "; # Get location of size field
877 0           my $Size = $$S{stat}{size};
878 0           my $off = $$S{stat}{fields}{st_size}{loc};
879              
880 0           SaveFirstFour;
881 0           Mov rdi, rax; # File name
882 0           Mov rax,4;
883 0           Lea rsi, "[rsp-$Size]";
884 0           Syscall;
885 0           Mov rax, "[$off+rsp-$Size]"; # Place size in rax
886 0           RestoreFirstFourExceptRax;
887             }
888              
889             sub ReadFile() # Read a file whose name is addressed by rax into memory. The address of the mapped memory and its length are returned in registers rax,rdi
890 0 0   0 1   {@_ == 0 or confess;
891 0           Comment "Read a file into memory";
892              
893 0           SaveFirstSeven; # Generated code
894 0           my ($local, $file, $addr, $size, $fdes) = AllocateAll8 4; # Local data
895              
896 0           Mov $file, rax; # Save file name
897              
898 0           StatSize; # File size
899 0           Mov $size, rax; # Save file size
900              
901 0           Mov rax, $file; # File name
902 0           OpenRead; # Open file for read
903 0           Mov $fdes, rax; # Save file descriptor
904              
905 0           my $d = extractMacroDefinitionsFromCHeaderFile "linux/mman.h"; # mmap constants
906 0           my $pa = $$d{MAP_PRIVATE};
907 0           my $ro = $$d{PROT_READ};
908              
909 0           Mov rax, 9; # mmap
910 0           Mov rsi, $size; # Amount of memory
911 0           Xor rdi, rdi; # Anywhere
912 0           Mov rdx, $ro; # Read write protections
913 0           Mov r10, $pa; # Private and anonymous map
914 0           Mov r8, $fdes; # File descriptor for file backing memory
915 0           Mov r9, 0; # Offset into file
916 0           Syscall;
917 0           Mov rdi, $size;
918 0           RestoreFirstSevenExceptRaxAndRdi;
919             }
920              
921             sub assemble(%) # Assemble the generated code
922 0     0 1   {my (%options) = @_; # Options
923 0           my $r = join "\n", map {s/\s+\Z//sr} @rodata;
  0            
924 0           my $d = join "\n", map {s/\s+\Z//sr} @data;
  0            
925 0           my $b = join "\n", map {s/\s+\Z//sr} @bss;
  0            
926 0           my $t = join "\n", map {s/\s+\Z//sr} @text;
  0            
927 0           my $a = <
928             section .rodata
929             $r
930             section .data
931             $d
932             section .bss
933             $b
934             section .text
935             global _start, main
936             _start:
937             main:
938             push rbp ; function prologue
939             mov rbp,rsp
940             $t
941             END
942              
943 0           my $c = owf(q(z.asm), $a); # Source file
944 0           my $e = q(z); # Executable file
945 0           my $l = q(z.txt); # Assembler listing
946 0           my $o = q(z.o); # Object file
947              
948 0           my $cmd = qq(nasm -f elf64 -g -l $l -o $o $c; ld -o $e $o; chmod 744 $e; $sde -ptr-check -- ./$e 2>&1);
949 0           say STDERR qq($cmd);
950 0           my $R = eval {qx($cmd)};
  0            
951 0 0         say STDERR readFile($l) if $options{list}; # Print listing if requested
952 0           say STDERR $R;
953 0           $R # Return execution results
954             }
955              
956             #d
957             #-------------------------------------------------------------------------------
958             # Export - eeee
959             #-------------------------------------------------------------------------------
960              
961 1     1   21 use Exporter qw(import);
  1         4  
  1         58  
962              
963 1     1   7 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         1  
  1         398  
964              
965             @ISA = qw(Exporter);
966             @EXPORT = qw();
967             @EXPORT_OK = qw(
968             );
969             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
970              
971             # podDocumentation
972             =pod
973              
974             =encoding utf-8
975              
976             =head1 Name
977              
978             Nasm::X86 - Generate Nasm assembler code
979              
980             =head1 Synopsis
981              
982             Write and execute x64 instructions from perl, for example:
983              
984             Use avx512 instructions to reorder data using 512 bit zmm registers:
985              
986             Start;
987             my $q = Rs my $s = join '', ('a'..'p')x4;;
988             Mov rax, Ds('0'x128);
989              
990             Vmovdqu32 zmm0, "[$q]";
991             Vprolq zmm1, zmm0, 32;
992             Vmovdqu32 "[rax]", zmm1;
993              
994             Mov rdi, length $s;
995             PrintOutMemory;
996             Exit;
997              
998             ok $s =~ m(abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop)s;
999             ok assemble() =~ m(efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl)s;
1000              
1001             Start a child process and wait for it, printing out the process identifiers of
1002             each process involved:
1003              
1004             Start; # Start the program
1005             Fork; # Fork
1006              
1007             Test rax,rax;
1008             If # Parent
1009             {Mov rbx, rax;
1010             WaitPid;
1011             PrintOutRegisterInHex rax;
1012             PrintOutRegisterInHex rbx;
1013             GetPid; # Pid of parent as seen in parent
1014             Mov rcx,rax;
1015             PrintOutRegisterInHex rcx;
1016             }
1017             sub # Child
1018             {Mov r8,rax;
1019             PrintOutRegisterInHex r8;
1020             GetPid; # Child pid as seen in child
1021             Mov r9,rax;
1022             PrintOutRegisterInHex r9;
1023             GetPPid; # Parent pid as seen in child
1024             Mov r10,rax;
1025             PrintOutRegisterInHex r10;
1026             };
1027              
1028             Exit; # Return to operating system
1029              
1030             my $r = assemble();
1031              
1032             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1033             # r9: 0000 0000 0003 0C63 #2 Pid of child
1034             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1035             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1036             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1037             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1038              
1039             Read this file:
1040              
1041             Start; # Start the program
1042             Mov rax, Rs($0); # File to read
1043             ReadFile; # Read file
1044             PrintOutMemory; # Print memory
1045             Exit; # Return to operating system
1046              
1047             my $r = assemble(); # Assemble and execute
1048             ok index($r, readFile($0)) > -1; # Output contains this file
1049              
1050             =head2 Installation
1051              
1052             You will need the Intel Software Development Emulator and the Networkwide
1053             Assembler installed on your test system. For full details of how to do this
1054             see: L
1055              
1056             =head1 Description
1057              
1058             Generate Nasm assembler code
1059              
1060              
1061             Version "202104013".
1062              
1063              
1064             The following sections describe the methods in each functional area of this
1065             module. For an alphabetic listing of all methods by name see L.
1066              
1067              
1068              
1069             =head1 Generate Network Assembler Code
1070              
1071             Generate assembler code that can be assembled with Nasm
1072              
1073             =head2 SetLabel($l)
1074              
1075             Set a label in the code section
1076              
1077             Parameter Description
1078             1 $l Label
1079              
1080             =head2 Start()
1081              
1082             Initialize the assembler
1083              
1084              
1085             B
1086              
1087              
1088              
1089             Start; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1090              
1091             PrintOutString "Hello World";
1092             Exit;
1093             ok assemble =~ m(Hello World);
1094              
1095              
1096             =head2 Ds(@d)
1097              
1098             Layout bytes in memory and return their label
1099              
1100             Parameter Description
1101             1 @d Data to be laid out
1102              
1103             B
1104              
1105              
1106             Start;
1107             my $q = Rs('a'..'z');
1108              
1109             Mov rax, Ds('0'x64); # Output area # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1110              
1111             Vmovdqu32(xmm0, "[$q]"); # Load
1112             Vprolq (xmm0, xmm0, 32); # Rotate double words in quad words
1113             Vmovdqu32("[rax]", xmm0); # Save
1114             Mov rdi, 16;
1115             PrintOutMemory;
1116             Exit;
1117             ok assemble() =~ m(efghabcdmnopijkl)s;
1118              
1119              
1120             =head2 Rs(@d)
1121              
1122             Layout bytes in read only memory and return their label
1123              
1124             Parameter Description
1125             1 @d Data to be laid out
1126              
1127             B
1128              
1129              
1130             Start;
1131             Comment "Print a string from memory";
1132             my $s = "Hello World";
1133              
1134             Mov rax, Rs($s); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1135              
1136             Mov rdi, length $s;
1137             PrintOutMemory;
1138             Exit;
1139             ok assemble =~ m(Hello World);
1140              
1141              
1142             =head2 Dbwdq($s, @d)
1143              
1144             Layout data
1145              
1146             Parameter Description
1147             1 $s Element size
1148             2 @d Data to be laid out
1149              
1150             =head2 Db(@bytes)
1151              
1152             Layout bytes in the data segment and return their label
1153              
1154             Parameter Description
1155             1 @bytes Bytes to layout
1156              
1157             =head2 Dw(@words)
1158              
1159             Layout words in the data segment and return their label
1160              
1161             Parameter Description
1162             1 @words Words to layout
1163              
1164             =head2 Dd(@dwords)
1165              
1166             Layout double words in the data segment and return their label
1167              
1168             Parameter Description
1169             1 @dwords Double words to layout
1170              
1171             =head2 Dq(@qwords)
1172              
1173             Layout quad words in the data segment and return their label
1174              
1175             Parameter Description
1176             1 @qwords Quad words to layout
1177              
1178             =head2 Rbwdq($s, @d)
1179              
1180             Layout data
1181              
1182             Parameter Description
1183             1 $s Element size
1184             2 @d Data to be laid out
1185              
1186             =head2 Rb(@bytes)
1187              
1188             Layout bytes in the data segment and return their label
1189              
1190             Parameter Description
1191             1 @bytes Bytes to layout
1192              
1193             =head2 Rw(@words)
1194              
1195             Layout words in the data segment and return their label
1196              
1197             Parameter Description
1198             1 @words Words to layout
1199              
1200             =head2 Rd(@dwords)
1201              
1202             Layout double words in the data segment and return their label
1203              
1204             Parameter Description
1205             1 @dwords Double words to layout
1206              
1207             =head2 Rq(@qwords)
1208              
1209             Layout quad words in the data segment and return their label
1210              
1211             Parameter Description
1212             1 @qwords Quad words to layout
1213              
1214             =head2 Comment(@comment)
1215              
1216             Insert a comment into the assembly code
1217              
1218             Parameter Description
1219             1 @comment Text of comment
1220              
1221             B
1222              
1223              
1224             Start;
1225              
1226             Comment "Print a string from memory"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1227              
1228             my $s = "Hello World";
1229             Mov rax, Rs($s);
1230             Mov rdi, length $s;
1231             PrintOutMemory;
1232             Exit;
1233             ok assemble =~ m(Hello World);
1234              
1235              
1236             =head2 Exit($c)
1237              
1238             Exit with the specified return code or zero if no return code supplied
1239              
1240             Parameter Description
1241             1 $c Return code
1242              
1243             B
1244              
1245              
1246             Start;
1247             PrintOutString "Hello World";
1248              
1249             Exit; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1250              
1251             ok assemble =~ m(Hello World);
1252              
1253              
1254             =head2 SaveFirstFour()
1255              
1256             Save the first 4 parameter registers
1257              
1258              
1259             =head2 RestoreFirstFour()
1260              
1261             Restore the first 4 parameter registers
1262              
1263              
1264             =head2 RestoreFirstFourExceptRax()
1265              
1266             Restore the first 4 parameter registers except rax so it can return its value
1267              
1268              
1269             =head2 SaveFirstSeven()
1270              
1271             Save the first 7 parameter registers
1272              
1273              
1274             =head2 RestoreFirstSeven()
1275              
1276             Restore the first 7 parameter registers
1277              
1278              
1279             =head2 RestoreFirstSevenExceptRax()
1280              
1281             Restore the first 7 parameter registers except rax which is being used to return the result
1282              
1283              
1284             =head2 RestoreFirstSevenExceptRaxAndRdi()
1285              
1286             Restore the first 7 parameter registers except rax and rdi which are being used to return the results
1287              
1288              
1289             =head2 If($then, $else)
1290              
1291             If
1292              
1293             Parameter Description
1294             1 $then Then - required
1295             2 $else Else - optional
1296              
1297             B
1298              
1299              
1300             Start;
1301             Mov rax, 0;
1302             Test rax,rax;
1303              
1304             If # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1305              
1306             {PrintOutRegisterInHex rax;
1307             } sub
1308             {PrintOutRegisterInHex rbx;
1309             };
1310             Mov rax, 1;
1311             Test rax,rax;
1312              
1313             If # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1314              
1315             {PrintOutRegisterInHex rcx;
1316             } sub
1317             {PrintOutRegisterInHex rdx;
1318             };
1319             Exit;
1320             ok assemble() =~ m(rbx.*rcx)s;
1321              
1322              
1323             =head2 For($body, $register, $limit, $increment)
1324              
1325             For
1326              
1327             Parameter Description
1328             1 $body Body
1329             2 $register Register
1330             3 $limit Limit on loop
1331             4 $increment Increment
1332              
1333             B
1334              
1335              
1336             Start; # Start the program
1337              
1338             For # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1339              
1340             {PrintOutRegisterInHex rax
1341             } rax, 16, 1;
1342             Exit; # Return to operating system
1343             my $r = assemble;
1344             ok $r =~ m(( 0000){3} 0000)i;
1345             ok $r =~ m(( 0000){3} 000F)i;
1346              
1347              
1348             =head2 S($body, %options)
1349              
1350             Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub
1351              
1352             Parameter Description
1353             1 $body Body
1354             2 %options Options.
1355              
1356             =head2 registerSize($r)
1357              
1358             Return the size of a register
1359              
1360             Parameter Description
1361             1 $r Register
1362              
1363             =head2 PushR(@r)
1364              
1365             Push registers onto the stack
1366              
1367             Parameter Description
1368             1 @r Register
1369              
1370             =head2 PopR(@r)
1371              
1372             Pop registers from the stack
1373              
1374             Parameter Description
1375             1 @r Register
1376              
1377             B
1378              
1379              
1380             Start;
1381             my $q = Rs my $s = join '', ('a'..'p')x4;;
1382             Mov rax, Ds('0'x128);
1383              
1384             Vmovdqu32 zmm0, "[$q]";
1385             Vprolq zmm1, zmm0, 32;
1386             Vmovdqu32 "[rax]", zmm1;
1387              
1388             Mov rdi, length $s;
1389             PrintOutMemory;
1390             Exit;
1391              
1392             ok $s =~ m(abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop)s;
1393             ok assemble() =~ m(efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl)s;
1394              
1395              
1396             =head2 PeekR($r)
1397              
1398             Peek at register on stack
1399              
1400             Parameter Description
1401             1 $r Register
1402              
1403             =head2 PrintOutNl()
1404              
1405             Write a new line
1406              
1407              
1408             B
1409              
1410              
1411             Start;
1412             Comment "Print a string from memory";
1413             my $s = "Hello World";
1414             Mov rax, Rs($s);
1415             Mov rdi, length $s;
1416             PrintOutMemory;
1417             Exit;
1418             ok assemble =~ m(Hello World);
1419              
1420              
1421             =head2 PrintOutString($string)
1422              
1423             Write a constant string to sysout.
1424              
1425             Parameter Description
1426             1 $string String
1427              
1428             B
1429              
1430              
1431             Start;
1432              
1433             PrintOutString "Hello World"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1434              
1435             Exit;
1436             ok assemble =~ m(Hello World);
1437              
1438              
1439             =head2 PrintOutMemory()
1440              
1441             Print the memory addressed by rax for a length of rdi
1442              
1443              
1444             =head2 PrintOutRaxInHex()
1445              
1446             Write the content of register rax to stderr in hexadecimal in big endian notation
1447              
1448              
1449             B
1450              
1451              
1452             Start;
1453             my $q = Rs('abababab');
1454             Mov(rax, "[$q]");
1455             PrintOutString "rax: ";
1456              
1457             PrintOutRaxInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1458              
1459             PrintOutNl;
1460             Xor rax, rax;
1461             PrintOutString "rax: ";
1462              
1463             PrintOutRaxInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1464              
1465             PrintOutNl;
1466             Exit;
1467             ok assemble() =~ m(rax: 6261 6261 6261 6261.*rax: 0000 0000 0000 0000)s;
1468              
1469              
1470             =head2 ClearRegisters(@registers)
1471              
1472             Clear registers by setting them to zero
1473              
1474             Parameter Description
1475             1 @registers Registers
1476              
1477             =head2 ReverseBytesInRax()
1478              
1479             Reverse the bytes in rax
1480              
1481              
1482             =head2 PrintOutRaxInReverseInHex()
1483              
1484             Write the content of register rax to stderr in hexadecimal in little endian notation
1485              
1486              
1487             B
1488              
1489              
1490             Start;
1491             Mov rax, 0x88776655;
1492             Shl rax, 32;
1493             Or rax, 0x44332211;
1494             PrintOutRaxInHex;
1495              
1496             PrintOutRaxInReverseInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1497              
1498             Exit;
1499             ok assemble =~ m(8877 6655 4433 2211 1122 3344 5566 7788)s;
1500              
1501              
1502             =head2 PrintOutRegisterInHex($r)
1503              
1504             Print any register as a hex string
1505              
1506             Parameter Description
1507             1 $r Name of the register to print
1508              
1509             B
1510              
1511              
1512             Start;
1513             my $q = Rs(('a'..'p')x4);
1514             Mov r8,"[$q]";
1515              
1516             PrintOutRegisterInHex r8; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1517              
1518             Exit;
1519             ok assemble() =~ m(r8: 6867 6665 6463 6261)s;
1520              
1521              
1522             =head2 PrintOutRipInHex()
1523              
1524             Print the instruction pointer in hex
1525              
1526              
1527             =head2 PrintOutRflagsInHex()
1528              
1529             Print the flags register in hex
1530              
1531              
1532             =head2 PrintOutRegistersInHex()
1533              
1534             Print the general purpose registers in hex
1535              
1536              
1537             B
1538              
1539              
1540             Start;
1541             my $q = Rs('abababab');
1542             Mov(rax, 1);
1543             Mov(rbx, 2);
1544             Mov(rcx, 3);
1545             Mov(rdx, 4);
1546             Mov(r8, 5);
1547             Lea r9, "[rax+rbx]";
1548              
1549             PrintOutRegistersInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1550              
1551             Exit;
1552             my $r = assemble();
1553             ok $r =~ m( r8: 0000 0000 0000 0005.* r9: 0000 0000 0000 0003.*rax: 0000 0000 0000 0001)s;
1554             ok $r =~ m(rbx: 0000 0000 0000 0002.*rcx: 0000 0000 0000 0003.*rdx: 0000 0000 0000 0004)s;
1555              
1556              
1557             =head2 PrintOutMemoryInHex()
1558              
1559             Dump memeory from teh address in rax for the length in rdi
1560              
1561              
1562             =head2 allocateMemory()
1563              
1564             Allocate the amount of memory specified in rax via mmap and return the address of the allocated memeory in rax
1565              
1566              
1567             B
1568              
1569              
1570             Start;
1571             my $N = 2048;
1572             my $q = Rs('a'..'p');
1573             Mov rax, $N;
1574              
1575             allocateMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1576              
1577             PrintOutRegisterInHex rax;
1578              
1579             Vmovdqu8 xmm0, "[$q]";
1580             Vmovdqu8 "[rax]", xmm0;
1581             Mov rdi,16;
1582             PrintOutMemory;
1583             PrintOutNl;
1584              
1585             Mov rdi, $N;
1586             freeMemory;
1587             PrintOutRegisterInHex rax;
1588             Exit;
1589             ok assemble() =~ m(abcdefghijklmnop)s;
1590              
1591             Start;
1592             my $N = 4096;
1593             my $S = registerSize rax;
1594             Mov rax, $N;
1595              
1596             allocateMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1597              
1598             PrintOutRegisterInHex rax;
1599             Mov rdi, $N;
1600             MemoryClear;
1601             PrintOutRegisterInHex rax;
1602             PrintOutMemoryInHex;
1603             Exit;
1604              
1605             my $r = assemble;
1606             if ($r =~ m((0000.*0000))s)
1607             {is_deeply length($1), 10269;
1608             }
1609              
1610              
1611             =head2 freeMemory()
1612              
1613             Free memory via mmap. The address of the memory is in rax, the length to free is in rdi
1614              
1615              
1616             B
1617              
1618              
1619             Start;
1620             my $N = 2048;
1621             my $q = Rs('a'..'p');
1622             Mov rax, $N;
1623             allocateMemory;
1624             PrintOutRegisterInHex rax;
1625              
1626             Vmovdqu8 xmm0, "[$q]";
1627             Vmovdqu8 "[rax]", xmm0;
1628             Mov rdi,16;
1629             PrintOutMemory;
1630             PrintOutNl;
1631              
1632             Mov rdi, $N;
1633              
1634             freeMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1635              
1636             PrintOutRegisterInHex rax;
1637             Exit;
1638             ok assemble() =~ m(abcdefghijklmnop)s;
1639              
1640             Start;
1641             my $N = 4096;
1642             my $S = registerSize rax;
1643             Mov rax, $N;
1644             allocateMemory;
1645             PrintOutRegisterInHex rax;
1646             Mov rdi, $N;
1647             MemoryClear;
1648             PrintOutRegisterInHex rax;
1649             PrintOutMemoryInHex;
1650             Exit;
1651              
1652             my $r = assemble;
1653             if ($r =~ m((0000.*0000))s)
1654             {is_deeply length($1), 10269;
1655             }
1656              
1657              
1658             =head2 Fork()
1659              
1660             Fork
1661              
1662              
1663             B
1664              
1665              
1666             Start; # Start the program
1667              
1668             Fork; # Fork # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1669              
1670              
1671             Test rax,rax;
1672             If # Parent
1673             {Mov rbx, rax;
1674             WaitPid;
1675             PrintOutRegisterInHex rax;
1676             PrintOutRegisterInHex rbx;
1677             GetPid; # Pid of parent as seen in parent
1678             Mov rcx,rax;
1679             PrintOutRegisterInHex rcx;
1680             }
1681             sub # Child
1682             {Mov r8,rax;
1683             PrintOutRegisterInHex r8;
1684             GetPid; # Child pid as seen in child
1685             Mov r9,rax;
1686             PrintOutRegisterInHex r9;
1687             GetPPid; # Parent pid as seen in child
1688             Mov r10,rax;
1689             PrintOutRegisterInHex r10;
1690             };
1691              
1692             Exit; # Return to operating system
1693              
1694             my $r = assemble();
1695              
1696             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1697             # r9: 0000 0000 0003 0C63 #2 Pid of child
1698             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1699             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1700             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1701             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1702              
1703             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1704             {ok $2 eq $4;
1705             ok $2 eq $5;
1706             ok $3 eq $6;
1707             ok $2 gt $6;
1708             }
1709              
1710             Start; # Start the program
1711             GetUid; # Userid
1712             PrintOutRegisterInHex rax;
1713             Exit; # Return to operating system
1714             my $r = assemble();
1715             ok $r =~ m(rax:( 0000){3});
1716              
1717              
1718             =head2 GetPid()
1719              
1720             Get process identifier
1721              
1722              
1723             B
1724              
1725              
1726             Start; # Start the program
1727             Fork; # Fork
1728              
1729             Test rax,rax;
1730             If # Parent
1731             {Mov rbx, rax;
1732             WaitPid;
1733             PrintOutRegisterInHex rax;
1734             PrintOutRegisterInHex rbx;
1735              
1736             GetPid; # Pid of parent as seen in parent # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1737              
1738             Mov rcx,rax;
1739             PrintOutRegisterInHex rcx;
1740             }
1741             sub # Child
1742             {Mov r8,rax;
1743             PrintOutRegisterInHex r8;
1744              
1745             GetPid; # Child pid as seen in child # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1746              
1747             Mov r9,rax;
1748             PrintOutRegisterInHex r9;
1749             GetPPid; # Parent pid as seen in child
1750             Mov r10,rax;
1751             PrintOutRegisterInHex r10;
1752             };
1753              
1754             Exit; # Return to operating system
1755              
1756             my $r = assemble();
1757              
1758             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1759             # r9: 0000 0000 0003 0C63 #2 Pid of child
1760             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1761             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1762             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1763             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1764              
1765             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1766             {ok $2 eq $4;
1767             ok $2 eq $5;
1768             ok $3 eq $6;
1769             ok $2 gt $6;
1770             }
1771              
1772             Start; # Start the program
1773             GetUid; # Userid
1774             PrintOutRegisterInHex rax;
1775             Exit; # Return to operating system
1776             my $r = assemble();
1777             ok $r =~ m(rax:( 0000){3});
1778              
1779              
1780             =head2 GetPPid()
1781              
1782             Get parent process identifier
1783              
1784              
1785             B
1786              
1787              
1788             Start; # Start the program
1789             Fork; # Fork
1790              
1791             Test rax,rax;
1792             If # Parent
1793             {Mov rbx, rax;
1794             WaitPid;
1795             PrintOutRegisterInHex rax;
1796             PrintOutRegisterInHex rbx;
1797             GetPid; # Pid of parent as seen in parent
1798             Mov rcx,rax;
1799             PrintOutRegisterInHex rcx;
1800             }
1801             sub # Child
1802             {Mov r8,rax;
1803             PrintOutRegisterInHex r8;
1804             GetPid; # Child pid as seen in child
1805             Mov r9,rax;
1806             PrintOutRegisterInHex r9;
1807              
1808             GetPPid; # Parent pid as seen in child # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1809              
1810             Mov r10,rax;
1811             PrintOutRegisterInHex r10;
1812             };
1813              
1814             Exit; # Return to operating system
1815              
1816             my $r = assemble();
1817              
1818             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1819             # r9: 0000 0000 0003 0C63 #2 Pid of child
1820             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1821             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1822             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1823             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1824              
1825             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1826             {ok $2 eq $4;
1827             ok $2 eq $5;
1828             ok $3 eq $6;
1829             ok $2 gt $6;
1830             }
1831              
1832             Start; # Start the program
1833             GetUid; # Userid
1834             PrintOutRegisterInHex rax;
1835             Exit; # Return to operating system
1836             my $r = assemble();
1837             ok $r =~ m(rax:( 0000){3});
1838              
1839              
1840             =head2 GetUid()
1841              
1842             Get userid of current process
1843              
1844              
1845             =head2 WaitPid()
1846              
1847             Wait for the pid in rax to complete
1848              
1849              
1850             B
1851              
1852              
1853             Start; # Start the program
1854             Fork; # Fork
1855              
1856             Test rax,rax;
1857             If # Parent
1858             {Mov rbx, rax;
1859              
1860             WaitPid; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1861              
1862             PrintOutRegisterInHex rax;
1863             PrintOutRegisterInHex rbx;
1864             GetPid; # Pid of parent as seen in parent
1865             Mov rcx,rax;
1866             PrintOutRegisterInHex rcx;
1867             }
1868             sub # Child
1869             {Mov r8,rax;
1870             PrintOutRegisterInHex r8;
1871             GetPid; # Child pid as seen in child
1872             Mov r9,rax;
1873             PrintOutRegisterInHex r9;
1874             GetPPid; # Parent pid as seen in child
1875             Mov r10,rax;
1876             PrintOutRegisterInHex r10;
1877             };
1878              
1879             Exit; # Return to operating system
1880              
1881             my $r = assemble();
1882              
1883             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
1884             # r9: 0000 0000 0003 0C63 #2 Pid of child
1885             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
1886             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
1887             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
1888             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
1889              
1890             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
1891             {ok $2 eq $4;
1892             ok $2 eq $5;
1893             ok $3 eq $6;
1894             ok $2 gt $6;
1895             }
1896              
1897             Start; # Start the program
1898             GetUid; # Userid
1899             PrintOutRegisterInHex rax;
1900             Exit; # Return to operating system
1901             my $r = assemble();
1902             ok $r =~ m(rax:( 0000){3});
1903              
1904              
1905             =head2 readTimeStampCounter()
1906              
1907             Read the time stamp counter
1908              
1909              
1910             B
1911              
1912              
1913             Start;
1914             for(1..10)
1915              
1916             {readTimeStampCounter; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1917              
1918             PrintOutRegisterInHex rax;
1919             }
1920             Exit;
1921             my @s = split /
1922             /, assemble();
1923             my @S = sort @s;
1924             is_deeply \@s, \@S;
1925              
1926              
1927             =head2 OpenRead()
1928              
1929             Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
1930              
1931              
1932             B
1933              
1934              
1935             Start; # Start the program
1936             Mov rax, Rs($0); # File to stat
1937              
1938             OpenRead; # Open file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1939              
1940             PrintOutRegisterInHex rax;
1941             Close(rax); # Close file
1942             PrintOutRegisterInHex rax;
1943             Exit; # Return to operating system
1944             my $r = assemble();
1945             ok $r =~ m(( 0000){3} 0003)i; # Expected file number
1946             ok $r =~ m(( 0000){4})i; # Expected file number
1947              
1948              
1949             =head2 Close($fdes)
1950              
1951             Close a file descriptor
1952              
1953             Parameter Description
1954             1 $fdes File descriptor
1955              
1956             B
1957              
1958              
1959             Start; # Start the program
1960             Mov rax, Rs($0); # File to stat
1961             OpenRead; # Open file
1962             PrintOutRegisterInHex rax;
1963              
1964             Close(rax); # Close file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1965              
1966             PrintOutRegisterInHex rax;
1967             Exit; # Return to operating system
1968             my $r = assemble();
1969             ok $r =~ m(( 0000){3} 0003)i; # Expected file number
1970             ok $r =~ m(( 0000){4})i; # Expected file number
1971              
1972              
1973             =head2 localData()
1974              
1975             Map local data
1976              
1977              
1978             =head2 LocalData::start($local)
1979              
1980             Start a local data area on the stack
1981              
1982             Parameter Description
1983             1 $local Local data descriptor
1984              
1985             =head2 LocalData::free($local)
1986              
1987             Free a local data area on the stack
1988              
1989             Parameter Description
1990             1 $local Local data descriptor
1991              
1992             =head2 LocalData::variable($local, $length, $comment)
1993              
1994             Add a local variable
1995              
1996             Parameter Description
1997             1 $local Local data descriptor
1998             2 $length Length of data
1999             3 $comment Optional comment
2000              
2001             =head2 LocalVariable::stack($variable)
2002              
2003             Address a local variable on the stack
2004              
2005             Parameter Description
2006             1 $variable Variable
2007              
2008             =head2 LocalData::allocate8($local, @comments)
2009              
2010             Add some 8 byte local variables and return an array of variable definitions
2011              
2012             Parameter Description
2013             1 $local Local data descriptor
2014             2 @comments Optional comment
2015              
2016             =head2 AllocateAll8($N)
2017              
2018             Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions...)
2019              
2020             Parameter Description
2021             1 $N Number of variables required
2022              
2023             =head2 MemoryClear()
2024              
2025             Clear memory - the address of the memory is in rax, the length in rdi
2026              
2027              
2028             =head2 StatSize()
2029              
2030             Stat a file whise name is addressed by rax to get its size in rax
2031              
2032              
2033             B
2034              
2035              
2036             Start; # Start the program
2037             Mov rax, Rs($0); # File to stat
2038              
2039             StatSize; # Stat the file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2040              
2041             PrintOutRegisterInHex rax;
2042             Exit; # Return to operating system
2043             my $r = assemble() =~ s( ) ()gsr;
2044             if ($r =~ m(rax:([0-9a-f]{16}))is) # Compare file size obtained with that from fileSize()
2045             {is_deeply $1, sprintf("%016X", fileSize($0));
2046             }
2047              
2048              
2049             =head2 ReadFile()
2050              
2051             Read a file whose name is addressed by rax into memory. The address of the mapped memory and its length are returned in registers rax,rdi
2052              
2053              
2054             B
2055              
2056              
2057             Start; # Start the program
2058             Mov rax, Rs($0); # File to read
2059              
2060             ReadFile; # Read file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2061              
2062             PrintOutMemory; # Print memory
2063             Exit; # Return to operating system
2064             my $r = assemble(); # Assemble and execute
2065             ok index($r, readFile($0)) > -1; # Output contains this file
2066              
2067              
2068             =head2 assemble(%options)
2069              
2070             Assemble the generated code
2071              
2072             Parameter Description
2073             1 %options Options
2074              
2075             B
2076              
2077              
2078             Start;
2079             PrintOutString "Hello World";
2080             Exit;
2081              
2082             ok assemble =~ m(Hello World); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2083              
2084              
2085              
2086              
2087             =head1 Private Methods
2088              
2089             =head2 label()
2090              
2091             Create a unique label
2092              
2093              
2094              
2095             =head1 Index
2096              
2097              
2098             1 L - Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions.
2099              
2100             2 L - Allocate the amount of memory specified in rax via mmap and return the address of the allocated memeory in rax
2101              
2102             3 L - Assemble the generated code
2103              
2104             4 L - Clear registers by setting them to zero
2105              
2106             5 L - Close a file descriptor
2107              
2108             6 L - Insert a comment into the assembly code
2109              
2110             7 L - Layout bytes in the data segment and return their label
2111              
2112             8 L - Layout data
2113              
2114             9 L - Layout double words in the data segment and return their label
2115              
2116             10 L - Layout quad words in the data segment and return their label
2117              
2118             11 L - Layout bytes in memory and return their label
2119              
2120             12 L - Layout words in the data segment and return their label
2121              
2122             13 L - Exit with the specified return code or zero if no return code supplied
2123              
2124             14 L - For
2125              
2126             15 L - Fork
2127              
2128             16 L - Free memory via mmap.
2129              
2130             17 L - Get process identifier
2131              
2132             18 L - Get parent process identifier
2133              
2134             19 L - Get userid of current process
2135              
2136             20 L - If
2137              
2138             21 L - Create a unique label
2139              
2140             22 L - Map local data
2141              
2142             23 L - Add some 8 byte local variables and return an array of variable definitions
2143              
2144             24 L - Free a local data area on the stack
2145              
2146             25 L - Start a local data area on the stack
2147              
2148             26 L - Add a local variable
2149              
2150             27 L - Address a local variable on the stack
2151              
2152             28 L - Clear memory - the address of the memory is in rax, the length in rdi
2153              
2154             29 L - Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
2155              
2156             30 L - Peek at register on stack
2157              
2158             31 L - Pop registers from the stack
2159              
2160             32 L - Print the memory addressed by rax for a length of rdi
2161              
2162             33 L - Dump memeory from teh address in rax for the length in rdi
2163              
2164             34 L - Write a new line
2165              
2166             35 L - Write the content of register rax to stderr in hexadecimal in big endian notation
2167              
2168             36 L - Write the content of register rax to stderr in hexadecimal in little endian notation
2169              
2170             37 L - Print any register as a hex string
2171              
2172             38 L - Print the general purpose registers in hex
2173              
2174             39 L - Print the flags register in hex
2175              
2176             40 L - Print the instruction pointer in hex
2177              
2178             41 L - Write a constant string to sysout.
2179              
2180             42 L - Push registers onto the stack
2181              
2182             43 L - Layout bytes in the data segment and return their label
2183              
2184             44 L - Layout data
2185              
2186             45 L - Layout double words in the data segment and return their label
2187              
2188             46 L - Read a file whose name is addressed by rax into memory.
2189              
2190             47 L - Read the time stamp counter
2191              
2192             48 L - Return the size of a register
2193              
2194             49 L - Restore the first 4 parameter registers
2195              
2196             50 L - Restore the first 4 parameter registers except rax so it can return its value
2197              
2198             51 L - Restore the first 7 parameter registers
2199              
2200             52 L - Restore the first 7 parameter registers except rax which is being used to return the result
2201              
2202             53 L - Restore the first 7 parameter registers except rax and rdi which are being used to return the results
2203              
2204             54 L - Reverse the bytes in rax
2205              
2206             55 L - Layout quad words in the data segment and return their label
2207              
2208             56 L - Layout bytes in read only memory and return their label
2209              
2210             57 L - Layout words in the data segment and return their label
2211              
2212             58 L - Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub
2213              
2214             59 L - Save the first 4 parameter registers
2215              
2216             60 L - Save the first 7 parameter registers
2217              
2218             61 L - Set a label in the code section
2219              
2220             62 L - Initialize the assembler
2221              
2222             63 L - Stat a file whise name is addressed by rax to get its size in rax
2223              
2224             64 L - Wait for the pid in rax to complete
2225              
2226             =head1 Installation
2227              
2228             This module is written in 100% Pure Perl and, thus, it is easy to read,
2229             comprehend, use, modify and install via B:
2230              
2231             sudo cpan install Nasm::X86
2232              
2233             =head1 Author
2234              
2235             L
2236              
2237             L
2238              
2239             =head1 Copyright
2240              
2241             Copyright (c) 2016-2021 Philip R Brenan.
2242              
2243             This module is free software. It may be used, redistributed and/or modified
2244             under the same terms as Perl itself.
2245              
2246             =cut
2247              
2248              
2249              
2250             # Tests and documentation
2251              
2252             sub test
2253 0     0 0   {my $p = __PACKAGE__;
2254 0           binmode($_, ":utf8") for *STDOUT, *STDERR;
2255 0 0         return if eval "eof(${p}::DATA)";
2256 0           my $s = eval "join('', <${p}::DATA>)";
2257 0 0         $@ and die $@;
2258 0           eval $s;
2259 0 0         $@ and die $@;
2260 0           1
2261             }
2262              
2263             test unless caller;
2264              
2265             1;
2266             # podDocumentation
2267             #__DATA__
2268 1     1   7 use Time::HiRes qw(time);
  1         1  
  1         12  
2269 1     1   963 use Test::More;
  1         59520  
  1         10  
2270              
2271             my $localTest = ((caller(1))[0]//'Nasm::X86') eq "Nasm::X86"; # Local testing mode
2272              
2273             Test::More->builder->output("/dev/null") if $localTest; # Reduce number of confirmation messages during testing
2274              
2275             $ENV{PATH} = $ENV{PATH}.":/var/isde:sde"; # Intel emulator
2276              
2277             if ($^O =~ m(bsd|linux)i) # Supported systems
2278             {if (confirmHasCommandLineCommand(q(nasm)) and # Network assembler
2279             confirmHasCommandLineCommand(q(sde64))) # Intel emulator
2280             {plan tests => 30;
2281             }
2282             else
2283             {plan skip_all =>qq(Nasm or Intel 64 emulator not available);
2284             }
2285             }
2286             else
2287             {plan skip_all =>qq(Not supported on: $^O);
2288             }
2289              
2290             my $start = time; # Tests
2291              
2292             #goto latest;
2293              
2294             if (1) { #TExit #TPrintOutString #Tassemble #TStart
2295             Start;
2296             PrintOutString "Hello World";
2297             Exit;
2298             ok assemble =~ m(Hello World);
2299             }
2300              
2301             if (1) { #TMov #TComment #TRs #TPrintOutNl
2302             Start;
2303             Comment "Print a string from memory";
2304             my $s = "Hello World";
2305             Mov rax, Rs($s);
2306             Mov rdi, length $s;
2307             PrintOutMemory;
2308             Exit;
2309             ok assemble =~ m(Hello World);
2310             }
2311              
2312             if (1) { #TPrintOutRaxInHex #TXor
2313             Start;
2314             my $q = Rs('abababab');
2315             Mov(rax, "[$q]");
2316             PrintOutString "rax: ";
2317             PrintOutRaxInHex;
2318             PrintOutNl;
2319             Xor rax, rax;
2320             PrintOutString "rax: ";
2321             PrintOutRaxInHex;
2322             PrintOutNl;
2323             Exit;
2324             ok assemble() =~ m(rax: 6261 6261 6261 6261.*rax: 0000 0000 0000 0000)s;
2325             }
2326              
2327             if (1) { #TPrintOutRegistersInHex #TLea
2328             Start;
2329             my $q = Rs('abababab');
2330             Mov(rax, 1);
2331             Mov(rbx, 2);
2332             Mov(rcx, 3);
2333             Mov(rdx, 4);
2334             Mov(r8, 5);
2335             Lea r9, "[rax+rbx]";
2336             PrintOutRegistersInHex;
2337             Exit;
2338             my $r = assemble();
2339             ok $r =~ m( r8: 0000 0000 0000 0005.* r9: 0000 0000 0000 0003.*rax: 0000 0000 0000 0001)s;
2340             ok $r =~ m(rbx: 0000 0000 0000 0002.*rcx: 0000 0000 0000 0003.*rdx: 0000 0000 0000 0004)s;
2341             }
2342              
2343             if (1) { #TVmovdqu32 #TVprolq #TDs
2344             Start;
2345             my $q = Rs('a'..'z');
2346             Mov rax, Ds('0'x64); # Output area
2347             Vmovdqu32(xmm0, "[$q]"); # Load
2348             Vprolq (xmm0, xmm0, 32); # Rotate double words in quad words
2349             Vmovdqu32("[rax]", xmm0); # Save
2350             Mov rdi, 16;
2351             PrintOutMemory;
2352             Exit;
2353             ok assemble() =~ m(efghabcdmnopijkl)s;
2354             }
2355              
2356             if (1) {
2357             Start;
2358             my $q = Rs(('a'..'p')x2);
2359             Mov rax, Ds('0'x64);
2360             Vmovdqu32(ymm0, "[$q]");
2361             Vprolq (ymm0, ymm0, 32);
2362             Vmovdqu32("[rax]", ymm0);
2363             Mov rdi, 32;
2364             PrintOutMemory;
2365             Exit;
2366             ok assemble() =~ m(efghabcdmnopijklefghabcdmnopijkl)s;
2367             }
2368              
2369             if (1) { #TPopR #TVmovdqu64
2370             Start;
2371             my $q = Rs my $s = join '', ('a'..'p')x4;;
2372             Mov rax, Ds('0'x128);
2373              
2374             Vmovdqu32 zmm0, "[$q]";
2375             Vprolq zmm1, zmm0, 32;
2376             Vmovdqu32 "[rax]", zmm1;
2377              
2378             Mov rdi, length $s;
2379             PrintOutMemory;
2380             Exit;
2381              
2382             ok $s =~ m(abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop)s;
2383             ok assemble() =~ m(efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl)s;
2384             }
2385              
2386             if (1) { #TPrintOutRegisterInHex
2387             Start;
2388             my $q = Rs(('a'..'p')x4);
2389             Mov r8,"[$q]";
2390             PrintOutRegisterInHex r8;
2391             Exit;
2392             ok assemble() =~ m(r8: 6867 6665 6463 6261)s;
2393             }
2394              
2395             if (1) { #TVmovdqu8
2396             Start;
2397             my $q = Rs('a'..'p');
2398             Vmovdqu8 xmm0, "[$q]";
2399             PrintOutRegisterInHex xmm0;
2400             Exit;
2401             ok assemble() =~ m(xmm0: 706F 6E6D 6C6B 6A69 6867 6665 6463 6261)s;
2402             }
2403              
2404             if (1) {
2405             Start;
2406             my $q = Rs('a'..'p', 'A'..'P', );
2407             Vmovdqu8 ymm0, "[$q]";
2408             PrintOutRegisterInHex ymm0;
2409             Exit;
2410             ok assemble() =~ m(ymm0: 504F 4E4D 4C4B 4A49 4847 4645 4443 4241 706F 6E6D 6C6B 6A69 6867 6665 6463 6261)s;
2411             }
2412              
2413             if (1) {
2414             Start;
2415             my $q = Rs(('a'..'p', 'A'..'P') x 2);
2416             Vmovdqu8 zmm0, "[$q]";
2417             PrintOutRegisterInHex zmm0;
2418             Exit;
2419             ok assemble() =~ m(zmm0: 504F 4E4D 4C4B 4A49 4847 4645 4443 4241 706F 6E6D 6C6B 6A69 6867 6665 6463 6261 504F 4E4D 4C4B 4A49 4847 4645 4443 4241 706F 6E6D 6C6B 6A69 6867 6665 6463 6261)s;
2420             }
2421              
2422             if (1) { #TallocateMemory #TfreeMemory
2423             Start;
2424             my $N = 2048;
2425             my $q = Rs('a'..'p');
2426             Mov rax, $N;
2427             allocateMemory;
2428             PrintOutRegisterInHex rax;
2429              
2430             Vmovdqu8 xmm0, "[$q]";
2431             Vmovdqu8 "[rax]", xmm0;
2432             Mov rdi,16;
2433             PrintOutMemory;
2434             PrintOutNl;
2435              
2436             Mov rdi, $N;
2437             freeMemory;
2438             PrintOutRegisterInHex rax;
2439             Exit;
2440             ok assemble() =~ m(abcdefghijklmnop)s;
2441             }
2442              
2443             if (1) { #TreadTimeStampCounter
2444             Start;
2445             for(1..10)
2446             {readTimeStampCounter;
2447             PrintOutRegisterInHex rax;
2448             }
2449             Exit;
2450             my @s = split /\n/, assemble();
2451             my @S = sort @s;
2452             is_deeply \@s, \@S;
2453             }
2454              
2455             if (1) { #TIf
2456             Start;
2457             Mov rax, 0;
2458             Test rax,rax;
2459             If
2460             {PrintOutRegisterInHex rax;
2461             } sub
2462             {PrintOutRegisterInHex rbx;
2463             };
2464             Mov rax, 1;
2465             Test rax,rax;
2466             If
2467             {PrintOutRegisterInHex rcx;
2468             } sub
2469             {PrintOutRegisterInHex rdx;
2470             };
2471             Exit;
2472             ok assemble() =~ m(rbx.*rcx)s;
2473             }
2474              
2475             if (1) { #TFork #TGetPid #TGetPPid #TWaitPid
2476             Start; # Start the program
2477             Fork; # Fork
2478              
2479             Test rax,rax;
2480             If # Parent
2481             {Mov rbx, rax;
2482             WaitPid;
2483             PrintOutRegisterInHex rax;
2484             PrintOutRegisterInHex rbx;
2485             GetPid; # Pid of parent as seen in parent
2486             Mov rcx,rax;
2487             PrintOutRegisterInHex rcx;
2488             }
2489             sub # Child
2490             {Mov r8,rax;
2491             PrintOutRegisterInHex r8;
2492             GetPid; # Child pid as seen in child
2493             Mov r9,rax;
2494             PrintOutRegisterInHex r9;
2495             GetPPid; # Parent pid as seen in child
2496             Mov r10,rax;
2497             PrintOutRegisterInHex r10;
2498             };
2499              
2500             Exit; # Return to operating system
2501              
2502             my $r = assemble();
2503              
2504             # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child
2505             # r9: 0000 0000 0003 0C63 #2 Pid of child
2506             # r10: 0000 0000 0003 0C60 #3 Pid of parent from child
2507             # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent
2508             # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
2509             # rcx: 0000 0000 0003 0C60 #6 Pid of parent
2510              
2511             if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s)
2512             {ok $2 eq $4;
2513             ok $2 eq $5;
2514             ok $3 eq $6;
2515             ok $2 gt $6;
2516             }
2517             }
2518              
2519             if (1) { #TFork #TGetPid #TGetPPid #TWaitPid
2520             Start; # Start the program
2521             GetUid; # Userid
2522             PrintOutRegisterInHex rax;
2523             Exit; # Return to operating system
2524             my $r = assemble();
2525             ok $r =~ m(rax:( 0000){3});
2526             }
2527              
2528             if (1) { #TStatSize
2529             Start; # Start the program
2530             Mov rax, Rs($0); # File to stat
2531             StatSize; # Stat the file
2532             PrintOutRegisterInHex rax;
2533             Exit; # Return to operating system
2534             my $r = assemble() =~ s( ) ()gsr;
2535             if ($r =~ m(rax:([0-9a-f]{16}))is) # Compare file size obtained with that from fileSize()
2536             {is_deeply $1, sprintf("%016X", fileSize($0));
2537             }
2538             }
2539              
2540             if (1) { #TOpenRead #TClose
2541             Start; # Start the program
2542             Mov rax, Rs($0); # File to stat
2543             OpenRead; # Open file
2544             PrintOutRegisterInHex rax;
2545             Close(rax); # Close file
2546             PrintOutRegisterInHex rax;
2547             Exit; # Return to operating system
2548             my $r = assemble();
2549             ok $r =~ m(( 0000){3} 0003)i; # Expected file number
2550             ok $r =~ m(( 0000){4})i; # Expected file number
2551             }
2552              
2553             if (1) { #TFor
2554             Start; # Start the program
2555             For
2556             {PrintOutRegisterInHex rax
2557             } rax, 16, 1;
2558             Exit; # Return to operating system
2559             my $r = assemble;
2560             ok $r =~ m(( 0000){3} 0000)i;
2561             ok $r =~ m(( 0000){3} 000F)i;
2562             }
2563              
2564             if (1) { #TPrintOutRaxInReverseInHex
2565             Start;
2566             Mov rax, 0x88776655;
2567             Shl rax, 32;
2568             Or rax, 0x44332211;
2569             PrintOutRaxInHex;
2570             PrintOutRaxInReverseInHex;
2571             Exit;
2572             ok assemble =~ m(8877 6655 4433 2211 1122 3344 5566 7788)s;
2573             }
2574              
2575             if (1) { #TallocateMemory #TfreeMemory
2576             Start;
2577             my $N = 4096;
2578             my $S = registerSize rax;
2579             Mov rax, $N;
2580             allocateMemory;
2581             PrintOutRegisterInHex rax;
2582             Mov rdi, $N;
2583             MemoryClear;
2584             PrintOutRegisterInHex rax;
2585             PrintOutMemoryInHex;
2586             Exit;
2587              
2588             my $r = assemble;
2589             if ($r =~ m((0000.*0000))s)
2590             {is_deeply length($1), 10269;
2591             }
2592             }
2593              
2594             if (1) { #TCall
2595             Start; # Start the program
2596             Mov rax,0x44332211;
2597             PrintOutRegisterInHex rax;
2598             my $s = S
2599             {PrintOutRegisterInHex rax;
2600             Inc rax;
2601             PrintOutRegisterInHex rax;
2602             };
2603             Call $s;
2604             PrintOutRegisterInHex rax;
2605             Exit; # Return to operating system
2606             my $r = assemble();
2607             ok $r =~ m(0000 0000 4433 2211.*2211.*2212.*0000 0000 4433 2212)s;
2608             }
2609              
2610             latest:;
2611              
2612             if (1) { #TReadFile #TPrintMemory
2613             Start; # Start the program
2614             Mov rax, Rs($0); # File to read
2615             ReadFile; # Read file
2616             PrintOutMemory; # Print memory
2617             Exit; # Return to operating system
2618             my $r = assemble(); # Assemble and execute
2619             ok index($r, readFile($0)) > -1; # Output contains this file
2620             }
2621              
2622             lll "Finished:", time - $start;