Page suivantePage pr�c�denteTable des mati�res

4. Portage et compilation

4.1 Symboles d�finis automatiquement

Vous pouvez trouver quels symboles votre version de gcc d�finit automatiquement en le lan�ant avec l'option -v. Par exemple cela donne �a chez moi :

$ 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__ -
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
#ifdef __linux__
/* ... code linux ... */
#endif /* linux */

Utilisez __linux__ pour cela, et pas linux. Bien que cette macro soit d�finie, ce n'est pas une sp�cification POSIX.

4.2 Options de compilation

La documentation des options de compilation se trouve dans les pages info de gcc (sous Emacs, utilisez C-h i 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 ftp://prep.ai.mit.edu/pub/gnu ou l'un des ses nombreux miroirs dont ftp://ftp.ibp.fr/pub/gnu.

La page de manuel gcc (gcc.1) est en principe, compl�tement d�pass�e. Cela vous met en garde si vous d�sirez la consulter.

Options de compilation

gcc peut r�aliser un certain nombre d'optimisations sur le code g�n�r� en ajoutant l'option -On � la ligne de commandes, o� n est un chiffre. La valeur de n, 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).

En interne, gcc interpr�te les options telles que -f et -m. Vous pouvez voir exactement ce qu'effectue le niveau sp�cifi� dans l'option -O en lan�ant gcc avec l'option -v et l'option (non document�e) -Q. Par exemple, l'option -O2, effectue les op�rations suivantes sur ma machine :

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

Utiliser un niveau d'optimisation sup�rieur � celui que le compilateur supporte (par exemple -O6) 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.

Les utilisateurs de gcc 2.7.0 � 2.7.2 devraient noter qu'il y a un bogue dans l'option -O2. Plus pr�cis�ment, la strength reduction 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 -fno-strength-reduce.

Sp�cification du processeur

Il existe d'autres options -m qui ne sont pas positionn�es lors de l'utilisation de -O mais qui sont n�anmoins utiles dans certains cas. C'est le cas pour les options -m386 et -m486, 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.

Il n'existe pas actuellement de -mpentium ou -m586. Linus a sugg�r� l'utilisation des options -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2, 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 :

� Mon avis est que l'option -mno-strength-reduce permet d'obtenir un code plus rapide sur un x86 (nota : je ne parle pas du bogue strength reduction, 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 strength reduction consiste en fait � rajouter des registres pour remplacer les multiplications par des additions. Je suspecte �galement -fcaller-saves de ne pas arranger la situation. �

Une autre id�e est que -fomit-frame-pointer 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 -fomit-frame-pointer, 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.

Le mot final sur le sujet provient de Linus :

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.

Internal compiler error: cc1 got fatal signal 11

Signal 11 correspond au signal SIGSEGV, ou bien segmentation violation. 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.

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 n'arrivez pas � reproduire le bogue --- 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). N'annoncez pas 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 `make zImage' ! `make zImage' doit compiler plus de 200 fichiers, et il en faut bien moins pour arriver � faire �chouer une compilation.

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.

4.3 Portabilit�

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 :-).

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.

Sp�cificit�s BSD (notamment bsd_ioctl, daemon et<sgtty.h>)

Vous pouvez compiler votre programme avec l'option -I/usr/include/bsd et faire l'�dition de liens avec -lbsd (en ajoutant -I/usr/include/bsd � la ligne CFLAGS et -lbsd � la ligne LDFLAGS dans votre fichier Makefile). Il est �galement n�cessaire de ne pas ajouter -D__USE_BSD_SIGNAL si vous voulez que les signaux BSD fonctionnent car vous les avez inclus automatiquement avec la ligne -I/usr/include/bsd et en incluant le fichier d'en-t�te <signal.h>.

Signaux manquants (SIGBUS, SIGEMT, SIGIOT, SIGTRAP, SIGSYS, etc.)

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) :

� 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 �

La mani�re la plus �l�gante de r�gler ce probl�me est de red�finir ces signaux � SIGUNUSED. La mani�re normale de proc�der est d'entourer le code avec les #ifdef appropri�s :

#ifdef SIGSYS
/* ... code utilisant les signaux non posix  .... */
#endif

Code K & R

GCC est un compilateur ANSI, or il existe beaucoup de code qui ne soit pas ANSI.

Il n'y a pas grand chose � faire, sauf rajouter l'option -traditional lors de la compilation. Il effectue certaines v�rifications suppl�mentaires. Consultez les pages info gcc.

Notez que l'option -traditional a pour unique effet de changer la forme du langage accept� par gcc. Par exemple, elle active l'option -fwritable-strings, 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.

Les symboles du pr�processeur produisent un conflit avecles prototypes du code

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 atoi() et atol().

sprintf()

Parfois, soyez prudent lorsque vous effectuez un portage � partir des sources de programmes fonctionnant sous SunOs, surtout avec la fonction sprintf(string, fmt, ...) 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.

fcntl et ses copains. O� se trouve la d�finition de FD_* et compagnie ?

