【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

PythonによるJITで関数呼び出し

前回、Pythonでポインタアクセスする方法が分かりました。

これを使ってC言語で書いたJITPythonに移植してみます。コードの詳細は以下を参照してください。

共通部 (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)