Observações Acerca de Construir Software

Aquelas pessoas que tenham construído um sistema LFS possivelmente estejam cientes dos princípios gerais da transferência e do desempacotamento de software. Alguma daquela informação está repetida aqui para aquelas novatas em construir o próprio software delas.

Cada conjunto de instruções de instalação contém um URL a partir do qual você pode transferir o pacote. Os remendos, no entanto, estão armazenados nos servidores do LFS e estão disponíveis via HTTP. Esses estão referenciados conforme necessários nas instruções de instalação.

Embora possa manter os arquivos do fonte onde quiser, presumimos que você desempacotou o pacote e mudou para o diretório criado pelo processo de desempacotamento (o diretório do fonte). Também presumimos que você descomprimiu quaisquer remendos exigidos e que eles estão no diretório imediatamente acima do diretório do fonte.

Nós não podemos enfatizar fortemente o suficiente que você deveria iniciar a partir de uma árvore limpa do fonte a cada vez. Isso significa que, se você tiver tido um erro durante a configuração ou a compilação, [então] geralmente é melhor deletar a árvore do fonte e desempacotá-la outra vez antes de tentar novamente. Isso, obviamente, não se aplica se você for um(a) usuário(a) avançado(a) habituado(a) a hackear Makefiles e código C; porém, se em dúvida, [então] inicie a partir de uma árvore limpa.

Construindo Software como um(a) Usuário(a) Não Privilegiado(a) (não root)

A regra de ouro da Administração do Sistema Unix é a de usar os seus super poderes somente quando necessário. Assim, o BLFS recomenda que você construa software como um(a) usuário(a) não privilegiado(a) e somente se torne o(a) usuário(a) root quando instalar o software. Essa filosofia é seguida em todos os pacotes neste livro. A menos que especificado de outra maneira, todas as instruções deveriam ser executadas como um(a) usuário(a) não privilegiado(a). O livro alertará você acerca de instruções que precisarem de privilégios do(a) root.

Desempacotando o Software

Se um arquivo estiver no formato .tar e comprimido, [então] ele é desempacotado executando-se um dos seguintes comandos:

tar -xvf nome_arquivo.tar.gz
tar -xvf nome_arquivo.tgz
tar -xvf nome_arquivo.tar.Z
tar -xvf nome_arquivo.tar.bz2
[Nota]

Nota

Você possivelmente omita o uso do parâmetro v nos comandos mostrados acima e abaixo se você desejar suprimir a listagem verbosa de todos os arquivos no arquivamento conforme eles forem extraídos. Isso pode ajudar a acelerar a extração, bem como torna quaisquer erros produzidos durante a extração mais óbvios para você.

Você também pode usar um método ligeiramente diferente:

bzcat nome_arquivo.tar.bz2 | tar -xv

Finalmente, ocasionalmente, temos um arquivo de remendo comprimido no formato .patch.gz ou .patch.bz2. A melhor maneira de aplicar o remendo é a de canalizar a saída gerada do descompressor para o utilitário patch. Por exemplo:

gzip -cd ../patchname.patch.gz | patch -p1

Ou para um remendo comprimido com bzip2:

bzcat ../nome_remendo.patch.bz2 | patch -p1

Averiguando a Integridade do Arquivo

Geralmente, para se averiguar se o arquivo transferido está completo, muitos(as) mantenedores(as) de pacote também distribuem somas de verificação md5 dos arquivos. Para averiguar a soma de verificação md5 dos arquivos transferidos, transfira ambos, o arquivo e o arquivo correspondente de soma de verificação md5, para o mesmo diretório (preferencialmente a partir de locais diferentes online) e (assumindo que arquivo.md5sum seja o arquivo de soma de verificação md5 transferido) execute o seguinte comando:

md5sum -c arquivo.md5sum

Se existirem quaisquer erros, [então] eles serão informados. Observe que o livro BLFS inclui somas de verificação md5 para todos os arquivos de fonte também. Para usar as somas de verificação md5 fornecidas pelo BLFS, você pode criar um arquivo.md5sum (coloque os dados da soma de verificação md5 e o nome exato do arquivo transferido na mesma linha de um arquivo, separados por espaço em branco) e executar o comando mostrado acima. Alternativamente, simplesmente execute o comando mostrado abaixo e compare a saída gerada para os dados da soma de verificação md5 mostrada no livro BLFS.

md5sum <nome_do_arquivo_transferido>

MD5 não é seguro criptograficamente, de forma que as somas de verificação md5 são fornecidas somente para se detectar mudanças não maliciosas para o conteúdo do arquivo. Por exemplo, um erro ou truncamento introduzido durante a transferência de rede de comunicação; ou uma atualização furtiva para o pacote oriunda do(a) desenvolvedor(a) (atualizando o conteúdo de um tarball liberado em vez de fazer um lançamento novo adequadamente).

Não existe maneira 100% segura de garantir a genuinidade dos arquivos do fonte. Assumindo que o(a) desenvolvedor(a) esteja gerenciando corretamente o sítio da web dele(a) (a chave privada não vazou e o domínio não esteja sequestrado); e que as âncoras de confiança tenham sido configuradas corretamente usando make-ca-1.14 no sistema BLFS; nós podemos razoavelmente confiar nos URLs de transferência para o sítio oficial da web do(a) desenvolvedor(a) com protocolo https. Observe que o próprio livro BLFS está publicado em um sítio da web com https, de forma que você já deveria ter alguma confiança no protocolo https ou você não confiaria no conteúdo do livro.

Se o pacote for transferido a partir de um local não oficial (por exemplo, um espelho local), [então] as somas de verificação geradas por algoritmos de resumo criptograficamente seguros (por exemplo, SHA256) podem ser usadas para averiguar a genuinidade do pacote. Transfira o arquivo da soma de verificação a partir do sítio da web oficial do(a) desenvolvedor(a) (ou algum lugar que você possa confiar) e compare a soma de verificação do pacote oriunda do local não oficial com ele. Por exemplo, a soma de verificação SHA256 pode ser verificada com o comando:

[Nota]

Nota

Se a soma de verificação e o pacote forem transferidos a partir do mesmo local não confiável, [então] você não ganharia melhoramento de segurança averiguando o pacote com a soma de verificação. O(A) atacante pode falsear a soma de verificação assim como comprometer o próprio pacote.

