ネイティブコンパイラ作成を支援するライブラリです。 ⇒ 関連日記
CompilerLibを利用した言語処理系です。
コンパイラの内部処理用ライブラリを想定しているため、言語処理系を含んでいません。テキストではなくオブジェクトを渡します。アセンブリをコードで組み立てて出力するというイメージです。
以下のC言語と同等のものを作成します。
CompilerLibを利用したコードは以下のようになります。
Moduleインスタンスからグローバル変数のポインタを取得するのがModule.GetInt32()です。ポインタが指す値はAddr32クラスを通して扱います。C++と対比させたイメージを示します。
【C++】
【CompilerLib】
アセンブラレベルでは、p_abcがEDXに割り当てられた場合、abcは[EDX]に相当します。アセンブリの[]で括ったアドレッシングがnew Addr32()に相当します。
この操作はCompilerLibでオブジェクト化されたアセンブリに反映されます。
以下のアセンブリと同等のものを作成します。
XORやLOOPでコードを節約できますが、ここでは使用しません。
CompilerLibを利用したコードは以下のようになります。
デフォルトコンストラクタで空のOpCodeを作成して、
アドレスを取得するためだけに使用しています。
- C#のネイティブコンパイラではありません。C#で作ったネイティブコンパイラ作成用のサポートライブラリです。
- 言語処理系を含まないため、特定の言語(C言語、C#等)をコンパイルすることはできません。
- 言語処理系を作成する場合、下層のリンク処理等をCompilerLibに丸投げすれば、PEの細かいことを気にせずにネイティブコンパイラを作ることができます。CompilerLibはそのためのライブラリです。
- C#で作ったプログラムを.NETなしにネイティブで動かすように変換することはできません。将来的にサポートする予定もありません。C#のAoTコンパイラを期待された方にはごめんなさい。
- 応用でC#のAoTコンパイラを作ることは可能です。原理的にはCILを抜き出して、x86コードを生成してCompilerLibに渡せばできるはずです。ただし説明は簡単ですがやるととんでもなく難しいので、現時点では開発する予定はありません。
CompilerLibを利用した言語処理系です。
- ソース: CompilerLib-0.6-20080825.zip - LLPML 1.0からバックポート
- ライセンス: パブリックドメイン
- 開発環境: Visual C# 2008 Express Edition ⇒ 無料ダウンロード(オフラインインストール推奨)
- ソース: CompilerLib-0.2-20070114.zip - ラベルをサポート
- ソース: CompilerLib-0.1-20070108.zip - 簡単なEXEを出力
- アセンブラの充実
- LLPML 1.0で必要になったニーモニックとオペランドの組み合わせしかサポートされていません。
- MMX/SSE2命令はある程度実装されていますが、FPU命令はまったく実装されていません。
- まともに使うには整備する必要があります。
- 自分自身の記述
- C#での機能拡張はほどほどにして、言語処理系を実装して自分自身を記述します。 ⇒ Eating one's own dog food
- x64対応
- いまどきこんなレイヤで作業をするなら、そろそろ手を出す時期でしょう。
コンパイラの内部処理用ライブラリを想定しているため、言語処理系を含んでいません。テキストではなくオブジェクトを渡します。アセンブリをコードで組み立てて出力するというイメージです。
以下のC言語と同等のものを作成します。
#include <windows.h> int main() { HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE); const char *hello = "Hello, World!\r\n"; DWORD dummy; WriteConsole(stdout, hello, strlen(hello), &dummy, NULL); return 0; }
CompilerLibを利用したコードは以下のようになります。
var module = new Module(); var c = new List<OpCode>(); const int STD_OUTPUT_HANDLE = -11; var GetStdHandle = module.GetFunction(CallType.Std, "kernel32.dll", "GetStdHandle"); var WriteConsole = module.GetFunction(CallType.Std, "kernel32.dll", "WriteConsoleW"); var ExitProcess = module.GetFunction(CallType.Std, "kernel32.dll", "ExitProcess"); // HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE); var stdout = new Addr32(module.GetInt32("stdout")); c.AddRange(GetStdHandle.Invoke(STD_OUTPUT_HANDLE)); c.Add(I386.Mov(stdout, Reg32.EAX)); var dummy = module.GetInt32("dummy"); var hello = "Hello, World!\r\n"; c.AddRange(WriteConsole.Invoke(stdout, hello, hello.Length, dummy, 0)); c.AddRange(ExitProcess.Invoke(0)); module.Text.OpCodes = c.ToArray(); module.Link("output.exe");
Moduleインスタンスからグローバル変数のポインタを取得するのがModule.GetInt32()です。ポインタが指す値はAddr32クラスを通して扱います。C++と対比させたイメージを示します。
【C++】
extern int g_abc; int *p_abc = &g_abc; int &abc = *p_abc;
【CompilerLib】
var p_abc = module.GetInt32("g_abc"); var abc = new Addr32(p_abc);
アセンブラレベルでは、p_abcがEDXに割り当てられた場合、abcは[EDX]に相当します。アセンブリの[]で括ったアドレッシングがnew Addr32()に相当します。
【アセンブリ】EDX → [EDX] 【C言語】edx → *edx 【CompilerLib】edx → new Addr32(edx)
この操作はCompilerLibでオブジェクト化されたアセンブリに反映されます。
// mov eax, [0x12345678] I386.Mov(Reg32.EAX, new Addr32(0x12345678)); // mov edx, 0x12345678 I386.Mov(Reg32.EDX, 0x12345678); // mov eax, [edx] I386.Mov(Reg32.EAX, new Addr32(Reg32.EDX));
以下のアセンブリと同等のものを作成します。
XORやLOOPでコードを節約できますが、ここでは使用しません。
mov eax, 0 mov ecx, 100 label: add eax, ecx dec ecx jnz label
CompilerLibを利用したコードは以下のようになります。
var c = new List<OpCode>(); c.Add(I386.Mov(Reg32.EAX, 0)); c.Add(I386.Mov(Reg32.ECX, 100)); var label = new OpCode(); c.Add(label); c.Add(I386.Add(Reg32.EAX, Reg32.ECX)); c.Add(I386.Dec(Reg32.ECX)); c.Add(I386.Jnz(label.Address));
デフォルトコンストラクタで空のOpCodeを作成して、
アドレスを取得するためだけに使用しています。
最新コメント