Bonjour à tous,
l'année dernière, j'étais tombé un peu par hasard sur une page web très intéressante où un gars présentait comment obtenir un exécutable tout petit à partir d'un code C quelconque.
En gros, ça partait de la compilation simple d'un hello world (enfin, d'un équivalent légèrement plus complexe pour l'exemple), et l'auteur passait des options de compilation "-O" de gcc à strip, puis au link statique, puis au nettoyage des en-têtes du binaire ELF, jusqu'à l'optimisation proprement dite de l'assembleur, à la main :-)
Cette page était (est) très intéressante, d'autant plus qu'elle permettait de bien comprendre le mécanisme des binaires sous linux.
Et il se trouve que j'ai passé une bonne partie de l'après midi à la rechercher sur le net sans succès.. C'est pourquoi je me tourne vers vous pour savoir si quelqu'un n'aurait pas un exemple similaire, le plus complet possible... Voire, on peut toujours rêver, ladite page dans ses signets :-)
# Voila ?
Posté par ckyl . Évalué à 10.
[^] # Re: Voila ?
Posté par Anonyme . Évalué à 3.
Et c'est vrai que le résultat est ... comment dire ... déconcertant.
Nous avons à faire a un hacker, et un vrai de vrai.
[^] # Re: Voila ?
Posté par Hugues Hiegel (site web personnel) . Évalué à 5.
Je ne sais comment te remercier... :-)
[^] # Re: Voila ?
Posté par Hugues Hiegel (site web personnel) . Évalué à 5.
Merci !
;)
[^] # Re: Voila ?
Posté par Obsidian . Évalué à 6.
Dommage que le « plussage » soit plafonné à 10 points.
Il est agréable de voir qu'il y a encore des gens qui se soucient de ce genre de chose à notre époque. D'ailleurs, à la mienne (il y a un peu plus de 10 ans :-) ), j'avais lancé le même genre de débat sur une messagerie Minitel à caractère informatique :
Question : Quelle est la taille minimum d'un fichier exécutable (sous DOS) ?
En utilisant un fichier *.COM, J'avais réussi à descendre à deux octets : CD 20
Cela codait l'instruction "INT 20h", vieil appel système mais toujours implémenté à l'époque pour terminer une application avant que le célèbre B8 00 4C CD 21 (pour MOV ax,4c00h; int 21h ) n'implémente le code de retour.
Je me suis quand même fait battre.
Un connecté m'a fait remarquer qu'en utilisant simplement "RET" (1 octet), on provoquait un retour depuis la pile, qui pointait la fin de l'unique segment du programme, laquelle était par convention initialisée à 00 00. Or, tout au début du même segment (donc en 00 00) se trouvait le PSP, qui par norme commençait toujours par un magic number invariable : CD 20 (ce qui bien sûr n'était probablement pas un hasard) !
Ensuite on s'est amusé à essayer de compiler un programme vide à l'aide de tous les compilateurs qui nous tombaient sous la main. La palme revenait au Turbo Pascal qui dépassait les 6Ko pour exécuter un programme strictement vide ! :-)
C'était beaucoup lorsque l'on avait l'habitude de s'échanger les fichiers par disquette, que les graveurs de CD étaient extrêmement rares et que les disques durs n'atteignaient pas 1Go.
[^] # Re: Voila ?
Posté par mac . Évalué à 1.
[^] # Re: Voila ?
Posté par icyfemur . Évalué à 3.
Égalité avec Ada alors, je viens de faire le test et un programme "vide" (procedure principale avec "null" dedans) compilé avec gnatmake -O3, puis strippé, fait également 6KiO.
[^] # Re: Voila ?
Posté par Obsidian . Évalué à 2.
Dans le même esprit, j'ai essayé hier soir sur mon PII/Mdk9.2 avec un gcc pas trop vieux le programme qu'il donne en exemple tout en haut de son exposé. Là où son exécutable commence à 8Ko, le mien atteint déjà 11Ko ! Après strip, en revanche, les tailles redeviennent comparables.
Un programme vide de 6Ko il y a 10/12 ans, c'est un peu comme si le même programme vide aujourd'hui atteignait 100 à 300 Ko ! :-)
# Intéressant mais...
Posté par Sébastien Koechlin . Évalué à 1.
De plus en plus, la mémoire occupée par les programmes me semble démente. Mon navigateur montre rapidement à plus d'une centaine de Mo (d'après le gestionnaire des taches de windows), et même si la mesure de la mémoire occupée est sujet à diverses interprétation (comment compter les fichiers mappés qui sont en lecture seule, les librairies partagées entre différentes applications, les pages qui ne sont jamais utilisées) je pense qu'il y a des efforts à faire.
Par contre, je suis très critique vis à vis des solutions citées:
Pour le strip, pas de contestation.
La compilation statique peut donner l'impression que le binaire est plus petit, mais on perd un gros intérêt des librairies dynamiques, qui est qu'il n'est plus nécessaire de les dupliquer en mémoire. Et puis le statique qui présente un intérêt pour un programme Hello World qui se contente d'appeler printf et exit est probablement un lourd handicape pour une application qui fait un peu plus de choses et qui va appeler bien plus de fonctions dans bien plus de librairies.
Supprimer les parties ELF du binaire qui ne sont pas indispensable permet de faire gagner quelques octets, quelques Ko même peut-être, mais on se contente de gagner un peu de place sur le disque, cela ne change rien à la mémoire, et sur plusieurs dizaines ou quelques centaines de Mo, c'est complètement négligeable.
Enfin l'optimisation en assembleur est à mon avis une régression au niveau des années 80. J'utilise à mi-temps un PPC, et évidement, l'assembleur concerne rarement cette architecture, une optimisation en assembleur pousse l'assertion Ordinateur = compatible IBM-PC qui masque une grande partie de la richesse de l'informatique, surtout libre.
Optimiser une routine en assembleur peut se comprendre dans des situations où le temps est critique, pour utiliser des instructions mal supportées par le compilateur, elles ont leur place dans les librairies de codage/décodage audio et vidéo, et dans ce cas, il existe des alternatives portable pour les autres architectures; mais aucune écriture en assembleur ne va permettre de gagner une quantité significative de mémoire lors de l'exécution, le gain de quelques octets ne justifie pas la lourdeur , les problèmes de portabilité et de maintenance de l'assembleur, le C est suffisement souple de ce coté.
[^] # Re: Intéressant mais...
Posté par Matthieu Moy (site web personnel) . Évalué à 4.
C'est une expérience rigolote pour obtenir le binaire le plus petit possible sur un programme quasi-vide. Dans la conclusion, il dit qu'il a un binaire pour lequel chaque octet est utile et expliqué. Voila, c'est rigolo, c'est tout.
[^] # Re: Intéressant mais...
Posté par Hugues Hiegel (site web personnel) . Évalué à 2.
L'exemple en lui même, c'est clair qu'il ne nous intéresse pas (qui voudrait d'un binaire qui ne fait que retourner 42 ?). Mais le voyage qui nous est offert est très riche en informations (comme tu le dis effectivement, il nous explique tout octet par octet), donc libre à toi d'en reprendre les infos qui t'intéressent dans le cadre de ton projet ou tes applications futures.
Rigolo peut être, mais en tout cas très instructif, et complètement à l'inverse du concours du compilateur qui fournit le binaire le plus gros sur un code source qui ne fait rien (ÇA, c'est "rigolo, c'est tout" ) :-)
[^] # ...oui mais non :-)
Posté par Hugues Hiegel (site web personnel) . Évalué à 1.
En fait, je pense que beaucoup l'auront compris, l'idée est d'avoir une sorte de guide qui permette de mieux comprendre le mécanisme d'exécution d'un programme. Pour quelqu'un qui fait de l'embarqué et/ou de la compilation croisée, c'est un plus de bien saisir ces mécanismes. Et moi, je recherche justement une compréhension aussi complète que possible desdits mécanismes (notamment en ce qui concerne les librairies dynamiques).
Et le site cité en premier commentaire est un excellent point de départ, explications reproductibles à l'appui. La théorie, je peux l'avoir sur n'importe quel autre site de même degré de sérieux; j'avais surtout besoin de la pratique pour mieux saisir ces mécanismes.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.