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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Différence de traduction entre char() et chr() par le compilateur Free Pascal
Par Clandestino

Le , par Clandestino

8PARTAGES

1  0 
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 :
Code : Sélectionner tout
1
2
3
4
function fchar (c: byte): char;
begin
fchar := char(c);
end;
Code : Sélectionner tout
1
2
3
4
function fchr (c: byte): char;
begin
fchr := chr(c);
end;
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...
Citation Envoyé par Droggo
Pris d'une brutale poussée de courage, je suis allé voir le code généré :

c'est exactement le même code, ce qui confirme l'impression que j'avais eue en voyant les temps d'exécution très proches, avec l'un ou l'autre plus ou moins rapide selon le test
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...

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de Clandestino
Membre éclairé https://www.developpez.com
Le 24/03/2007 à 13:16
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 : Sélectionner tout
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
program 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.
Au niveau des resultats, les fonctions sont donc équivalentes...

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....
1  0 
Avatar de Clandestino
Membre éclairé https://www.developpez.com
Le 24/03/2007 à 13:30
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 fchr

Code : Sélectionner tout
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.
Explications sommaires:
@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 : Sélectionner tout
1
2
adresse de fchar : 00401018
adresse de fchr : 00401025
1  0 
Avatar de Clandestino
Membre éclairé https://www.developpez.com
Le 24/03/2007 à 13:40
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 : Sélectionner tout
 writeln((integer(@fchr))-(integer(@fchar)));
Il y a donc 13 bytes entre le debut de ma fonction fchar et le debut de ma fonction fchr...

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é....
1  0 
Avatar de Clandestino
Membre éclairé https://www.developpez.com
Le 24/03/2007 à 14:14
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 : Sélectionner tout
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;
J'ai pris la précution dans ce bout de code asm de sauvegarder les registres utilisés puis de les retribuer, habitué que je suis à faire celà quand je patche depuis un debuggueur... je ne suis pas certain que celà soit nécessaire depuis un code source à compiler... d'autres sauront sans doute me préciser ce sujet...
1  0 
Avatar de Clandestino
Membre éclairé https://www.developpez.com
Le 24/03/2007 à 14:17
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 : Sélectionner tout
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
66
program 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.
voici le résultat chez moi:
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
adresse 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
1  0 
Avatar de Clandestino
Membre éclairé https://www.developpez.com
Le 24/03/2007 à 15:53
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 : Sélectionner tout
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
adresse 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
Pour Linux aussi donc , freepascal compile le meme code ... Par contre il lui faut 32 bytes pour la meme chose...
1  0 
Avatar de Clandestino
Membre éclairé https://www.developpez.com
Le 24/03/2007 à 16:17
La fonction fchar s'arrète plus tot que 32 bytes, de manière classique par un
Code : Sélectionner tout
1
2
008048CA  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...
1  0 
Avatar de droggo
Expert confirmé https://www.developpez.com
Le 24/03/2007 à 18:18
Al,

Vérifié sur Windows : freePascal aligne sur 16 bits !! Hou la honte

(options: optimisation pour la vitesse, -O2)
1  0 
Avatar de droggo
Expert confirmé https://www.developpez.com
Le 24/03/2007 à 13:40
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.
0  0 
Avatar de droggo
Expert confirmé https://www.developpez.com
Le 24/03/2007 à 13:52
Hi,
Citation Envoyé par Clandestino
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 : Sélectionner tout
 writeln((integer(@fchr))-(integer(@fchar)));
Il y a donc 13 bytes entre le debut de ma fonction fchar et le debut de ma fonction fchr...

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é....
Sauf si le compilateur a ajouté des instructions "bidons" pour aligner le code, cas fréquent avec les compilateurs sachant vraiment optimiser à fond (presque à fond, les processeurs modernes sont très difficiles à optimiser totalement).

Autant prendre un désassembleur et aller voir le code.
0  0