Comment gcc trouve-t-il le fichier d'en-tête suivant?

10

J'ai inclus sys/ptrace.hdans mon programme C.

La sortie de /usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -vdonne les chemins suivants où gcc recherche les fichiers d'en-tête

#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed
 /usr/include
End of search list.

la sortie de gcc -Mmon programme donne les emplacements des fichiers d'en-tête suivants

    pt.o: pt.c /usr/include/stdc-predef.h /usr/include/stdio.h \
 /usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
 /usr/include/x86_64-linux-gnu/bits/wordsize.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include/stddef.h \
 /usr/include/x86_64-linux-gnu/bits/types.h \
 /usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \
 /usr/include/_G_config.h /usr/include/wchar.h \
 /usr/lib/gcc/x86_64-linux-gnu/4.8/include/stdarg.h \
 /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
 /usr/include/x86_64-linux-gnu/bits/sys_errlist.h \
 /usr/include/x86_64-linux-gnu/sys/ptrace.h

Puisqu'il /usr/include/x86_64-linux-gnu/n'est pas contenu dans la première sortie, comment gcc le trouve- sys/ptrace.ht-il?

ÉDITER:

La sortie des echo '#include <sys/ptrace.h>' | gcc -fsyntax-only -xc -v -H -résultats dans

Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04) 
user912083132
la source
Il regarde récursivement /usr/include.. Quel problème essayez-vous de résoudre?
Ramhound
Il ne semble pas qu'il regarde récursivement. Si tel était le cas, il ne serait pas nécessaire d'inclure le préfixe sys /. Inclure simplement ptrace.h, par exemple, ne fonctionne pas.
user912083132
Je ne pense pas que vous avez inclus /sys/ptrace.hmais sys/ptrace.h, non?
user253751
C'est presque certainement un bogue dans les correctifs "multiarch" de GCC. Le répertoire /usr/include/x86_64-linux-gnu est traité comme un répertoire système et doit être inclus dans la liste des chemins de recherche imprimée par gcc -v. Je ne sais pas comment quelqu'un a réussi à résoudre ce bogue; si je me souviens bien, la façon la plus évidente d'ajouter des répertoires système comprend les ajoute à ce qui est imprimé par -v. (J'ai écrit ~ 50% du préprocesseur de GCC, mais c'était il y a 15 ans, donc je me souviens peut-être de quelque chose.)
zwol
@Ramhound Il ne recherche certainement pas récursivement ci-dessous /usr/include. Cela briserait à peu près toutes les bibliothèques C du monde.
zwol

Réponses:

12

Réponse plus courte.

Votre question concerne la sortie de cc1 -v, mais cela ne prend pas en compte le CPP (C Pre-Processor) et ses inclus sont mélangés dans toute la chaîne de compilation. Si vous exécutez cpp -vsur votre système, vous devriez voir, un mélange d'inclusions qui ressemble à la sortie de cc1 -vmais avec au moins le /usr/include/x86_64-linux-gnuchemin d'accès qui y est ajouté.

Réponse plus longue.

Puisqu'il /usr/include/x86_64-linux-gnu/n'est pas contenu dans la première sortie, comment gcc le trouve- sys/ptrace.ht-il?

Techniquement, il /usr/include/x86_64-linux-gnu/n'est pas explicitement défini dans la première sortie, mais /usr/include/l'est définitivement. Et c'est un chemin de recherche par défaut comme expliqué dans la documentation officielle de GNU GCC :

GCC recherche plusieurs en-têtes pour les en-têtes. Sur un système Unix normal, si vous ne le lui demandez pas autrement, il recherchera les en-têtes demandés avec #include <file>dans:

  • / usr / local / include
  • libdir / gcc / target / version / include
  • / usr / target / include
  • / usr / include

Et expliqué plus en détail ici:

GCC recherche les en-têtes demandés avec d' #include "file"abord dans le répertoire contenant le fichier courant, puis dans les répertoires spécifiés par les -iquoteoptions, puis aux mêmes endroits il aurait cherché un en-tête demandé avec des crochets angulaires. Par exemple, si /usr/include/sys/stat.hcontient # include "types.h", GCC recherche d' types.habord dans /usr/include/sys, puis dans son chemin de recherche habituel.