Dans <sys/time.h>. Si vous utilisez fcntl vous voudrez probablement inclure <unistd.h> �galement, pour avoir le prototype de la fonction.

D'une mani�re g�n�rale, la page de manuel pour une fonction donne la liste des fichiers d'en-t�te � inclure.

Le timeout de select(). Les programmescommencent dans un �tat d'attente active

A une certaine �poque, le param�tre timeout de la fonction select() �tait utilis� en lecture seule. C'est pourquoi la page de manuel comporte une mise en garde :

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 time. 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().

Mais tout arrive avec le temps ! Lors d'un retour de select(), l'argument timeout 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 timeout auront pour r�sultat un retour imm�diat.

Pour r�soudre le probl�me, il suffit de mettre la valeur timeout dans la structure � chaque appel de select(). Le code initial �tait

 struct timeval timeout;
 timeout.tv_sec = 1;
 timeout.tv_usec = 0;
 while (some_condition)
 select(n,readfds,writefds,exceptfds,&timeout);
et doit devenir :
 struct timeval timeout;
 while (some_condition)
 {
 timeout.tv_sec = 1;
 timeout.tv_usec = 0;
 select(n,readfds,writefds,exceptfds,&timeout);
 }

Certaines versions de Mosaic �taient connues � une certaine �poque pour avoir ce probl�me.

La vitesse de rotation du globe terrestre �tait inversement proportionnelle � la vitesse de transfert des donn�es !

Appels syst�mes interrompus

Symptomes :

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.

Probl�mes :

Les syst�mes POSIX v�rifient les signaux plus souvent que d'autres Unix plus anciens. Linux peux lancer les gestionnaires de signaux :

Sur d'autres syst�mes d'exploitation, il est possible que vous ayez � inclure dans cette cat�gorie les appels syst�mes suivants : creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(), send(), wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop().

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 errno = EINTR. Le programme n'est pas forc�ment au courant de ce qui s'est pass� et donc s'arr�te.

Vous pouvez choisir deux solutions pour r�soudre ce probl�me.

(1)Dans tout gestionnaire de signaux que vous mettez en place, ajoutez l'option SA_RESTART au niveau de sigaction. Par exemple, modifiez

 signal (signal_id, mon_gestionnaire_de_signaux);
en
 signal (signal_id, mon_gestionnaire_de_signaux);
 {
 struct sigaction sa;
 sigaction (signal_id, (struct sigaction *)0, &sa);
#ifdef SA_RESTART
 sa.sa_flags |= SA_RESTART;
#endif
#ifdef SA_INTERRUPT
 sa.sa_flags &= ~ SA_INTERRUPT;
#endif
 sigaction (signal_id, &sa, (struct sigaction *)0);
 }

Notez que lors de certains appels syst�mes vous devrez souvent regarder si errno n'a pas �t� positionn�e � EINTR par vous m�me comme avec read(), write(), ioctl(), select(), pause() et connect().

(2) A la recherche de EINTR :

Voici deux exemples avec read() et ioctl(),

Voici le code original utilisant read()

int result;
while (len> 0)
{
 result = read(fd,buffer,len);
 if (result < 0)
 break;
 buffer += result;
 len -= result;
}
et le nouveau code

int result;
while (len> 0)
{
 result = read(fd,buffer,len);
 if (result < 0)
 {
 if (errno != EINTR)
 break;
 }
 else
 {
 buffer += result;
 len -= result;
 }
}
Voici un code utilisant ioctl()

int result;
result = ioctl(fd,cmd,addr);
et cela devient
int result;
do
{
 result = ioctl(fd,cmd,addr);
}
while ((result == -1) && (errno == EINTR));

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 SV_INTERRUPT ou SA_INTERRUPT.

Les cha�nes et leurs acc�s en �critures (ou les programmes qui provoquent des� segmentation fault � d'une mani�re al�atoire)

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 texte(code) 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 �.

Cela peut poser certains probl�mes avec d'anciens codes, par exemple ceux qui utilisent la fonction mktemp() avec une cha�ne constante comme argument. mktemp() essaye d'�crire dans la cha�ne pass�e en argument.

Pour r�soudre ce probl�me,

  1. compilez avec l'option -fwritable-strings pour indiquer � gcc de mettre les cha�nes constantes dans l'espace de donn�es
  2. 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.

Pourquoi l'appel � execl() �choue ?

Tout simplement parce que vous l'utilisez mal. Le premier argument d'execl est le programme que vous d�sirez ex�cuter. Le second et ainsi de suite sont en fait le �l�ments du tableau argv que vous appelez. Souvenez-vous que argv[0] est traditionnellement fix� m�me si un programme est lanc� sans argument. Vous devriez donc �crire :

execl("/bin/ls","ls",NULL);
et pas
execl("/bin/ls", NULL);

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.

(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 ldd).


Page suivantePage pr�c�denteTable des mati�res

Hosting by: Hurra Communications GmbH
Generated: 2007-01-26 18:01:30