sha256sum -c arquivo.sha256sum

Se o GnuPG-2.4.5 estiver instalado, [então] você também pode averiguar a genuinidade do pacote com uma assinatura GPG. Importe a chave pública GPG do(a) desenvolvedor(a) com:

gpg --recv-key ID_da_chave

ID_da_chave deveria ser substituído pelo ID da chave oriundo de algum lugar que você possa confiar (por exemplo, copie-o a partir do sítio da web oficial do(a) desenvolvedor(a) usando https). Agora, você consegue averiguar a assinatura com:

gpg --recv-key arquivo.sig arquivo

A vantagem da assinatura GnuPG é, tão logo você importou uma chave pública que possa ser confiada, você pode transferir ambos, o pacote e a assinatura dele, a partir do mesmo local não oficial e averiguá-los com a chave pública. Assim, você não precisaria conectar com o sítio da web oficial do(a) desenvolvedor(a) para ir buscar uma soma de verificação para cada lançamento novo. Você somente precisa atualizar a chave pública se ela estiver expirada ou revogada.

Criando Arquivos de Registro Durante a Instalação

Para pacotes mais largos, é conveniente se criar arquivos de registro em vez de olhar fixamente para a tela esperando pegar um erro ou aviso em particular. Os arquivos de registro também são úteis para depuração e para manter registros. O seguinte comando permite a você criar um registro da instalação. Substitua <comando> pelo comando que você pretende executar.

( <comando> 2>&1 | tee compile.log && exit $PIPESTATUS )

2>&1 redireciona as mensagens de erro para o mesmo local que a saída gerada padrão. O comando tee permite visualizar a saída gerada enquanto se registra os resultados em um arquivo. Os parênteses em volta do comando executam o comando inteiro em um sub shell; e, finalmente, o comando exit $PIPESTATUS garante que o resultado do <comando> seja retornado como o resultado e não o resultado do comando tee.

Usando Múltiplos Processadores

Para muitos sistemas modernos com múltiplos processadores (ou núcleos) o tempo de compilação para um pacote pode ser reduzido realizando-se um "make paralelo", ou configurando-se uma variável de ambiente, ou dizendo-se ao aplicativo make para simultaneamente executar múltiplas tarefas.

Por exemplo, uma CPU Intel Core i9-13900K contém 8 núcleos de desempenho (P) e 16 núcleos de eficiência (E), e os núcleos P suportam SMT (Simultaneous MultiThreading, também conhecido como Hyper-Threading), portanto cada núcleo P pode executar duas camadas simultaneamente e o núcleo Linux tratará cada núcleo P como dois núcleos lógicos. Como resultado, existem 32 núcleos lógicos no total. Para utilizar todos esses núcleos lógicos executando make, nós podemos configurar uma variável de ambiente para dizer ao make para executar 32 tarefas simultaneamente:

export MAKEFLAGS='-j32'

ou apenas construir com:

make -j32

Se você tiver aplicado o sed opcional quando da construção do ninja no LFS, [então] você pode usar:

export NINJAJOBS=32

quando um pacote usar o ninja; ou apenas:

ninja -j32

Se você não tiver certeza acerca do número de núcleos lógicos, execute o comando nproc.

Para make, o número padrão de tarefas é 1. Mas para ninja, o número padrão de tarefas é N + 2, se o número de núcleos lógicos N for maior que 2; ou N + 1 se N for 1 ou 2. A razão para usar um número de tarefas ligeiramente maior que o número de núcleos lógicos é a de manter todos os processadores lógicos ocupados, mesmo se algumas tarefas estiverem realizando operações de E/S.

Observe que as chaves -j somente limitam as tarefas paralelas iniciadas por make ou ninja, mas cada tarefa ainda pode gerar os próprios processos ou camadas dela. Por exemplo, ld.gold usará várias camadas para vinculação, e alguns testes de pacotes podem gerar várias camadas para testar propriedades de segurança de camadas. Não existe uma maneira genérica de o sistema de construção saber o número de processos ou camadas gerados por uma tarefa. Portanto, geralmente nós não deveríamos considerar o valor passado com -j como um limite rígido do número de núcleos lógicos a serem usados. Leia-se “Use o Grupo de Controle do Linux para Limitar o Uso de Recursos” se quiser configurar tal limite tão rígido.

Geralmente o número de processos não deveria exceder muito o número de elementos de processamento suportados pela CPU. Para listar os processadores em teu sistema, emita: grep processor /proc/cpuinfo.

Em alguns casos, usar múltiplos processos possivelmente resulte em uma condição de 'corrida' onde o sucesso da construção depende da ordem dos comandos executados pelo aplicativo make. Por exemplo, se um executável precisar do Arquivo A e do Arquivo B, [então] tentar-se vincular o aplicativo antes que um dos componentes dependentes esteja disponível resultará em uma falha. Essa condição geralmente surge, pois o(a) desenvolvedor(a) do aplicativo não designou adequadamente todos os pré requisitos necessários para realizar uma etapa no Makefile.

Se isso ocorrer, a melhor maneira de se proceder é a de se voltar para uma construção de processador único. Adicionar -j1 a um comando make substituirá a configuração semelhante na variável de ambiente MAKEFLAGS.

[Importante]

Importante

Outro problema possivelmente ocorra com CPUs modernas, as quais tem um monte de núcleos. Cada trabalho iniciado consome memória e, se a soma da memória necessária para cada trabalho exceder da memória disponível, [então] você possivelmente encontre, ou uma interrupção de kernel Out of Memory (OOM), ou troca intensa, que retardará a construção além de limites razoáveis.

Algumas compilações com o g++ possivelmente consumam até 2,5 GB de memória, de forma que, para estar seguro(a), você deveria restringir o número de trabalhos a (Memória Total em GB)/2,5, ao menos para pacotes grandes, tais como o LLVM; o WebKitGtk; o QtWebEngine; ou o Libreoffice.

Use o Grupo de Controle do Linux para Limitar o Uso de Recursos

