Articles tagués “objdump

Boucle infinie en C : for ou while ?

On s’est déjà posé cette question, on a déjà vu ou entendu des gens la poser : faut-il faire une boucle infinie avec while (1) ou avec for( ; ; ) en C ?

La logique veut que le résultat soit le même. J’ai donc fait le test avec le compilateur MinGW sous Windows 7 64 bits. J’ai pour cela écrit deux fonctions placées dans le fichier boucles.c :

void withFor(void)
{
    for(;;)
        ;
}

void withWhile(void)
{
    while(1)
        ;
}

J’ai ensuite utilisé objdump pour déassembler le code :

PS D:\Users\pgradot\Documents\C\out\Debug> objdump.exe -d .\boucles.o
.\boucles.o:     file format pe-i386

Disassembly of section .text:

00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   eb fe                   jmp    3 

00000005 :
   5:   55                      push   %ebp
   6:   89 e5                   mov    %esp,%ebp
   8:   eb fe                   jmp    8 
   a:   90                      nop
   b:   90                      nop

Les 2 NOP à la fin m’ont étonné. J’ai inversé les positions des fonctions dans le fichier et j’ai alors constaté que les 2 NOP étaient toujours à la fin du fichier déassemblé. Pour m’assurer qu’ils ne faisaient effectivement pas partie des fonctions (vu les JMP, c’était quasiment certain), j’ai rajouté une fonction à la fin du fichier :

void withWhile(void)
{
    while(1)
        ;
}

void withFor(void)
{
    for(;;)
        ;
}

void nop()
{

}

Voici le résultat en assembleur :

PS D:\Users\pgradot\Documents\C\out\Debug> objdump.exe -d .\boucles.o

.\boucles.o:     file format pe-i386

Disassembly of section .text:

00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   eb fe                   jmp    3 

00000005 :
   5:   55                      push   %ebp
   6:   89 e5                   mov    %esp,%ebp
   8:   eb fe                   jmp    8 

0000000a :
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   5d                      pop    %ebp
   e:   c3                      ret
   f:   90                      nop

Il n’y a donc aucune différence de performance entre les deux boucles, en tout cas avec ce compilateur et cette cible. J’ai des résultats similaires avec llvm/gcc et otool sous Mac OS X. Il en y a qui disent que for( ; ; ) donnerait de meilleures performances que while (1) car il y a une évaluation de condition dans la deuxième écriture. A l’évidence, mon compilateur ne se laisse pas avoir. Ce n’était peut-être pas le cas avec des compilateurs anciens mais je pense qu’on peut dire que cela relève maintenant des mythes du passé.

Il peut enfin rester des considérations sémantiques : laquelle des deux écritures représentent mieux l’idée de boucle infinie ? Je pense que cette discussion de stackoverflow les résume bien. En particulier, j’aime beaucoup l’écriture suivante qui y est proposée et donc la sémantique est parfaite ^^ :

#define EVER ;;

void forever(void)
{
        for(EVER)
                ;
}

On peut regarder le code en sortie du pré-processeur :

gcc -E -P boucles.c 

void forever(void)
{
 for(;;)
  ;
}

Et enfin l’assembleur généré :

otool -tv boucles.o 
boucles.o:
(__TEXT,__text) section
_forever:
0000000000000000	pushq	%rbp
0000000000000001	movq	%rsp,%rbp
0000000000000004	jmp	0x00000004

Avis personnel : je met toujours while (1) (ou while (true) en Java). C’est pour moi l’écriture la plus claire à lire et surtout la plus facile à dire.
« Hey machin, j’ai fait une boucle for(;;) !
– Gné ?!
– Oui, un while un quoi !
– Ahhhhh ! »


objcopy et les valeurs possibles pour bfdname

Depuis hier, je m’amuse à mettre en place une chaîne de compilation gcc pour microcontrôleur STM32F2/F4 (Cortex M3/M4 respectivement) dans Eclipse. Je dis amusement mais c’est totalement professionnel : travaillant au support technique et devant aider nos clients qui vont travailler avec notre nouveau produit qui fonctionne avec gcc, je me dois de maîtriser cette chaîne de compilation !

