Reputation: 763
I got a very special problem. For a VM I need to copy code from the instruction functions to a ubyte array and then execute this array (the technic is similiar to the inline macro vm in gcc), basically it works like this:
__gshared void * sp = null, sb = null; //stack pointer and stack base
__gshared void add() //the function is just there to access the instruction code
{
asm{db "INSTRUCTIONCODESTART";} //this is a key to know where the instruction code starts
//instruction code here (sample instruction add, pops 2 values from the stack and pushes its result)
sp += 4;
*cast(uint*)sp += *cast(uint*)(sp - 4);
asm{db "INSTRUCTIONCODEEND";} //this is a key to know where instruction code ends
}
In the Init method, every instruction code gets its own buffer, and in the buffer is every byte between the INSTRUCTIONCODESTART and the INSTRUCTIONCODEEND key. I make this array executeable through the windows VirtualProtect call.
So Far, everything works as expected, but when I try to do a function call as an instruction, I will get an error.
__gshared void testcall(){}
__gshared void call()
{
asm{db "INSTRUCTIONCODESTART";} //this is a key to know where the instruction code starts
//instruction code here (just calls a D function)
testcall(); //this somehow throws an error
asm{db "INSTRUCTIONCODEEND";} //this is a key to know where instruction code ends
}
Btw I tested the instructions with the following code
void instructiontest()
{
uint dummy;
ubyte[] buf = getFunctionCode(&add) ~ 0xC3; //gets code of instruction, appends 0xC3 at it ("ret" instruction, for test purposes only to see if it returns to the D code without errors)
VirtualProtect(cast(void*)buf, buf.length, PAGE_EXECUTE_READWRITE, &dummy); //makes it executeable
dummy = cast(uint)&buf[0];
asm
{
call dummy[EBP];
}
print("instruction worked without errors!");
}
So far, every simple instruction (add, mul, sub, push0, push1, ...) works, but if I try to get the code of an instruction with a function call, it throws an error
I would be happy and very thankful about any help. (btw I need function calls in the instruction in order to let the script language communicate with D)
Upvotes: 1
Views: 412
Reputation:
You should really disassemble the code in order to get a clear view of what it's doing and why your code is breaking. The disassembly for your call
function is:
0000000000414db8 <_D4test4callFZv>:
414db8: 55 push rbp
414db9: 48 8b ec mov rbp,rsp
414dbc: 48 83 ec 08 sub rsp,0x8
414dc0: 53 push rbx
414dc1: 41 54 push r12
414dc3: 41 55 push r13
414dc5: 41 56 push r14
414dc7: 41 57 push r15
414dc9: 49 rex.WB
414dca: 4e 53 rex.WRX push rbx
414dcc: 54 push rsp
414dcd: 52 push rdx
414dce: 55 push rbp
414dcf: 43 54 rex.XB push r12
414dd1: 49 rex.WB
414dd2: 4f rex.WRXB
414dd3: 4e rex.WRX
414dd4: 43 rex.XB
414dd5: 4f rex.WRXB
414dd6: 44 rex.R
414dd7: 45 53 rex.RB push r11
414dd9: 54 push rsp
414dda: 41 52 push r10
414ddc: 54 push rsp
414ddd: e8 ce ff ff ff call 414db0 <_D4test8testcallFZv>
414de2: 49 rex.WB
414de3: 4e 53 rex.WRX push rbx
414de5: 54 push rsp
414de6: 52 push rdx
414de7: 55 push rbp
414de8: 43 54 rex.XB push r12
414dea: 49 rex.WB
414deb: 4f rex.WRXB
414dec: 4e rex.WRX
414ded: 43 rex.XB
414dee: 4f rex.WRXB
414def: 44 rex.R
414df0: 45 rex.RB
414df1: 45 rex.RB
414df2: 4e rex.WRX
414df3: 44 rex.R
414df4: 41 5f pop r15
414df6: 41 5e pop r14
414df8: 41 5d pop r13
414dfa: 41 5c pop r12
414dfc: 5b pop rbx
414dfd: c9 leave
414dfe: c3 ret
414dff: 90 nop
414dc9
is where the start marker begins, 414ddc
is where it ends (inclusive). 414de2
is where your end marker begins, 414df3
is where it ends (inclusive). So, ripping that out, we have:
0000000000414db8 <_D4test4callFZv>:
414db8: 55 push rbp
414db9: 48 8b ec mov rbp,rsp
414dbc: 48 83 ec 08 sub rsp,0x8
414dc0: 53 push rbx
414dc1: 41 54 push r12
414dc3: 41 55 push r13
414dc5: 41 56 push r14
414dc7: 41 57 push r15
; code start marker here
414ddd: e8 ce ff ff ff call 414db0 <_D4test8testcallFZv>
; code end marker here
414df4: 41 5f pop r15
414df6: 41 5e pop r14
414df8: 41 5d pop r13
414dfa: 41 5c pop r12
414dfc: 5b pop rbx
414dfd: c9 leave
414dfe: c3 ret
414dff: 90 nop
You're clearly not copying some prologue and epilogue code here. But that, in and of itself, should not be terribly problematic.
I tried this program:
void main()
{
foo();
}
void foo()
{
auto addr = &bar;
asm { call addr; }
}
void bar()
{
asm { naked; call baz; ret; }
}
void baz()
{
}
It works for me. Frankly, I can't tell where your problem is. Most of the code you pasted can't just be copied into a source file and be compiled, so it's quite hard to tell what's going wrong. I hope some of the information here can help you. You're most likely going to have to attach a debugger and find out what's wrong; don't expect to mess with low-level stuff like this without getting your hands dirty. ;)
BTW, I tested on 64-bit x86 in Linux.
Either way, what you're doing is highly unportable, undefined, etc. Be warned. It may work out, but you're working with zero guarantees.
Upvotes: 2
Reputation: 62106
It's hard to tell what all the mistakes are without seeing the full disassembly of all the relevant code, but...
x86 code generally isn't position-independent, which means copying it to a different place and executing it there may and most often will fail.
You're most likely copying code that trashes registers (including ebp
and esp
) and the stack contents.
Upvotes: 0