Différence de traduction entre char() et chr() par le compilateur Free Pascal
Par Clandestino
Le 2007-03-24 12:56:28, par Clandestino, Membre éclairé
Introduction
Suite à la lecture d'un post sur le sujet ( http://www.developpez.net/forums/sho...d.php?t=296299 ) , j'ai vérifié dans mon debugger s'il y avait une différence entre les deux fonctions suivantes :
En quelques secondes je me suis rendu compte que le compilateur avait traduit ces deux fonctions de la meme manière, les instructions en assembleur étant exactement les memes pour les deux fonctions. Ceci confirmait l'analyse de Droggo...
Envoyé par Droggo
J'ai soudain été étonné de voir transparaitre dans les réponses suivantes, que l'analyse d'un binaire ou le debuggage de process n'est pas forcément évident pour beaucoup...
Prenant à mon tour mon courage à deux mains , je vai vous montrer comment vous pouvez démontrer sans aucun outil (ormis votre cerveau et un compilateur pascal ), que ces deux fonctions sont équivalentes...
J'espère que ce post vous sera utile ou vous interessera...
Suite à la lecture d'un post sur le sujet ( http://www.developpez.net/forums/sho...d.php?t=296299 ) , j'ai vérifié dans mon debugger s'il y avait une différence entre les deux fonctions suivantes :
Code : |
1 2 3 4 | function fchar (c: byte): char; begin fchar := char(c); end; |
Code : |
1 2 3 4 | function fchr (c: byte): char; begin fchr := chr(c); end; |
Prenant à mon tour mon courage à deux mains
J'espère que ce post vous sera utile ou vous interessera...
-
ClandestinoMembre éclairéPremiers tests
La première chose évidente est de tester que les deux fonctions renvoient les memes valeurs ...
me voici donc en train d'ecrire puis de compiler et lancer ce petit test qui s'amuse à calculer les resultats pour tous les bytes de 1 à 255:
Code : 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
26program test; // mes fonctions à tester function fchar (c: byte): char; begin fchar := char(c); end; function fchr (c: byte): char; begin fchr := chr(c); end; // variables du programme Var i : byte; // programme principal begin for i := 1 to 255 do if fchar(i)<>fchr(i) then writeln('test sur ',i,' valeurs differentes') else writeln('test sur ',i,' valeurs egales'); readln; end.
Je pousse le vice jusqu'à changer la variable i en integer et tenter une boucle de 1 à 500...
Freepascal compile correctement et le test se passe sans erreur...
Après ces tests de résultats passons à une analyse des entrailles....le 24/03/2007 à 13:16 -
ClandestinoMembre éclairéAuto Analyse 1 : adresse des fonctions
Je décide donc d'analyser le programme par instructions...
Je modifie le corps principal de mon programme et ecris ce petit code qui me permet de récupérer les adresses en memoire des fonctions fchar et fchrCode : 1
2
3
4
5
6// programme principal begin writeln('adresse de fchar : ', hexstr(integer(@fchar),8)); writeln('adresse de fchr : ', hexstr(integer(@fchr),8)); readln; end.
@fchar représente un pointeur vers fchar, cette expression contient donc l'adresse memoire ou la fonction commence.
Le transtypage par integer() est là pour permettre l'utilisation de la fonction hexstr de freepascal, qui attend en premier parametre un integer, et qui permet d'ecrire l'adresse trouvée dans un format hexadecimal classique sur 8 chiffres...
Avec freepascal, et sur mon systeme d'exploitation j'obtiens:
Code : 1
2adresse de fchar : 00401018 adresse de fchr : 00401025
le 24/03/2007 à 13:30 -
ClandestinoMembre éclairéPetit moment de reflexion et strategie de demonstration
Me voici avec les adresses des deux fonctions...
je calcule combien de byte il y a entre les deux debuts de fonction :attention les chiffres donnés étaient en hexadécimal, je laisse mon programme me donner le résultat
Code : writeln((integer(@fchr))-(integer(@fchar)));
Ma fonction fchar fait donc au maximum 13 bytes...
Pour prouver que mes deux fonctions sont égales, il me suffit donc de dumper 13 bytes de memoire à partir de l'adresse de fchar, et 13 bytes de memoire à partir de fchr, puis de comparer les resultats.... Si je trouve la meme chose, c'est démontré....le 24/03/2007 à 13:40 -
ClandestinoMembre éclairéDump memoire des deux fonctions
j'ai effectué ce test avec le compilateur freepascal sous windows vista... je vous encourage à tester la meme chose dans d'autres situations...
Ma demonstration ne s'applique, jusqu'à preuve du contraire qu'à cette configuration...
J'ai intégré un peu d'assembleur dans mon code, ceci rendant ce mini tuto un peu plus interessant
Voici une fonction pour recupérer un byte à une adresse memoire donnée :
Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// ma fonction de dump Function ReadByteAtAddress( adresse: dword): byte; var d: dword; begin asm push eax // sauvegarde de eax sur la pile push edi // sauvegarde d'edi sur la pile mov edi, dword ptr[adresse] // edi reçoit l'adresse de la fonction mov eax, dword ptr[edi] // eax reçoit le dword à cette adresse mov d, eax // je stocke le resultat dans d pop edi // restauration de edi depuis la pile pop eax // restauration de eax depuis la pile end; // ici je mets un dword dans un byte, je perds donc les informations de 3 bytes // suivants que je lirai ensuite. // c'est un choix délibéré de lire byte par byte juste parceque la fonction // hexstr me renverrai sinon un resultat à lire de droite à gauche... ReadByteAtAddress := d; end;
le 24/03/2007 à 14:14 -
ClandestinoMembre éclairéPogramme final
Il ne nous reste donc plus qu'à lire deux fois treize bytes et afficher le résultat pour voir ou nous en sommes...
Ceci peut être fait ainsi :
Code : 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66program test; // mes fonctions à tester function fchar (c: byte): char; begin fchar := char(c); end; function fchr (c: byte): char; begin fchr := chr(c); end; // ma fonction de dump Function ReadByteAtAddress( adresse: dword): byte; var d: dword; begin asm push eax // sauvegarde de eax sur la pile push edi // sauvegarde d'edi sur la pile mov edi, dword ptr[adresse] // edi reçoit l'adresse de la fonction mov eax, dword ptr[edi] // eax reçoit le dword à cette adresse mov d, eax // je stocke le resultat dans d pop edi // restauration de edi depuis la pile pop eax // restauration de eax depuis la pile end; // ici je met un dword dans un byte, je perds donc les informations de 3 bytes // suivants que je lirai ensuite. // j'ai choisi de lire byte par byte juste parceque la fonction // hexstr me renverrai sinon un resultat à lire de droite à gauche... ReadByteAtAddress := d; end; // variables du programme Var i : integer; buffchar : array[1..13] of byte; buffchr : array[1..13] of byte; // programme principal begin i:=0; writeln('adresse de fchar : ', hexstr(integer(@fchar),8)); writeln('adresse de fchr : ', hexstr(integer(@fchr),8)); writeln; writeln(' Dump fchar Dump fchr'); writeln; writeln('adresse byte adresse byte'); writeln; repeat inc(i); buffchar[I]:= ReadByteAtAddress(integer(@fchar)+i-1); buffchr[I]:= ReadByteAtAddress(integer(@fchr)+i-1); writeln(hexstr(integer(@fchar)+i-1,8),' ', hexstr(buffchar[i],2), ' ', hexstr(integer(@fchr)+i-1,8),' ', hexstr(buffchr[i],2)); until i = 13; readln; end.
Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20adresse de fchar : 00401018 adresse de fchr : 00401025 Dump fchar Dump fchr adresse byte adresse byte 00401018 55 00401025 55 00401019 89 00401026 89 0040101A E5 00401027 E5 0040101B 83 00401028 83 0040101C EC 00401029 EC 0040101D 04 0040102A 04 0040101E 8A 0040102B 8A 0040101F 45 0040102C 45 00401020 08 0040102D 08 00401021 C9 0040102E C9 00401022 C2 0040102F C2 00401023 04 00401030 04 00401024 00 00401031 00
le 24/03/2007 à 14:17 -
ClandestinoMembre éclairéJe viens de faire le test à l'instant sous mon pc sous linux mandriva 2007, compilation avec lazarus...
J'ai du ajouter simplement la directive spécifiant l'assembleur à utiliser :
{$ASMMODE intel}
Voici le résultat :Code : 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
37
38
39
40adresse de fchar : 080480B8 adresse de fchr : 080480D8 32 bytes Dump fchar Dump fchr adresse byte adresse byte 080480B8 55 080480D8 55 080480B9 89 080480D9 89 080480BA E5 080480DA E5 080480BB 83 080480DB 83 080480BC EC 080480DC EC 080480BD 08 080480DD 08 080480BE 88 080480DE 88 080480BF 45 080480DF 45 080480C0 FC 080480E0 FC 080480C1 8A 080480E1 8A 080480C2 45 080480E2 45 080480C3 FC 080480E3 FC 080480C4 88 080480E4 88 080480C5 45 080480E5 45 080480C6 F8 080480E6 F8 080480C7 8A 080480E7 8A 080480C8 45 080480E8 45 080480C9 F8 080480E9 F8 080480CA C9 080480EA C9 080480CB C3 080480EB C3 080480CC 8D 080480EC 8D 080480CD B4 080480ED B4 080480CE 26 080480EE 26 080480CF 00 080480EF 00 080480D0 00 080480F0 00 080480D1 00 080480F1 00 080480D2 00 080480F2 00 080480D3 8D 080480F3 8D 080480D4 74 080480F4 74 080480D5 26 080480F5 26 080480D6 00 080480F6 00 080480D7 90 080480F7 90
le 24/03/2007 à 15:53 -
ClandestinoMembre éclairéLa fonction fchar s'arrète plus tot que 32 bytes, de manière classique par un
Code : 1
2008048CA C9 LEAVE 008048CB C3 RET
(voir cliché de la fenetre de désassemblage joint)
Du code rempli l'intervale entre les deux fonctions
Le meme code rempli l'intervalle entre la deuxième fonction... et le reste du programme...
La comparaison aurait pu echouer.... J'ai eu de la chance sur ce coup-ci...le 24/03/2007 à 16:17 -
droggoExpert confirméAl,
Vérifié sur Windows : freePascal aligne sur 16 bits !! Hou la honte
(options: optimisation pour la vitesse, -O2)le 24/03/2007 à 18:18 -
droggoExpert confirméHi,
Dans ce que tu appelles "ces deux fonctions", une seule notation en est une (chr), l'autre est un transtypage.
Que le compilateur les traite de la même manière et génère le même code ne rentre pas en compte pour parler de fonction au lieu de transtypage, d'autant qu'il est bien possible que d'autres compilateurs ne feront pas de même, bien que ce soit fort probable.le 24/03/2007 à 13:40 -
droggoExpert confirméHi,
Envoyé par Clandestino
Autant prendre un désassembleur et aller voir le code.le 24/03/2007 à 13:52