Вызов __cdecl из __stdcall функции не трогая стек

Тема в разделе "WASM.BEGINNERS", создана пользователем mofer, 4 окт 2006.

  1. mofer

    mofer Константин

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    10
    допустим есть функция
    int __cdecl func_c(int a, int b, int c)
    {
    return a+b+c; // это для примера
    }

    а я хочу сделать функцию типа
    int __stdcall InvokeFunc(void* func_c, int a, int b, int c)
    или в общем случае
    int __stdcall InvokeFunc(void* func_c, int число параметров, [параметры])

    которая не трогая стек с параметрами просто передает управление на func_c

    InvokeFunc proc stdcall public, funcptr:lol: WORD
    pop ecx ; save return address
    pop edx ; Get function pointer
    push ecx ; Restore return address
    jmp edx ; Transfer control to the function pointer
    InvokeFunc endp

    объясните почему портится ESP (на [число параметров]*4) и как это поправить?
     
  2. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    mofer
    cdecl не чистит стек. Можно сделать так:
    Код (Text):
    1. int __cdecl func_c(void* unused_param, int a, int b, int c)
    2. {
    3.     return a+b+c; // это для примера
    4. }
    5.  
    6. InvokeFunc proc stdcall public, funcptr:DWORD
    7.  pop ecx
    8.  pop edx
    9.  push ecx
    10.  call edx
    11.  pop ecx
    12.  pop edx
    13.  shl edx,2
    14.  add esp,edx
    15.  jmp ecx
    16. InvokeFunc endp
    Хотя проще сделать InvokeFunc cdecl.
     
  3. Jupiter

    Jupiter Jupiter

    Публикаций:
    0
    Регистрация:
    12 авг 2004
    Сообщения:
    532
    Адрес:
    Russia
    а компилится во что? дизасм этого кода покажи
    в конце просто retn?
     
  4. mofer

    mofer Константин

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    10
    > Quantum
    > int __cdecl func_c(void* unused_param, int a, int b, int c)
    мне нельзя портить func_c (это какая нибудь экспортируемая в длл функция)

    > Хотя проще сделать InvokeFunc cdecl.
    это нужно чтобы вызывать __cdecl функции из C# (а там по умолчанию все в __sdcall)

    > Jupiter
    я не понял про что ты...

    ------------------------------------------
    int __cdecl func_c(int a, int b, int c)
    int __stdcall func_s(int a, int b, int c)

    func_c(1,2,3):
    push 3
    push 2
    push 1
    call func_c
    add esp, 12

    func_s(1,2,3):
    push 3
    push 2
    push 1
    call func_s

    Я правильно понимаю вызов эти двух функций отличает только на сдвиг esp?

    -----

    InvokeFunc proc stdcall public, funcptr:lol: WORD
    pop ecx ; return address
    pop edx ; Get function pointer
    call edx ; передать управление с возвратом
    add esp, 12 ; сдвинуть указатель
    InvokeFunc endp

    Это правильно для моего примера?
     
  5. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    mofer
    Тогда eip можно сохранить в глобальную переменную:
    Код (Text):
    1. InvokeFunc proc stdcall public, funcptr:DWORD
    2.  pop DWORD PTR [save_eip]
    3.  pop edx
    4.  call edx
    5.  pop edx
    6.  shl edx,2
    7.  add esp,edx
    8.  jmp DWORD PTR [save_eip]
    9. InvokeFunc endp
    Да, cdecl не чистит после себя стек и это приходится делать тому, кто вызывает её, а stdcall всё делает сама!
     
  6. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Или добавлять в конец зарезервированный параметр dwReserved, в который и записывать адрес возврата

    A вообще-то универсальная InvokeFunc с косвенным call, да еще и с jmp вместо ret это плохо с точки зрения быстродействия ;)
     
  7. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    mofer
    Прежде всего следует определиться с точным прототипом этой ф-ции.
    Если имеется в виду
    Код (Text):
    1. int __stdcall InvokeFunc(void* func_c, int cParam, ...)
    то все эти извращения не нужны: __stdcall с переменным кол. арг. - это то же самое (по части стека), что и __cdecl.
     
  8. Pinkbyte

    Pinkbyte Member

    Публикаций:
    0
    Регистрация:
    3 сен 2006
    Сообщения:
    106
    Поддерживаю green, ибо сам убедился на примере WinAPI, где все функции stdcall а wsprintа - cdecl
     
  9. mofer

    mofer Константин

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    10
    green
    Мне не нужен точный прототип фукции... я просто использую то что по стэку __sdcall и __cdecl одинаковы.
    Я делаю длл c описанно фукцией а потом могу вызывать любую функцию __cdecl из C# где все функции __stdcall.

    для примера, есть функция:
    int __cdecl DLLFUNC_C(int a1, int a2, ... int an) в библиотеке a.dll

    я делаю классик чтобы динамически ее вызывать

    Код (Text):
    1. class Class1{
    2.   [DllImport("kernel32")]
    3.   public extern static int LoadLibrary(string lpLibFileName);
    4.   [DllImport("kernel32")]
    5.   public extern static bool FreeLibrary(int hLibModule);
    6.   [DllImport("kernel32", CharSet=CharSet.Ansi)]
    7.   public extern static int GetProcAddress(int hModule, string lpProcName);
    8.  
    9.   [DllImport("Invoke")]
    10.   public extern static int InvokeFunc(int funcptr, int a1, int a2, ... int an, число параметров);
    11.  
    12.   static void Main(string[] args) {
    13.     int hmod=LoadLibrary("a.dll");
    14.     int funcaddr=GetProcAddress(hmod, "DLLFUNC_C");
    15.     int result=InvokeFunc(funcaddr, a1, a2, ... an, n);
    16.     FreeLibrary(hmod);
    17.   }
    18. }
    прототип я определяю в зависимости от используемой функции и вызываю ее динамически как native фунцию.

    InvokeFunc(funcaddr, a1, a2, ... an, n);
    фукция передает управление на funcaddr которая вычитывает из стека все параметры
    затем считывает последний параметр и сдвигает esp на нужное число.

    всем спасибо за коментарии!
     
  10. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    mofer
    Тогда так, по идее, должно работать:
    Код (Text):
    1. ; int "__stdcall" InvokeFunc(void* func_c, int число параметров, [параметры])
    2. InvokeFunc proc
    3.         mov    eax, [esp+8]
    4.         xchg   eax, [esp+4]
    5.         add    esp, 0Ch
    6.         call   eax
    7.         mov    eax, [esp-8]
    8.         mov    ecx, [esp-0Ch]
    9.         shl    eax, 2
    10.         add    esp, eax
    11.         jmp    ecx
    12. InvokeFunc endp