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 Makefile
s e código C; porém, se em dúvida, [então]
inicie a partir de uma árvore limpa.
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
.
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
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
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:
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-keyarquivo
.sigarquivo
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.
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.
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
.
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.
À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.
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 Makefile
s; 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.
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.
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.
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.
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.
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).
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.
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:
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.
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.
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.
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.
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.
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.
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=
seja passando-se
<N>
--jobs
para cargo. Para compilar o próprio
rustc, especificar-se <N>
--jobs
para invocações do
x.py (juntamente com
a variável de ambiente <N>
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.
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.
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 -l
desnecessárias da linha
de comando, ou seja, a biblioteca compartilhada foo
lib
só será vinculada se um
símbolo em foo
lib
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.
foo
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.
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.