Cela implique donc que le x86_64-linux-gnu/chemin est simplement inséré /usr/include/*/sys/comme ceci:

/usr/include/x86_64-linux-gnu/sys/ptrace.h

C'est du moins ce que je pensais initialement dans une version antérieure de cette question . Mais après avoir consulté ce site, l'explication de ce qui se passe est un peu plus détaillée et la réponse directe de ce site au contenu équivalent à ce que j'ai publié ci-dessus est republiée ci-dessous; je souligne:

mais c'est une sorte de réponse insipide (et aussi incomplète). Il doit sûrement y avoir un moyen pour que GCC vous dise exactement où il va finir par chercher ses fichiers d'en-tête? Eh bien, bien qu'il soit pratique de considérer GCC comme une seule application monolithique qui prend les fichiers de code source et crache des programmes de travail, c'est techniquement une collection d'autres programmes qui s'enchaînent pour produire le fichier objet compilé final. Le premier d'entre eux est CPP, abréviation de C Pre-Processor , dont le travail consiste à rechercher des directives de compilation comme #includeet à modifier le code source comme spécifié par elles; en cas d'inclusion, en copiant le contenu d'un autre fichier dans celui en cours. Vous pouvez voir où il recherche ces fichiers en lui passant l'indicateur -v:

Sachez que le CPP (C Pre-Processor) est la première étape du processus du compilateur, regardons la sortie «include» de cpp -vmon système de test Ubuntu 12.04.5:

#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include

Là, vous pouvez voir clairement /usr/include/x86_64-linux-gnu. Et pour comparer, voici la sortie «include» similaire /usr/lib/gcc/x86_64-linux-gnu/4.6/cc1 -vdu même système de test Ubuntu 12.04.5:

#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/4.6/include-fixed
 /usr/include

Notez comment /usr/include/x86_64-linux-gnuest clairement inséré dans le mix par l'action initiale CPP (C Pre-Processor). Et le message sur ce site continue pour expliquer d'où viennent ces chemins; encore une fois, je souligne:

ce chemin est en fait intégré à CPP (qui fait partie de GCC) au moment de la compilation; si pour une raison quelconque vous finissez par supprimer l'un de ces répertoires, il sera toujours vérifié pour chaque compilation. Chaque répertoire est recherché dans l'ordre dans lequel il est répertorié ici; si un fichier est trouvé dans /usr/local/include, les trois répertoires suivants ne seront pas vérifiés.

Donc, tout se résume à l'appel du CPP (C Pre-Processor) en tant que première partie d'une chaîne de compilation C.

JakeGould
la source
Pourquoi x86_64-linux-gnu / est-il poussé au milieu?
user912083132
@ user912083132: C'est la $TARGETpartie que j'ai mentionnée dans ma réponse et mon commentaire. C'est la sortie de config.guessquand GCC a été compilé, ou qui a été donnée à son configurescript avec le --targetdrapeau. La vraie question est, comment ce chemin est-il assemblé? Revient-il simplement dans la même liste, s'ajoutant $TARGETà chacun, après avoir échoué à trouver l'en-tête la première fois?
Warren Young
@ user912083132 Mise à jour de ma réponse avec de nouvelles informations glanées. Veuillez le relire; la réponse explique que cela vient du CPP (C Pre-Processor).
JakeGould
2

À moins de me plonger dans le code source de GCC, je ne peux pas vous donner de "pourquoi", mais je peux vous dire que la version de GCC que j'ai ici retombe /usr/include/$TARGETaprès avoir épuisé les choix que vous et JakeGould avez trouvés . Vous pouvez le voir comme ceci:

$ strace -f -e open gcc -c foo.c -o foo.o 2>&1 | grep ptrace.h

foo.ccontient un #include <sys/ptrace.h>.

Vous avez besoin de l' -fargument ici parce que les gccenfants sont engendrés pour effectuer le travail de compilation proprement dit. Vous avez besoin du 2>&1car straceécrit ses résultats dans stderr, pas stdout.

Notez que vous obtenez des ENOENTerreurs pour tous les répertoires documentés avant d'essayer enfin celui qui réussit.

Warren Young
la source