Generate and run x64 Advanced Vector Extensions assembler programs from Perl
This Perl module generates and runs x64 Advanced Vector Extensions assembler programs using Perl as a powerful macro assembler. It contains methods to perform useful macro functions such as dumping x/y/zmm* registers to facilitate the debugging of the generated programs or interacting with the operating system.
The GitHub Action in this repo shows how to install nasm and the Intel Software Development Emulator used to assemble and then run the programs generated by this module.
Test cases can be seen at the end of file lib/Nasm/X86.pm and in the t folder. The test cases are run by the GitHub Action at steps Test and Prove.
This module is part of the Perl Zero project: using Perl 5 to create a minimal, modern version of Perl which generates x86 assembler code directly. Perl Zero is process friendly: every data structure used is completely relocatable and so can be moved directly between different processes via a file or a socket. A Perl Zero program is a single expression with no key words: only user defined infix operators and expressions are used to construct programs. Perl Zero leverages Perl 5 as its macro assembler and CPAN as its module repository. Please feel free to join in.
Useful links
Avx512 instructions
Use Advanced Vector Extensions instructions to add and subtract bytes using 512 bit zmm registers:
my $a = Vz a, Rb((map {"0x${_}0"} 0..9, 'a'..'f')x4);
my $b = Vz b, Rb((map {"0x0${_}"} 0..9, 'a'..'f')x4);
$a ->loadZmm(0); # Show variable in zmm0
$b ->loadZmm(1); # Show variable in zmm1
($a + $b)->loadZmm(2); # Add bytes and show in zmm2
($a - $b)->loadZmm(3); # Subtract bytes and show in zmm3
PrintOutRegisterInHex "zmm$_" for 0..3;
is_deeply Assemble, <<END;
zmm0: F0E0 D0C0 B0A0 9080 7060 5040 3020 1000 F0E0 D0C0 B0A0 9080 7060 5040 3020 1000 F0E0 D0C0 B0A0 9080 7060 5040 3020 1000 F0E0 D0C0 B0A0 9080 7060 5040 3020 1000
zmm1: 0F0E 0D0C 0B0A 0908 0706 0504 0302 0100 0F0E 0D0C 0B0A 0908 0706 0504 0302 0100 0F0E 0D0C 0B0A 0908 0706 0504 0302 0100 0F0E 0D0C 0B0A 0908 0706 0504 0302 0100
zmm2: FFEE DDCC BBAA 9988 7766 5544 3322 1100 FFEE DDCC BBAA 9988 7766 5544 3322 1100 FFEE DDCC BBAA 9988 7766 5544 3322 1100 FFEE DDCC BBAA 9988 7766 5544 3322 1100
zmm3: E1D2 C3B4 A596 8778 695A 4B3C 2D1E 0F00 E1D2 C3B4 A596 8778 695A 4B3C 2D1E 0F00 E1D2 C3B4 A596 8778 695A 4B3C 2D1E 0F00 E1D2 C3B4 A596 8778 695A 4B3C 2D1E 0F00
END
Dynamic string held in an arena
Create a dynamic byte string, add some content to it, write the byte string to a file and then execute it:
use Nasm::X86 qw(:all);
my $s = CreateByteString; # Create a string
$s->ql(<<END); # Write [code](https://en.wikipedia.org/wiki/Computer_program) to execute
#!/usr/bin/bash
whoami
ls -lapwd
END
$s->write; # Write [code](https://en.wikipedia.org/wiki/Computer_program) to a temporary [file](https://en.wikipedia.org/wiki/Computer_file)
$s->bash; # Execute the temporary [file](https://en.wikipedia.org/wiki/Computer_file)
$s->unlink; # Unlink the temporary [file](https://en.wikipedia.org/wiki/Computer_file)
my $u = qx(whoami); chomp($u); # Check we got the expected response
ok Assemble =~ m($u); # Assemble, run, [test](https://en.wikipedia.org/wiki/Software_testing) output
Process management
Start a child process and wait for it, printing out the process identifiers of each process involved:
use Nasm::X86 qw(:all);
Fork; # Fork
Test rax,rax;
If # Parent
{Mov rbx, rax;
WaitPid;
PrintOutRegisterInHex rax;
PrintOutRegisterInHex rbx;
GetPid; # Pid of parent as seen in parent
Mov rcx,rax;
PrintOutRegisterInHex rcx;
}
[sub](https://perldoc.perl.org/perlsub.html) # Child
{Mov r8,rax;
PrintOutRegisterInHex r8;
GetPid; # Child pid as seen in child
Mov r9,rax;
PrintOutRegisterInHex r9;
GetPPid; # Parent pid as seen in child
Mov r10,rax;
PrintOutRegisterInHex r10;
};
my $r = Assemble; # Assemble [test](https://en.wikipedia.org/wiki/Software_testing) and run
# r8: 0000 0000 0000 0000 #1 Return from [fork](https://en.wikipedia.org/wiki/Fork_(system_call)) as seen by child
# r9: 0000 0000 0003 0C63 #2 Pid of child
# r10: 0000 0000 0003 0C60 #3 Pid of parent from child
# rax: 0000 0000 0003 0C63 #4 Return from [fork](https://en.wikipedia.org/wiki/Fork_(system_call)) as seen by parent
# rbx: 0000 0000 0003 0C63 #5 Wait for child pid result
# rcx: 0000 0000 0003 0C60 #6 Pid of parent
Read a file and print it out
Read this file and print it out:
use Nasm::X86 qw(:all);
Mov rax, Rs($0); # File to read
ReadFile; # Read [file](https://en.wikipedia.org/wiki/Computer_file)
PrintOutMemory; # Print memory
my $r = Assemble; # Assemble and execute
ok index($r, readFile($0)) > -1; # Output contains this [file](https://en.wikipedia.org/wiki/Computer_file) ```
For documentation see: [CPAN](https://metacpan.org/pod/Nasm::X86)