Machine code: Difference between revisions
Line 1,172: | Line 1,172: | ||
=={{header|Scala}}== |
=={{header|Scala}}== |
||
{{incomplete|Scala| The text below cannot be considered a contribution that specifically addresses the task, it is rather a subjective judgement. Please either contribute code or remove and place in the talk section of this page. Regards.}} |
|||
PEEK, POKE and inserting machine opcode makes your system vulnerable which is not quite professional. |
PEEK, POKE and inserting machine opcode makes your system vulnerable which is not quite professional. |
||
Revision as of 15:44, 13 July 2021
You are encouraged to solve this task according to the task description, using any language you may know.
The task requires poking machine code directly into memory and executing it. The machine code is the architecture-specific opcodes which have the simple task of adding two unsigned bytes together and making the result available to the high-level language.
For example, the following assembly language program is given for x86 (32 bit) architectures:
<lang asm>mov EAX, [ESP+4] add EAX, [ESP+8] ret</lang>
This would translate into the following opcode bytes: <lang>139 68 36 4 3 68 36 8 195</lang>
Or in hexadecimal: <lang>8B 44 24 04 03 44 24 08 C3</lang>
- Task
If different than 32-bit x86, specify the target architecture of the machine code for your example. It may be helpful to also include an assembly version of the machine code for others to reference and understand what is being executed. Then, implement the following in your favorite programming language:
- Poke the necessary opcodes into a memory location.
- Provide a means to pass two values to the machine code.
- Execute the machine code with the following arguments: unsigned-byte argument of value 7; unsigned-byte argument of value 12; The result would be 19.
- Perform any clean up actions that are appropriate for your chosen language (free the pointer or memory allocations, etc.)
AutoHotkey
MCode Tutorial (Forum Thread)
MCode4GCC (Forum Thread | GitHub) - An MCode generator using the GCC Compiler. <lang AutoHotkey>MCode(Var, "8B44240403442408C3") MsgBox, % DllCall(&Var, "Char",7, "Char",12) Var := "" return
MCode(ByRef code, hex) { ; allocate memory and write Machine Code there
VarSetCapacity(code, StrLen(hex) // 2) Loop % StrLen(hex) // 2 NumPut("0x" . SubStr(hex, 2 * A_Index - 1, 2), code, A_Index - 1, "Char")
}</lang>
BBC BASIC
Note that BBC BASIC for Windows includes an 80386/80486 assembler as standard!
<lang bbcbasic> REM Claim 9 bytes of memory
SYS "GlobalAlloc",0,9 TO code%
REM Poke machine code into it P%=code% [OPT 0 mov EAX, [ESP+4] add EAX, [ESP+8] ret ]
REM Run code SYS code%,7,12 TO result% PRINT result%
REM Free memory SYS "GlobalFree",code% END</lang>
C
<lang C>#include <stdio.h>
- include <sys/mman.h>
- include <string.h>
int test (int a, int b) {
/* mov EAX, [ESP+4] add EAX, [ESP+8] ret */ char code[] = {0x8B, 0x44, 0x24, 0x4, 0x3, 0x44, 0x24, 0x8, 0xC3}; void *buf; int c; /* copy code to executable buffer */ buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON,-1,0);
memcpy (buf, code, sizeof(code)); /* run code */ c = ((int (*) (int, int))buf)(a, b); /* free buffer */ munmap (buf, sizeof(code)); return c;
}
int main () {
printf("%d\n", test(7,12)); return 0;
}</lang>
COBOL
This solution is a 64-bit adaptation of the task, using the macOS ABI and 64-bit instructions. The assembly code in question is:
<lang Assembler>pushq %rbp
movq %rsp, %rbp
movl %edi, -0x4(%rbp)
movl %esi, -0x8(%rbp)
movl -0x4(%rbp), %esi
addl -0x8(%rbp), %esi
movl %esi, -0xc(%rbp)
movl -0xc(%rbp), %eax
popq %rbp
retq
</lang>
The 64-bit "wrapper code" used by the PicoLisp and Go implementations have the parameters 7
and 12
baked into it, so I opted for a pure 64-bit implementation rather than manipulating the 64-bit stack to support the 32-bit instructions.
<lang COBOL> >>SOURCE FORMAT IS FIXED
IDENTIFICATION DIVISION. PROGRAM-ID. MC. DATA DIVISION. WORKING-STORAGE SECTION. 01 INSTRUCTIONS. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'55'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'48'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'89'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'E5'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'89'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'7D'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'FC'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'89'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'75'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'F8'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'8B'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'75'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'FC'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'03'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'75'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'F8'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'89'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'75'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'F4'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'8B'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'45'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'F4'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'5D'. 03 USAGE BINARY-CHAR UNSIGNED VALUE H'C3'. 01 MMAP. 03 MMAP-ADDR USAGE POINTER VALUE NULL. 03 MMAP-LEN USAGE BINARY-LONG UNSIGNED VALUE 24. 03 MMAP-PROT USAGE BINARY-INT VALUE H'0007'. 03 MMAP-FLAGS USAGE BINARY-INT VALUE H'1002'. 03 MMAP-FD USAGE BINARY-INT VALUE -1. 03 MMAP-OFFSET USAGE BINARY-LONG VALUE 0. 03 CODE-PTR USAGE PROCEDURE-POINTER. 01 ARG-A USAGE BINARY-INT VALUE 7. 01 ARG-B USAGE BINARY-INT VALUE 12. 01 RESULT USAGE BINARY-INT. LINKAGE SECTION. 01 MACHINE-CODE PIC X(24). PROCEDURE DIVISION. MAIN SECTION. PERFORM SET-UP. CALL CODE-PTR USING BY VALUE ARG-A BY VALUE ARG-B RETURNING RESULT. DISPLAY RESULT. PERFORM TEAR-DOWN. STOP RUN.
SET-UP SECTION. CALL 'mmap' USING BY VALUE MMAP-ADDR BY VALUE MMAP-LEN BY VALUE MMAP-PROT BY VALUE MMAP-FLAGS BY VALUE MMAP-FD BY VALUE MMAP-OFFSET RETURNING CODE-PTR. SET ADDRESS OF MACHINE-CODE TO CODE-PTR. MOVE INSTRUCTIONS TO MACHINE-CODE.
TEAR-DOWN SECTION. SET ADDRESS OF MACHINE-CODE TO NULL. CALL 'munmap' USING BY VALUE CODE-PTR BY VALUE MMAP-LEN.
</lang>
- Output:
+0000000019
Commodore BASIC
For Commodore computers, machine language routines were often written to increase speed of a program, provide functionality not otherwise provided for in BASIC, or execute code in the background. It was frequent to see small machine language routines incorporated into BASIC programs for these purposes.
The machine code shown for adding two numbers targets the MOS 65xx/85xx architecture (6502, 6510, etc.) and is given as follows:
<lang>Assembly Hexadecimal Decimal
CLC 18 24 LDA $2000 AD 00 20 173 0 32 ADC $2001 6D 01 20 109 1 32 STA $2002 8D 02 20 141 2 32 RTS 60 96</lang>
- The numbers to be added must be poked into locations $2000 and $2001 (8192 and 8193)
- The machine code is called for execution at $2003 with the SYS statement. (Note, since we are using ADd with Carry, we should CLear the Carry flag first to be sure the result is accurate.)
- The result is stored in location $2002 (8194).
- The machine code must issue a RTS (ReTurn from Subroutine) to allow BASIC to continue.
Note: No memory management is performed in this example, which would protect the machine code from being overwritten by BASIC. On some models, machine code can be place in RAM locations that are not affected by BASIC, such as the range from $C000 to $CFFF (49152 to 53247) on the Commodore 64.
This example, using RAM space at $2000, has been tested to work on the Commodore PET (32k), VIC-20 (28k), Commodore 64, Commodore Plus/4, and Commodore 128. Other configurations are possible unique to each machine.
<lang gwbasic>10 print chr$(147); 15 ml=8192 20 if peek(ml+3)<>173 and peek(ml+12)<>96 then gosub 100 30 for ad=ml to ml+2:poke ad,0:next 40 poke ml,7:poke ml+1,12 50 print "before:";peek(ml+2) 60 sys ml+3 70 print "after:";peek(ml+2) 80 end 100 rem machine language loader 105 for ad=ml+3 to ml+12 110 read b 115 poke ad,b 120 next 125 return 8195 data 24 :rem clc 8196 data 173,0,32 :rem lda $2000 8199 data 109,1,32 :rem adc $2001 8202 data 141,2,32 :rem sta $2002 8205 data 96 :rem rts</lang>
- Output:
BEFORE: 0 AFTER: 19 READY. █
Common Lisp
<lang lisp>;;Note that by using the 'CFFI' library, one can apply this procedure portably in any lisp implementation;
- in this code however I chose to demonstrate only the implementation-dependent programs.
- CCL
- Allocate a memory pointer and poke the opcode into it
(defparameter ptr (ccl::malloc 9))
(loop for i in '(139 68 36 4 3 68 36 8 195)
for j from 0 do (setf (ccl::%get-unsigned-byte ptr j) i))
- Execute with the required arguments and return the result as an unsigned-byte
(ccl::ff-call ptr :UNSIGNED-BYTE 7 :UNSIGNED-BYTE 12 :UNSIGNED-BYTE)
- Output = 19
- Free the pointer
(ccl::free ptr)
- SBCL
(defparameter mmap (list 139 68 36 4 3 68 36 8 195))
(defparameter pointer (sb-alien:make-alien sb-alien:unsigned-char (length mmap)))
(defparameter callp (loop for byte in mmap
for i from 0
do (setf (sb-alien:deref pointer i) byte) finally (return (sb-alien:cast pointer (function integer integer integer)))))
(sb-alien:alien-funcall callp 7 12)
(loop for i from 0 below 18 collect (sb-alien:deref ptr i))
(sb-alien:free-alien pointer)
- CLISP
(defparameter mmap (list 139 68 36 4 3 68 36 8 195))
(defparameter POINTER (FFI:FOREIGN-ADDRESS (FFI:FOREIGN-ALLOCATE 'FFI:UINT8 :COUNT 9)))
(loop for i in mmap
for j from 0 do (FUNCALL #'(SETF FFI:MEMORY-AS) i POINTER 'FFI:INT j))
(FUNCALL
(FFI:FOREIGN-FUNCTION POINTER
(LOAD-TIME-VALUE (FFI:PARSE-C-TYPE '(FFI:C-FUNCTION (:ARGUMENTS 'FFI:INT 'FFI:INT) (:RETURN-TYPE FFI:INT) (:LANGUAGE :STDC)))))
7 12)
(FFI:FOREIGN-FREE POINTER) </lang>
Cowgol
<lang cowgol>include "cowgol.coh";
- Run machine code at cptr, given two 32-bit arguments,
- return the value returned from EAX.
sub RunCode(cptr: [uint8], arg1: uint32, arg2: uint32): (rslt: uint32) is
# Inline assembly is supported, so this whole rigmarole # is not even necessary. # Though note that this (obviously) depends on the assembly back-end used. # Linux as uses AT&T syntax, so that's what I'm doing here. # Cowgol supports many processors but this will, obviously, only work # on x86. @asm "pushl (",arg1,")"; # Push the two arguments on the stack @asm "pushl (",arg2,")"; @asm "call *(",cptr,")"; # Call the code at the pointer @asm "movl %eax, (",rslt,")"; # Store the result in rslt @asm "popl %eax"; # Clean up the stack @asm "popl %eax";
end sub;
- Store code in an array. This is enough to make it available.
var code: uint8[] := {139, 68, 36, 4, 3, 68, 36, 8, 195};
- Use the function
print_i32(RunCode(&code as [uint8], 7, 12)); # this prints 7+12 = 19 print_nl();
- As a demonstration, this shows it can be patched at runtime to multiply instead
code[4] := 247; code[5] := 100; print_i32(RunCode(&code as [uint8], 7, 12)); # this prints 7*12 = 84 print_nl();</lang>
- Output:
19 84
D
In D you usually use a nicer asm {}
statement for similar purposes.
Generally new operating systems forbid execution of any address unless it's known to contain executable code. This is a basic version that unlike the C entry executes from array memory. This may crash on some operating systems. <lang d>int test(in int a, in int b) pure nothrow @nogc {
/* mov EAX, [ESP+4] add EAX, [ESP+8] ret */ immutable ubyte[9] code = [0x8B, 0x44, 0x24, 0x4, 0x3, 0x44, 0x24, 0x8, 0xC3]; alias F = extern(C) int function(int, int) pure nothrow @nogc; immutable f = cast(F)code.ptr; return f(a, b); // Run code.
}
void main() {
import std.stdio;
test(7, 12).writeln;
}</lang>
- Output:
19
Go
This task requires the use of 'cgo' which enables Go to interface with C code by importing a pseudo-package called "C".
Although Go supports both 32-bit and 64-bit architectures, I'm writing this on a 64-bit Ubuntu 16.04 system. I've therefore utilized the PicoLisp entry's 'glue code' to enable the 32-bit code to run on it.
There doesn't appear to be a way to cast a pointer to a native buffer to a Go function pointer so that the machine code can be run directly. I've therefore written a C function to perform this step and embedded it in the program which 'cgo' allows us to do. <lang go>package main
import "fmt"
/*
- include <stdio.h>
- include <stdlib.h>
- include <sys/mman.h>
- include <string.h>
typedef unsigned char byte; typedef byte (*mcfunc) (byte, byte);
void runMachineCode(void *buf, byte a, byte b) {
mcfunc fp = (mcfunc)buf; printf("%d\n", fp(a, b));
}
- /
import "C"
func main() {
code := []byte{ 144, // Align 144, 106, 12, // Prepare stack 184, 7, 0, 0, 0, 72, 193, 224, 32, 80, 139, 68, 36, 4, 3, 68, 36, 8, // Rosetta task code 76, 137, 227, // Get result 137, 195, 72, 193, 227, 4, 128, 203, 2, 72, 131, 196, 16, // Clean up stack 195, // Return } le := len(code) buf := C.mmap(nil, C.size_t(le), C.PROT_READ|C.PROT_WRITE|C.PROT_EXEC, C.MAP_PRIVATE|C.MAP_ANON, -1, 0) codePtr := C.CBytes(code) C.memcpy(buf, codePtr, C.size_t(le)) var a, b byte = 7, 12 fmt.Printf("%d + %d = ", a, b) C.runMachineCode(buf, C.byte(a), C.byte(b)) C.munmap(buf, C.size_t(le)) C.free(codePtr)
}</lang>
- Output:
7 + 12 = 19
Julia
Julia cannot execute machine code directly, but can embed C and C++ with the Cxx library. <lang julia>using Cxx
cxx"""
- include <stdio.h>
- include <sys/mman.h>
- include <string.h>
int test (int a, int b) {
/* mov EAX, [ESP+4] add EAX, [ESP+8] ret */ char code[] = {0x8B, 0x44, 0x24, 0x4, 0x3, 0x44, 0x24, 0x8, 0xC3}; void *buf; int c; /* copy code to executable buffer */ buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON,-1,0); memcpy (buf, code, sizeof(code)); /* run code */ c = ((int (*) (int, int))buf)(a, b); /* free buffer */ munmap (buf, sizeof(code)); return c;
}
int main () {
printf("%d\n", test(7,12)); return 0;
} """
julia_function = @cxx main() julia_function() </lang>
Kotlin
This task presents a number of issues for Kotlin Native which at the time of writing (August 2017) is still in the earlier stages of development:-
1. The language doesn't (yet) have an unsigned Byte type, though this is easily solved by subtracting 256 from unsigned values between 128 and 255 inclusive and then using the signed Byte type.
2. As far as x86 is concerned, the language is currently only targetting 64-bit platforms including Ubuntu 14.04 on which I'm writing this. Rather than rewrite the task using x64 opcodes, I've used the PicoLisp entry's 'glue code' to enable the 32-bit machine code to run on a 64-bit system.
3. There doesn't appear to be a way to cast a pointer to a native buffer to a Kotlin function pointer so that the machine code can be run. I've therefore written a 'one line' C helper function (in mcode.def) to perform this step and compiled it to a library (mcode.klib) so that it can be called from Kotlin code. <lang C>// mcode.def ---
static inline unsigned char runMachineCode(void *code, unsigned char a, unsigned char b) {
return ((unsigned char (*) (unsigned char, unsigned char))code)(a, b);
}</lang>
<lang scala>// Kotlin Native version 0.3
import kotlinx.cinterop.* import string.* import mman.* import mcode.*
fun main(args: Array<String>) {
memScoped { val bytes = byteArrayOf( 144 - 256, // Align 144 - 256, 106, 12, // Prepare stack 184 - 256, 7, 0, 0, 0, 72, 193 - 256, 224 - 256, 32, 80, 139 - 256, 68, 36, 4, 3, 68, 36, 8, // Rosetta task code 76, 137 - 256, 227 - 256, // Get result 137 - 256, 195 - 256, 72, 193 - 256, 227 - 256, 4, 128 - 256, 203 - 256, 2, 72, 131 - 256, 196 - 256, 16, // Clean up stack 195 - 256 // Return ) val len = bytes.size val code = allocArray<ByteVar>(len) for (i in 0 until len) code[i] = bytes[i] val buf = mmap(null, len.toLong(), PROT_READ or PROT_WRITE or PROT_EXEC, MAP_PRIVATE or MAP_ANON, -1, 0) memcpy(buf, code, len.toLong()) val a: Byte = 7 val b: Byte = 12 val c = runMachineCode(buf, a, b) munmap(buf, len.toLong()) println("$a + $b = ${if(c >= 0) c.toInt() else c + 256}") }
}</lang>
- Output:
7 + 12 = 19
Lua
Thankfully, native Lua has no such provisions. This task should probably be marked "omit from|Lua", except that the task is not explicit whether or not only native facilities may be used. The task cites the Common Lisp example as reference, which does use an external FFI library to accomplish the task, so implies that external facilities may be used. If such tactics are allowed, then the LuaJIT distribution includes FFI. Or, wrap the "test" function of the C example, and expose it to Lua as an external library - that is well within Lua's capabilities (one of Lua's strengths, actually). Or take the modified approach used in the Kotlin example - Lua can easily build the raw byte array, then pass it to an external C library to execute. Or, simply compile the C example, then call it via os.execute("cexample.exe"). No code is given, as the task is outside the scope of pure/native Lua, but it would be easy to accomplish if external support were allowed.
M2000 Interpreter
We can execute machine code, in a buffer for code. We can't push to stack and then call, we can use a buffer for data. If eax is non zero then error raised, with error number the eax number. When execute code the code buffer can't be used to write over. so we have to use a buffer for data for read/write data. This example perform these: At Datamem(1) put 500, eax=5100, eax add Datamem(1), eax add 5, store eax to Datamem(0). We have an option to clear eax, or use it to return value as error code. We have to leave all other registers, and stack as we found it. Both running in Wine (Linux 64bit) too
<lang M2000 Interpreter>
Module Checkit {
Buffer DataMem as Long*10 Return DataMem, 1:=500 ' second Long Print Eval(DataMem, 1)+5100+5=5605 \\ Now we do math executing machine code Buffer Code ExecMem as byte*1024 Address=0 EmbLong(0xb8, 5100) ' mov eax,5100 EmbByteByte(0x83, 0xC0, 5) ' add eax,0x5 EmbByteLong(0x3,0x5, DataMem(1)) ' add eax, [DataMem(1)] EmbLong(0xa3, DataMem(0)) ' mov [DataMem(0)], eax \\ split rem to execute xor eax eax (eax=0) Rem : EmbByte(0x31, 0xC0) ' xor eax, eax Ret() ' Return \\ Try ok { Execute Code ExecMem, 0 } \\If Eax <>0 then we get error, so we read error as Uint() \\ Error read once then change to zero m=Uint(Error) \\ Hex is Print Hexadecimal for unsigned numbers Hex m Print m=5605 Print Error=0, ok=False Print Eval(DataMem, 0)=5605, Eval(DataMem, 0) \\ sub used as Exit here Sub Ret() Return ExecMem, Address:=0xC3 Address++ End Sub Sub EmbByteByte() Return ExecMem, Address:=Number, Address+1:=Number, Address+2:=Number Address+=3 End Sub Sub EmbByte() Return ExecMem, Address:=Number, Address+1:=Number Address+=2 End Sub Sub EmbLong() Return ExecMem, Address:=Number, Address+1:=Number as Long Address+=5 End Sub Sub EmbByteLong() Return ExecMem, Address:=Number, Address+1:=Number, Address+2:=Number as Long Address+=6 End Sub
} CheckIt </lang>
Using a lambda function with closures two buffers (buffers are objects in M2000 to handle memory blocks). This also add 12 +7 as the task want (but with no pushing to stack, but poke to data buffer)
<lang M2000 Interpreter> Function MyAdd {
Buffer DataMem as Long*2 Buffer Code ExecMem as byte*32 Address=0 EmbByte(0x31, 0xC0) EmbByteLong(0x3,0x5, DataMem(0)) ' add eax, [DataMem(0)] EmbByteLong(0x3,0x5, DataMem(1)) ' add eax, [DataMem(1)] EmbLong(0xa3, DataMem(0)) ' mov [DataMem(0)], eax Rem : EmbByte(0x31, 0xC0) ' xor eax, eax Ret() ' Return =lambda ExecMem, DataMem (a as double, b as double)-> { Return DataMem, 0:=a, 1:=b Try ok { Execute Code ExecMem, 0 } If not ok then { =Uint(Error) } Else { =Eval(DataMem, 0) } } Sub Ret() Return ExecMem, Address:=0xC3 Address++ End Sub Sub EmbByte() Return ExecMem, Address:=Number, Address+1:=Number Address+=2 End Sub Sub EmbLong() Return ExecMem, Address:=Number, Address+1:=Number as Long Address+=5 End Sub Sub EmbByteLong() Return ExecMem, Address:=Number, Address+1:=Number, Address+2:=Number as Long Address+=6 End Sub
} \\ Produce a lambda function with machine code inside UnsingedAdd=MyAdd() Print UnsingedAdd(12, 7), UnsingedAdd(500, 100) </lang>
Nim
<lang nim>import posix
when defined(macosx) or defined(bsd):
const MAP_ANONYMOUS = 0x1000
elif defined(solaris):
const MAP_ANONYMOUS = 0x100
else:
var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
proc test(a, b: cint): cint =
# mov EAX, [ESP+4] # add EAX, [ESP+8] var code = [0x8B'u8, 0x44, 0x24, 0x4, 0x3, 0x44, 0x24, 0x8, 0xC3]
# create executable buffer var buf = mmap(nil, sizeof(code), PROT_READ or PROT_WRITE or PROT_EXEC, MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
# copy code to buffer copyMem(addr buf, addr code[0], sizeof(code))
# run code {.emit: "`result` = ((int (*) (int, int))&`buf`)(`a`,`b`);".}
# free buffer discard munmap(buf, sizeof(code))
echo test(7, 12)</lang>
PARI/GP
GP can't peek and poke into memory, but PARI can add in those capabilities via C.
<lang c>#include <stdio.h>
- include <sys/mman.h>
- include <string.h>
- include <pari/pari.h>
int test(int a, int b) {
char code[] = {0x8B, 0x44, 0x24, 0x4, 0x3, 0x44, 0x24, 0x8, 0xC3}; void *buf; int c; /* copy code to executable buffer */ buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON,-1,0); memcpy (buf, code, sizeof(code)); /* run code */ c = ((int (*) (int, int))buf)(a, b); /* free buffer */ munmap (buf, sizeof(code)); return c;
}
void init_auto(void) {
pari_printf("%d\n", test(7,12)); return 0;
}</lang>
Pascal
Tested under Linux with Freepascal 2.6.4-32BIt ( like the Code used ) cdecl doesn't work in Freepascal under Linux 64-bit <lang pascal>Program Example66; {Inspired... program to demonstrate the MMap function. Freepascal docs } Uses
BaseUnix,Unix;
const
code : array[0..9] of byte = ($8B, $44, $24, $4, $3, $44, $24, $8, $C3, $00); a :longInt= 12; b :longInt= 7;
type
tDummyFunc = function(a,b:LongInt):LongInt;cdecl;
Var
Len,k : cint; P : Pointer;
begin
len := sizeof(code); P:= fpmmap(nil, len+1 , PROT_READ OR PROT_WRITE OR PROT_EXEC, MAP_ANONYMOUS OR MAP_PRIVATE, -1, // for MAP_ANONYMOUS 0); If P = Pointer(-1) then Halt(4);
for k := 0 to len-1 do pChar(p)[k] := char(code[k]);
k := tDummyFunc(P)(a,b);
Writeln(a,'+',b,' = ',k); if fpMUnMap(P,Len)<>0 Then Halt(fpgeterrno);
end.</lang>
- output
12+7 = 19
Phix
atom mem = allocate(9) poke(mem,{#8B,#44,#24,#04,#03,#44,#24,#08,#C3}) constant mfunc = define_c_func({},mem,{C_INT,C_INT},C_INT) ?c_func(mfunc,{12,7}) free(mem)
In Phix the #ilASM statement (which has guards to allow 32/64/WIN/LNX variants) is usually used for inline assembly, for example (but sticking to the task):
atom mem = allocate(9) poke(mem,{#8B,#44,#24,#04,#03,#44,#24,#08,#C3}) integer res #ilASM{ mov eax,[mem] call :%pLoadMint -- eax:=(int32)eax, in case mem>#3FFFFFFF push 12 push 7 call eax add esp,8 mov [res],eax } ?res free(mem)
Better yet, albeit deviating somewhat from the task (and this runs on both 32 and 64 bit):
integer res #ilASM{ jmp @f ::add [32] mov eax,[esp+4] add eax,[esp+8] [64] mov rax,[rsp+8] add rax,[rsp+16] [] ret @@: push 12 push 7 call :add [32] add esp,8 mov [res],eax [64] add rsp,16 mov [res],rax [] } ?res
All three cases output 19
PicoLisp
The following runs on 64-bit PicoLisp. Therefore we need some glue code to interface to the task's 32-bit code. <lang PicoLisp>(setq P
(struct (native "@" "malloc" 'N 39) 'N # Align 144 # nop 144 # nop
# Prepare stack 106 12 # pushq $12 184 7 0 0 0 # mov $7, %eax 72 193 224 32 # shl $32, %rax 80 # pushq %rax
# Rosetta task code 139 68 36 4 3 68 36 8
# Get result 76 137 227 # mov %r12, %rbx 137 195 # mov %eax, %ebx 72 193 227 4 # shl $4, %rbx 128 203 2 # orb $2, %bl
# Clean up stack 72 131 196 16 # add $16, %rsp
# Return 195 ) # ret foo (>> 4 P) )
- Execute
(println (foo))
- Free memory
(native "@" "free" NIL P)</lang> Output:
19
PL/M
This code runs on an 8080 (or Z80) processor.
PL/M does not assume the existence of any operating system features or come with a standard library, so communicating with the OS actually has to be done in the same manner as calling (any other) machine language routines.
<lang plm>100H:
/* 8080 MACHINE CODE TO ADD TWO BYTES:
79 MOV A,C ; LOAD FIRST ARG INTO ACCUMULATOR 83 ADD E ; ADD SECOND ARG TO ACCUMULATOR C9 RET ; RETURN */
DECLARE ADD$8080 DATA (79H, 83H, 0C9H);
/* THE 8080 PL/M CALLING CONVENTION IS THAT THE
NEXT-TO-LAST ARG IS PUT IN (B)C, THE LAST ARG IN (D)E. (THE REST ARE IN MEMORY BUT WE DO NOT NEED ANY MORE.) THE RETURN ARGUMENT SHOULD BE IN THE ACCUMULATOR. WE CAN DEFINE A WRAPPER PROCEDURE TO DECLARE THE TYPES OF THE ARGUMENTS. */
EXEC$ADD: PROCEDURE (A,B) BYTE;
DECLARE (A,B) BYTE; /* WE CAN 'GO TO' CONSTANTS OR VARIABLES, BUT NOT TO EXPRESSIONS. SO WE HAVE TO FETCH THE ADDRESS FIRST. */ DECLARE LOC ADDRESS; LOC = .ADD$8080; GO TO LOC;
END EXEC$ADD;
/* IN FACT, PL/M DOES NOT COME WITH ANY STANDARD LIBARIES.
IT IS FROM BEFORE THE TIME THAT YOU COULD ASSUME THERE WOULD EVEN BE AN OPERATING SYSTEM, THOUGH CP/M (THE PREDECESSOR TO DOS) WOULD QUICKLY BECOME STANDARD. WE NEED TO USE THIS EXACT TRICK TO GET CP/M TO PRINT THE RESULT TO THE OUTPUT. LUCKILY (AND NOT COINCIDENTALLY), THE CP/M SYSCALL ENTRY POINT IS COMPATIBLE WITH THE PL/M CALLING CONVENTION. */
BDOS: PROCEDURE (FUNC, ARG);
DECLARE FUNC BYTE; DECLARE ARG ADDRESS; /* 5 IS THE CP/M BDOS ENTRY POINT */ GO TO 5;
END BDOS;
/* WE ALSO NEED OUR OWN NUMBER OUTPUT ROUTINE. WE CAN WRITE
IT IN PL/M, THEN USE THE ABOVE ROUTINE TO TELL CP/M TO PRINT THE RESULT. */
PRINT$NUMBER: PROCEDURE(N);
DECLARE S (4) BYTE INITIAL ('...$'); DECLARE P ADDRESS; DECLARE (N, C BASED P) BYTE; /* EXTRACT EACH DIGIT AND WRITE THEM BACKWARDS TO A STRING */ P = .S(3);
DIGIT:
P = P-1; C = (N MOD 10) + '0'; N = N/10; IF N > 0 THEN GO TO DIGIT;
/* TELL CP/M TO PRINT THE RESULTING STRING */ CALL BDOS(9, P);
END PRINT$NUMBER;
/* USING OUR OWN MACHINE CODE WORKS IN THE SAME WAY */ CALL PRINT$NUMBER( EXEC$ADD( 7, 12) ); /* THIS PRINTS 19 */
CALL BDOS(0,0); /* EXIT */ EOF</lang>
- Output:
19
PureBasic
Using the Windows API:
<lang PureBasic>CompilerIf #PB_Compiler_Processor <> #PB_Processor_x86
CompilerError "Code requires a 32-bit processor."
CompilerEndIf
- Machine code using the Windows API
Procedure MachineCodeVirtualAlloc(a,b)
- vm = VirtualAlloc_(#Null,?ecode-?scode,#MEM_COMMIT,#PAGE_EXECUTE_READWRITE)
If(*vm) CopyMemory(?scode, *vm, ?ecode-?scode) eax_result=CallFunctionFast(*vm,a,b) VirtualFree_(*vm,0,#MEM_RELEASE) ProcedureReturn eax_result EndIf
EndProcedure
rv=MachineCodeVirtualAlloc( 7, 12) MessageRequester("MachineCodeVirtualAlloc",Str(rv)+Space(50),#PB_MessageRequester_Ok)
- HEAP_CREATE_ENABLE_EXECUTE=$00040000
Procedure MachineCodeHeapCreate(a,b) hHeap=HeapCreate_(#HEAP_CREATE_ENABLE_EXECUTE,?ecode-?scode,?ecode-?scode)
If(hHeap) CopyMemory(?scode, hHeap, ?ecode-?scode) eax_result=CallFunctionFast(hHeap,a,b) HeapDestroy_(hHeap) ProcedureReturn eax_result EndIf
EndProcedure
rv=MachineCodeHeapCreate(7,12) MessageRequester("MachineCodeHeapCreate",Str(rv)+Space(50),#PB_MessageRequester_Ok) End
- 8B442404 mov eax,[esp+4]
- 03442408 add eax,[esp+8]
- C20800 ret 8
DataSection scode: Data.a $8B,$44,$24,$04,$03,$44,$24,$08,$C2,$08,$00 ecode: EndDataSection</lang>
Python
The ctypes module is meant for calling existing native code from Python, but you can get it to execute your own bytes with some tricks. The bulk of the code is spent establishing an executable memory area - once that's done, the actual execution takes just a few lines.
<lang Python>import ctypes import os from ctypes import c_ubyte, c_int
code = bytes([0x8b, 0x44, 0x24, 0x04, 0x03, 0x44, 0x24, 0x08, 0xc3])
code_size = len(code)
- copy code into an executable buffer
if (os.name == 'posix'):
import mmap executable_map = mmap.mmap(-1, code_size, mmap.MAP_PRIVATE | mmap.MAP_ANON, mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC) # we must keep a reference to executable_map until the call, to avoid freeing the mapped memory executable_map.write(code) # the mmap object won't tell us the actual address of the mapping, but we can fish it out by allocating # some ctypes object over its buffer, then asking the address of that func_address = ctypes.addressof(c_ubyte.from_buffer(executable_map))
elif (os.name == 'nt'):
# the mmap module doesn't support protection flags on Windows, so execute VirtualAlloc instead code_buffer = ctypes.create_string_buffer(code) PAGE_EXECUTE_READWRITE = 0x40 # Windows constants that would usually come from header files MEM_COMMIT = 0x1000 executable_buffer_address = ctypes.windll.kernel32.VirtualAlloc(0, code_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE) if (executable_buffer_address == 0): print('Warning: Failed to enable code execution, call will likely cause a protection fault.') func_address = ctypes.addressof(code_buffer) else: ctypes.memmove(executable_buffer_address, code_buffer, code_size) func_address = executable_buffer_address
else:
# for other platforms, we just hope DEP isn't enabled code_buffer = ctypes.create_string_buffer(code) func_address = ctypes.addressof(code_buffer)
prototype = ctypes.CFUNCTYPE(c_int, c_ubyte, c_ubyte) # build a function prototype from return type and argument types func = prototype(func_address) # build an actual function from the prototype by specifying the address res = func(7,12) print(res) </lang>
Racket
<lang racket>#lang racket/base
(require ffi/unsafe)
- set up access to racket internals
(define scheme-malloc-code
(get-ffi-obj 'scheme_malloc_code #f (_fun (len : _intptr) -> _pointer)))
(define scheme-free-code
(get-ffi-obj 'scheme_free_code #f (_fun _pointer -> _void)))
(define opcodes '(139 68 36 4 3 68 36 8 195))
(define code (scheme-malloc-code 64))
(for ([byte opcodes]
[i (in-naturals)]) (ptr-set! code _ubyte i byte))
(define function (cast code _pointer (_fun _ubyte _ubyte -> _ubyte)))
(function 7 12)
(scheme-free-code code)</lang>
Raku
I don't know how to translate this C line <lang C>c = ((int (*) (int, int))buf)(a, b);</lang> so cannot solve the task with an idiomatic solution. I have also tried with Go's approach by adding a helper program but it also doesn't work out. Nonetheless I just present the attempt here so perhaps someone can fix that in 10 seconds. <lang perl6>use NativeCall;
constant PROT_READ = 0x1; # constant PROT_WRITE = 0x2; # constant PROT_EXEC = 0x4; # from local /usr/include/bits/mman.h constant MAP_PRIVATE = 0x02; # constant MAP_ANON = 0x20; #
sub mmap(Pointer $addr, size_t $length, int32 $prot, int32 $flags,
int32 $fd, size_t $offset --> Pointer) is native { * };
sub memcpy(Pointer $dest, Pointer $src, size_t $size --> Pointer) is native {*} sub munmap(Pointer $addr, size_t $length) is native { * };
sub test (uint8 $a, uint8 $b) {
my $code = CArray[uint8].new( 0x90, 0x90, 0x6A, 0xC, 0xB8, 0x7, 0x0, 0x0, 0x0, 0x48, 0xC1, 0xE0, 0x20, 0x50, 0x8B, 0x44, 0x24, 0x4, 0x3, 0x44, 0x24, 0x8, 0x4C, 0x89, 0xE3, 0x89, 0xC3, 0x48, 0xC1, 0xE3, 0x4, 0x80, 0xCB, 0x2, 0x48, 0x83, 0xC4, 0x10, 0xC3 ); my $buf = mmap(Pointer, nativesizeof($code), PROT_READ +| PROT_WRITE +| PROT_EXEC, MAP_PRIVATE +| MAP_ANON, -1, 0);
memcpy($buf, nativecast(Pointer,$code), nativesizeof($code));
my $c; # = ((int (*) (int, int))buf)(a, b);
munmap($buf, nativesizeof($code));
return $c = "Incomplete Attempt";
}
say test 7, 12;</lang>
In the mean time, here is a less desirable approach by writing a wrapper for the C entry, with the 64 bit instructions from PicoLisp ..
test.c
<lang C>#include <stdio.h>
- include <stdlib.h>
- include <sys/mman.h>
- include <string.h>
int test (int a, int b) {
char code[] = { 0x90, 0x90, 0x6A, 0xC, 0xB8, 0x7, 0x0, 0x0, 0x0, 0x48, 0xC1, 0xE0, 0x20, 0x50, 0x8B, 0x44, 0x24, 0x4, 0x3, 0x44, 0x24, 0x8, 0x4C, 0x89, 0xE3, 0x89, 0xC3, 0x48, 0xC1, 0xE3, 0x4, 0x80, 0xCB, 0x2, 0x48, 0x83, 0xC4, 0x10, 0xC3 };
void *buf; int c; /* copy code to executable buffer */ buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON,-1,0); memcpy (buf, code, sizeof(code)); /* run code */ c = ((int (*) (int, int))buf)(a, b); /* free buffer */ munmap (buf, sizeof(code)); return c;
}</lang> mcode.raku <lang perl6>#!/usr/bin/env raku
- 20200501 Raku programming solution
use NativeCall;
constant LIBTEST = '/home/user/LibTest.so';
sub test(uint8 $a, uint8 $b) returns uint8 is native(LIBTEST) { * };
say test 7, 12; </lang>
- Output:
gcc -Wall -fPIC -shared -o LibTest.so test.cfile LibTest.so LibTest.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=90d2695df9a56b88a57144147fb9288ac07b172f, not stripped ./mcode.raku
19
Rust
This is heavily inspired by https://www.jonathanturner.org/2015/12/building-a-simple-jit-in-rust.html
Hence, only working on Linux (the only other way to disable memory execution protection on other OSes was to use other crates, which kind of defeats the purpose.)
<lang Rust>extern crate libc;
- [cfg(all(
target_os = "linux", any(target_pointer_width = "32", target_pointer_width = "64")
))] fn main() {
use std::mem; use std::ptr;
let page_size: usize = 4096; let (bytes, size): (Vec<u8>, usize) = if cfg!(target_pointer_width = "32") { ( vec![0x8b, 0x44, 0x24, 0x04, 0x03, 0x44, 0x24, 0x08, 0xc3], 9, ) } else { (vec![0x48, 0x89, 0xf8, 0x48, 0x01, 0xf0, 0xc3], 7) }; let f: fn(u8, u8) -> u8 = unsafe { let mut page: *mut libc::c_void = ptr::null_mut(); libc::posix_memalign(&mut page, page_size, size); libc::mprotect( page, size, libc::PROT_EXEC | libc::PROT_READ | libc::PROT_WRITE, ); let contents: *mut u8 = page as *mut u8; ptr::copy(bytes.as_ptr(), contents, 9); mem::transmute(contents) };
let return_value = f(7, 12); println!("Returned value: {}", return_value); assert_eq!(return_value, 19);
}
- [cfg(any(
not(target_os = "linux"), not(any(target_pointer_width = "32", target_pointer_width = "64"))
))] fn main() {
println!("Not supported on this platform.");
} </lang>
Scala
PEEK, POKE and inserting machine opcode makes your system vulnerable which is not quite professional.
Considered to be more harmful than useful.
Smalltalk
I agree that this is more harmful than useful. The only target audience are compiler writers and Smalltalk-core developers (of which I guess are not too many around).
Also, this is highly cpu specific, the task is for an x86 and also assuming a particular calling convention; both is probably (definitely) not the case (most, incl. myself are on 64bit machines these days).
Anyway, as a sketch, here is how to do it (I won't waste time in making an x86_64 version for the particular calling convention used on my machine; and yes: it is even different between Unix and Windows systems!):
First we need a way to allocate executable memory (btw. we should probably also care to flush any instruction caches, which I won't go into here); This is very Smalltalk dialect specific, and probably not supported on other Smalltalks; in ST/X, where inline C-code can be compiled dynamically, we can define it as:
<lang smalltalk>!ExternalBytes class methods!
mapExecutableBytes:size %{
- include <sys/mman.h>
void *mem; OBJ retVal; int nBytes = __intVal(size);
mem = mmap(nil, nBytes, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0); if (mem != MAP_FAILED) { RETURN( __MKEXTERNALBYTES_N(mem, nBytes)); }
%}.
self primitiveFailed
! !</lang>
next, we need the correct code; the following presents the x86 version (but do not expect this code to NOT crash the Smalltalk VM, as the calling convention is certainly wrong..) <lang smalltalk>OperatingSystem getCPUType = #'x86' ifTrue:[
code := #[0x8B 0x44 0x24 0x04 0x03 0x44 0x24 0x08 0xC3].
] ifFalse:[
self error:'unsupported cpu'
].
handle := ExternalBytes mapExecutableBytes:100. handle replaceFrom:1 with:code.
" dump it (debugging only)... " e'code at {handle address hexPrintString} is:' printCR. (handle copyFrom:1 to:50) asByteArray hexPrintString printCR.
" create an ExternalFunction for it " func := ExternalLibraryFunction new code:handle address. func name:'unnamed' module:nil returnType:#int argumentTypes:#(int int). func beCallTypeC. func printCR.
" now call it " result := func invokeWithArguments:{10 . 20} </lang> With a few more tricks, it is even possible to install that function as a method in a class; but additional code needs to be generated, to assert that the passed data is correctly boxed/unboxed.
Swift
Using 64-bit glue code since Swift has limited 32-bit support on x86.
<lang swift>import Foundation
typealias TwoIntsOneInt = @convention(c) (Int, Int) -> Int
let code = [
144, // Align 144, 106, 12, // Prepare stack 184, 7, 0, 0, 0, 72, 193, 224, 32, 80, 139, 68, 36, 4, 3, 68, 36, 8, // Rosetta task code 76, 137, 227, // Get result 137, 195, 72, 193, 227, 4, 128, 203, 2, 72, 131, 196, 16, // Clean up stack 195, // Return
] as [UInt8]
func fudge(x: Int, y: Int) -> Int {
let buf = mmap(nil, code.count, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0)
memcpy(buf, code, code.count)
let fun = unsafeBitCast(buf, to: TwoIntsOneInt.self) let ret = fun(x, y)
munmap(buf, code.count)
return ret
}
print(fudge(x: 7, y: 12)) </lang>
Tcl
<lang tcl>package require critcl
critcl::ccode {
#include <sys/mman.h>
}
- Define a command using C. The C is embedded in Tcl, and will be
- built into a shared library at runtime. Note that Tcl does not
- provide a native way of doing this sort of thing; this thunk is
- mandatory.
critcl::cproc runMachineCode {Tcl_Obj* codeObj int a int b} int {
int size, result; unsigned char *code = Tcl_GetByteArrayFromObj(codeObj, &size); void *buf;
/* copy code to executable buffer */ buf = mmap(0, (size_t) size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0); memcpy(buf, code, (size_t) size); /* run code */ result = ((int (*) (int, int)) buf)(a, b); /* dispose buffer */ munmap(buf, (size_t) size);
return result;
}
- But now we have our thunk, we can execute arbitrary binary blobs
set code [binary format c* {0x8B 0x44 0x24 0x4 0x3 0x44 0x24 0x8 0xC3}]
puts [runMachineCode $code 7 12]</lang>
Note that it would be more common to put that thunk in its own package (e.g., machineCodeThunk
) and then just do something like this:
<lang tcl>package require machineCodeThunk 1.0
set code [binary format c* {0x8B 0x44 0x24 0x4 0x3 0x44 0x24 0x8 0xC3}] puts [runMachineCode $code 7 12]</lang>
- Programming Tasks
- Solutions by Programming Task
- AutoHotkey
- BBC BASIC
- C
- COBOL
- Commodore BASIC
- Common Lisp
- Cowgol
- D
- Go
- Julia
- Kotlin
- Lua
- Lua examples needing attention
- Examples needing attention
- M2000 Interpreter
- Nim
- PARI/GP
- Pascal
- Phix
- PicoLisp
- PL/M
- PureBasic
- Python
- Racket
- Raku
- Rust
- Scala
- Scala examples needing attention
- Smalltalk
- Swift
- Tcl
- Critcl
- Mathematica/Omit