在gcc中使用内联汇编程序调用方法

时间:2012-10-04 17:22:36

标签: c++ gcc assembly v8 gas

正如我所说,我正在尝试使用内联asm使用gcc调用方法。所以,我搜索了x86如何工作,以及调用约定是什么,然后我尝试了一些简单的调用女巫完美的工作。然后我尝试嵌入v8,这是我最初的目标,但它没有那么好用...... 这是我的代码:

v8::Handle<v8::Value> V8Method::staticInternalMethodCaller(const v8::Arguments& args, int argsize, void* object, void* method)
{
    int i = 0;
    char* native_args;

    // Move the ESP to the end of the array (argsize is the array size in byte)
    asm("subl %1, %%esp;"
        "movl %%esp, %0;"
        : "=r"(native_args)
        : "r"(argsize));


    // This for loop only converts V8 type to native type,
    // and puts them in the array:

    for (; i < args.Length(); ++i)
    {
        if (args[i]->IsInt32())
        {
            *(int*)(native_args) = args[i]->Int32Value();

            native_args += sizeof(int);
        }
        else if (args[i]->IsNumber())
        {
            *(float*)(native_args) = (float)(args[i]->NumberValue());

            native_args += sizeof(float);
        }
    }

    // Then call the method:

    asm("call *%1;" : : "c"(object), "r"(method));

    return v8::Null();
}

这是生成的程序集:

__ZN3srl8V8Method26staticInternalMethodCallerERKN2v89ArgumentsEiPvS5_:
LFB1178:
    .cfi_startproc
    .cfi_personality 0,___gxx_personality_v0
    .cfi_lsda 0,LLSDA1178
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    pushl   %ebx
    subl    $68, %esp
    .cfi_offset 3, -12
    movl    $0, -12(%ebp)
    movl    12(%ebp), %eax
/APP
 # 64 "method.cpp" 1
    subl %eax, %esp; movl %esp, %ebx; addl $4, %esp
 # 0 "" 2
/NO_APP
    movl    %ebx, -16(%ebp)
    jmp L74
L77:
    movl    -12(%ebp), %eax
    movl    %eax, (%esp)
    movl    8(%ebp), %ecx
LEHB25:
    call    __ZNK2v89ArgumentsixEi
LEHE25:
    subl    $4, %esp
    movl    %eax, -36(%ebp)
    leal    -36(%ebp), %eax
    movl    %eax, %ecx
    call    __ZNK2v86HandleINS_5ValueEEptEv
    movl    %eax, %ecx
LEHB26:
    call    __ZNK2v85Value7IsInt32Ev
LEHE26:
    testb   %al, %al
    je  L75
    movl    -12(%ebp), %eax
    movl    %eax, (%esp)
    movl    8(%ebp), %ecx
LEHB27:
    call    __ZNK2v89ArgumentsixEi
LEHE27:
    subl    $4, %esp
    movl    %eax, -32(%ebp)
    leal    -32(%ebp), %eax
    movl    %eax, %ecx
    call    __ZNK2v86HandleINS_5ValueEEptEv
    movl    %eax, %ecx
LEHB28:
    call    __ZNK2v85Value10Int32ValueEv
LEHE28:
    movl    %eax, %edx
    movl    -16(%ebp), %eax
    movl    %edx, (%eax)
    movl    -16(%ebp), %eax
    movl    (%eax), %ebx
    movl    $LC4, 4(%esp)
    movl    $__ZSt4cout, (%esp)
LEHB29:
    call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    -16(%ebp), %edx
    movl    %edx, (%esp)
    movl    %eax, %ecx
    call    __ZNSolsEPKv
    subl    $4, %esp
    movl    $LC5, 4(%esp)
    movl    %eax, (%esp)
    call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    %ebx, (%esp)
    movl    %eax, %ecx
    call    __ZNSolsEi
    subl    $4, %esp
    movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp)
    movl    %eax, %ecx
    call    __ZNSolsEPFRSoS_E
    subl    $4, %esp
    addl    $4, -16(%ebp)
    jmp L76
L75:
    movl    -12(%ebp), %eax
    movl    %eax, (%esp)
    movl    8(%ebp), %ecx
    call    __ZNK2v89ArgumentsixEi
LEHE29:
    subl    $4, %esp
    movl    %eax, -28(%ebp)
    leal    -28(%ebp), %eax
    movl    %eax, %ecx
    call    __ZNK2v86HandleINS_5ValueEEptEv
    movl    %eax, %ecx
LEHB30:
    call    __ZNK2v85Value8IsNumberEv
LEHE30:
    testb   %al, %al
    je  L76
    movl    -12(%ebp), %eax
    movl    %eax, (%esp)
    movl    8(%ebp), %ecx
