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