2012-10-04 5 views
0

그래서 gcc를 사용하여 asm을 사용하는 메소드를 호출하려고합니다. 그래서, 저는 x86이 어떻게 작동 하는지를 조사했습니다. 그리고 호출 규칙은 무엇입니까? 그렇다면 쉬운 호출 마녀를 완벽하게 시험해 보았습니다. 여기에 생성 된 조립되어gcc에서 인라인 어셈블러를 사용하는 메소드

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 

그럼 난 내 원래의 목표였다 V8을 포함하려고 노력하지만 잘 작동하지 않았다 ... 여기 내 코드입니다 그래서,이 정적 메서드는 콜백 (일부 서명 검사를하기 전에) 마녀가 유효한 C++ 네이티브 args를 제공하는 특정 메서드를 호출해야합니다. 조금 속도를 높이고 args의 복사본을 피하기 위해 로컬 배열에 모든 param을로드하고이 배열을 인수로 만들기 위해 ESP를 수정하려고합니다.

메서드 호출이 제대로 작동하지만 올바른 인수를 얻지 못했습니다 ... 함수 호출, 호출 규칙 및 많은 테스트 (모두 성공적 이었음)에 대한 많은 연구를 수행했지만, 무슨 일이 벌어지고 있는지 이해하고 ... 내가 놓친 것이 있습니까?

기본적으로, 수신자가 내 경우, ESP의 상단에 인수를 얻을 예정이다 (배열이 유효 나는 정확한 것을) ... 배열

내가 GCC를 사용합니다.

+2

프랑스어 또는 프로없는 것에 대해 후회하지 말고 * * 우리를이 혼란을 입힌 매우 유감 바랍니다 :

는이 같은 일이 당신이 원하는 일을 할 것이라고 상상! 평범한 사람들이 읽을 수 있도록 코드를 포맷하십시오 ... (새로운 행, 공백, 들여 쓰기, typedef 등이 포함됩니다) Merci! –

답변

2

시도하는 동안 많은 문제가 있습니다. 컴파일러 아마 그 지역 변수 및 인수를 참조 할 %esp을 사용하고 있기 때문에

  • 당신은 인라인 어셈블리를 사용 %esp을 수정할 수 없습니다. 컴파일러가 %ebp을 대신 사용하는 경우 일 수 있지만 보장은 없습니다.

  • 돌아 가기 전에 %esp 수정 사항을 실행 취소하지 마십시오.

  • 인라인 어셈블리에서 %esp이 부작용임을 선언해야합니다.

  • 침묵하는 첫 번째 인수로 object을 전달해야 할 수도 있습니다. method은 정적 메서드가 아닌 인스턴스 메서드입니다.

  • 이 모든

    당신이 사용중인 호출 규칙에 따라 달라 cdecl, stdcall

+0

컴파일러는'% ebp'을 사용하고 있습니다. 보장이 없지만 생성 된 ASM을 보면'% ebp'가 사용됩니다. 그리고 네,'% esp' 수정을 취소하는 것을 잊지 만 인수가 호출 수신자 내부에서 유효하지 않다는 사실을 변경하지는 않습니다 ... 부작용이 있습니까? C++에는'% ecx'를 사용하여 객체 (''c "(object)')를 전달하는 THISCALL 규약이 있으며, 인수로 cdecl처럼 동작합니다 ... – SRLKilling

+0

더 많은 가능성 :'args [i] -> Int32Value()'네이티브 인수 배열을 clobbering 있습니까? 아마'float'을'double'으로 전달해야할까요? 잘못된 주장은 정확히 어떻게 생겼습니까? –

+0

그건 그렇고, gcc의'thiscall'은'% ecx'에서'this'를 전달하지 않습니다. 스택에 전달됩니다. http://en.wikipedia.org/wiki/X86_calling_conventions#thiscall –

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(); 
} 
+0

흠,이 라이브러리는 훌륭해 보이지만 저는 C++이 아니에요 ... 게다가 라이브러리를 사용하기로 결정하더라도 여전히 배우고 싶습니다. 실수는 어디 있는지 알고 싶습니다. 내 코드에서 무엇이 실패하는지 이해하십시오. – SRLKilling

관련 문제