Pourquoi deux binaires de programmes avec seulement des commentaires modifiés ne correspondent-ils pas exactement dans gcc?

110

J'ai créé deux programmes C

  1. Programme 1

    int main()
    {
    }
  2. Programme 2

    int main()
    {
    //Some Harmless comments
    }

AFAIK, lors de la compilation, le compilateur (gcc) doit ignorer les commentaires et les espaces blancs redondants, et donc la sortie doit être similaire.

Mais quand j'ai vérifié les md5sums des binaires de sortie, ils ne correspondent pas. J'ai également essayé de compiler avec l'optimisation -O3et -Ofastmais ils ne correspondaient toujours pas.

Que se passe-t-il ici?

EDIT: les commandes exactes et les sommes md5 sont (t1.c est le programme 1 et t2.c est le programme 2)

gcc ./t1.c -o aaa
gcc ./t2.c -o bbb
98c1a86e593fd0181383662e68bac22f  aaa
c10293cbe6031b13dc6244d01b4d2793  bbb

gcc ./t2.c -Ofast -o bbb
gcc ./t1.c -Ofast -o aaa
2f65a6d5bc9bf1351bdd6919a766fa10  aaa
c0bee139c47183ce62e10c3dbc13c614  bbb


gcc ./t1.c -O3 -o aaa
gcc ./t2.c -O3 -o bbb
564a39d982710b0070bb9349bfc0e2cd  aaa
ad89b15e73b26e32026fd0f1dc152cd2  bbb

Et oui, les md5sums correspondent à plusieurs compilations avec les mêmes indicateurs.

BTW mon système est gcc (GCC) 5.2.0etLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux

Utilisateur enregistré
la source
17
Veuillez inclure vos indicateurs de ligne de commande exacts. Par exemple, les informations de débogage sont-elles incluses dans les binaires? Si tel est le cas, le changement des numéros de ligne l'affecterait évidemment ...
Jon Skeet
4
La somme MD5 est-elle cohérente entre plusieurs versions du même code?
unenthusiasticuser
3
Je ne peux pas reproduire cela. J'aurais deviné que cela est dû au fait que le GCC intègre tout un tas de métadonnées dans des binaires lors de leur compilation (y compris les horodatages). Si vous pouviez ajouter les indicateurs de ligne de commande précis que vous avez utilisés, ce sera utile.
cyphar
2
Au lieu de simplement vérifier les sommes MD5 et de rester bloqué, hexdump et diff pour voir exactement quels octets diffèrent
MM
12
Bien que la réponse à la question "qu'est-ce qui est différent entre les deux sorties du compilateur?" est intéressant, je note que la question repose sur une hypothèse injustifiée: que les deux produits devraient être identiques et que nous avons besoin d' expliquer pourquoi ils sont différents. Tout ce que le compilateur vous promet, c'est que lorsque vous lui donnez un programme C légal, la sortie est un exécutable légal qui implémente ce programme. Le fait que deux exécutions du compilateur produisent le même binaire n'est pas une garantie du standard C.
Eric Lippert

Réponses:

159

C'est parce que les noms de fichiers sont différents (bien que la sortie des chaînes soit la même). Si vous essayez de modifier le fichier lui-même (plutôt que d'avoir deux fichiers), vous remarquerez que les binaires de sortie ne sont plus différents. Comme Jens et moi l'avons dit, c'est parce que GCC décharge toute une charge de métadonnées dans les binaires qu'il construit, y compris le nom exact du fichier source (et AFAICS fait de même).

Essaye ça:

$ cp code.c code2.c subdir/code.c
$ gcc code.c -o a
$ gcc code2.c -o b
$ gcc subdir/code.c -o a2
$ diff a b
Binary files a and b differ
$ diff a2 b
Binary files a2 and b differ
$ diff -s a a2
Files a and a2 are identical

