<HTML> <HEAD> <TITLE>Portage et compilation</TITLE> </HEAD> <BODY> <H1>4. <A NAME="s4"></A>Portage et compilation</H1> <P> <A HREF="GCC-HOWTO.html#toc4">Contenu de cette section</A></P> <A NAME="index.25"></A> <H2>4.1 <A NAME="ss4.1"></A> Symboles définis automatiquement </H2> <P>Vous pouvez trouver quels symboles votre version de gcc définit automatiquement en le lançant avec l'option <CODE>-v</CODE>. Par exemple cela donne ça chez moi : <BLOCKQUOTE><CODE> <PRE> $ echo 'main(){printf("Bonjour !\n");}' | gcc -E -v - Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs gcc version 2.7.2 /usr/lib/gcc-lib/i486-box-linux/2.7.2/cpp -lang-c -v -undef -D__GNUC__=2 -D__GNUC_MINOR__=7 -D__ELF__ -Dunix -Di386 -Dlinux -D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__i386 -D__linux -Asystem(unix) -Asystem(posix) -Acpu(i386) -Amachine(i386) -D__i486__ - </PRE> </CODE></BLOCKQUOTE> Si vous écrivez du code qui utilise des spécificités Linux, il est souhaitable d'implémenter le code non portable de la manière suivante <BLOCKQUOTE><CODE> <PRE> #ifdef __linux__ /* ... code linux ... */ #endif /* linux */ </PRE> </CODE></BLOCKQUOTE> </P> <P>Utilisez <CODE>__linux__</CODE> pour cela, et <EM>pas</EM> <CODE>linux</CODE>. Bien que cette macro soit définie, ce n'est pas une spécification POSIX.</P> <P></P> <H2>4.2 <A NAME="ss4.2"></A> Options de compilation</H2> <P> La documentation des options de compilation se trouve dans les pages <EM>info</EM> de gcc (sous Emacs, utilisez <CODE>C-h i</CODE> puis sélectionnez l'option `gcc'). Votre distribution peut ne pas avoir installé la documentation ou bien vous pouvez en avoir une ancienne. Dans ce cas, la meilleure chose à faire est de récupérer les sources de gcc depuis <A HREF="ftp://prep.ai.mit.edu/pub/gnu">ftp://prep.ai.mit.edu/pub/gnu</A> ou l'un des ses nombreux miroirs dont <A HREF="ftp://ftp.ibp.fr/pub/gnu">ftp://ftp.ibp.fr/pub/gnu</A> .</P> <P>La page de manuel gcc (<CODE>gcc.1</CODE>) est en principe, complètement dépassée. Cela vous met en garde si vous désirez la consulter.</P> <P></P> <A NAME="index.27"></A> <A NAME="index.26"></A> <H3>Options de compilation </H3> <P> gcc peut réaliser un certain nombre d'optimisations sur le code généré en ajoutant l'option <CODE>-O</CODE><EM>n</EM> à la ligne de commandes, où <EM>n</EM> est un chiffre. La valeur de <EM>n</EM>, et son effet exact, dépend de la version de gcc, mais s'échelonne normalement entre 0 (aucune optimisation) et 2 (un certain nombre) ou 3 (toutes les optimisations possibles).</P> <P>En interne, gcc interprète les options telles que <CODE>-f</CODE> et <CODE>-m</CODE>. Vous pouvez voir exactement ce qu'effectue le niveau spécifié dans l'option <CODE>-O</CODE> en lançant gcc avec l'option <CODE>-v</CODE> et l'option (non documentée) <CODE>-Q</CODE>. Par exemple, l'option <CODE>-O2</CODE>, effectue les opérations suivantes sur ma machine : <BLOCKQUOTE><CODE> <PRE> enabled: -fdefer-pop -fcse-follow-jumps -fcse-skip-blocks -fexpensive-optimizations -fthread-jumps -fpeephole -fforce-mem -ffunction-cse -finline -fcaller-saves -fpcc-struct-return -frerun-cse-after-loop -fcommon -fgnu-linker -m80387 -mhard-float -mno-soft-float -mno-386 -m486 -mieee-fp -mfp-ret-in-387 </PRE> </CODE></BLOCKQUOTE> </P> <P>Utiliser un niveau d'optimisation supérieur à celui que le compilateur supporte (par exemple <CODE>-O6</CODE>) aura le même effet qu'utiliser le plus haut niveau géré. Distribuer du code où la compilation est configurée de cette manière est une très mauvaise idée -- si d'autres optimisations sont incorporées dans de versions futures, vous (ou d'autres utilisateurs) pouvez vous apercevoir que cela ne compile plus, ou bien que le code généré ne fait pas les actions désirées.</P> <P> <A NAME="index.28"></A> Les utilisateurs de gcc 2.7.0 à 2.7.2 devraient noter qu'il y a un bogue dans l'option <CODE>-O2</CODE>. Plus précisément, la <EM>strength reduction</EM> ne fonctionne pas. Un patch a été implémenté pour résoudre ce problème, mais vous devez alors recompiler gcc. Sinon, vous devrez toujours compiler avec l'option <CODE>-fno-strength-reduce</CODE>.</P> <P></P> <P></P> <H3>Spécification du processeur</H3> <P></P> <P></P> <P> Il existe d'autres options <CODE>-m</CODE> qui ne sont pas positionnées lors de l'utilisation de <CODE>-O</CODE> mais qui sont néanmoins utiles dans certains cas. C'est le cas pour les options <CODE>-m386</CODE> et <CODE>-m486</CODE>, qui indiquent à gcc de générer un code plus ou moins optimisé pour l'un ou l'autre type de processeur. Le code continuera à fonctionner sur les deux processeurs. Bien que le code pour 486 soit plus important, il ne ralentit pas l'exécution du programme sur 386.</P> <P>Il n'existe pas actuellement de <CODE>-mpentium</CODE> ou <CODE>-m586</CODE>. Linus a suggéré l'utilisation des options <CODE>-m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2</CODE>, pour exploiter les optimisations du 486 tout en perdant de la place due aux problèmes d'alignements (dont le Pentium n'a que faire). Michael Meissner (de Cygnus) nous dit :</P> <P> <BLOCKQUOTE> " Mon avis est que l'option <CODE> -mno-strength-reduce </CODE> permet d'obtenir un code plus rapide sur un x86 (nota : je ne parle pas du bogue <EM>strength reduction</EM>, qui est un autre problème). Cela s'explique en raison du peu de registres dont disposent ces processeurs (et la méthode de GCC qui consiste à grouper les registres dans l'ordre inverse au lieu d'utiliser d'autres registres n'arrange rien). La <EM>strength reduction</EM> consiste en fait à rajouter des registres pour remplacer les multiplications par des additions. Je suspecte également <CODE> -fcaller-saves</CODE> de ne pas arranger la situation. " </BLOCKQUOTE> </P> <P> <BLOCKQUOTE> Une autre idée est que <CODE>-fomit-frame-pointer</CODE> n'est pas obligatoirement une bonne idée. D'un côté, cela peut signifier qu'un autre registre est disponible pour une allocation. D'un autre côté, vue la manière dont les processeurs x86 codent leur jeu d'instruction, cela peut signifier que la pile des adresses relatives prend plus de place que les adresses de fenêtres relatives, ce qui signifie en clair que moins de cache est disponible pour l'exécution du processus. Il faut préciser que l'option <CODE>-fomit-frame-pointer</CODE>, signifie que le compilateur doit constamment ajuster le pointeur de pile après les appels, alors qu'avec une fenêtre, il peut laisser plusieurs appels dans la pile. </BLOCKQUOTE> </P> <P>Le mot final sur le sujet provient de Linus :</P> <P> <BLOCKQUOTE> Remarquez que si vous voulez des performances maximales, ne me croyez pas : testez ! Il existe tellement d'options de gcc, et il est possible que cela ne soit une réelle optimisation que pour vous. </BLOCKQUOTE> </P> <P></P> <A NAME="index.33"></A> <A NAME="index.32"></A> <A NAME="index.31"></A> <A NAME="index.30"></A> <A NAME="index.29"></A> <H3><CODE>Internal compiler error: cc1 got fatal signal 11</CODE> </H3> <P> Signal 11 correspond au signal SIGSEGV, ou bien <EM>segmentation violation</EM>. Normalement, cela signifie que le programme s'est mélangé les pointeurs et a essayé d'écrire là où il n'en a pas le droit. Donc, cela pourrait être un bug de gcc.</P> <P>Toutefois, gcc est un logiciel assez testé et assez remarquable de ce côté. Il utilise un grand nombre de structures de données complexes, et un nombre impressionnant de pointeurs. En résumé, c'est le plus pointilleux des testeurs de mémoire existants. Si vous <EM>n'arrivez pas à reproduire le bogue</EM> --- si cela ne s'arrête pas au même endroit lorsque vous retentez la compilation --- c'est plutôt un problème avec votre machine (processeur, mémoire, carte mère ou bien cache). <B>N'annoncez pas</B> la découverte d'un nouveau bogue si votre ordinateur traverse tous les tests du BIOS, ou s'il fonctionne correctement sous Windows ou autre : ces tests ne valent rien. Il en va de même si le noyau s'arrête lors du `<CODE>make zImage</CODE>' ! `<CODE>make zImage</CODE>' doit compiler plus de 200 fichiers, et il en faut bien moins pour arriver à faire échouer une compilation.</P> <P></P> <P> Si vous arrivez à reproduire le bogue et (mieux encore) à écrire un petit programme qui permet de mettre en évidence cette erreur, alors vous pouvez envoyer le code soit à la FSF, soit dans la liste linux-gcc. Consultez la documentation de gcc pour plus de détails concernant les informations nécessaires.</P> <P></P> <H2>4.3 <A NAME="ss4.3"></A> Portabilité</H2> <P> Cette phrase a été dite un jour : si quelque chose n'a pas été porté vers Linux alors ce n'est pas important de l'avoir :-).</P> <P>Plus sérieusement, en général seules quelques modifications mineures sont nécessaires car Linux répond à 100% aux spécifications POSIX. Il est généralement sympathique d'envoyer à l'auteur du programme les modifications effectuées pour que le programme fonctionne sur Linux, pour que lors d'une future version, un `make' suffise pour générer l'exécutable.</P> <P></P> <H3>Spécificités BSD (notamment <CODE>bsd_ioctl</CODE>, <CODE>daemon</CODE> et<CODE><sgtty.h></CODE>)</H3> <P> Vous pouvez compiler votre programme avec l'option <CODE>-I/usr/include/bsd</CODE> et faire l'édition de liens avec <CODE>-lbsd</CODE> (en ajoutant <CODE>-I/usr/include/bsd</CODE> à la ligne <CODE>CFLAGS</CODE> et <CODE>-lbsd</CODE> à la ligne <CODE>LDFLAGS</CODE> dans votre fichier <CODE>Makefile</CODE>). Il est également nécessaire de ne <B>pas</B> ajouter <CODE>-D__USE_BSD_SIGNAL</CODE> si vous voulez que les signaux BSD fonctionnent car vous les avez inclus automatiquement avec la ligne <CODE>-I/usr/include/bsd</CODE> et en incluant le fichier d'en-tête <CODE><signal.h></CODE>.</P> <P></P> <A NAME="index.38"></A> <A NAME="index.37"></A> <A NAME="index.36"></A> <A NAME="index.35"></A> <A NAME="index.34"></A> <H3>Signaux <EM>manquants</EM> (<CODE>SIGBUS</CODE>, <CODE>SIGEMT</CODE>, <CODE>SIGIOT</CODE>, <CODE>SIGTRAP</CODE>, <CODE>SIGSYS</CODE>, etc.) </H3> <P> Linux respecte les spécifications POSIX. Ces signaux n'en font pas partie (cf. ISO/IEC 9945-1:1990 - IEEE Std 1003.1-1990, paragraphe B.3.3.1.1) :</P> <P> <BLOCKQUOTE> " Les signaux SIGBUS, SIGEMT, SIGIOT, SIGTRAP, et SIGSYS ont été omis de la norme POSIX.1 car leur comportement est dépendant de l'implémentation et donc ne peut être répertorié d'une manière satisfaisante. Certaines implémentations peuvent fournir ces signaux mais doivent documenter leur effet " </BLOCKQUOTE> </P> <P></P> <P>La manière la plus élégante de régler ce problème est de redéfinir ces signaux à <CODE>SIGUNUSED</CODE>. La manière <EM>normale</EM> de procéder est d'entourer le code avec les <CODE>#ifdef</CODE> appropriés :</P> <P> <BLOCKQUOTE><CODE> <PRE> #ifdef SIGSYS /* ... code utilisant les signaux non posix .... */ #endif </PRE> </CODE></BLOCKQUOTE> </P> <P></P> <A NAME="index.39"></A> <H3>Code K & R </H3> <P> GCC est un compilateur ANSI, or il existe beaucoup de code qui ne soit pas ANSI. </P> <P>Il n'y a pas grand chose à faire, sauf rajouter l'option <CODE>-traditional</CODE> lors de la compilation. Il effectue certaines vérifications supplémentaires. Consultez les pages info gcc.</P> <P>Notez que l'option <CODE>-traditional</CODE> a pour unique effet de changer la forme du langage accepté par gcc. Par exemple, elle active l'option <CODE>-fwritable-strings</CODE>, qui déplace toutes les chaînes de caractères vers l'espace de données (depuis l'espace de texte, où elle ne peuvent pas être modifiées). Ceci augmente la taille de la mémoire occupée par le programme.</P> <P></P> <A NAME="index.41"></A> <A NAME="index.40"></A> <H3>Les symboles du préprocesseur produisent un conflit avecles prototypes du code </H3> <P> Un des problèmes fréquents se produit lorsque certaines fonctions standards sont définies comme macros dans les fichiers d'en-tête de Linux et le préprocesseur refusera de traiter des prototypes identiques. Par exemple, cela peut arriver avec <CODE>atoi()</CODE> et <CODE>atol()</CODE>.</P> <P></P> <A NAME="index.42"></A> <H3><CODE>sprintf()</CODE> </H3> <P> Parfois, soyez prudent lorsque vous effectuez un portage à partir des sources de programmes fonctionnant sous SunOs, surtout avec la fonction <CODE>sprintf(string, fmt, ...)</CODE> car elle renvoie un pointeur sur la chaîne de caractères alors que Linux (suivant la norme ANSI) retourne le nombre de caractères recopiés dans la chaîne de caractères.</P> <P></P> <P></P> <A NAME="index.49"></A> <A NAME="index.48"></A> <A NAME="index.47"></A> <A NAME="index.46"></A> <A NAME="index.45"></A> <A NAME="index.44"></A> <A NAME="index.43"></A> <H3><CODE>fcntl</CODE> et ses copains. Où se trouve la définition de <CODE>FD_*</CODE> et compagnie ? </H3> <P> Dans <CODE><sys/time.h></CODE>. Si vous utilisez <CODE>fcntl</CODE> vous voudrez probablement inclure <CODE><unistd.h></CODE> également, pour avoir le prototype de la fonction.</P> <P>D'une manière générale, la page de manuel pour une fonction donne la liste des fichiers d'en-tête à inclure.</P> <P></P> <A NAME="index.50"></A> <H3>Le timeout de <CODE>select()</CODE>. Les programmescommencent dans un état d'attente active </H3> <P> A une certaine époque, le paramètre timeout de la fonction <CODE>select()</CODE> était utilisé en lecture seule. C'est pourquoi la page de manuel comporte une mise en garde :</P> <P> <BLOCKQUOTE> select() devrait retourner normalement le temps écoulé depuis le timeout initial, s'il s'est déclenché, en modifiant la valeur pointée par le paramètre <CODE>time</CODE>. Cela sera peut-être implémenté dans les versions ultérieures du système. Donc, il n'est pas vraiment prudent de supposer que les données pointées ne seront pas modifiées lors de l'appel à select(). </BLOCKQUOTE> </P> <P>Mais tout arrive avec le temps ! Lors d'un retour de <CODE>select()</CODE>, l'argument <CODE>timeout</CODE> recevra le temps écoulé depuis la dernière réception de données. Si aucune donnée n'est arrivée, la valeur sera nulle, et les futurs appels à cette fonction utilisant le même <CODE>timeout</CODE> auront pour résultat un retour immédiat.</P> <P>Pour résoudre le problème, il suffit de mettre la valeur <CODE>timeout</CODE> dans la structure à chaque appel de <CODE>select()</CODE>. Le code initial était <BLOCKQUOTE><CODE> <PRE> struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; while (some_condition) select(n,readfds,writefds,exceptfds,&amp;timeout); </PRE> </CODE></BLOCKQUOTE> et doit devenir : <BLOCKQUOTE><CODE> <PRE> struct timeval timeout; while (some_condition) { timeout.tv_sec = 1; timeout.tv_usec = 0; select(n,readfds,writefds,exceptfds,&amp;timeout); } </PRE> </CODE></BLOCKQUOTE> </P> <P>Certaines versions de Mosaic étaient connues à une certaine époque pour avoir ce problème. </P> <P>La vitesse de rotation du globe terrestre était inversement proportionnelle à la vitesse de transfert des données !</P> <P></P> <A NAME="index.52"></A> <A NAME="index.51"></A> <H3>Appels systèmes interrompus </H3> <H3>Symptomes :</H3> <P>Lorsqu'un processus est arrêté avec un Ctrl-Z et relancé - ou bien lorsqu'un autre signal est déclenché dans une situation différente : par exemple avec un Ctrl-C, la terminaison d'un processus, etc, on dit qu'il y a " interruption d'un appel système " , ou bien " write : erreur inconnue " ou des trucs de ce genre.</P> <P></P> <H3>Problèmes :</H3> <P>Les systèmes POSIX vérifient les signaux plus souvent que d'autres Unix plus anciens. Linux peux lancer les gestionnaires de signaux : <UL> <LI> d'une manière asynchrone (sur un top d'horloge)</LI> <LI> lors d'un retour de n'importe quel appel système</LI> <LI> pendant l'exécution des appels systèmes suivants : <CODE>select()</CODE>, <CODE>pause()</CODE>, <CODE>connect()</CODE>, <CODE>accept()</CODE>, <CODE>read()</CODE> sur des terminaux, des sockets, des pipes ou des fichiers situés dans <CODE>/proc</CODE>, <CODE>write()</CODE> sur des terminaux, des sockets, des pipes ou des imprimantes, <CODE>open()</CODE> sur des FIFOs, des lignes PTYs ou séries, <CODE>ioctl()</CODE> sur des terminaux, <CODE>fcntl()</CODE> avec la commande <CODE>F_SETLKW</CODE>, <CODE>wait4()</CODE>, <CODE>syslog()</CODE>, et toute opération d'ordre TCP ou NFS. </LI> </UL> </P> <P>Sur d'autres systèmes d'exploitation, il est possible que vous ayez à inclure dans cette catégorie les appels systèmes suivants : <CODE>creat()</CODE>, <CODE>close()</CODE>, <CODE>getmsg()</CODE>, <CODE>putmsg()</CODE>, <CODE>msgrcv()</CODE>, <CODE>msgsnd()</CODE>, <CODE>recv()</CODE>, <CODE>send()</CODE>, <CODE>wait()</CODE>, <CODE>waitpid()</CODE>, <CODE>wait3()</CODE>, <CODE>tcdrain()</CODE>, <CODE>sigpause()</CODE>, <CODE>semop()</CODE>.</P> <P></P> <P></P> <P>Si un signal (que le programme désire traiter) est lancé pendant l'exécution d'un appel système, le gestionnaire est lancé. Lorsque le gestionnaire du signal se termine, l'appel système détecte qu'il a été interrompu et se termine avec la valeur -1 et <CODE>errno = EINTR</CODE>. Le programme n'est pas forcément au courant de ce qui s'est passé et donc s'arrête.</P> <P>Vous pouvez choisir deux solutions pour résoudre ce problème.</P> <P>(1)Dans tout gestionnaire de signaux que vous mettez en place, ajoutez l'option <CODE>SA_RESTART</CODE> au niveau de <EM>sigaction</EM>. Par exemple, modifiez <BLOCKQUOTE><CODE> <PRE> signal (signal_id, mon_gestionnaire_de_signaux); </PRE> </CODE></BLOCKQUOTE> en <BLOCKQUOTE><CODE> <PRE> signal (signal_id, mon_gestionnaire_de_signaux); { struct sigaction sa; sigaction (signal_id, (struct sigaction *)0, &amp;sa); #ifdef SA_RESTART sa.sa_flags |= SA_RESTART; #endif #ifdef SA_INTERRUPT sa.sa_flags &amp;= ~ SA_INTERRUPT; #endif sigaction (signal_id, &amp;sa, (struct sigaction *)0); } </PRE> </CODE></BLOCKQUOTE> </P> <P>Notez que lors de certains appels systèmes vous devrez souvent regarder si <CODE>errno</CODE> n'a pas été positionnée à <CODE>EINTR</CODE> par vous même comme avec <CODE>read()</CODE>, <CODE>write()</CODE>, <CODE>ioctl()</CODE>, <CODE>select()</CODE>, <CODE>pause()</CODE> et <CODE>connect()</CODE>. </P> <P>(2) A la recherche de <CODE>EINTR</CODE> :</P> <P>Voici deux exemples avec <CODE>read()</CODE> et <CODE>ioctl()</CODE>,</P> <P>Voici le code original utilisant <CODE>read()</CODE></P> <P> <BLOCKQUOTE><CODE> <PRE> int result; while (len > 0) { result = read(fd,buffer,len); if (result < 0) break; buffer += result; len -= result; } </PRE> </CODE></BLOCKQUOTE> et le nouveau code </P> <P> <BLOCKQUOTE><CODE> <PRE> int result; while (len > 0) { result = read(fd,buffer,len); if (result < 0) { if (errno != EINTR) break; } else { buffer += result; len -= result; } } </PRE> </CODE></BLOCKQUOTE> Voici un code utilisant <CODE>ioctl()</CODE></P> <P> <BLOCKQUOTE><CODE> <PRE> int result; result = ioctl(fd,cmd,addr); </PRE> </CODE></BLOCKQUOTE> et cela devient <BLOCKQUOTE><CODE> <PRE> int result; do { result = ioctl(fd,cmd,addr); } while ((result == -1) && (errno == EINTR)); </PRE> </CODE></BLOCKQUOTE> </P> <P>Il faut remarquer que dans certaines versions d'Unix de type BSD on a l'habitude de relancer l'appel système. Pour récupérer les interruptions d'appels systèmes, vous devez utiliser les options <CODE>SV_INTERRUPT</CODE> ou <CODE>SA_INTERRUPT</CODE>.</P> <P></P> <A NAME="index.56"></A> <A NAME="index.55"></A> <A NAME="index.54"></A> <A NAME="index.53"></A> <H3>Les chaînes et leurs accès en écritures (ou les programmes qui provoquent des" segmentation fault " d'une manière aléatoire) </H3> <P> GCC a une vue optimiste en ce qui concerne ses utilisateurs, en croyant qu'ils respectent le fait qu'une chaîne dite constante l'est réellement. Donc, il les range dans la zone <EM>texte(code)</EM> du programme, où elles peuvent être chargées puis déchargées à partir de l'image binaire de l'exécutable située sur disque (ce qui évite d'occuper de l'espace disque). Donc, toute tentative d'écriture dans cette chaîne provoque un " segmentation fault ".</P> <P>Cela peut poser certains problèmes avec d'anciens codes, par exemple ceux qui utilisent la fonction <CODE>mktemp()</CODE> avec une chaîne constante comme argument. <CODE>mktemp()</CODE> essaye d'écrire dans la chaîne passée en argument.</P> <P>Pour résoudre ce problème, <OL> <LI>compilez avec l'option <CODE>-fwritable-strings</CODE> pour indiquer à gcc de mettre les chaînes constantes dans l'espace de données</LI> <LI> réécrire les différentes parties du code pour allouer une chaîne non constante puis effectuer un strcpy des données dedans avant d'effectuer l'appel.</LI> </OL> </P> <P></P> <A NAME="index.57"></A> <H3>Pourquoi l'appel à <CODE>execl()</CODE> échoue ? </H3> <P> Tout simplement parce que vous l'utilisez mal. Le premier argument d'<CODE>execl</CODE> est le programme que vous désirez exécuter. Le second et ainsi de suite sont en fait le éléments du tableau <CODE>argv</CODE> que vous appelez. Souvenez-vous que <CODE>argv[0]</CODE> est traditionnellement fixé même si un programme est lancé sans argument. Vous devriez donc écrire : <BLOCKQUOTE><CODE> <PRE> execl("/bin/ls","ls",NULL); </PRE> </CODE></BLOCKQUOTE> et pas <BLOCKQUOTE><CODE> <PRE> execl("/bin/ls", NULL); </PRE> </CODE></BLOCKQUOTE> </P> <P></P> <P> Lancer le programme sans argument est considéré comme étant une demande d'affichage des bibliothèques dynamiques associées au programme, si vous utilisez le format a.out. ELF fonctionne d'une manière différente.</P> <P></P> <P>(Si vous désirez ces informations, il existe des outils plus simples; consultez la section sur le chargement dynamique, ou la page de manuel de <CODE>ldd</CODE>).</P> <P></P> <P></P> <HR> <P> Chapitre <A HREF="GCC-HOWTO-5.html">suivant</A>, Chapitre <A HREF="GCC-HOWTO-3.html">Précédent</A> <P> Table des matières de <A HREF="GCC-HOWTO.html#toc4">ce chapitre</A>, <A HREF="GCC-HOWTO.html#toc">Table des matières</A> générale</P> <P> <A HREF="GCC-HOWTO.html">Début</A> du document, <A HREF="#0"> Début de ce chapitre</A></P> </BODY> </HTML>