D内联汇编程序:函数调用出错

时间:2012-07-15 11:19:40

标签: assembly call d inline-assembly

我遇到了一个非常特殊的问题。 对于VM,我需要将代码从指令函数复制到ubyte数组,然后执行此数组(技术类似于gcc中的内联宏vm),基本上它的工作原理如下:

__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
}

在Init方法中,每个指令代码都有自己的缓冲区,缓冲区中是INSTRUCTIONCODESTART和INSTRUCTIONCODEEND键之间的每个字节。我通过Windows VirtualProtect调用使这个数组可执行。

到目前为止,一切都按预期工作,但当我尝试将函数调用作为指令时,我将收到错误。

__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
}

顺便说一下,我用以下代码测试了说明

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!");
}

到目前为止,每个简单的指令(add,mul,sub,push0,push1,...)都有效,但如果我尝试使用函数调用获取指令代码,则会抛出错误

我会很高兴并且非常感谢任何帮助。 (顺便说一下,我需要在指令中进行函数调用才能让脚本语言与D进行通信)

2 个答案:

答案 0 :(得分:2)

您应该真正反汇编代码,以便清楚地了解代码的作用以及代码破坏的原因。 call函数的反汇编是:

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是开始标记开始的地方,414ddc是结束的地方(包括)。 414de2是您的结束标记开始的地方,414df3是它结束的地方(包括在内)。所以,抓住这个,我们有:

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

你显然没有在这里复制一些序言和结语代码。但是,这本身不应该是非常有问题的。

我试过这个程序:

void main()
{
    foo();
}

void foo()
{
    auto addr = &bar;

    asm { call addr; }
}

void bar()
{
    asm { naked; call baz; ret; }
}

void baz()
{
}

它对我有用。坦率地说,我不知道你的问题在哪里。您粘贴的大部分代码都无法复制到源文件中并进行编译,因此很难分清哪些内容出错。我希望这里的一些信息可以帮到你。您最有可能必须附加调试器并找出错误的原因;不要吝啬像这样的低级别的东西,而不要弄脏你的手。 ;)

BTW,我在Linux上测试了64位x86。

无论哪种方式,您正在做的事情都是高度不可移植,未定义等等。请注意。它可能会有用,但你正在零保证工作。

答案 1 :(得分:0)

如果没有看到所有相关代码的完全反汇编,很难说出所有的错误,但是......

  1. x86代码通常与位置无关,这意味着将其复制到其他地方并在那里执行它可能并且通常会失败。

  2. 您最有可能复制用于删除寄存器(包括ebpesp)以及堆栈内容的代码。