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;