（以下では前提として実行するCPUをx86とします。SPARCとかの人はごめんなさい）

Haskellから任意の機械語のコードを実行するにはどうすればよいのだろう。

Foreign.PtrにFunPtrという型が定義してあり、これは機械語コードの入っているメモリへのポインタを示す。

さらに、

type IntFunction = CInt -> IO () foreign import ccall "dynamic" mkFun :: FunPtr IntFunction -> IntFunction

などとすることによりFunPtrの指すコードを呼び出すためのラッパを生成できる。

型ごとに別個のラッパが必要になり、必要に応じて自動的に定義されるわけでもないので、必要なものは個別に書いてやる必要がある。

これらを用いれば、Haskellからmallocを用いてメモリを確保し、そこにデータを書き込み、それをラッパにかけてやることによって、Haskellから任意の機械語コードを実行することができるだろう。

まず、利便性のために、ラッパを自動的に選択できるよう、FunPtr a から a に変換できる型のクラスを作っておく。

class MkFun a where mkFun :: FunPtr a -> a foreign import ccall "dynamic" mkFunInt :: FunPtr (IO Int) -> IO Int foreign import ccall "dynamic" mkFunInt2Int :: FunPtr (Int -> IO Int) -> (Int -> IO Int) instance MkFun (IO Int) where mkFun = mkFunInt instance MkFun (Int -> IO Int) where mkFun = mkFunInt2Int

当面はIntを返す関数とIntを取ってIntを返す関数のみをサポートしておく。

機械語列からはその関数の型を推論することなどできないので、関数の型はこちらが与えてやる。その際に、コードに対して型が付与できるように型を作る。

newtype CodeBlock a = CodeBlock (Ptr Word8)

newtypeでなくtypeでやると、typeは単なるエイリアスになるため、うまく型が付かない。

メモリを確保してコードを書き込むのと機械語列を実行する関数を作る。

writeCode :: [Word8] -> CodeBlock a writeCode code = unsafePerformIO $ do let len = length code q <- mallocArray len pokeArray q code return $ CodeBlock q exec :: MkFun a => CodeBlock a -> a exec (CodeBlock p) = mkFun $ castPtrToFunPtr p



これで次のようにして望みのコードを実行させることができる。

foo :: CodeBlock (IO Int) foo = writeCode [ 0xb8, 0x09, 0x03, 0x00, 0x00 -- mov eax,777 , 0xc3 -- ret ] main = do ret <- exec foo print ret



fooは単に777を返す関数で、mainはそれを呼び出してprintしている。

$ ghc --make AsmTest.hs

$ ./AsmTest

777



さて、これでインラインアセンブリの可能性が見えてきた。当然のことながら次は機械語列をHaskellから生成したいという要求が出てくるので、そちら方面に話を進めることにする。