Cela explique pourquoi vos md5sums ne changent pas entre les versions, mais ils sont différents entre les différents fichiers. Si vous le souhaitez, vous pouvez faire ce que Jens a suggéré et comparer la sortie de stringschaque binaire, vous remarquerez que les noms de fichiers sont incorporés dans le binaire. Si vous voulez "corriger" cela, vous pouvez striples binaires et les métadonnées seront supprimées:

$ strip a a2 b
$ diff -s a b
Files a and b are identical
$ diff -s a2 b
Files a2 and b are identical
$ diff -s a a2
Files a and a2 are identical
cyphar
la source
EDIT: mis à jour pour indiquer que vous pouvez supprimer les binaires pour "résoudre" le problème.
cyphar le
30
Et c'est pourquoi vous devriez comparer la sortie de l'assembly, pas les sommes de contrôle MD5.
Courses de légèreté en orbite le
1
J'ai posé une question complémentaire ici .
Federico Poloni
4
Selon le format du fichier objet, le temps de compilation est également stocké dans les fichiers objet. Ainsi, l'utilisation de fichiers COFF pour les fichiers d'exemple a et a2 ne serait pas identique.
Martin Rosenau
28

La raison la plus courante est les noms de fichiers et les horodatages ajoutés par le compilateur (généralement dans la partie des informations de débogage des sections ELF).

Essayez de courir

 $ strings -a program > x
 ...recompile program...
 $ strings -a program > y
 $ diff x y

et vous pourriez voir la raison. J'ai déjà utilisé cela pour trouver pourquoi la même source provoquerait un code différent lorsqu'elle était compilée dans différents répertoires. La découverte a été que la __FILE__macro s'est développée en un nom de fichier absolu , différent dans les deux arborescences.

Jens
la source
1
Selon gcc.gnu.org/ml/gcc-help/2007-05/msg00138.html (obsolète, je sais), ils ne sauvegardent pas les horodatages et cela pourrait être un problème lié à l' éditeur de liens. Cependant, je me souviens avoir lu une histoire récemment sur la façon dont une entreprise de sécurité profilait les habitudes de travail d'une équipe de piratage en utilisant les informations d'horodatage GCC dans leurs binaires.
cyphar le
3
Et pour ne pas mentionner qu'OP déclare que «les md5sums correspondent à travers plusieurs compilations avec les mêmes indicateurs», ce qui indique probablement que ce ne sont pas les horodatages qui sont à l'origine du problème. Cela est probablement dû au fait que ce sont des noms de fichiers différents.
cyphar le
1
@cyphar Différents noms de fichiers doivent également être capturés par l'approche strings / diff.
Jens le
15

Remarque : rappelez-vous que le nom du fichier source va dans le binaire non rayé, donc deux programmes provenant de fichiers source nommés différemment auront des hachages différents.

Dans des situations similaires, si ce qui précède ne s'applique pas , vous pouvez essayer:

  • courir stripcontre le binaire pour enlever un peu de graisse. Si les binaires supprimés sont les mêmes, il y a des métadonnées qui ne sont pas essentielles au fonctionnement du programme.
  • générer une sortie intermédiaire d'assemblage pour vérifier que la différence ne se trouve pas dans les instructions réelles du processeur (ou, cependant, pour mieux identifier où se trouve réellement la différence )
  • utilisez stringsou videz les deux programmes en hexadécimal et exécutez un diff sur les deux vidages hexadécimaux. Une fois la ou les différences localisées, vous pouvez essayer de voir s'il y a une rime ou une raison (PID, horodatage, horodatage du fichier source ...). Par exemple, vous pouvez avoir une routine stockant l'horodatage au moment de la compilation à des fins de diagnostic.
LSerni
la source
Mon système est gcc (GCC) 5.2.0etLinux 4.2.0-1-MANJARO #1 SMP PREEMPT x86_64 GNU/Linux
Utilisateur enregistré
2
Vous devriez essayer effectivement faire deux fichiers séparés. Je ne pouvais pas non plus le reproduire en modifiant un seul fichier.
cyphar le
Oui, les noms de fichiers sont coupables. Je peux obtenir les mêmes md5sums si je compile les programmes du même nom.
Utilisateur enregistré le