Às vezes queremos limitar o uso de recursos quando construímos um pacote. Por exemplo, quando temos 8 núcleos lógicos, podemos querer usar somente 6 núcleos para construir o pacote e reservar outros 2 núcleos para reproduzir um filme. O núcleo Linux fornece um recurso chamado grupos de controle (cgroup) para tal necessidade.

Habilite o grupo de controle na configuração do núcleo, em seguida reconstrua o núcleo e reinicialize se necessário:

General setup --->
  [*] Control Group support --->                                       [CGROUPS]
    [*] Memory controller                                                [MEMCG]
    [*] Cpuset controller                                              [CPUSETS]

Certifique-se de que Sudo-1.9.15p5 esteja instalado. Para executar make -j5 com os primeiros 4 núcleos lógicos e 8 GB de memória do sistema, emita:

bash -e << \EOF
  sudo mkdir /sys/fs/cgroup/$$
  sudo sh -c \
    "echo +memory +cpuset > /sys/fs/cgroup/cgroup.subtree_control"
  sudo sh -c \
    "echo 0-3 > /sys/fs/cgroup/$$/cpuset.cpus"
  sudo sh -c \
    "echo $(bc -e '8*2^30') > /sys/fs/cgroup/$$/memory.high"
  (
    sudo sh -c "echo $BASHPID > /sys/fs/cgroup/$$/cgroup.procs"
    exec make -j5
  )
  sudo rmdir /sys/fs/cgroup/$$
EOF

Com 8589934592 (a saída gerada de bc -e '8*2^30', 2^30 representa 230, ou seja, um Gigabyte) na entrada memory.high , um limite flexível de uso de memória está configurado. Se os processos no cgroup (make e todos os descendentes dele) usarem mais de 8 GB de memória do sistema no total, o núcleo irá desacelerar os processos e tentará recuperar a memória do sistema proveniente deles. Mas eles ainda podem usar mais de 8 GB de memória do sistema. Se você quiser definir um limite rígido, substitua memory.high com memory.max. Mas fazer isso causará a interrupção dos processos se 8 GB não forem suficientes para eles.

0-3 na entrada cpuset.cpus faz com que o núcleo execute somente os processos no cgroup nos núcleos lógicos com números 0, 1, 2 ou 3. Você possivelmente precise ajustar essa configuração baseada no mapeamento entre os núcleos lógicos e os núcleos físicos. Por exemplo, com uma CPU Intel Core i9-13900K, os núcleos lógicos 0, 2, 4, ..., 14 são mapeados para as primeiras camadas dos oito núcleos P físicos; os núcleos lógicos 1, 3, 5, ..., 15 são mapeados para as segundas camadas dos núcleos P físicos; e os núcleos lógicos 16, 17, ..., 31 são mapeados para os 16 núcleos E físicos. Portanto, se quisermos usar quatro camadas de quatro núcleos P, precisamos especificar 0,2,4,6 em vez de 0-3. Observe que os outros modelos de CPU podem usar um esquema de mapeamento diferente. Se você não tiver certeza acerca do mapeamento entre os núcleos lógicos e os núcleos físicos, execute o comando lscpu --extended que gerará IDs de núcleo lógico na coluna CPU e IDs de núcleo físico na coluna CORE.

Quando o comando nproc ou ninja executa em um cgroup, ele usará o número de núcleos lógicos atribuídos para o cgroup como a contagem de núcleos lógicos do sistema. Por exemplo, em um cgroup com núcleos lógicos 0-3 atribuídos, nproc imprimirá 4 e ninja executará 6 (4 + 2) tarefas simultaneamente se nenhuma configuração -j for fornecida explicitamente.

Leia-se o arquivo Documentation/admin-guide/cgroup-v2.rst na árvore do fonte do núcleo Linux para a explicação detalhada das entradas do pseudo sistema de arquivos cgroup2 referidas no comando.

Procedimentos Automatizados de Construção

Existem ocasiões onde automatizar a construção de um pacote pode vir a calhar. Todo mundo tem razões próprias para querer automatizar a construção e todo mundo faz isso de maneira própria. Criar Makefiles; scripts do Bash; scripts do Perl; ou, simplesmente, uma lista de comandos usados para recortar e colar, são apenas alguns dos métodos que você pode usar para automatizar a construção de pacotes do BLFS. Detalhar como e fornecer exemplos das muitas maneiras que você pode automatizar a construção de pacotes está além do escopo desta seção. Esta seção exporá você ao uso do redirecionamento de arquivo e do comando yes para ajudar a fornecer ideias acerca do como automatizar suas construções.

Redirecionamento de Arquivo para Automatizar a Entrada Gerada

Você achará ocasiões ao longo da sua jornada BLFS quando você se deparará com um pacote que tenha um comando solicitando informação. Essa informação poderia ser detalhes de configuração; um caminho de diretório; ou uma resposta a um acordo de licença. Isso pode apresentar um desafio para automatizar a construção desse pacote. Ocasionalmente, você será consultado(a) para diferentes informações em uma série de perguntas. Um método para automatizar esse tipo de cenário exige colocar as respostas desejadas em um arquivo e usar redirecionamento, de forma que o aplicativo use os dados no arquivo como as respostas para as perguntas.

Isso, efetivamente, faz com que a suíte de teste use as respostas no arquivo como a entrada gerada para as perguntas. Ocasionalmente você possivelmente termine fazendo um bocado de tentativa e erro para determinar o formato exato do seu arquivo de entrada gerada para algumas coisas, porém, tão logo determinado e documentado, você consegue usar isso para automatizar a construção do pacote.

Usando o yes para Automatizar a Entrada Gerada

Ocasionalmente você somente precisará fornecer uma resposta ou fornecer a mesma resposta para muitas solicitações. Para tais instâncias, o comando yes funciona realmente bem. O comando yes pode ser usado para fornecer uma resposta (a mesma) para uma ou mais instâncias de perguntas. Ele pode ser usado para simular o pressionamento apenas da tecla Enter; informar a tecla Y; ou informar uma sequência de caracteres de texto. Talvez a maneira mais fácil de mostrar o uso dele é em um exemplo.

Primeiro, crie um script curto do Bash informando os seguintes comandos:

cat > blfs-yes-test1 << "EOF"
#!/bin/bash

echo -n -e "\n\nPor favor, digite algo (ou nada) e pressione Enter ---> "

read A_STRING