LEHB31:
    call    __ZNK2v89ArgumentsixEi
LEHE31:
    subl    $4, %esp
    movl    %eax, -24(%ebp)
    leal    -24(%ebp), %eax
    movl    %eax, %ecx
    call    __ZNK2v86HandleINS_5ValueEEptEv
    movl    %eax, %ecx
LEHB32:
    call    __ZNK2v85Value11NumberValueEv
LEHE32:
    fstps   -44(%ebp)
    flds    -44(%ebp)
    movl    -16(%ebp), %eax
    fstps   (%eax)
    movl    -16(%ebp), %eax
    movl    (%eax), %ebx
    movl    $LC4, 4(%esp)
    movl    $__ZSt4cout, (%esp)
LEHB33:
    call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    -16(%ebp), %edx
    movl    %edx, (%esp)
    movl    %eax, %ecx
    call    __ZNSolsEPKv
    subl    $4, %esp
    movl    $LC5, 4(%esp)
    movl    %eax, (%esp)
    call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
    movl    %ebx, (%esp)
    movl    %eax, %ecx
    call    __ZNSolsEf
    subl    $4, %esp
    movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp)
    movl    %eax, %ecx
    call    __ZNSolsEPFRSoS_E
    subl    $4, %esp
    addl    $4, -16(%ebp)
L76:
    incl    -12(%ebp)
L74:
    movl    8(%ebp), %ecx
    call    __ZNK2v89Arguments6LengthEv
    cmpl    -12(%ebp), %eax
    setg    %al
    testb   %al, %al
    jne L77
movl    16(%ebp), %eax
    movl    20(%ebp), %edx
    movl    %eax, %ecx
/APP
 # 69 "method.cpp" 1
    call *%edx;
 # 0 "" 2
/NO_APP
    call    __ZN2v84NullEv
    leal    -20(%ebp), %edx
    movl    %eax, (%esp)
    movl    %edx, %ecx
    call    __ZN2v86HandleINS_5ValueEEC1INS_9PrimitiveEEENS0_IT_EE
    subl    $4, %esp
    movl    -20(%ebp), %eax
jmp L87
L83:
    movl    %eax, (%esp)
    call    __Unwind_Resume
L84:
    movl    %eax, (%esp)
    call    __Unwind_Resume
L85:
    movl    %eax, (%esp)
    call    __Unwind_Resume
L86:
    movl    %eax, (%esp)
    call    __Unwind_Resume
LEHE33:
L87:
    movl    -4(%ebp), %ebx
    leave
    .cfi_restore 5
    .cfi_restore 3
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc

所以,这个静态方法是一个回调(之前我做了一些签名检查),女巫应该调用提供有效C ++本机args的特定方法。为了加速一点并避免args的副本,我试图在本地数组中加载所有param,然后修改ESP以使这个数组成为一个参数。

方法调用运行良好,但我没有得到正确的参数...我已经做了很多关于函数调用,调用约定和大量测试(这些都是成功的)的研究,但我没有了解发生了什么......我错过了什么?

基本上,被调用者应该在esp的顶部获取它的参数,在我的例子中,数组......(我确切地知道数组是有效的)

我使用GCC。

2 个答案:

答案 0 :(得分:2)

您尝试的内容存在许多问题。

  • 您无法使用内联汇编修改%esp,因为编译器 可能正在使用%esp来引用其局部变量和参数。如果编译器使用%ebp代替, 可以,但不能保证。

  • 在返回之前,您永远不会撤消%esp修改。

  • 在内联汇编中,您需要声明%esp是副作用的。

  • 您可能需要将object作为静默的第一个参数传递。 method是一个实例方法,而不是静态方法?

  • 所有这些取决于您正在使用的调用约定:cdeclstdcall等。

答案 1 :(得分:0)

我建议不要自己尝试这样做,有很多烦人的小细节必须得到完全正确。我建议改为使用FFCALL library,特别是avcall方法来完成此操作。

我想像这样的东西会做你想要的:

v8::Handle<v8::Value> V8Method::staticInternalMethodCaller(const v8::Arguments& args, int argsize, void* object, void* method)
{
    // Set up the argument list with the function pointer, return type, and
    // pointer to value storing the return value (assuming int, change if
    // necessary)
    int return_value;
    av_alist alist;
    av_start_int(alist, method, &return_value);

    for(int i = args.Length() - 1; i >= 0; i--)
    {
        // Push the arguments onto the argument list
        if (args[i]->IsInt32())
        {
            av_int(alist, args[i]->Int32Value());
        }
        else if (args[i]->IsNumber())
        {
            av_double(alist, (float)(args[i]->NumberValue());
        }
    }

    av_call(alist);  // Call the function

    return v8::Null();
}