Como o gcc encontra o seguinte arquivo de cabeçalho?

10

Eu incluí sys/ptrace.hno meu programa C.

A saída de /usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -vfornece os seguintes caminhos em que o gcc procura por arquivos de cabeçalho

#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.

saída de gcc -Mpara o meu programa fornece os seguintes locais de arquivo de cabeçalho

    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

Como /usr/include/x86_64-linux-gnu/não está contido na primeira saída, como o gcc encontra sys/ptrace.h?

EDITAR:

A saída dos echo '#include <sys/ptrace.h>' | gcc -fsyntax-only -xc -v -H -resultados em

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
fonte
Ele está olhando recursivamente /usr/include.. Que problema você está tentando resolver?
Ramhound
Não parece que parece recursivamente. Nesse caso, não haveria necessidade de incluir o sys / prefix. Incluir apenas ptrace.h, por exemplo, não funciona.
user912083132
Eu não acho que você incluiu , /sys/ptrace.hmas sys/ptrace.h, certo?
user253751
Isso é quase certamente um bug nos patches "multiarch" do GCC. O diretório /usr/include/x86_64-linux-gnu está sendo tratado como um diretório de inclusão do sistema e deve ser incluído na lista de caminhos de pesquisa impressa por gcc -v. Não sei como alguém conseguiu alcançar esse bug; se bem me lembro, a maneira mais óbvia de adicionar diretórios de inclusão do sistema os adiciona ao que é impresso -v. (Eu escrevi ~ 50% do pré-processador do GCC, mas isso foi há 15 anos, para que eu possa ser misremembering alguma coisa.)
Zwol
@ Ramhound Definitivamente não pesquisa recursivamente abaixo /usr/include. Isso quebraria quase todas as bibliotecas C do mundo.
Zwol 03/10/2015

Respostas:

12

Resposta mais curta.

Sua pergunta é sobre a saída de cc1 -v, mas isso não afeta o CPP (pré-processador C) e inclui os que são misturados em toda a cadeia de compilação. Se você rodar cpp -vem seu sistema, verá uma mistura de inclusões semelhante à saída de, cc1 -vmas com pelo menos o /usr/include/x86_64-linux-gnucaminho adicionado.

Resposta mais longa.

Como /usr/include/x86_64-linux-gnu/não está contido na primeira saída, como o gcc encontra sys/ptrace.h?

Tecnicamente, /usr/include/x86_64-linux-gnu/não é explicitamente definido na primeira saída, mas /usr/include/definitivamente é. E esse é um caminho de pesquisa padrão, conforme explicado na documentação oficial do GNU GCC :

O GCC procura em vários lugares diferentes cabeçalhos. Em um sistema Unix normal, se você não o instruir de outra forma, ele procurará os cabeçalhos solicitados com #include <file>:

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

E mais explicado aqui:

O GCC procura os cabeçalhos solicitados #include "file"primeiro no diretório que contém o arquivo atual, depois nos diretórios especificados pelas -iquoteopções e, nos mesmos locais, em que procuraria um cabeçalho solicitado com colchetes angulares. Por exemplo, se /usr/include/sys/stat.hcontiver # include "types.h", o GCC procurará types.hprimeiro /usr/include/sys, depois o caminho de pesquisa usual.

Portanto, isso implica que o x86_64-linux-gnu/caminho é simplesmente inserido /usr/include/*/sys/assim:

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

Pelo menos foi o que pensei inicialmente em uma versão anterior desta questão . Mas, depois de verificar este site, a explicação do que está acontecendo é um pouco mais detalhada e a resposta direta desse site ao conteúdo equivalente ao que eu publiquei acima é postada abaixo; ênfase em negrito é minha:

mas essa é uma resposta insensata (e também incompleta). Certamente deve haver uma maneira de fazer com que o GCC lhe diga exatamente onde ele acabará procurando os arquivos de cabeçalho? Bem, embora seja conveniente pensar no GCC como um único aplicativo monolítico que capta arquivos de código-fonte e cospe programas de trabalho, é tecnicamente uma coleção de outros programas que se unem para produzir o arquivo final de objeto compilado. O primeiro deles é o CPP, abreviação de Pré-processador C , cuja tarefa é procurar diretivas do compilador #includee modificar o código-fonte especificado por elas; no caso de inclusão, copiando o conteúdo de outro arquivo para o atual. Você pode ver onde ele procura esses arquivos passando o sinalizador -v:

Saiba que o CPP (pré-processador C) é o primeiro passo no processo do compilador, vamos dar uma olhada na saída "incluir" do cpp -vmeu sistema de teste 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á você pode ver claramente /usr/include/x86_64-linux-gnu. E para comparar, aqui está a saída "include" semelhante /usr/lib/gcc/x86_64-linux-gnu/4.6/cc1 -vno mesmo sistema de teste 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

Observe como /usr/include/x86_64-linux-gnué claramente inserido no mix pela ação inicial do CPP (pré-processador C). E a publicação nesse site continua explicando de onde vêm esses caminhos; novamente a ênfase ousada é minha:

esse caminho é realmente incorporado ao CPP (que faz parte do GCC) no momento da compilação; se, por qualquer motivo, você excluir um desses diretórios, ele ainda será verificado em cada compilação. Cada diretório é pesquisado na ordem em que está listado aqui; se um arquivo for encontrado /usr/local/include, os próximos três diretórios não serão verificados.

Portanto, tudo se resume ao CPP (pré-processador C) sendo chamado como a primeira parte de uma cadeia de compilação C.

JakeGould
fonte
Por que x86_64-linux-gnu / é empurrado para o meio?
user912083132
@ user912083132: Essa é a $TARGETparte que mencionei na minha resposta e comentário. É o resultado de config.guessquando o GCC foi compilado ou que foi fornecido ao seu configurescript com o --targetsinalizador. A verdadeira questão é: como esse caminho é montado? Ele volta à mesma lista, acrescentando $TARGETa cada uma, depois de não encontrar o cabeçalho pela primeira vez?
21815 Warren Young
@ user912083132 Atualizei minha resposta com algumas informações recém-obtidas. Por favor, releia; A resposta explica que é proveniente do CPP (pré-processador C).
JakeGould #
2

Antes de me aprofundar no código-fonte do GCC, não posso lhe dar um "porquê", mas posso lhe dizer que a versão do GCC que tenho aqui recai /usr/include/$TARGETdepois de esgotar as opções que você e JakeGould encontraram . Você pode vê-lo assim:

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

onde foo.ccontém um #include <sys/ptrace.h>.

Você precisa do -fargumento aqui porque gccgera filhos para fazer o trabalho de compilação real. Você precisa do 2>&1porque straceestá gravando seus resultados no stderr, não no stdout.

Observe que você obtém ENOENTerros para todos os diretórios documentados antes de finalmente tentar o que é bem-sucedido.

Warren Young
fonte