if test "$A_STRING" = ""; then A_STRING="Apenas a tecla Enter foi pressionada"
else A_STRING="Você informou '$A_STRING'"
fi

echo -e "\n\n$A_STRING\n\n"
EOF
chmod 755 blfs-yes-test1

Agora execute o script emitindo ./blfs-yes-test1 a partir da linha de comando. Ele aguardará por uma resposta, que pode ser algo (ou nada) seguida pela tecla Enter. Depois de informar alguma coisa, o resultado será ecoado para a tela. Agora use o comando yes para automatizar a entrada de uma resposta:

yes | ./blfs-yes-test1

Perceba que canalizar o próprio yes para o script resulta em y sendo passada para o script. Agora tente com uma sequência de caracteres de texto:

yes 'Este é algum texto' | ./blfs-yes-test1

A sequência exata de caracteres foi usada como a resposta para o script. Finalmente, tente usando uma sequência de caracteres vazia (nula):

yes '' | ./blfs-yes-test1

Perceba que isso resulta na passagem apenas do pressionamento da tecla Enter para o script. Isso é útil para ocasiões quando a resposta padrão para a solicitação for suficiente. Essa sintaxe é usada nas instruções do Net-tools para aceitar todos os padrões para as muitas solicitações durante a etapa de configuração. Você possivelmente agora remova o script de teste, se desejado.

Redirecionamento de Arquivo para Automatizar a Saída Gerada

Para a finalidade de automatizar a construção de alguns pacotes, especialmente aqueles que exigem que você leia um acordo de licença em uma página por vez, exige-se usar um método que evite ter que pressionar uma tecla para exibir cada página. Redirecionar a saída gerada para um arquivo pode ser usado nessas instâncias para auxiliar com a automação. A seção anterior nesta página tocou na criação de arquivos de registro da saída gerada da construção. O método de redirecionamento mostrado lá usou o comando tee para redirecionar a saída gerada para um arquivo enquanto também exibia a saída gerada na tela. Aqui, a saída gerada somente será enviada para um arquivo.

Novamente, a maneira mais fácil para demonstrar a técnica é de mostrar um exemplo. Primeiro, emita o comando:

ls -l /usr/bin | less

Certamente, você será exigido(a) a visualizar a saída gerada uma página por vez, pois o filtro less foi usado. Agora tente o mesmo comando, porém, dessa vez, redirecione a saída gerada para um arquivo. O arquivo especial /dev/null pode ser usado em vez do nome de arquivo mostrado, porém você não terá arquivo de registro para examinar:

ls -l /usr/bin | less > redirect_test.log 2>&1

Perceba que, dessa vez, o comando imediatamente retornou ao prompt do shell sem ter que paginar ao longo da saída gerada. Você agora possivelmente remova o arquivo de registro.

O último exemplo usará o comando yes em combinação com o redirecionamento da saída gerada para desviar-se de ter que paginar ao longo da saída gerada e, então, fornecerá um y para uma solicitação. Essa técnica poderia ser usada em instâncias quando, de outra maneira, você teria que paginar ao longo da saída gerada de um arquivo (como um acordo de licença) e, então, responder à pergunta de você aceita o acima?. Para esse exemplo, outro conjunto curto de comandos sequenciais do Bash é exigido:

cat > blfs-yes-test2 << "EOF"
#!/bin/bash

ls -l /usr/bin | less

echo -n -e "\n\nVocê curtiu ler isso? (y,n) "

read A_STRING

if test "$A_STRING" = "y"; then A_STRING="Você informou a tecla 'y'"
else A_STRING="Você NÃO informou a tecla 'y'"
fi

echo -e "\n\n$A_STRING\n\n"
EOF
chmod 755 blfs-yes-test2

Esse script pode ser usado para simular um aplicativo que exige que você leia um acordo de licença, então responda apropriadamente que aceita o acordo antes do aplicativo instalar qualquer coisa. Primeiro, execute o script sem quaisquer técnicas de automação emitindo ./blfs-yes-test2.

Agora emita o seguinte comando que usa duas técnicas de automação, tornando-o adequado para uso em um script automatizado de construção:

yes | ./blfs-yes-test2 > blfs-yes-test2.log 2>&1

Se desejado, emita tail blfs-yes-test2.log para ver o final da saída gerada paginada e a confirmação de que y foi passada ao longo para o script. Tão logo satisfeito que ele funciona como deveria, você possivelmente remova o script e o arquivo de registro.

Finalmente, tenha em mente que existem muitas maneiras de automatizar e (ou) roteirizar os comandos de construção. Não existe maneira única correta para fazê-lo. Sua imaginação é o único limite.

Dependências

Para cada pacote descrito, o BLFS lista as dependências conhecidas. Essas são listadas sob vários títulos, cujo significado é como segue:

  • Exigida significa que o pacote alvo não pode ser construído corretamente sem que a dependência tenha sido instalada primeiro, exceto se a dependência for considerada de tempo de execução, o que significa que o pacote alvo pode ser construído, mas não pode funcionar sem ela.

    Observe que um pacote alvo pode começar a funcionar de muitas maneiras sutis: um arquivo de configuração instalado pode fazer o sistema init, o processo de segundo plano cron ou o processo de segundo plano de barramento executar um aplicativo automaticamente; outro pacote usando o pacote alvo como dependência pode executar um aplicativo oriundo do pacote alvo no sistema de construção; e as seções de configuração no livro BLFS também podem executar um aplicativo a partir de um pacote recém-instalado. Portanto, se estiver instalando o pacote alvo sem uma dependência Exigida (tempo de execução) instalada, você deveria instalar a dependência o mais rápido possível depois da instalação do pacote alvo.

  • Recomendada significa que o BLFS sugere fortemente que esse pacote seja instalado primeiro (exceto se for considerada tempo de execução, veja-se abaixo) para uma construção limpa e livre de problemas, que não terá problemas durante o processo de construção ou em tempo de execução. As instruções no livro assumem que esses pacotes estão instalados. Algumas mudanças ou soluções alternativas possivelmente sejam exigidas se esses pacotes não estiverem instalados. Se uma dependência recomendada for considerada tempo de execução, isso significa que o BLFS sugere fortemente que essa dependência seja instalada antes de usar o pacote, para obter funcionalidade completa.

  • Opcional significa que esse pacote pode ser instalado para funcionalidade adicional. Frequentemente, o BLFS descreverá a dependência para explicar a funcionalidade adicional que resultará. Algumas dependências opcionais são automaticamente selecionadas pelo pacote alvo se a dependência estiver instalada, enquanto outras também precisam de opções de configuração adicionais para serem habilitadas quando o pacote alvo for construído. Essas opções adicionais frequentemente estão documentadas no livro BLFS. Se uma dependência opcional for dita como tempo de execução, significa que você pode instalar a dependência depois de instalar o pacote alvo para suportar alguns recursos opcionais do pacote alvo se precisar desses recursos.

    Uma dependência opcional pode estar fora do BLFS. Se você precisar de uma tal dependência opcional externa para alguns recursos necessários, leia Indo Além do BLFS para as dicas gerais acerca de instalar um pacote fora do BLFS.

