IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

Appel de fonction d'une DLL


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut Appel de fonction d'une DLL
    Bonjour,

    Je travaille sur la création d'un langage interprété qui sera utilisé comme support pour l'exécution de jeux vidéo, mais dont les fonctions multimédias ne seront pas implémentés par défaut dans le langage. Son interpréteur doit donc à terme supporter la gestion de plugins compilés sous forme de DLL qui permettront la gestion sonore, graphique, etc. Cela passe par l'appel de fonctions dont le prototype et les types de variables sont inconnus (mais le seront lors de l'interpréation grâce à des informations dans la string table des DLL). L'interpréteur est codé en C++, langage qui ne permet pas ce genre d'appels à ma connaissance. Pour ce faire, j'utilise donc le code assembleur suivant :

    number = nombre de paramètres pour la fonction à appeler
    parameters = pointeur vers un tableau contenant les pointeurs des variables utilisées en tant que paramètres de la fonction à appeler.
    functionPtr = pointeur de la fonction à appeler, stockée dans une DLL chargée en mémoire.
    returnValue = valeur retournée par la fonction exécutée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
            mov ecx,number
            mov ebx,parameters
            dec ecx
            js appel
        boucle:
            push [ebx+ecx*4]
            dec ecx
            jns boucle
        appel:
            call functionPtr
            mov returnValue,eax
    Ce code est fonctionnel, il me permet bel et bien d'appeler la fonction que je souhaite utiliser et son exécution se fait avec les bons paramètres (en l'occurence, je testais avec l'ouverture d'une boîte de dialogue Win32). Néanmoins, le programme crashe juste après et le compilateur (Visual C++ Express 2005) m'affiche ceci :

    Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
    Et je n'arrive pas à me débarrasser de ce problème autrement qu'en appelant pas la fonction via le code assembleur.

    Quelle est l'erreur dans mon code ?

  2. #2
    Membre confirmé Avatar de dapounet
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2007
    Messages
    469
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2007
    Messages : 469
    Points : 567
    Points
    567
    Par défaut
    Salut,

    C'est peut-être parce que la fonction utilise la convention cdecl : les paramètres sont toujours sur la pile quand elle retourne à la fonction appelante. Il faut simplement ajouter à ESP le nombre d'octets passés en paramètres. Si moins de 12 octets ont été placés il est plus court d'utiliser un ou deux POP.
    :wq

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut
    Merci pour cette réponse précise

    La fonction de la DLL utilise effectivement la convention cdecl, mais je ne parviens à corriger le programme. J'aurais sans doute dû préciser que je débute dans la programmation Assembleur, et qui se je cromprend parfaitement les quelques lignes présentées plus haut, les modifier d'avère plus complexe.

    Un simple "move esp,[number*4]" suffit-il, et si oui à quel endroit du code serait-il judicieux de le placer ? Actuellement, cela provoque un crash.

  4. #4
    Membre actif

    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 193
    Points : 277
    Points
    277
    Par défaut
    La fonction déclarer dans la dll possède un certain nombre de paramêtres.
    La fonction appelante doit mettre le même nombre de paramètres en pile par des push.Ceci pour appel standard.

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut
    Connaissant justement ce nombre de paramètres, je ne pense pas que le problème vienne de là.

  6. #6
    Membre éclairé
    Avatar de edfed
    Profil pro
    être humain
    Inscrit en
    Décembre 2007
    Messages
    476
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : être humain

    Informations forums :
    Inscription : Décembre 2007
    Messages : 476
    Points : 701
    Points
    701
    Billets dans le blog
    1
    Par défaut
    d'apres ce que j'en sais, un call par la pile:

    push param1
    push param2
    call [function] <----- noter les [ ]

    =

    invoke function,param1,param2

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut
    Dans mon cas, je ne peut pas utiliser invoke, ne connaissant pas le nombre de paramètres de la fonction à appeler au moment de la compilation. Je ne le sais qu'à l'interprétation du programme.

    J'ai ajouté, les crochets (d'ailleurs je ne comprends pas pourquoi les ajouter aucune opération n'étant faite), ça plante toujours avec l'ESP.

  8. #8
    Membre éclairé
    Avatar de edfed
    Profil pro
    être humain
    Inscrit en
    Décembre 2007
    Messages
    476
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : être humain

    Informations forums :
    Inscription : Décembre 2007
    Messages : 476
    Points : 701
    Points
    701
    Billets dans le blog
    1
    Par défaut
    invoke est une macro

    cette macro accepte un nombre variable de parametres.
    pour connaitre le nombre de parametre pour une syscall undocumented, il faut dessassembler un programme qui'lutilise, et regarder combien de push sont fait, les ADD esp,4 sont des push...

  9. #9
    Membre confirmé Avatar de dapounet
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2007
    Messages
    469
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2007
    Messages : 469
    Points : 567
    Points
    567
    Par défaut
    Citation Envoyé par -Mod- Voir le message
    Merci pour cette réponse précise

    La fonction de la DLL utilise effectivement la convention cdecl, mais je ne parviens à corriger le programme. J'aurais sans doute dû préciser que je débute dans la programmation Assembleur, et qui se je cromprend parfaitement les quelques lignes présentées plus haut, les modifier d'avère plus complexe.

    Un simple "move esp,[number*4]" suffit-il, et si oui à quel endroit du code serait-il judicieux de le placer ? Actuellement, cela provoque un crash.
    Par exemple si tu pousses trois paramètres d'un DWORD chacun (ils en font toujours au moins un) tu ajoutes "add esp, 12" après le CALL.

    [Edit]

    Donc dans ton cas logiquement tu devrais multiplier le nombre de paramètres par 4 et l'ajouter à ESP, si aucun paramètre n'est plus grand qu'un DWORD.
    :wq

  10. #10
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut
    Ca continue de planter avec cette jolie erreur d'ESP, je commence donc à me demander si ce n'est pas l'interaction C/Asm qui pose problème. Voici donc la fonction complète, en espérant que quelqu'un verra une bêtise...

    En tous cas déjà merci pour toutes vos réponses .

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    void* Call::FunctionCall(string functionName)
    {
     
    	HMODULE lib = LoadLibrary(L"AdvancedGUI.dll");
    	funcTable[functionName] = (Function) GetProcAddress(lib,functionName.c_str());
     
    	LPSTR c1 = "Test";
     
    	int number = 1;
     
            int* returnValue = new int;
    	Function functionPtr = funcTable[functionName];
     
    	void** parameters[1];
    	parameters[0] = (void**) &c1;
     
        _asm
        {
            mov ecx,number
            mov ebx,parameters
            dec ecx
            js appel
        boucle:
            push [ebx+ecx*4]
            dec ecx
            jns boucle
        appel:
            call [functionPtr]
    		add esp,[number*4]
            mov returnValue,eax
        }
     
    	FreeLibrary(lib);
     
    	return (void*) returnValue;
    }
    Note : je compte à terme remplacer la déclaration du tableau parameters par une allocation dynamique.

    @ elfed : oui, j'ai bien compris ^^. Mais comme je l'expliquais plus haut, je travaille sur un interpréteur. Le code assembleur est compilé dans l'interpréteur. Je ne peux donc pas modifier dynamiquement le nombre de paramètres à l'intérieur même du code source.

  11. #11
    Membre confirmé
    Homme Profil pro
    .
    Inscrit en
    Juin 2002
    Messages
    239
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : .
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2002
    Messages : 239
    Points : 567
    Points
    567
    Par défaut
    La ligne : add esp,[number*4]
    me parait douteuse ...

    Je propose plutôt :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    mov returnValue,eax
    mov  eax,number
    shl  eax,2
    add  esp,eax
    ( un double décalage vers la gauche revient à multiplier par 4 )

  12. #12
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut
    Merveilleux, ça fonctionne parfaitement de cette manière ! Merci beaucoup !

  13. #13
    Membre actif

    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 193
    Points : 277
    Points
    277
    Par défaut
    L'utilisation de invoke mérite une petite précision.
    Un prototype déclaré comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MonProgC proto C :DWORD,:DWORD,:VARARG
    Accepte un nombre quelconque d'arguments (minimum 2) et permet d'utiliser invoke pour appeler la fonction,ce qui évite tout ce fatras d'ennuis.

  14. #14
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut
    Ouille. Finalement pas résolu. Plus de problèmes avec l'ESP apparemment, mais je ne parviens pas à appeler des fonctions à plus d'un paramètre. Ce qui me laisse perplexe quant au fonctionnant de l'instruction push...

    Le compilateur m'indique à l'exécution que le pointeur du second paramètre est invalide.

    J'ai bien essayé d'ajouter manuellement les push, mais cela ne fonctionne pas au-delà d'un paramètre.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
        LPSTR c1 = "Test";
     
        int number = 2;
     
        Function functionPtr = funcTable[functionName];
     
        int* parameters[2];
        parameters[0] = (int*) &c1;
        parameters[1] = (int*) &c1;
     
        _asm
        {
            mov ecx,number
            mov ebx,parameters
            dec ecx
            js appel
        boucle:
            push [ebx+ecx*4]
            dec ecx
            jns boucle
        appel:
            call [functionPtr]
    	mov returnValue,eax
    	mov eax,number
    	shl eax,2
    	add esp,eax
        }

  15. #15
    Membre actif

    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 193
    Points : 277
    Points
    277
    Par défaut
    Salut,
    Si Le problème est le passage d'argument entre le c et l'assembleur inline,Il faut demander au compilateur c de produire un fichier désassembler.
    /FAs
    Attention aux paramètres passés par adresse et par valeur.En passant les paramètres par valeur,le compilateur procède a une succesion de push ayant pour effet de recopier la variable dans la pile.

  16. #16
    Membre confirmé Avatar de dapounet
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2007
    Messages
    469
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2007
    Messages : 469
    Points : 567
    Points
    567
    Par défaut
    Je ne sais pas comment fonctionne l'ASM inline de VC++, mais si le code que tu lui donnes est inséré tel quel il y a beaucoup de chances pour qu'il modifie les registres qui contenaient des variables. Il y a sûrement une syntaxe qui permet d'avertir le compilateur qu'on utilise certains registres, l'idéal ce serait de l'utiliser (pour debugger rapidement ajoute juste une série de PUSH au début du code pour sauvegarder les registres que tu vas modifier et restaure-les avec des POP à la fin et regarde si ça marche).
    La fonction de la DLL peut aussi modifier EAX, ECX ou EDX, ça peut perturber le code qui se trouve après l'ASM inline si il y en a.
    :wq

  17. #17
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 50
    Points : 34
    Points
    34
    Par défaut
    Ce que je cherche à faire est visiblement trop compliqué pour moi et je n'ai pas énormément de temps pour apprendre à 100% l'assembleur, donc si quelqu'un est capable de trouver le problème ainsi et de me donner le code qui serait fonctionnel comme correctif, ce serait vraiment génial... Je risque autrement devoir trouver une autre méthode ou de reporter ce projet à plus tard.

    Voilà donc le du code désassemblé provenant de la fonction appelée de la DLL, et que je ne parviens pas vraiment à comprendre complètement avec mes connaissances :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    int Test2(LPSTR test,LPSTR test2)
    {
    10011710  push        ebp  
    10011711  mov         ebp,esp 
    10011713  sub         esp,0C0h 
    10011719  push        ebx  
    1001171A  push        esi  
    1001171B  push        edi  
    1001171C  lea         edi,[ebp-0C0h] 
    10011722  mov         ecx,30h 
    10011727  mov         eax,0CCCCCCCCh 
    1001172C  rep stos    dword ptr es:[edi] 
    	MessageBox(0,test,test2,16);
    1001172E  mov         esi,esp 
    10011730  push        10h  
    10011732  mov         eax,dword ptr [test2] 
    10011735  push        eax  
    10011736  mov         ecx,dword ptr [test] 
    10011739  push        ecx  
    1001173A  push        0    
    1001173C  call        dword ptr [__imp__MessageBoxA@16 (10018314h)] 
    10011742  cmp         esi,esp // Erreur pointée ici.
    10011744  call        @ILT+335(__RTC_CheckEsp) (10011154h) 
    	return 0;
    10011749  xor         eax,eax 
    }

  18. #18
    Membre confirmé
    Homme Profil pro
    .
    Inscrit en
    Juin 2002
    Messages
    239
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : .
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2002
    Messages : 239
    Points : 567
    Points
    567
    Par défaut
    " Ce qui me laisse perplexe quant au fonctionnant de l'instruction push... "

    Je ne pense pas que l'instruction push soit en cause dans ce problème.
    Il me semble que l'erreur provient de l'instruction push [ebx+ecx*4].

    Je cite :
    parameters = pointeur vers un tableau contenant les pointeurs des variables utilisées en tant que paramètres de la fonction à appeler.

    Dans ce cas, il ne faut pas mettre le pointeur du paramètre sur la pile, mais le paramètre lui-même.

    Je suggère donc de remplacer push [ebx+ecx*4] par :

    mov eax,[ebx+ecx*4]
    push [eax]

    .

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. probleme appel de fonction dans une DLL
    Par sylvain.cool dans le forum C++
    Réponses: 12
    Dernier message: 19/06/2008, 17h00
  2. Appel de fonctions d'une DLL C++ depuis Java
    Par max_rossito dans le forum API standards et tierces
    Réponses: 1
    Dernier message: 11/01/2007, 22h54
  3. pb d'appel aux fonctions d'une DLL (visual C++ 6.0)
    Par touti35 dans le forum Visual C++
    Réponses: 4
    Dernier message: 12/12/2006, 09h37
  4. Appel de fonction d'une DLL en TANSAC SQL
    Par sylvain114d dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 19/01/2006, 10h21
  5. Appel aux fonctions d'une DLL externe ??
    Par Fbartolo dans le forum Access
    Réponses: 7
    Dernier message: 21/11/2005, 17h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo