www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Thunking problems with arch issues

reply Hiemlick Hiemlicker <HH reign.com> writes:
The following code works on dmd x64. Fails on dmd x32 and ldc 
x64. The problem is the passed variable.




import std.stdio;


version (Windows)
{
     import core.sys.windows.windows;

     void makeExecutable(ubyte[] code)
     {
         DWORD old;
         VirtualProtect(code.ptr, code.length, 
PAGE_EXECUTE_READWRITE, &old);
     }
}
else
version (linux)
{
     import core.sys.posix.sys.mman;
     import core.sys.posix.unistd;

     static if (!is(typeof(&mprotect)))
         extern(C) int mprotect(void*, size_t, int);

     void makeExecutable(ubyte[] code)
     {
         auto pageSize = sysconf(_SC_PAGE_SIZE);
         auto address = ((cast(size_t)code.ptr) & ~(pageSize-1));
         int pageCount =
             (address/pageSize == (address+code.length)/pageSize) 
? 1 : 2;
         mprotect(cast(void*)address, pageSize * pageCount,
             PROT_READ | PROT_WRITE | PROT_EXEC);
     }
}
else
     static assert(0, "TODO");

R function(A) delegate2function(R, A...)(R delegate(A) d)
{
     enum size_t TEMPLATE1 = cast(size_t)0x01234567_01234567;
     enum size_t TEMPLATE2 = cast(size_t)0x89ABCDEF_89ABCDEF;

     static R functionTemplate(A args)
     {
         R delegate(A) d;
         d.ptr     = cast(typeof(d.ptr    ))TEMPLATE1;
         d.funcptr = cast(typeof(d.funcptr))TEMPLATE2;
         return d(args);
     }

     static void functionTemplateEnd() {}

     static void replaceWord(ubyte[] a, size_t from, size_t to)
     {
         foreach (i; 0..a.length - size_t.sizeof + 1)
         {
             auto p = cast(size_t*)(a.ptr + i);
             if (*p == from)
             {
                 *p = to;
                 return;
             }
         }
         assert(0);
     }

     auto templateStart = cast(ubyte*)&functionTemplate;
     auto templateEnd   = cast(ubyte*)&functionTemplateEnd;
     auto templateBytes = templateStart[0 .. templateEnd - 
templateStart];

     // must allocate type with pointers, otherwise GC won't scan 
it
     auto functionWords = new void*[(templateBytes.length / 
(void*).sizeof) + 3];

     // store context in word-aligned boundary, so the GC can find 
it
     functionWords[0] = d.ptr;
     functionWords[1] = d.funcptr;
     functionWords = functionWords[2..$];

     auto functionBytes = 
(cast(ubyte[])functionWords)[0..templateBytes.length];
     functionBytes[] = templateBytes[];

     replaceWord(functionBytes, TEMPLATE1, cast(size_t)d.ptr    );
     replaceWord(functionBytes, TEMPLATE2, cast(size_t)d.funcptr);
     makeExecutable(functionBytes);

     return cast(typeof(return)) functionBytes.ptr;
}




public struct sExtThread(T)
{
	uint Id = 0;
	HANDLE Handle;
	alias callbackType = extern (Windows) uint function(void*);	
	

	public void Create(uint delegate(T) c, T param, bool suspended = 
false, uint stackSize = 16000)
	{	
		Handle = CreateThread(cast(SECURITY_ATTRIBUTES*)null, 
stackSize, cast(callbackType)delegate2function(c), 
cast(void*)&param, (suspended) ? CREATE_SUSPENDED : 0, &Id);
	}

	
}



void main()
{
     import std.stdio;

	string q = "WTFSDFA";
	sExtThread!string t;
	t.Create((p)
	{
		writeln(q);
		writeln(p);
		return 1;
	}, "ASDFASDF");
	

	getchar();
}


p is invalid.

Obviously isn't not necessary to pass p but still, somethings 
wrong. I stole the delegate2function from Vladimir "The 
Propeller" Panteleev! Thanks Vlad!

It doesn't seem to be a problem with x86 since LDC x64 has the 
same problem(or writes junk I guess). Could be an issue with 
delegate2function but it's a bit over my head.
Jul 01 2016
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
Couple of things could be happening.

1) Alignments are off, aligning of data really really matters when 
dealing with executable code.
2) For Windows only. Don't forget to call FlushInstructionCache. Before 
executing. 
https://msdn.microsoft.com/en-us/library/windows/desktop/ms679350(v=vs.85).aspx
3) Don't use new to get your executable memory, nope nope nope. Use 
things like VirtualAlloc as malloc doesn't work right see the first point.
Jul 01 2016
parent Hiemlick Hiemlicker <HH reign.com> writes:
On Saturday, 2 July 2016 at 01:51:03 UTC, rikki cattermole wrote:
 Couple of things could be happening.

 1) Alignments are off, aligning of data really really matters
Where? I rewrote the code to use size_t and same problem. If alignments were off chances are it wouldn't exhibit the issues in the way it does(work fine for 64 and not 86, not for ldc).
 when dealing with executable code.
 2) For Windows only. Don't forget to call 
 FlushInstructionCache. Before executing. 
 https://msdn.microsoft.com/en-us/library/windows/desktop/ms679350(v=vs.85).aspx
Tried, didn't work.
 3) Don't use new to get your executable memory, nope nope nope. 
 Use things like VirtualAlloc as malloc doesn't work right see 
 the first point.
Well.. I see these as general statements that don't actually address the real problem in the code. I rewrote the code and it has the same problem. I see no reason why it shouldn't work. It does not manipulate the code, and new seems to allocate aligned(if not it would work some of the time and fail others). It specifically has the problem when used as a windows callback. I guess it has to do with the calling convention. Here is the complete test code: https://dpaste.dzfl.pl/d8cea3b39d0d
Jul 02 2016