[C/C++] appel dynamique de fonction
@xi@g@me
Messages postés
67
Date d'inscription
Statut
Membre
Dernière intervention
-
@xi@g@me Messages postés 67 Date d'inscription Statut Membre Dernière intervention -
@xi@g@me Messages postés 67 Date d'inscription Statut Membre Dernière intervention -
Bonjour,
il y a un problème sur lequel je bloque depuis un certain temps, et je voudrais savoir si l'un d'entre vous peut m'aider (même si la solution nécessite de l'assembleur c'est pas un problème, et la portabilité m'importe peu (développement ciblé Windows exlu sous VS 2005 C++))
En fait je souhaite créer une bibliothèque dynamique (DLL) qui soit capable d'appeler des fonctions qui lui sont passées plus tard. Je m'explique
Le développeur qui utilise ma bibliothèque va créer une fonction, par exemple maFonction(int, int char*);
Ensuite, il me donne le pointeur de la fonction, puis il me dit que cette fonction prend 2 int et un char * (ce que l'on a aucun moyen de savoir à la compilation)
La DLL doit être capable d'appeller cette fonction avec les arguments demandés (la bibliothèque génère ces arguments en fonction d'autres entrées utilisateur)
Comment faire?
merci à tous d'avance
@xi@g@me
il y a un problème sur lequel je bloque depuis un certain temps, et je voudrais savoir si l'un d'entre vous peut m'aider (même si la solution nécessite de l'assembleur c'est pas un problème, et la portabilité m'importe peu (développement ciblé Windows exlu sous VS 2005 C++))
En fait je souhaite créer une bibliothèque dynamique (DLL) qui soit capable d'appeler des fonctions qui lui sont passées plus tard. Je m'explique
Le développeur qui utilise ma bibliothèque va créer une fonction, par exemple maFonction(int, int char*);
Ensuite, il me donne le pointeur de la fonction, puis il me dit que cette fonction prend 2 int et un char * (ce que l'on a aucun moyen de savoir à la compilation)
La DLL doit être capable d'appeller cette fonction avec les arguments demandés (la bibliothèque génère ces arguments en fonction d'autres entrées utilisateur)
Comment faire?
merci à tous d'avance
@xi@g@me
A voir également:
- [C/C++] appel dynamique de fonction
- Fonction si et - Guide
- Nommez une application d'appel vidéo ou de visioconférence - Guide
- Tableau croisé dynamique - Guide
- Appel privé - Guide
- Exemple tableau croisé dynamique télécharger - Télécharger - Tableur
17 réponses
Salut,
Non ça ne necessite pas d'assembleur :-)
Par contre il faudra que le pointeur de fonction en question fourni par l'utilisateur soit de nombre de paramètres variables, ou plus exactement ne prenne qu'un paramètre: un argument de type va_list (type standard fourni dans stdarg.h représentant une liste d'argument.
Quelques explications ici:
http://www.linux-kheops.com/doc/man/manfr/man-ascii-0.9/man3/va_start.3.txt.html
Un exemple avec une fonction qui additionne deux entiers:
Bien sûr il faut quand même être sûr du type de retour, c'est la moindre des choses :-)
Non ça ne necessite pas d'assembleur :-)
Par contre il faudra que le pointeur de fonction en question fourni par l'utilisateur soit de nombre de paramètres variables, ou plus exactement ne prenne qu'un paramètre: un argument de type va_list (type standard fourni dans stdarg.h représentant une liste d'argument.
Quelques explications ici:
http://www.linux-kheops.com/doc/man/manfr/man-ascii-0.9/man3/va_start.3.txt.html
Un exemple avec une fonction qui additionne deux entiers:
//Fichier source du developpeur #include <stdarg.h> int add(va_list args) { int arg1, arg2; arg1 = va_arg(args, int); arg2 = va_arg(args, int); return arg1 + arg2; } int main() { int test = exec_user_func(add, 1, 2); printf("%d\n", test); // Affichera 3 return 0; } //Fichier dll #include <stdarg.h> typedef (int) (*func_variable) (va_list); int exec_user_func(func_variable func, ...) { int ret; va_list args; va_start(args, func); ret = func(args); va_end(args); }
Bien sûr il faut quand même être sûr du type de retour, c'est la moindre des choses :-)
salut,
et merci pour ta réponse rapide.
le souci est que je ne souhaite pas que l'utilisateur aie a utiliser va_list car il pourrait lancer ses méthodes de lui même parfois. j'ai pensé a va_list, mais je ne souhaite pas l'utiliser. Je voudrais que l'utilisateur aie a déclarer ses fonctions comme d'habitude (ex : void add(int, int);)
ass-tu une idée?
et merci pour ta réponse rapide.
le souci est que je ne souhaite pas que l'utilisateur aie a utiliser va_list car il pourrait lancer ses méthodes de lui même parfois. j'ai pensé a va_list, mais je ne souhaite pas l'utiliser. Je voudrais que l'utilisateur aie a déclarer ses fonctions comme d'habitude (ex : void add(int, int);)
ass-tu une idée?
ba là je me demande si c'est faisable.
En C++ ça me semble impossible car la fonction a dans son nom le nombre et type d'argument une fois compilé.
en général, dans ce genre de cas, tu passe par un paramètre générique comme void*.
En C++ ça me semble impossible car la fonction a dans son nom le nombre et type d'argument une fois compilé.
en général, dans ce genre de cas, tu passe par un paramètre générique comme void*.
De toutes façons même en C je doute que ça soit faisable comme tu le souhaites.
L'idéal comme le Char Snipeur, c'est d'avoir une fonction qui prends en paramètre un pointeur vers une structure, puis de l'appeller par la fonction de dll qui prendra le paramètre en tant que void *.
L'idéal comme le Char Snipeur, c'est d'avoir une fonction qui prends en paramètre un pointeur vers une structure, puis de l'appeller par la fonction de dll qui prendra le paramètre en tant que void *.
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
salut,
En faisant des recherches sur internet, c'est effectivement ce que j'ai remarqué...
ne peut t-on pas faire en utilisant du code assembleur?
par exemple on fait un push des paramètres et ensuite un call sur la fonction (et un pop pour enlever les paramètres)
En faisant des recherches sur internet, c'est effectivement ce que j'ai remarqué...
ne peut t-on pas faire en utilisant du code assembleur?
par exemple on fait un push des paramètres et ensuite un call sur la fonction (et un pop pour enlever les paramètres)
en fait, un premier appel sert à donner à la DLL le pointeur de la fonction.
Ensuite, c'est assez complexe mais disons que l'utilisateur indique indirectement les paramètres formels de la fonction pour faire simple
une fois toutes les fonctions et leurs paramètres formels envoyés, l'utilisateur appelle une fonction qui lance un algorithme dans la DLL qui est susceptible d'appeler les fonctions transmises par l'utilisateur.
Ensuite, c'est assez complexe mais disons que l'utilisateur indique indirectement les paramètres formels de la fonction pour faire simple
une fois toutes les fonctions et leurs paramètres formels envoyés, l'utilisateur appelle une fonction qui lance un algorithme dans la DLL qui est susceptible d'appeler les fonctions transmises par l'utilisateur.
ben en fait, ce qu'il me faudrait c'est savoir comment implémenter du code assembleur dans Visual Studio 2005 et aussi comment faire pour savoir en assembleur ou sont stockées mes variables C.
Après il suffit d'expérimenter ^^'
Après il suffit d'expérimenter ^^'
Dans VC:
Pour trouver tes variables, ah ben là.... :-)
C'est tout un truc à apprendre.
Les variables locales aux fonctions sont soit dans la pile soit dans des registres.
Généralement c'est les deux:
Les variables locales sont dans la pile. Mais dès qu'il faut faire un calcul sur ces variables, on les met dans des registres. Une fois le calcul fait, on les remets eventuellement dans la pile.
Mais pour optimiser, parfois le compilo cantone les variables dans les registres.
Quant aux variables globales sont dans des sections du binaire (dans la mémoire donc...quelque part).
Ici tu trouveras des renseignements dans les derniers chapitres : http://www.drpaulcarter.com/pcasm/
Le passage de paramètres, c'est dans la pile. Par défaut avec la convention cdecl, mais sous windows les fonctions de l'api sont en stdcall: http://www.commentcamarche.net/faq/sujet 4874 programmation conventions de passage de parametres sous x86
Voilà voilà. Ca va pas être facile au début :-)
__asm { mov eax, ebx etc.... }
Pour trouver tes variables, ah ben là.... :-)
C'est tout un truc à apprendre.
Les variables locales aux fonctions sont soit dans la pile soit dans des registres.
Généralement c'est les deux:
Les variables locales sont dans la pile. Mais dès qu'il faut faire un calcul sur ces variables, on les met dans des registres. Une fois le calcul fait, on les remets eventuellement dans la pile.
Mais pour optimiser, parfois le compilo cantone les variables dans les registres.
Quant aux variables globales sont dans des sections du binaire (dans la mémoire donc...quelque part).
Ici tu trouveras des renseignements dans les derniers chapitres : http://www.drpaulcarter.com/pcasm/
Le passage de paramètres, c'est dans la pile. Par défaut avec la convention cdecl, mais sous windows les fonctions de l'api sont en stdcall: http://www.commentcamarche.net/faq/sujet 4874 programmation conventions de passage de parametres sous x86
Voilà voilà. Ca va pas être facile au début :-)
ok, merci beaucoup pour toutes ces infos ^^
dès que j'ai le temps j'essaye!!! (avec ce prof de génie logiciel on est pas sorti ^^')
dès que j'ai le temps j'essaye!!! (avec ce prof de génie logiciel on est pas sorti ^^')
salut à tous
j'avance un peu dans la résolution du problème, mais je suis tombé sur quelques trus assez amusants :
La première chose que j'ai faite est de créer ce petit programme :
(ici je fonctionne en stdcall pour le moment)
je compile donc ce code en faisant g++ -S, et j'obtiens ceci (simplifié):
dans la partie déclarations des variables (ou je fais int b = 35; etc), je vois ceci : (LC1 c'est la chaine de caractères)
On comprend donc en toute logique que dans la pile d'appel (ebp) on met de -8 à -5 la chaine de caractères (déclarée en dernier), de -12 à -9 l'entier b et de -16 à -13 l'entier a. J'en déduit donc que le compilateur met les variables dans l'ordre "inverse" de leur déclaration.
Ce qui m'a emmené à faire ceci :
Le principe étant simple, (on créée 2 pointeurs de fonction a paramètre vides (void) qu'on affecte avec les fonctions à utiliser (avec un cast de type à l'aide de f_ptr), ensuite on empile en assembleur les paramètres des fonctions puis on appelle celles-ci grâce au pointeur), le code compile et fonctionne.
Pour pouvoir continuer, j'ai donc voulu écrire une fonction qui prend plusieurs paramètres pour voir comment elle est appelée. Pour ceci, je rajoute les paramètres avant a, b et tot afin d'éviter de changer leur adresse dans la pile d'appel.
j'obtiens donc le code suivant :
j'ai rajouté la fonction écrire2, ainsi que les variables de types long long, char et short (tant qu'à faire...)
j'obtiens ceci en assembleur (code complet)
je m'intéresse à la partie déclaration des variables, et là :
l'entier b (35) a été déplacé a -16, le a à -20 et la chaine de caractères à -12 :S
alors ma question est pourquoi il n'y a plus rien à -8 (une chaine de caractères étant un pointeur sur caractères (machine 32 bits) elle ne prend que 4 octets et donc que de -12 à -9, et aussi pourquoi le long long ne prend que de -32 (jusqu'à -25) et laisse donc de -24 à -21 libre???
est-ce que quelqu'un à une idée? je ne comprend pas le comportement du compilateur
merci d'avance et bon courage :p ^^
j'avance un peu dans la résolution du problème, mais je suis tombé sur quelques trus assez amusants :
La première chose que j'ai faite est de créer ce petit programme :
(ici je fonctionne en stdcall pour le moment)
#include <stdio.h> void __attribute__((stdcall)) ecrire(int a) { printf("%d\n", a); } void __attribute__((stdcall)) afficher(char * s) { printf("%s\n", s); } int main(int argc, char * argv[]) { int a = 24; int b = 35; char * tot = "totohffdfdvfgsjhdhjkgsgbdshfejgtrjhdfbvsdjbfjsvjfsyuedbfhndbsjvfjsdbfjhsdgfjgsjgeyugfhjsgfjsvfjgeygesyfv fgsgfeyu/n \n \%d frrty /t fdthdjhskdvnbnjfherkuiugfdhgbn nsejirhtier et la ca marche encore ???????h"; // affichage de 35 ecrire(b); // affichage de toto afficher(tot); // affichage de 24 ecrire(a); }
je compile donc ce code en faisant g++ -S, et j'obtiens ceci (simplifié):
main: .LFB4: leal 4(%esp), %ecx .LCFI6: andl $-16, %esp pushl -4(%ecx) .LCFI7: pushl %ebp .LCFI8: movl %esp, %ebp .LCFI9: pushl %ecx .LCFI10: subl $20, %esp .LCFI11: movl $24, -16(%ebp) movl $35, -12(%ebp) movl $.LC1, -8(%ebp) movl -12(%ebp), %eax movl %eax, (%esp) call _Z6ecrirei subl $4, %esp movl -8(%ebp), %eax movl %eax, (%esp) call _Z8afficherPc subl $4, %esp movl -16(%ebp), %eax movl %eax, (%esp) call _Z6ecrirei subl $4, %esp movl $0, %eax movl -4(%ebp), %ecx leave leal -4(%ecx), %esp ret
dans la partie déclarations des variables (ou je fais int b = 35; etc), je vois ceci : (LC1 c'est la chaine de caractères)
movl $24, -16(%ebp) movl $35, -12(%ebp) movl $.LC1, -8(%ebp)
On comprend donc en toute logique que dans la pile d'appel (ebp) on met de -8 à -5 la chaine de caractères (déclarée en dernier), de -12 à -9 l'entier b et de -16 à -13 l'entier a. J'en déduit donc que le compilateur met les variables dans l'ordre "inverse" de leur déclaration.
Ce qui m'a emmené à faire ceci :
#include <stdio.h> void __attribute__((stdcall)) ecrire(int a) { printf("%d\n", a); } void __attribute__((stdcall)) afficher(char * s) { printf("%s\n", s); } typedef void (*f_ptr)(); int main(int argc, char * argv[]) { void (*ptr)() = (f_ptr)(ecrire); void (*ptr2)() = (f_ptr)(afficher); int a = 24; int b = 35; char * tot = "totohffdfdvfgsjhdhjkgsgbdshfejgtrjhdfbvsdjbfjsvjfsyuedbfhndbsjvfjsdbfjhsdgfjgsjgeyugfhjsgfjsvfjgeygesyfv fgsgfeyu/n \n \%d frrty /t fdthdjhskdvnbnjfherkuiugfdhgbn nsejirhtier et la ca marche encore ???????h"; // affichage de 35 asm("pushl %eax"); asm("movl -12(%ebp), %eax"); asm("movl %eax, (%esp)"); ptr(); asm ("popl %eax"); asm("subl $4, %esp"); // affichage de tot asm("pushl %eax"); asm("movl -8(%ebp), %eax"); asm("movl %eax, (%esp)"); ptr2(); asm("popl %eax"); asm("subl $4, %esp"); // affichage de 24 asm("pushl %eax"); asm("movl -16(%ebp), %eax"); asm("movl %eax, (%esp)"); ptr(); asm("popl %eax"); asm("subl $4, %esp"); }
Le principe étant simple, (on créée 2 pointeurs de fonction a paramètre vides (void) qu'on affecte avec les fonctions à utiliser (avec un cast de type à l'aide de f_ptr), ensuite on empile en assembleur les paramètres des fonctions puis on appelle celles-ci grâce au pointeur), le code compile et fonctionne.
Pour pouvoir continuer, j'ai donc voulu écrire une fonction qui prend plusieurs paramètres pour voir comment elle est appelée. Pour ceci, je rajoute les paramètres avant a, b et tot afin d'éviter de changer leur adresse dans la pile d'appel.
j'obtiens donc le code suivant :
#include <stdio.h> void __attribute__((stdcall)) ecrire(int a) { printf("%d\n", a); } void __attribute__((stdcall)) ecrire2(short c, char d, long long x, int a) { printf("%d-%d-%d-%d\n", c, d, x, a); } void __attribute__((stdcall)) afficher(char * s) { printf("%s\n", s); } typedef void (*f_ptr)(); int main(int argc, char * argv[]) { void (*ptr)() = (f_ptr)(ecrire); void (*ptr2)() = (f_ptr)(afficher); short c = 12; char d = 'k'; long long x = 285145145; int a = 24; int b = 35; char * tot = "totohffdfdvfgsjhdhjkgsgbdshfejgtrjhdfbvsdjbfjsvjfsyuedbfhndbsjvfjsdbfjhsdgfjgsjgeyugfhjsgfjsvfjgeygesyfv fgsgfeyu/n \n \%d frrty /t fdthdjhskdvnbnjfherkuiugfdhgbn nsejirhtier et la ca marche encore ???????h"; // affichage de 35 asm("pushl %eax"); asm("movl -12(%ebp), %eax"); asm("movl %eax, (%esp)"); ptr(); asm ("popl %eax"); asm("subl $4, %esp"); // affichage de toto asm("pushl %eax"); asm("movl -8(%ebp), %eax"); asm("movl %eax, (%esp)"); ptr2(); asm("popl %eax"); asm("subl $4, %esp"); // appel de ecrire2 ecrire2(c, d, x, a); // affichage de 24 asm("pushl %eax"); asm("movl -16(%ebp), %eax"); asm("movl %eax, (%esp)"); ptr(); asm("popl %eax"); asm("subl $4, %esp"); }
j'ai rajouté la fonction écrire2, ainsi que les variables de types long long, char et short (tant qu'à faire...)
j'obtiens ceci en assembleur (code complet)
.file "test.cpp" .text .align 2 .globl _Z8afficherPc .type _Z8afficherPc, @function _Z8afficherPc: .LFB4: pushl %ebp .LCFI0: movl %esp, %ebp .LCFI1: subl $8, %esp .LCFI2: movl 8(%ebp), %eax movl %eax, (%esp) call puts leave ret $4 .LFE4: .size _Z8afficherPc, .-_Z8afficherPc .globl __gxx_personality_v0 .section .rodata .LC0: .string "%d-%d-%d-%d\n" .text .align 2 .globl _Z7ecrire2scxi .type _Z7ecrire2scxi, @function _Z7ecrire2scxi: .LFB3: pushl %ebp .LCFI3: movl %esp, %ebp .LCFI4: pushl %ebx .LCFI5: subl $52, %esp .LCFI6: movl 8(%ebp), %eax movl 12(%ebp), %edx movw %ax, -12(%ebp) movb %dl, -16(%ebp) movl 16(%ebp), %eax movl %eax, -24(%ebp) movl 20(%ebp), %eax movl %eax, -20(%ebp) movsbl -16(%ebp),%ecx movswl -12(%ebp),%ebx movl 24(%ebp), %eax movl %eax, 20(%esp) movl -24(%ebp), %eax movl -20(%ebp), %edx movl %eax, 12(%esp) movl %edx, 16(%esp) movl %ecx, 8(%esp) movl %ebx, 4(%esp) movl $.LC0, (%esp) call printf addl $52, %esp popl %ebx popl %ebp ret $20 .LFE3: .size _Z7ecrire2scxi, .-_Z7ecrire2scxi .section .rodata .align 4 .LC1: .string "totohffdfdvfgsjhdhjkgsgbdshfejgtrjhdfbvsdjbfjsvjfsyuedbfhndbsjvfjsdbfjhsdgfjgsjgeyugfhjsgfjsvfjgeygesyfv fgsgfeyu/n \n %d frrty /t fdthdjhskdvnbnjfherkuiugfdhgbn nsejirhtier et la ca marche encore ???????h" .text .align 2 .globl main .type main, @function main: .LFB5: leal 4(%esp), %ecx .LCFI7: andl $-16, %esp pushl -4(%ecx) .LCFI8: pushl %ebp .LCFI9: movl %esp, %ebp .LCFI10: pushl %ebx .LCFI11: pushl %ecx .LCFI12: subl $80, %esp .LCFI13: movl $_Z6ecrirei, -44(%ebp) movl $_Z8afficherPc, -40(%ebp) movw $12, -36(%ebp) movb $107, -33(%ebp) movl $285145145, -32(%ebp) movl $0, -28(%ebp) movl $24, -20(%ebp) movl $35, -16(%ebp) movl $.LC1, -12(%ebp) #APP pushl %eax movl -12(%ebp), %eax movl %eax, (%esp) #NO_APP movl -44(%ebp), %eax call *%eax #APP popl %eax subl $4, %esp pushl %eax movl -8(%ebp), %eax movl %eax, (%esp) #NO_APP movl -40(%ebp), %eax call *%eax #APP popl %eax subl $4, %esp #NO_APP movsbl -33(%ebp),%ecx movswl -36(%ebp),%ebx movl -20(%ebp), %eax movl %eax, 16(%esp) movl -32(%ebp), %eax movl -28(%ebp), %edx movl %eax, 8(%esp) movl %edx, 12(%esp) movl %ecx, 4(%esp) movl %ebx, (%esp) call _Z7ecrire2scxi subl $20, %esp #APP pushl %eax movl -16(%ebp), %eax movl %eax, (%esp) #NO_APP movl -44(%ebp), %eax call *%eax #APP popl %eax subl $4, %esp #NO_APP movl $0, %eax leal -8(%ebp), %esp popl %ecx popl %ebx popl %ebp leal -4(%ecx), %esp ret .LFE5: .size main, .-main .section .rodata .LC2: .string "%d\n" .text .align 2 .globl _Z6ecrirei .type _Z6ecrirei, @function _Z6ecrirei: .LFB2: pushl %ebp .LCFI14: movl %esp, %ebp .LCFI15: subl $8, %esp .LCFI16: movl 8(%ebp), %eax movl %eax, 4(%esp) movl $.LC2, (%esp) call printf leave ret $4 .LFE2: .size _Z6ecrirei, .-_Z6ecrirei .section .eh_frame,"a",@progbits .Lframe1: .long .LECIE1-.LSCIE1 .LSCIE1: .long 0x0 .byte 0x1 .string "zP" .uleb128 0x1 .sleb128 -4 .byte 0x8 .uleb128 0x5 .byte 0x0 .long __gxx_personality_v0 .byte 0xc .uleb128 0x4 .uleb128 0x4 .byte 0x88 .uleb128 0x1 .align 4 .LECIE1: .LSFDE1: .long .LEFDE1-.LASFDE1 .LASFDE1: .long .LASFDE1-.Lframe1 .long .LFB4 .long .LFE4-.LFB4 .uleb128 0x0 .byte 0x4 .long .LCFI0-.LFB4 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI1-.LCFI0 .byte 0xd .uleb128 0x5 .align 4 .LEFDE1: .LSFDE3: .long .LEFDE3-.LASFDE3 .LASFDE3: .long .LASFDE3-.Lframe1 .long .LFB3 .long .LFE3-.LFB3 .uleb128 0x0 .byte 0x4 .long .LCFI3-.LFB3 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI4-.LCFI3 .byte 0xd .uleb128 0x5 .byte 0x4 .long .LCFI6-.LCFI4 .byte 0x83 .uleb128 0x3 .align 4 .LEFDE3: .LSFDE5: .long .LEFDE5-.LASFDE5 .LASFDE5: .long .LASFDE5-.Lframe1 .long .LFB5 .long .LFE5-.LFB5 .uleb128 0x0 .byte 0x4 .long .LCFI7-.LFB5 .byte 0xc .uleb128 0x1 .uleb128 0x0 .byte 0x9 .uleb128 0x4 .uleb128 0x1 .byte 0x4 .long .LCFI8-.LCFI7 .byte 0xc .uleb128 0x4 .uleb128 0x4 .byte 0x4 .long .LCFI9-.LCFI8 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI10-.LCFI9 .byte 0xd .uleb128 0x5 .byte 0x4 .long .LCFI12-.LCFI10 .byte 0x84 .uleb128 0x4 .byte 0x83 .uleb128 0x3 .align 4 .LEFDE5: .LSFDE7: .long .LEFDE7-.LASFDE7 .LASFDE7: .long .LASFDE7-.Lframe1 .long .LFB2 .long .LFE2-.LFB2 .uleb128 0x0 .byte 0x4 .long .LCFI14-.LFB2 .byte 0xe .uleb128 0x8 .byte 0x85 .uleb128 0x2 .byte 0x4 .long .LCFI15-.LCFI14 .byte 0xd .uleb128 0x5 .align 4 .LEFDE7: .ident "GCC: (GNU) 4.1.2 20071124 (Red Hat 4.1.2-42)" .section .note.GNU-stack,"",@progbits
je m'intéresse à la partie déclaration des variables, et là :
.LCFI13: movl $_Z6ecrirei, -44(%ebp) movl $_Z8afficherPc, -40(%ebp) movw $12, -36(%ebp) movb $107, -33(%ebp) movl $285145145, -32(%ebp) movl $0, -28(%ebp) movl $24, -20(%ebp) movl $35, -16(%ebp) movl $.LC1, -12(%ebp)
l'entier b (35) a été déplacé a -16, le a à -20 et la chaine de caractères à -12 :S
alors ma question est pourquoi il n'y a plus rien à -8 (une chaine de caractères étant un pointeur sur caractères (machine 32 bits) elle ne prend que 4 octets et donc que de -12 à -9, et aussi pourquoi le long long ne prend que de -32 (jusqu'à -25) et laisse donc de -24 à -21 libre???
est-ce que quelqu'un à une idée? je ne comprend pas le comportement du compilateur
merci d'avance et bon courage :p ^^
Salut.
Je suis une quiche grave en assembleur.
Mais pour tes histoires de décalage, j'ai peux être un début de réponse : à cause de l'alignement.
Pour accéder plus vite aux données, des fois le compilateur les décales, car il lit par paquet. Regarde le chapitre 7 de ce livre, c'est à ça que ça m'a fait pensé : https://beuss.developpez.com/tutoriels/pcasm/
Après, il est possible que ça n'est rien à voir. Peut être intercale t'il des données masqué à ces endroits, essai éventuellement de les lire.
Je suis une quiche grave en assembleur.
Mais pour tes histoires de décalage, j'ai peux être un début de réponse : à cause de l'alignement.
Pour accéder plus vite aux données, des fois le compilateur les décales, car il lit par paquet. Regarde le chapitre 7 de ce livre, c'est à ça que ça m'a fait pensé : https://beuss.developpez.com/tutoriels/pcasm/
Après, il est possible que ça n'est rien à voir. Peut être intercale t'il des données masqué à ces endroits, essai éventuellement de les lire.
Il n'y a plus à -8 parce que ebx etc ecx on t été pushés.
Donc il y a ces deux registres entre (%ebp) et 8(%ebp).
Par contre entre 21 et 24, c'est le mystère... Peut être l'alignement oui.
En tout cas, comme tu l'as vu le comportement d'un compilateur n'est pas facile à prévoir, donc l'idéal c'est d'utiliser des références évoluées à tes variables.
Voilà comment je verrais bien ton code:
C'est un peu spécial et pas facile à expliquer.
Il faudrait que tu lises ça: https://www.ibm.com/developerworks/linux/library/l-ia/index.html
PS: Je peux pas tester le code ici donc ya sûrement quelques erreurs.
Donc il y a ces deux registres entre (%ebp) et 8(%ebp).
Par contre entre 21 et 24, c'est le mystère... Peut être l'alignement oui.
En tout cas, comme tu l'as vu le comportement d'un compilateur n'est pas facile à prévoir, donc l'idéal c'est d'utiliser des références évoluées à tes variables.
Voilà comment je verrais bien ton code:
#include <stdio.h> void __attribute__((stdcall)) ecrire(int a) { printf("%d\n", a); } void __attribute__((stdcall)) ecrire2(short c, char d, long x, int a) { printf("%d-%d-%d-%d\n", c, d, x, a); } void __attribute__((stdcall)) afficher(char * s) { printf("%s\n", s); } typedef void (*f_ptr)(); int main(int argc, char * argv[]) { void (*ptr)() = (f_ptr)(ecrire); void (*ptr2)() = (f_ptr)(afficher); short c = 12; char d = 'k'; long x = 285145145; int a = 24; int b = 35; char * tot = "totohffdfdvfgsjhdhjkgsgbdshfejgtrjhdfbvsdjbfjsvjfsyuedbfhndbsjvfjsdbfjhsdgfjgsjgeyugfhjsgfjsvfjgeygesyfv fgsgfeyu/n \n \%d frrty /t fdthdjhskdvnbnjfherkuiugfdhgbn nsejirhtier et la ca marche encore ???????h"; __asm__ __volatile__( // affichage de 35 "pushl %1\n" "call *(%7)\n" "add$4, %esp\n" // affichage de toto "pushl %0\n" "call *(%6)\n" "add $4, %esp\n" // appel de ecrire2 ecrire2(c, d, x, a); // affichage de 24 "pushl %2\n" "pushl %3\n" "pushw %4\n" "pushw %5\n" "call ecrire2\n" "add $0xc, %esp\n"; : : "r" (tot), "r"(b), "r"(a), "r"(x), "r"(d), "r"(c), "r"(ptr2), "r"(ptr) );
C'est un peu spécial et pas facile à expliquer.
Il faudrait que tu lises ça: https://www.ibm.com/developerworks/linux/library/l-ia/index.html
PS: Je peux pas tester le code ici donc ya sûrement quelques erreurs.
je crois comprendre ce que tu veux dire...
cela me semble est une solution, mais j'ai encore quelques questions
en utilisant ta méthode, est-ce qu'il serait possible par exemple de préparer l'appel à la fonction en plusieurs parties?
je m'explique : quand la bibli recoit un pointeur de fonction, et ses paramètres je ne peux pas tout faire directement sinon il me faudrait du code assembleur pour chaque cas.
Ce que je voudrais avoir au final serait un mélange de C(++) et d'asm pour empiler dynamiquement mes paramètres en fonction de ceux qui me sont demandés
ex:
param=0
boucle
switch(param)
si (param = char)
code_assembleur_pour_pusher_char
si (param = int)
code_assembleur_pour_pusher_int
etc....
fin switch
param++
si (param = nb_param) quitter_la_boucle
fin_boucle
code_assembleur_pour_appeler_la_fonction
code_assembleur_pour_nettoyer_la_pile_a_la_fin_de_l_appel_de_la_fonction
est-ce que cette solution peut marcher dans une DLL en sachant que les utilisateurs donnent les fonctions a appeler une fois la DLL compilée (donc elle ne sait rien avant)?
merci d'avance
cela me semble est une solution, mais j'ai encore quelques questions
en utilisant ta méthode, est-ce qu'il serait possible par exemple de préparer l'appel à la fonction en plusieurs parties?
je m'explique : quand la bibli recoit un pointeur de fonction, et ses paramètres je ne peux pas tout faire directement sinon il me faudrait du code assembleur pour chaque cas.
Ce que je voudrais avoir au final serait un mélange de C(++) et d'asm pour empiler dynamiquement mes paramètres en fonction de ceux qui me sont demandés
ex:
param=0
boucle
switch(param)
si (param = char)
code_assembleur_pour_pusher_char
si (param = int)
code_assembleur_pour_pusher_int
etc....
fin switch
param++
si (param = nb_param) quitter_la_boucle
fin_boucle
code_assembleur_pour_appeler_la_fonction
code_assembleur_pour_nettoyer_la_pile_a_la_fin_de_l_appel_de_la_fonction
est-ce que cette solution peut marcher dans une DLL en sachant que les utilisateurs donnent les fonctions a appeler une fois la DLL compilée (donc elle ne sait rien avant)?
merci d'avance