Après bien des galères pour trouver / comprendre les flags de compilation et ajouter d’innombrables dossiers à mon include path et à mon source path, c’est bon : ça compile et ça linke. Le plugin CDT pour Eclipse ne m’a pas vraiment emballé, je dois bien l’avouer. Il ne me reste plus qu’à flasher ma carte avec l’exécutable créé mais son petit défaut est qu’il est au format elf. Ce n’est pas pratique dans mon cas car j’utilise le ST-Link/V2 embarqué sur mes cartes  STM3220/40G-EVAL et l’utilitaire qui va avec, le STM32 ST-Link Utility. Or, cet outil réclame en entrée un fichier au format dit « raw binary« . Ces fichiers portent souvent l’extension hex.

La suite GNU Binary Utilities offre l’utilitaire objcopy pour réaliser ce genre de tâches. La documentation utilise à gogo une valeur formelle bfdname, le nom du Binary File Descriptor. Bien. Très bien. Et je lui donne quelle valeur réelle à ce machin ? La documentation est muette à ce sujet et Google ne donne pas de résultats très probants. En fait, c’est tout simple : il suffit d’interroger directement l’objcopy qui est installé sur votre ordinateur ! L’option --help suffit pour avoir une liste non détaillée. On aura tout en bas quelques lignes de plus que les pages de manuel d’Internet : les formats supportés.

D:\Users\pgradot> objcopy.exe --help
Usage: C:\Program Files (x86)\CodeBlocks\MinGW\bin\objcopy.exe [option(s)] in-file [out-file]
 Copies a binary file, possibly transforming it in the process
 The options are:
  -I --input-target       Assume input file is in format 
  -O --output-target      Create an output file in format 
  -B --binary-architecture   Set output arch, when input is arch-less
  -F --target             Set both input and output format to 
     --debugging                   Convert debugging information, if possible
[...]
  @                          Read options from 
  -V --version                     Display this program's version number
  -h --help                        Display this output
     --info                        List object formats & architectures supported
C:\Program Files (x86)\CodeBlocks\MinGW\bin\objcopy.exe: supported targets: pe-i386 pei-i386 elf32-i386 elf32-little elf
32-big srec symbolsrec verilog tekhex binary ihex
Report bugs to <http://www.sourceware.org/bugzilla/>

On peut aussi utiliser l’option --info pour plus de détails.

D:\Users\pgradot> objcopy.exe --info
BFD header file version (GNU Binutils) 2.22
pe-i386
 (header little endian, data little endian)
  i386
pei-i386
 (header little endian, data little endian)
  i386
elf32-i386
 (header little endian, data little endian)
  i386
elf32-little
 (header little endian, data little endian)
  i386
elf32-big
 (header big endian, data big endian)
  i386
srec
 (header endianness unknown, data endianness unknown)
  i386
symbolsrec
 (header endianness unknown, data endianness unknown)
  i386
verilog
 (header endianness unknown, data endianness unknown)
  i386
tekhex
 (header endianness unknown, data endianness unknown)
  i386
binary
 (header endianness unknown, data endianness unknown)
  i386
ihex
 (header endianness unknown, data endianness unknown)
  i386

               pe-i386 pei-i386 elf32-i386 elf32-little elf32-big srec
          i386 pe-i386 pei-i386 elf32-i386 elf32-little elf32-big srec

               symbolsrec verilog tekhex binary ihex
          i386 symbolsrec verilog tekhex binary ihex

Tout simplement. Je sais maintenant que je dois faire : objcopy.exe -I elf32-little -O binary inputFile.elf outputFile.bin.

Notez que d’autres commandes utilisent bdfname comme valeur de paramètres, comme objdump.


Concevoir un site comme celui-ci avec WordPress.com
Commencer