Как передать аргументы в функции C ++, когда я вызываю их из встроенной сборки - PullRequest
8 голосов
/ 22 февраля 2010

Итак, я хотел бы иметь возможность вызывать функции из DLL C ++. По определенным причинам я хотел бы вызывать их из блока __asm ​​в моем коде C ++. У меня такой вопрос: я знаю, что прежде чем вызывать функцию, я должен поместить ее аргументы в стек в порядке, указанном в соглашении о вызовах функции. Однако могу ли я просто сделать что-то вроде этого:

int a=5;   
double b = 5.0;  
__asm{  
       push b 
       push a  
       call functionAddress  
}

Что меня беспокоит, так это тот факт, что я помню, что стандартный размер слова в ассемблере составляет 2 байта, в то время как размер int в C ++ обычно составляет 4 байта, и 8 байтов для double. Так, в приведенном выше примере, я действительно выдвигаю полное значение каждой переменной или только первые пару байтов? Если приведенный выше код неверен, что будет правильным способом сделать это? Кроме того, если функция, которую мы вызываем, возвращает значение типа double, где хранится это значение? Я предполагаю, что это не может быть в регистре, потому что он может хранить только 32 бита (4 байта). Любая помощь с этим беспорядком будет принята с благодарностью:)

Ответы [ 3 ]

13 голосов
/ 23 февраля 2010

Для передачи 8-байтовых значений, таких как double, вы не сможете использовать обычную PUSH инструкцию. И вы также не помещаете параметры с плавающей точкой (или удваивает) в стек с плавающей точкой. Вам необходимо поместить эти жирные параметры в стек «вручную». Например, нажать & pi; в качестве параметра функции f:

  __asm {
    FLDPI                    // load pi onto FP stack
    SUB ESP,8                // make room for double on processor stack
    FSTP QWORD PTR [ESP]     // store pi in proc stack slot (and pop from FP stack)
    CALL f
    ADD ESP,8                // clean up stack (assuming f is _cdecl)
  }
4 голосов
/ 22 февраля 2010

32-битная архитектура x86 автоматически дополняет значения, помещаемые в стек, до 32 бит.

Есть что-то, что вы должны иметь в виду. Если функция, которую вы вызываете, использует соглашение о вызовах __cdecl, вы должны «вытолкнуть» то, что вы нажали впоследствии. Однако для функций __stdcall вы не должны этого делать.

extern "C" int    __cdecl   function1(int, double);
extern "C" double __stdcall function2(char, char*);

int a = 5;
double b = 5.0;
int retval1;
char c = '5';
char *d = "Hello";
double retval2;

__asm {
    push b
    push a
    call function1
    add esp, 4*2 // "pop" what we pushed
    mov retval1, eax
    push d
    push c
    call function2
    mov retval2, eax
}
1 голос
/ 22 февраля 2010

Как правило, вы вводите полный размер компьютерного слова. Это зависит от чипа, но на 32-битной Intel будет 4 байта, а на 64-битной Intel будет 8 (в зависимости от компилятора - Visual Studio по-прежнему поддерживает только сборку IA32 - 4 байта).

Лучший ответ - посмотреть документацию для вашего конкретного компилятора.

...