Usando os Fontes Mais Atuais de Pacote

Ocasionalmente você possivelmente se encontre em uma situação no livro onde um pacote não construirá ou não funcionará adequadamente. Apesar dos(as) Editores(as) tentarem garantir que cada pacote no livro construa e funcione adequadamente, ocasionalmente um pacote tenha sido negligenciado ou não foi testado com esta versão particular do BLFS.

Se você descobrir que um pacote não construirá ou não funcionará adequadamente, [então] você deveria ver se existe uma versão mais recente do pacote. Tipicamente isso significa você ir ao sítio da web do(a) mantenedor(a) e transferir o tarball mais recente e tentar construir o pacote. Se você não conseguir determinar o sítio da web do(a) mantenedor(a) olhando para os URLs de transferência, [então] use o Google e consulte o nome do pacote. Por exemplo, na barra de pesquisa do Google, digite: 'nome_do_pacote download' (omita as aspas) ou algo semelhante. Ocasionalmente, digitar: 'nome_do_pacote home page' resultará em você encontrar o sítio da web do(a) mantenedor(a).

Despojando Mais Uma Vez

No LFS, a remoção de símbolos de depuração e entradas desnecessárias na tabela de símbolos foi discutida algumas vezes. Ao construir pacotes BLFS, geralmente não existem instruções especiais que discutam a remoção novamente. A remoção pode ser feita durante a instalação de um pacote ou posteriormente.

Despojando enquanto se Instala um Pacote

Existem várias maneiras de se despojar executáveis instalados por um pacote. Elas dependem do sistema de construção usado (veja-se abaixo a seção acerca de sistemas de construção), de modo que somente algumas generalidades podem ser listadas aqui:

[Nota]

Nota

Os métodos a seguir que usam o recurso de um sistema de construção ("autotools", "meson" ou "cmake") não despojarão bibliotecas estáticas, se alguma estiver instalada. Felizmente não existem muitas bibliotecas estáticas no BLFS, e uma biblioteca estática sempre pode ser despojada com segurança executando strip --strip-unneeded nela manualmente.

  • Os pacotes que usam "Autotools" geralmente tem um alvo install-strip nos arquivos Makefile gerados deles. Portanto, instalar executáveis despojados é apenas uma questão de usar make install-strip em vez de make install.

  • Os pacotes que usam o sistema de construção do meson conseguem aceitar -D strip=true ao executar meson. Se tiver esquecido de adicionar essa opção executando o meson, você também consegue executar meson install --strip em vez de ninja install.

  • cmake gera alvos install/strip para ambos os geradores Unix Makefiles e Ninja (o padrão é Unix Makefiles no Linux). Portanto, basta executar make install/strip ou ninja install/strip em vez das contrapartes install.

  • A remoção (ou não geração) de símbolos de depuração também consegue ser obtida removendo-se as opções -g<alguma_coisa> em chamadas "C/C++". Como fazer isso é muito específico para cada pacote. E não remove entradas desnecessárias da tabela de símbolos. Portanto, não será explicado em detalhes aqui. Veja-se também abaixo os parágrafos acerca de otimização.

Despojando Executáveis Instalados

