2013-08-09 2 views
2

일반적인 방법으로 stdcall 규칙을 사용하는 것으로 알려진 함수를 호출하고 float을 반환하는 어셈블리 루틴이 있습니다. 이 함수는 마샬링 프레임 워크에서 stdcall 함수를 스크립팅 언어에 표시하는 데 사용됩니다.어셈블리를 사용하여 부동 소수점을 반환하는 __stdcall 함수의 결과를 얻는 방법

template<typename FuncPtr, typename ArgType> 
float float_invoke(FuncPtr f, int nargs, ArgType * args) 
{ 
    uint64_t result = stdcall_invoke_return_float(
     nargs * sizeof(ArgType), 
     reinterpret_cast<const char *>(args), 
     reinterpret_cast<void *>(f) 
    ); 
    return *reinterpret_cast<float *>(&result); 
} 
:

inline uint64_t stdcall_invoke_return_float(int args_size_bytes, 
              const char * args_ptr, 
              void * func_ptr) 
{ 
    uint64_t result; 
    assert(
     0 == args_size_bytes % 4 
      || !"argument size must be a multiple of 4 bytes"); 
#if defined(__GNUC__) 
    asm 
    (
     /* INPUT PARAMS: %0 is the address where top of FP stack to be stored 
     *    %1 is the number of BYTES to push onto the stack, */ 
     /*     and during the copy loop it is the address of */ 
     /*     the next word to push */ 
     /*    %2 is the base address of the array */ 
     /*    %3 is the address of the function to call */ 
      "testl %1, %1 # If zero argument bytes given, skip \n\t" 
      "je 2f  # right to the function call.  \n\t" 
      "addl %2, %1\n" 
     "1:\n\t" 
      "subl $4, %1 # Push arguments onto the stack in \n\t" 
      "pushl (%1)  # reverse order. Keep looping while \n\t" 
      "cmp %2, %1 # addr to push (%1) > base addr (%2) \n\t" 
      "jg 1b  # Callee cleans up b/c __stdcall. \n" 
     "2:\n\t" 
      "call * %3  # Callee will leave result in ST0 \n\t" 
      "fsts %0  # Copy 32-bit float from ST0->result" 
     : "=m" (result) 
     : "r" (args_size_bytes), "r" (args_ptr), "mr" (func_ptr) 
     : "%eax", "%edx", "%ecx" /* eax, ecx, edx are caller-save */, "cc" 
    ); 
#else 
#pragma error "Replacement for inline assembler required" 
#endif 
    return result; 
} 

이 쉽게 테스트 케이스를 작성할 수 있도록 약간의 접착제가 그냥 : 여기에 배경

는 Win32에서는 MinGW 4.3 컴파일 GNU 인라인 어셈블리를 사용하여 함수의

이제이 함수를 호출하는 몇 가지 테스트 사례가 있습니다.

__stdcall float TestReturn1_0Float() 
{ return 1.0f; } 

__stdcall float TestFloat(float a) 
{ return a; } 

__stdcall float TestSum2Floats(float a, float b) 
{ return a + b; } 

static const float args[2] = { 10.0f, -1.0f }; 

assert_equals(1.0f, float_invoke(TestReturn1_0Float, 0, args)); // test 1 
assert_equals(10.0f, float_invoke(TestFloat, 1, args));   // test 2 
assert_equals(-1.0f, float_invoke(TestFloat, 1, args + 1));  // test 3 
assert_equals(9.0f, float_invoke(TestSumTwoFloats, 2, args)); // test 4 

문제

임의로 테스트 3은 -1.0을 반환하는 대신 가비지 출력을 무작위로 제공합니다.

나는

  • call 명령 전에 어떤 상태를 유지하지 못하는 해요 궁금 해요?
  • fsts 명령어로 상태가 엉망이됩니까?
  • float 값을 stdcall에서 가져 오는 방법을 근본적으로 잘못 이해했습니다. float ???

모두 도움을 주시면 대단히 감사하겠습니다.

답변

1

Windows 컴퓨터가 부족하여 완전히 테스트 할 수 없습니다. 리눅스에서, 다음은 나에게 부양 기능의 리턴 코드를 가져옵니다

extern float something(int); 

#include 
#include 

int main(int argc, char **argv) 
{ 
    int val = atoi(argv[1]); 
    float ret; 

    asm("pushl %1\n\t" 
     "call * %2\n\t" 
     "addl $4, %%esp" 
     : "=t"(ret) 
     : "r"(val), "r"(something) 
     : "%eax", "%ecx", "%edx", "memory", "cc"); 

    printf("something(%d) == %f\n", val, ret); 
    return 0; 
}

키가 "=t"(ret) 제약의 사용이다 - GCC의에서 부동 소수점 스택의 상단을 가져 볼 Machine Constraints (조작). 윈도우 stdcallfloat 결과를 반환한다면 fld/fst은 필요하지 않으면 컴파일러가 수행 할 수 있으므로 ST(0)도 마찬가지입니다.

또한 인라인 어셈블리 내에서 함수를 호출 할 때 memorycc 등고선을 지정해야합니다.

+0

'= t' 제약 조건과 일관되게 작동하는 것 같습니다. 나는 여전히 * fsts 명령어를 사용하는 이유가 불일치하게 작동한다는 것을 알고 싶다. 또한, 왜 메모리 clobber가 필요합니까? – 0xbe5077ed

+0

메모리 "clobber"는 사실 _barrier_입니다. 이것은 컴파일러가 인라인'asm()'블록을 이러한 방식으로 명령한다는 것을 의미하며 나머지 명령어들은 모든로드/저장이 완료되기 전에 완료되고 그 후에 다시 수행됩니다.'cc'와 마찬가지로, 조건 코드에 대해서도 이전의 비교 결과를 나타냅니다 (asm 블록 앞에'cmp'를 쓰고 그 결과를 테스트하지는 않습니다). –

1

함수 포인터에 대한 메모리 참조가 허용됩니다. GCC는 인라인 어셈블리가 변경하지 않는다는 잘못된 가정하에 스택 포인터에 대한 참조를 생성하기 쉽습니다.

+0

그건 아주 좋은 지적입니다. 해체는 그것이 사실인지 쉽게 알 수 있습니다. 컴파일러는 함수의 코드 위치 (즉, _compile/link time_ constant)를 알고 있지만 현재 스택 포인터와의 오프셋은 알지 못하기 때문에 (stackpointer 변수가 다양하기 때문에 _runtime_ 변수가 있기 때문에 그렇게 생각하지는 않습니다. 호출 컨텍스트 및/또는 호출 스레드에 따라 다름). Stackpointer 관련 _function addresses_은 ... 스택에있는 실행 코드가 ... "사용되지 않음"이므로 비정상입니다. –

관련 문제