|
|
|
|
Configuration: Linux Firefox 3.0.1
|
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: //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 :-) Alors comme ça je fais le zouave hein? |
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? |
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*. Salutation ! avant je croyais, maintenant je suis fixé.Jésus Christ Char Snipeur |
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 *. Alors comme ça je fais le zouave hein? |
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)
|
on le lui dit par l'appel d'autres fonctions ^^
|
|
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. |
|
Aîe ! ma tête...
Bonne chance, et dit nous si tu y arrive. Salutation ! avant je croyais, maintenant je suis fixé.Jésus Christ Char Snipeur |
Oui moi aussi bobo les neurones là...
Alors comme ça je fais le zouave hein? |
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 ^^' |
|
Dans VC:
__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://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 :-) Alors comme ça je fais le zouave hein? |
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 ^^') |
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)
#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é : http://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. Salutation ! avant je croyais, maintenant je suis fixé.Jésus Christ Char Snipeur |
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: #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: http://www.ibm.com/developerworks/linux/library/l-ia.html PS: Je peux pas tester le code ici donc ya sûrement quelques erreurs. Le gâteau est un mensonge! |
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
|
ok merci je vous tiens au jus
+++ |
Résultats pour [C/C++] appel dynamique de fonction
Résultats pour [C/C++] appel dynamique de fonction
Résultats pour [C/C++] appel dynamique de fonction