前回、Pythonでポインタアクセスする方法が分かりました。
これを使ってC言語で書いたJITをPythonに移植してみます。コードの詳細は以下を参照してください。
共通部 (jit.py)
import sys, struct from ctypes import * def VirtualAlloc(address, size, allocationType, protect): VirtualAlloc = windll.kernel32.VirtualAlloc VirtualAlloc.restype = POINTER(ARRAY(c_ubyte, size)) VirtualAlloc.argtype = [c_void_p, c_size_t, c_int, c_int] return VirtualAlloc(address, size, allocationType, protect)[0] VirtualFree = windll.kernel32.VirtualFree VirtualFree.argtype = [c_void_p, c_int, c_int] MEM_COMMIT = 0x1000 MEM_RELEASE = 0x8000 PAGE_EXECUTE_READWRITE = 0x40 putchar = CFUNCTYPE(None, c_int)(lambda ch: sys.stdout.write(chr(ch))) getaddr = CFUNCTYPE(c_void_p, c_void_p)(lambda p: p) def conv32(dw): return map(lambda x: ord(x), struct.pack( "<l" if dw < 0 else "<L", dw))
相対アドレス
from jit import * codes = [ 0x6a, 0x41, # push 65 0xe8, 0x00, 0x00, 0x00, 0x00, # call xxxx 0x83, 0xc4, 0x04, # add esp, 4 0xc3, # ret ] p = VirtualAlloc(0, len(codes), MEM_COMMIT, PAGE_EXECUTE_READWRITE) p[:] = codes p[3:7] = conv32(getaddr(putchar) - (getaddr(p) + 7)) CFUNCTYPE(None)(getaddr(p))() VirtualFree(p, 0, MEM_RELEASE)
絶対アドレス
from jit import * codes = [ 0x6a, 0x41, # push 65 0xb8, 0x00, 0x00, 0x00, 0x00, # mov eax, 0 0xff, 0xd0, # call eax 0x83, 0xc4, 0x04, # add esp, 4 0xc3, # ret ] p = VirtualAlloc(0, len(codes), MEM_COMMIT, PAGE_EXECUTE_READWRITE) p[:] = codes p[3:7] = conv32(getaddr(putchar)) CFUNCTYPE(None)(getaddr(p))() VirtualFree(p, 0, MEM_RELEASE)
引数渡し
from jit import * codes = [ 0x6a, 0x41, # push 65 0xff, 0x54, 0x24, 0x08, # call [esp+8] 0x83, 0xc4, 0x04, # add esp, 4 0xc3, # ret ] p = VirtualAlloc(0, len(codes), MEM_COMMIT, PAGE_EXECUTE_READWRITE) p[:] = codes CFUNCTYPE(c_void_p)(getaddr(p))(putchar) VirtualFree(p, 0, MEM_RELEASE)