O utilitário strip muda arquivos no local, o que possivelmente quebre alguma coisa que os usem se estiverem carregados na memória. Observe que se um arquivo estiver em uso, mas recém removido do disco (ou seja, não sobrescrito nem modificado), isso não será um problema, pois o núcleo consegue usar arquivos deletados. Veja-se /proc/*/maps e é provável que você veja algumas entradas (deleted). O mv apenas remove o arquivo de destino a partir do diretório, mas não toca no conteúdo dele, de modo que satisfaça a condição para o núcleo usar o arquivo antigo (deletado). Porém, essa abordagem pode desanexar links rígidos em cópias duplicadas, causando um inchaço que, obviamente, é indesejado, pois estamos despojando para reduzir o tamanho do sistema. Se dois arquivos em um mesmo sistema de arquivos compartilharem o mesmo número de inode, eles serão links rígidos entre eles e deveríamos reconstruir o link. O conjunto de comandos sequenciais abaixo é apenas um exemplo. Ele deveria ser executado como o(a) usuário(a) root:

cat > /usr/sbin/strip-all.sh << "EOF"
#!/usr/bin/bash

if [ $EUID -ne 0 ]; then
  echo "Precisa ser root"
  exit 1
fi

last_fs_inode=
last_file=

{ find /usr/lib -type f -name '*.so*' ! -name '*dbg'
  find /usr/lib -type f -name '*.a'
  find /usr/{bin,sbin,libexec} -type f
} | xargs stat -c '%m %i %n' | sort | while read fs inode file; do
       if ! readelf -h $file >/dev/null 2>&1; then continue; fi
       if file $file | grep --quiet --invert-match 'not stripped'; then continue; fi

       if [ "$fs $inode" = "$last_fs_inode" ]; then
         ln -f $last_file $file;
         continue;
       fi

       cp --preserve $file    ${file}.tmp
       strip --strip-unneeded ${file}.tmp
       mv ${file}.tmp $file

       last_fs_inode="$fs $inode"
       last_file=$file
done
EOF
chmod 744 /usr/sbin/strip-all.sh

Se você instalar aplicativos em outros diretórios, como /opt ou /usr/local, você possivelmente queira despojar os arquivos lá também . Basta adicionar outros diretórios a escanear na lista composta de comandos find entre chaves.

Para mais informações acerca de despojamento, veja-se https://www.technovelty.org/linux/stripping-shared-libraries.html.

Trabalhando com diferentes sistemas de construção

Existem, agora, três sistemas de construção em uso comum para converter código fonte C ou C++ em aplicativos ou bibliotecas compilados e os detalhes deles (particularmente, descobrir acerca de opções disponíveis e os valores padrão delas) diferem. Possivelmente seja mais fácil entender os problemas causados por algumas escolhas (tipicamente, execução lenta; ou uso inesperado de, ou omissão de, otimizações) iniciando-se com as variáveis de ambiente CFLAGS, CXXFLAGS e LDFLAGS. Também existem alguns aplicativos que usam Rust.

A maioria dos(as) construtores(as) do LFS e do BLFS provavelmente está ciente dos básicos de CFLAGS e CXXFLAGS para alterar como um aplicativo é compilado. Tipicamente, alguma forma de otimização é usada pelos(as) desenvolvedores(as) de aplicativos (-O2 ou -O3), ocasionalmente com a criação de símbolos de depuração (-g), como padrões.

Se existirem sinalizadores contraditórios (por exemplo, múltiplos valores -O), o último valor será usado. Ocasionalmente, isso significa que os sinalizadores especificados em variáveis de ambiente serão escolhidos antes dos valores codificados rigidamente no Makefile, e, portanto, ignorados. Por exemplo, onde um(a) usuário(a) especificar -O2 e isso for seguido por -O3, a construção usará -O3.

Existem várias outras coisas que podem ser passadas em CFLAGS ou em CXXFLAGS, tais como permitir-se usar as extensões de conjunto de instruções disponíveis com uma microarquitetura específica (por exemplo, -march=amdfam10 ou -march=native) ajustar o código gerado para uma microarquitetura específica (por exemplo, -mtune=tigerlake ou -mtune=native; se -mtune= não for usada, a microarquitetura oriunda da configuração -march= será usada) ou especificar-se um padrão específico para C ou C++ (-std=c++17, por exemplo). Porém, uma coisa que agora veio à tona é que os(as) programadores(as) poderiam incluir asserções de depuração no código deles(as), esperando que sejam desabilitadas em lançamentos usando-se -D NDEBUG. Especificamente, se o Mesa-24.1.5 for construído com essas asserções habilitadas, algumas atividades, tais como o carregamento de níveis dos jogos, podem tomar tempos extremamente longos, mesmo em placas de vídeo de alta qualidade.

Autotools com Make

Essa combinação frequentemente é descrita como CMMI (configure; make; make install) e é usada aqui também para cobrir os poucos pacotes que tenham um conjunto de comandos sequenciais de configuração que não seja gerado por autotools.

Ocasionalmente, executar-se ./configure --help produzirá opções úteis acerca de chaves que poderiam ser usadas. Em outras ocasiões, depois de olhar para a saída gerada a partir do configure, você possivelmente precise olhar para os detalhes do script para descobrir pelo que ele estava procurando atualmente.

Muitos scripts de configuração escolherão quaisquer CFLAGS ou CXXFLAGS a partir do ambiente, porém os pacotes CMMI variam acerca do como esses serão misturados com quaisquer sinalizadores que, de outra maneira, seriam usados (variadamente: ignorados; usados para substituir a sugestão do(a) programador(a); usados antes da sugestão do(a) programador(a); ou usados depois da sugestão do(a) programador(a)).

Na maioria dos pacotes CMMI, executar-se make listará cada comando e o executará, intercalado com quaisquer avisos. Porém, alguns pacotes tentam ser silenciosos e mostram somente qual arquivo eles estão compilando ou vinculando em vez de mostrar a linha de comando. Se você precisar inspecionar o comando, seja por causa de um erro, seja apenas para ver quais opções e sinalizadores estão sendo usados, adicionar V=1 à invocação do make possivelmente ajude.

CMake

O CMake funciona de uma maneira muito diferente e ele tem duas estruturas de retaguarda que conseguem ser usadas no BLFS: make e ninja. A estrutura de retaguarda padrão é o make, porém o ninja pode ser mais rápido sobre pacotes grandes com múltiplos processadores. Para usar o ninja, especifique -G Ninja no comando cmake. Entretanto, existem alguns pacotes que criam erros fatais nos arquivos ninja deles, porém constroem com sucesso usando o padrão dos Makefiles do Unix.

A parte mais difícil do usar-se o CMake é saber quais opções você poderia desejar especificar. A única maneira de se obter uma lista do que o pacote conhece é a de executar cmake -LAH e olhar para a saída gerada para esta configuração padrão.

Talvez a coisa mais importante acerca do CMake é que ele tem uma variedade de valores CMAKE_BUILD_TYPE e esses afetam os sinalizadores. O padrão é o de que isso não seja configurado e nenhum sinalizador seja gerado. Quaisquer CFLAGS ou CXXFLAGS no ambiente serão usadas. Se o(a) programador(a) tiver codificado quaisquer asserções de depuração, essas estarão habilitadas, a menos que -D NDEBUG seja usado. Os seguintes valores CMAKE_BUILD_TYPE gerarão os sinalizadores mostrados e esses virão depois de quaisquer sinalizadores no ambiente e, portanto, terão precedência.

Valor Sinalizadores
Debug -g
Release -O3 -D NDEBUG
RelWithDebInfo -O2 -g -D NDEBUG
MinSizeRel -Os -D NDEBUG

O "CMake" tenta produzir construções silenciosas. Para ver os detalhes dos comandos que estão sendo executados, use make VERBOSE=1 ou ninja -v.

Por padrão, o "CMake" trata a instalação de arquivos diferentemente dos outros sistemas de construção: se um arquivo já existir e não for mais recente que um arquivo que o sobrescreveria, então o arquivo não será instalado. Isso possivelmente seja um problema se um(a) usuário(a) quiser registrar qual arquivo pertence a um pacote, seja usando LD_PRELOAD, ou listando arquivos mais recentes que um carimbo de tempo. O padrão pode ser mudado definindo-se a variável CMAKE_INSTALL_ALWAYS como um ("1") no ambiente, por exemplo, via export.

Meson

O Meson tem algumas semelhanças com o CMake, porém muitas diferenças. Para obter os detalhes das definições que você possivelmente queira mudar, você pode olhar para o meson_options.txt que normalmente está no diretório de nível de topo.

Se você já configurou o pacote executando o meson e, agora, deseja mudar uma ou mais configurações, [então] você ou pode remover o diretório de construção, recriá-lo e usar as opções alteradas; ou, dentro do diretório de construção, executar meson configure, por exemplo, para configurar uma opção:

meson configure -D <some_option>=true

Se você fizer isso, [então] o arquivo meson-private/cmd_line.txt mostrará os últimos comandos que foram usados.

O Meson fornece os seguintes valores de tipo de construção e os sinalizadores que eles habilitam vem depois de quaisquer sinalizadores fornecidos no ambiente e, portanto, tem precedência.

  • simples: nenhum sinalizador adicionado. Isso é para os(as) distribuidores(as) fornecerem os próprios CFLAGS, CXXFLAGS e LDFLAGS deles(as). Não existe razão óbvia para usar isso no BLFS.

  • depuração: -g - isso é o padrão, se nada for especificado, seja no meson.build, seja na linha de comando. Entretanto, resulta em binários grandes e lentos, de forma que nós deveríamos substitui-lo no BLFS.

  • depuração otimizada: -O2 -g : isso é o padrão, especificado no meson.build, de alguns pacotes.

  • lançamento: -O3 (ocasionalmente um pacote forçará -O2 aqui) - esse é o tipo de construção que usamos para a maioria dos pacotes com sistema de construção Meson no BLFS.

O sinalizador -D NDEBUG está implícito pelo tipo de construção de lançamento para alguns pacotes (por exemplo Mesa-24.1.5). Também pode ser fornecido explicitamente passando-se -D b_ndebug=true.

Para ver os detalhes dos comandos que estão sendo executados em um pacote usando o meson, use ninja -v.

Rustc e Cargo

A maioria dos aplicativos rustc lançados é fornecida como engradado (tarballs de fonte), que consultarão um servidor para verificar as versões atuais de dependências e, então, as baixarão conforme necessário. Esses pacotes são construídos usando-se cargo --release. Na teoria, você consegue manipular a RUSTFLAGS para mudar o nível de otimização (padrão para --release é 3, isto é, -Copt-level=3, semelhante a -03) ou para forçá-lo a construir para a máquina na qual está sendo compilado, usando -Ctarget-cpu=native; porém, na prática, isso parece não fazer uma diferença significante.

Se você estiver compilando um programa Rust independente (como um arquivo .rs desempacotado) executando rustc diretamente, você deveria especificar -O (a abreviatura de -Copt-level=2) ou -Copt-level=3, caso contrário ele fará uma compilação não otimizada e executará muito mais lento. Se estiver compilando o programa para depurá-lo, substitua as opções -O ou -Copt-level= por -g para produzir um programa não otimizado com informações de depuração.

Semelhante ao ninja, por padrão cargo usa todos os núcleos lógicos. Isso frequentemente pode ser contornado, seja exportando-se CARGO_BUILD_JOBS=<N> seja passando-se --jobs <N> para cargo. Para compilar o próprio rustc, especificar-se --jobs <N> para invocações do x.py (juntamente com a variável de ambiente CARGO_BUILD_JOBS=, que se parece com uma abordagem cinto e suspensórios, porém parece ser necessária) funciona na maioria. A exceção é a de executar-se os testes quando construir-se o rustc; alguns deles, ainda assim, usarão todas as CPUs online, pelo menos desde o rustc-1.42.0.

Otimizando a construção

Muitas pessoas preferirão otimizar compilações como acharem melhor, fornecendo CFLAGS ou CXXFLAGS. Para uma introdução às opções disponíveis com o gcc e com o g++, veja-se https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Optimize-Options.html. O mesmo conteúdo também pode ser encontrado em info gcc.

Alguns pacotes são padronizados como -O2 -g, outros como -O3 -g, e se CFLAGS ou CXXFLAGS forem fornecidas, elas podem ser adicionadas aos padrões do pacote, substituir os padrões do pacote ou até mesmo serem ignoradas. Existem detalhes acerca de alguns pacotes de área de trabalho que estavam mais atualizados em abril de 2019 em https://www.linuxfromscratch.org/~ken/tuning/ - em particular, README.txt, tuning-1-packages-and-notes.txt e tuning-notes-2B.txt. A coisa específica a lembrar é que se quiser experimentar alguns dos sinalizadores mais interessantes, você possivelmente precise forçar construções detalhadas para confirmar o que está sendo usado.

Claramente, se estiver otimizando teu próprio aplicativo, você pode gastar tempo para perfilá-lo e, talvez, recodificar algo dele, se ele estiver lento demais. Porém, para construir um sistema inteiro, essa abordagem é impraticável. No geral, -O3 geralmente produz aplicativos mais rápidos que -O2. Especificar-se -march=native também é benéfico, porém significa que você não pode mover os binários para uma máquina incompatível - isso também pode se aplicar a máquinas mais novas, não apenas às máquinas mais antigas. Por exemplo, os aplicativos compilados para amdfam10 executam em Phenoms antigos; Kaveris; e Ryzens; porém, os aplicativos compilados para um Kaveri não executarão em um Ryzen, pois certos códigos de operação não estão presentes. Similarmente, se você construir para um Haswell, nem tudo executará em um SandyBridge.

[Nota]

Nota

Atente-se que o nome de uma configuração -march nem sempre corresponde à linha de base da microarquitetura com o mesmo nome. Por exemplo, os processadores Intel Celeron baseados em Skylake não suportam AVX, mas -march=skylake assume AVX e até mesmo AVX2.

Quando uma biblioteca compartilhada é construída pelo GCC, um recurso chamado interposição semântica é habilitado por padrão. Quando a biblioteca compartilhada se refere a um nome de símbolo com ligação externa e visibilidade padrão, se o símbolo existir tanto na biblioteca compartilhada quanto no executável principal, a interposição semântica garante que o símbolo no executável principal sempre seja usado. Esse recurso foi inventado na tentativa de tornar o comportamento de vincular uma biblioteca compartilhada e vincular uma biblioteca estática o mais semelhante possível. Hoje, somente um pequeno número de pacotes ainda depende da interposição semântica, mas o recurso ainda está ativado por padrão do GCC, fazendo com que muitas otimizações sejam desabilitadas para bibliotecas compartilhadas porque entram em conflito com a interposição semântica. A opção -fno-semantic-interposition pode ser passada para gcc ou g++ para desabilitar a interposição semântica e habilitar mais otimizações para bibliotecas compartilhadas. Essa opção é usada como padrão de alguns pacotes (por exemplo Python-3.12.5) e também é o padrão do Clang.

Existem também várias outras opções que algumas pessoas alegam que são benéficas. Na pior das hipóteses, você consegue recompilar e testar e, então, descobrir que, em seu uso, as opções não fornecem um benefício.

Se construir módulos Perl ou Python, em geral as CFLAGS e CXXFLAGS usadas são aquelas que foram usadas por esses pacotes ancestrais.

Para LDFLAGS, existem três opções que podem ser usadas para otimização. Elas são bastante seguras de usar e o sistema de construção de alguns pacotes usa algumas dessas opções como padrão.

Com -Wl,-O1, o vinculador otimizará a tabela de resumo para acelerar a vinculação dinâmica. Observe que -Wl,-O1 não tem nenhuma relação com o sinalizador de otimização do compilador -O1.

Com -Wl,--as-needed, o vinculador desconsiderará opções -lfoo desnecessárias da linha de comando, ou seja, a biblioteca compartilhada libfoo só será vinculada se um símbolo em libfoo realmente estiver referenciado pelo executável ou biblioteca compartilhada sendo vinculado. Às vezes, isso pode atenuar os problemas de dependências excessivas de bibliotecas compartilhadas causados pela libtool.

Com -Wl,-z,pack-relative-relocs, o vinculador gera uma forma mais compactada das entradas relativas de realocação para PIEs e bibliotecas compartilhadas. Ele reduz o tamanho do PIE vinculado ou da biblioteca compartilhada e acelera o carregamento do PIE ou da biblioteca compartilhada.

O prefixo -Wl, é necessário porque, apesar da variável ser chamada LDFLAGS, o conteúdo dela é na verdade passado para o gcc (ou g++, clang, etc.) durante o estágio de ligação, não passado diretamente para o ld.

Opções para fortalecer a construção

Mesmo em sistemas de área de trabalho, existe ainda um monte de vulnerabilidades exploráveis. Para muitas dessas, o ataque vem via javascript em um navegador. Frequentemente, uma série de vulnerabilidades é usada para ganhar acesso a dados (ou, às vezes, para pwn, isto é, dominar, a máquina e instalar rootkits). A maioria das distribuições comerciais aplicará várias medidas de fortalecimento.

No passado, existia o LFS Reforçado, onde o gcc (uma versão muito mais antiga) era forçado a usar o reforçamento (com opções para desativar parte dele na base do por pacote). Os livros atuais do LFS e BLFS estão levando adiante uma parte do espírito dele ao habilitar PIE (-fPIE -pie) e SSP (-fstack-protector-strong) como padrões para GCC e clang. E os lincadores (ld.bfd e ld.gold) também habilitaram -Wl,-z,relro, o que torna uma parte da Global Offset Table (GOT) imutável, por padrão desde Binutils 2.27. O que está sendo abordado aqui é diferente - primeiro você tem que ter certeza de que o pacote está realmente usando teus sinalizadores adicionados e não os substituindo-os.

Para opções de reforço que são razoavelmente baratas, existe alguma discussão no link "ajuste" acima (ocasionalmente, uma ou mais dessas opções podem ser inadequadas para um pacote). Essas opções são -D _FORTIFY_SOURCE=2 (ou -D _FORTIFY_SOURCE=3 que é mais seguro, mas com maior sobrecarga de desempenho) e (para C++) -D _GLIBCXX_ASSERTIONS. Nas máquinas modernas, isso deveria ter somente um pequeno impacto na rapidez com que as coisas executam e, muitas vezes, não serão perceptíveis.

As principais distribuições usam muito mais, como:

  • -Wl,-z,now: desabilita vinculação preguiçosa para aprimorar -Wl,-z,relro, de forma que todo o GOT possa se tornar imutável.

  • -fstack-clash-protection: impede o(a) atacante de usar um deslocamento grande o suficiente e não verificado adequadamente para pular a página de proteção de pilha colocada pelo núcleo e o canário de pilha colocado por -fstack-protector=strong, e modificar a pilha a partir de um endereço de pilha, ou vice-versa.

  • -ftrivial-auto-var-init=zero: inicializa algumas variáveis preenchendo zero bytes se elas não forem inicializadas por outros meios.

  • -fcf-protection=full: utiliza a tecnologia CET da Intel e da AMD para limitar os endereços alvo das instruções de transferência de fluxo de controle. Para torná-lo realmente eficaz para um pacote, todos os pacotes que fornecem uma biblioteca compartilhada para o pacote usar precisam ser construídos com essa opção, bem como o próprio pacote, a Glibc precisa ser configurada com a opção --enable-cet habilitada, e o sistema precisa executar no Intel Tiger Lake ou mais recente, ou no AMD Zen 3 ou mais recente. Se o critério não for atendido, o programa compilado com essa opção ainda executará, mas não realmente protegido pelo CET.

No GCC 14, a opção -fhardened é uma abreviação para habilitar todas as opções de reforçamento mencionadas acima. Ela configura -D _FORTIFY_SOURCE=3 em vez de -D _FORTIFY_SOURCE=2.

Você também pode encontrar o assim chamado retpoline de espaço de usuário(a) (-mindirect-branch=thunk etc.), que é o equivalente das mitigações de espectro aplicadas para o núcleo Linux no final de 2018. As mitigações do núcleo causaram muitas reclamações acerca da perda de desempenho. Se tiver um servidor de produção, você pode desejar considerar testar isso, junto com as outras opções disponíveis, para ver se o desempenho ainda é suficiente.

Embora o gcc tenha muitas opções de fortalecimento, os pontos fortes do clang/LLVM estão em outro lugar. Algumas opções que o gcc fornece são ditas serem menos efetivas no clang/LLVM.