Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Written in the D programming language.
- /**
- * Forwards function pointers to delegates.
- * Parameters are converted between calling conventions.
- *
- * Currently supports:
- *
- * extern(Windows) function -> extern(D) delegate
- *
- * extern(C) function -> extern(D) delegate
- *
- * extern(D) function -> extern(D) delegate
- *
- * Author:
- * Daniel Murphy (yebblies@gmail.com)
- *
- * Adapted from:
- * www.codeproject.com/KB/cpp/thunk32.aspx?msg=2168100
- *
- * See site for license
- *
- */
- module yebblies.thunk;
- version(Windows)
- {
- import core.sys.windows.windows : VirtualAlloc, VirtualFree, FlushInstructionCache, MEM_COMMIT, PAGE_EXECUTE_READWRITE, MEM_DECOMMIT, GetCurrentProcess;
- } else version(posix)
- {
- import core.sys.posix.sys.mman : mmap, PROT_WRITE, PROT_EXEC, MAP_PRIVATE, MAP_FAILED, munmap;
- } else {
- static assert(0);
- }
- class Thunk(string linkage, U : R delegate(P), R, P...) if (linkage == "Windows" || linkage == "C" || linkage == "D")
- {
- public:
- static if (linkage == "Windows")
- {
- extern(Windows) R function(P) ptr;
- } else static if (linkage == "C") {
- extern(C) R function(P) ptr;
- } else static if (linkage == "D") {
- extern(D) R function(P) ptr;
- } else {
- static assert(0);
- }
- private:
- align(1)
- struct ThunkCode
- {
- static if (linkage != "D")
- {
- ubyte mov_eax;
- void* this_ptr;
- }
- ubyte mov_ecx;
- void* func_ptr;
- ubyte mov_edx;
- void* tt_ptr;
- ushort jmp_edx;
- };
- static if (linkage == "Windows")
- {
- extern(Windows) static R ThunkThis(P p)
- {
- U fdg = void;
- asm {
- mov [fdg], EAX;
- mov [fdg+4], ECX;
- };
- return fdg(p);
- }
- } else static if (linkage == "C")
- {
- extern(C) static R ThunkThis(P p)
- {
- U fdg = void;
- asm {
- mov [fdg], EAX;
- mov [fdg+4], ECX;
- };
- return fdg(p);
- }
- } else static if (linkage == "D")
- {
- U dgstore;
- extern(D) static R ThunkThis(P p)
- {
- U* dgptr = void;
- asm {
- mov [dgptr], ECX;
- };
- return (*dgptr)(p);
- }
- }
- public:
- this(U dg)
- {
- version(Windows)
- {
- auto tc = cast(ThunkCode*)VirtualAlloc(null, ThunkCode.sizeof, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- } else {
- auto p = mmap(null, ThunkCode.sizeof, PROT_WRITE | PROT_EXEC, MAP_PRIVATE, 0, 0);
- assert(p != MAP_FAILED);
- auto tc = cast(ThunkCode*)p;
- }
- static if (linkage == "Windows" || linkage == "C")
- {
- // Put ptr and fptr in some registers that aren't touched by the
- // function call, then jump to the handler above.
- // Indirect jump using edx
- //
- // mov eax, thisptr
- // mov ecx, funcptr
- // mov edx, &ThunkThis
- // jmp edx
- tc.mov_eax = 0xB8;
- tc.this_ptr = dg.ptr;
- tc.mov_ecx = 0xB9;
- tc.func_ptr = dg.funcptr;
- tc.mov_edx = 0xBA;
- tc.tt_ptr = &ThunkThis;
- tc.jmp_edx = 0xE2FF;
- } else { // Linkage == "D"
- // We can't touch eax because D uses it for passing parameters sometimes.
- // Instead store the delegate information inside this class instance, and pass a ptr in ECX
- // mov eax, thisptr
- // mov ecx, funcptr
- // mov edx, &ThunkThis
- // jmp edx
- dgstore = dg;
- tc.mov_ecx = 0xB9;
- tc.func_ptr = &dgstore;
- tc.mov_edx = 0xBA;
- tc.tt_ptr = &ThunkThis;
- tc.jmp_edx = 0xE2FF;
- }
- version(Windows)
- {
- FlushInstructionCache(GetCurrentProcess(), tc, ThunkCode.sizeof);
- } else {
- }
- ptr = cast(typeof(ptr))tc;
- //writefln("%d", tc.lea_ecx);//, *cast(ushort*)(ptr));
- }
- ~this()
- {
- version(Windows)
- {
- VirtualFree(ptr, ThunkCode.sizeof, MEM_DECOMMIT);
- } else {
- munmap(ptr, ThunkCode.sizeof);
- }
- }
- };
- /**
- * Creates a Thunk object
- *
- * Example:
- ----------------
- import std.stdio;
- void main()
- {
- class C
- {
- int a;
- void func(int b) { writeln("a == ", a, "\nb == ", b); }
- }
- auto c = new C();
- c.a = 7;
- auto th = thunk!"C"(&c.func);
- // th.ptr is extern(C) void function(int)
- th.ptr(3);
- // prints a == 7, b == 3
- }
- ---------------
- */
- Thunk!(linkage, U, R, P) thunk(string linkage, U : R delegate(P), R, P...)(U dg) if (linkage == "Windows" || linkage == "C" || linkage == "D")
- {
- return new Thunk!(linkage, U, R, P)(dg);
- }
- unittest
- {
- class C
- {
- float receiver(int n, int b, real c)
- {
- return (n + b) * c;
- }
- };
- auto g = 12.f;
- float fn(int n, real b, float c)
- {
- return (n + b) * c + g;
- }
- auto c = new C();
- version(Windows)
- {
- auto th1 = thunk!"Windows"( &c.receiver);
- auto th2 = thunk!"Windows"( &fn);
- }
- auto th3 = thunk!"C"( &c.receiver);
- auto th4 = thunk!"C"( &fn);
- auto th5 = thunk!"D"( &c.receiver);
- auto th6 = thunk!"D"( &fn);
- version(Windows)
- {
- assert(th1.ptr(3, 7, 10.f) == 100.f);
- assert(th2.ptr(3, 7354.L, 10.f) == (3 + 7354.L) * 10.f + 12.f);
- }
- assert(th3.ptr(3, 7, 10.f) == 100.f);
- assert(th4.ptr(3, 7354.L, 10.f) == (3 + 7354.L) * 10.f + 12.f);
- assert(th5.ptr(3, 7, 10.f) == 100.f);
- assert(th6.ptr(3, 7354.L, 10.f) == (3 + 7354.L) * 10.f + 12.f);
- class D
- {
- int func(int a) { return a + 17; }
- }
- auto d = new D;
- auto th7 = thunk!"D"(&d.func);
- assert(th7.ptr(12) == 29);
- }
- void main() {}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement