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

FFI

DLLからシンボルがエクスポートされていれば、HaskellからFFIで呼び出せることが分かりました。独自言語(Andromeda)で開発した簡易リンカでDLLを出力してHaskellから呼び出してみました。

※独自言語はEXEしか出力できないため、独自言語から直接DLLをコンパイルするのではなく、リンカを作ってDLLを出力しています。

実際にリンカをビルドしてDLLを出力できるようにまとめたセットを用意しました。コンパイラがバンドルされているので、.NET Framework 3.5環境ではすぐに試せるようになっています。

リンカのコード

helloというシンボルをエクスポートしたDLLを出力するコードは以下の通りです。

var exe = new PEModule;
exe.File.Characteristics |= IMAGE_FILE_DLL;
exe.WinNT.SubSystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;

var text = new Block;
var MessageBoxW = new OpCode;

// DllMain()
text.AddOpCode(I386.Ret);

// hello()
exe.Export("hello", text.CurrentAddress);
text.AddOpCode(I386.PushD(1));
text.AddOpCode(I386.PushAd(exe.GetString("だいあろぐ")));
text.AddOpCode(I386.PushAd(exe.GetString("こんにちは、世界!")));
text.AddOpCode(I386.PushD(0));
text.AddOpCode(I386.Call(MessageBoxW));
text.AddOpCode(I386.Ret);
text.AddOpCode(MessageBoxW);
text.AddOpCode(I386.JmpAd(exe.Import("USER32.DLL", "MessageBoxW")));

var dll = "hello.dll";
exe.WriteFile(dll, text, true);

println();
printfln(dll + " を出力しました。");
pause();

呼び出すHaskell側は前回と同じです。

test2.hs
{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign

foreign import ccall "hello" hello :: IO ()

main = do hello

独自言語に関しては、リンカだけでなくコンパイラまで作ってセルフホスティングに持ち込みたかったのですが、方向性に悩んで中断したままです。