Hi I am new to Nim, and I am giving it a try for low-level programming.
I am trying this example from Rosettacode: https://rosettacode.org/wiki/Machine_code#Nim
But for x86-64 and using pure Nim (instead of falling back on C code):
import posix
const
prot = PROT_READ or PROT_WRITE or PROT_EXEC
when defined(macosx) or defined(bsd):
const MAP_ANONYMOUS = 0x1000
else:
var
MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
type
IntToInt = proc(num: uint32): uint32 {.nimcall.}
proc generate_add4(): IntToInt =
var code = [
0x48'u8, 0x89'u8, 0xf8'u8, # mov rax, rdi
0x48'u8, 0x83'u8, 0xc0'u8, 0x00'u8, # add rax, 4
0xc3'u8 # ret
]
var mem = mmap(nil, code.len, prot, MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
copyMem(mem, code[0].unsafeAddr, code.len)
result = cast[IntToInt](mem)
discard munmap(mem, code.len)
var add4 = generate_add4()
echo add4(3)
Currently it fails with: SIGSEGV: Illegal storage access. (Attempt to read from nil?)
On repl.it: https://repl.it/@eterps/nimmachinecode
Any idea how to get this to work?
Ah, yes that was a bit silly (the Rosettacode example works in a different way). I changed it to:
import posix
const
prot = PROT_READ or PROT_WRITE or PROT_EXEC
when defined(macosx) or defined(bsd):
const MAP_ANONYMOUS = 0x1000
else:
var
MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
type
IntToInt = proc(num: uint32): uint32 {.nimcall.}
var code = [
0x48'u8, 0x89'u8, 0xf8'u8, # mov rax, rdi
0x48'u8, 0x83'u8, 0xc0'u8, 0x00'u8, # add rax, 4
0xc3'u8 # ret
]
var mem = mmap(nil, code.len, prot, MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
copyMem(mem, addr code[0], code.len)
var add4 = cast[IntToInt](mem)
echo add4(3)
discard munmap(mem, code.len)
It outputs '3' now, not the right answer yet, but at least no segfault. Thanks I'll look into this further.
I found an error in the machine code as well, this code sample works as expected:
import posix
const
prot = PROT_READ or PROT_WRITE or PROT_EXEC
when defined(macosx) or defined(bsd):
const MAP_ANONYMOUS = 0x1000
else:
var
MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
type
IntToInt = proc(num: uint32): uint32 {.nimcall.}
var code = [
0x48'u8, 0x89, 0xf8, # mov rax, rdi
0x48, 0x05, 0x04, 0x00, 0x00, 0x00, # add rax, 4
0xc3 # ret
]
var mem = mmap(nil, code.len, prot, MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
copyMem(mem, addr code[0], code.len)
var add4 = cast[IntToInt](mem)
echo add4(3)
discard munmap(mem, code.len)