03 - Dependências e Arquivo modules.dep
video: https://youtu.be/... no frontmatter da nota.1. A Arquitetura da Resolução Simbólica no Ring 0
A modularidade do Kernel Linux não se baseia em blocos isolados e monolíticos, mas em uma rede densamente interconectada de bibliotecas e provedores de serviços operando em Ring 0. Assim como aplicações no espaço do usuário dependem de bibliotecas compartilhadas estáticas ou dinâmicas (.so e a glibc), os Módulos do Kernel (.ko) dependem uns dos outros através da exportação e importação de símbolos (funções e variáveis globais em C).
Do ponto de vista estrutural, um arquivo .ko é um arquivo objeto ELF realocável (tipo ET_REL). Durante sua compilação, se o código fonte invocar uma função que não está definida internamente (por exemplo, um driver de rede chamando register_netdev(), que pertence ao subsistema central de rede), o compilador deixa uma referência em branco na tabela de símbolos do ELF, classificando-a como um símbolo não resolvido (unresolved symbol).
Para que o módulo seja inserido na memória viva do kernel com sucesso, todo símbolo não resolvido deve apontar para um endereço de memória real. O kernel expõe suas próprias funções vitais através de macros no código C, primariamente EXPORT_SYMBOL() e EXPORT_SYMBOL_GPL(). Se um módulo "A" exporta uma função que o módulo "B" invoca, estabelece-se uma relação de dependência arquitetural estrita: o módulo "A" deve obrigatoriamente ser carregado na memória e alocado antes do módulo "B".
2. A Ferramenta depmod e a Análise Estática de Grafos
Delegar a um administrador de sistemas a tarefa de rastrear manualmente qual driver USB genérico um adaptador Wi-Fi específico exige para funcionar seria inviável. Para solucionar isso, o ecossistema Linux delega o mapeamento topológico à ferramenta depmod (Dependency Module).
O depmod não carrega módulos na memória; ele é uma ferramenta de análise estática de binários. Ao ser executado, ele varre todo o diretório de módulos da versão atual do kernel (tipicamente /lib/modules/$(uname -r)/), abrindo cada arquivo .ko e lendo rigorosamente suas seções ELF (.symtab, .strtab e .modinfo).
A operação interna do depmod obedece à seguinte sequência algorítmica:
-
Indexação de Símbolos Exportados: Ele cria uma tabela hash global em memória contendo todos os símbolos fornecidos pelo próprio núcleo monolítico (via leitura indireta ou
System.map) e por todos os módulos que declaramEXPORT_SYMBOL. -
Resolução de Símbolos Não Resolvidos: Em seguida, itera sobre cada módulo e verifica suas requisições de símbolos externos. Para cada requisição, ele busca na tabela hash qual módulo provedor atende àquela chamada.
-
Construção do Grafo Acíclico Dirigido (DAG): Com as relações mapeadas, o
depmodconstrói um DAG na memória, representando a ordem cronológica obrigatória de carregamento. Se detectar dependências circulares (Módulo A depende de B, que depende de A), a ferramenta acusa uma violação de design irrecuperável e falha a construção da árvore.
3. A Anatomia do Arquivo modules.dep
O resultado primário legível por humanos dessa análise profunda é o arquivo /lib/modules/$(uname -r)/modules.dep. Este arquivo de texto plano atua como o banco de dados oficial de relacionamentos de dependência para a árvore do kernel em questão.
A sintaxe estrutural do arquivo é espartana e orientada a dicionário. Cada linha define o caminho relativo de um módulo alvo, seguido por dois pontos (:), e então a lista separada por espaços de todos os módulos dos quais ele depende.
Exemplo de estrutura interna:
Plaintext
kernel/drivers/net/wireless/intel/iwlwifi/iwlwifi.ko: kernel/net/mac80211/mac80211.ko kernel/net/wireless/cfg80211.ko
kernel/net/mac80211/mac80211.ko: kernel/net/wireless/cfg80211.ko
kernel/net/wireless/cfg80211.ko:
Neste exemplo hierárquico:
-
O driver primário de hardware (
iwlwifi.ko) depende tanto do framework MAC 802.11 quanto do subsistema central de configuração Wi-Fi (cfg80211.ko). -
O framework MAC 802.11 depende do subsistema de configuração.
-
O subsistema de configuração não possui dependências listadas (ou seja, depende exclusivamente de funções embutidas estaticamente no
vmlinuxprincipal).
Quando ferramentas de alto nível (como o modprobe) são instruídas a carregar o iwlwifi, elas leem o modules.dep, resolvem a cadeia de trás para frente e invocam o carregamento sequencial de cfg80211, mac80211 e, finalmente, iwlwifi.
4. Otimização Arquitetural: Arquivos Binários Indexados (.bin)
Embora o arquivo modules.dep em texto plano seja excelente para depuração manual, o parsing (análise léxica de strings) de um arquivo de texto de vários megabytes durante o processo crítico de boot (no initramfs, onde ciclos de CPU atrasam a inicialização da máquina) gera um overhead inaceitável.
Para solucionar este gargalo, o depmod contemporâneo (gerenciado pelo projeto kmod) não gera apenas o arquivo de texto, mas gera paralelamente um conjunto de matrizes binárias altamente otimizadas:
-
modules.dep.bin: A versão codificada em binário puro domodules.dep. A bibliotecalibkmodmapeia este arquivo diretamente para a memória virtual através da syscallmmap(). Isso permite pesquisas no formato de árvore de busca (search tree e trie) instantâneas em complexidade $O(1)$ ou $O(\log N)$, pulando o custo computacional de conversão de strings. -
modules.symbols.bin: Um índice reverso que mapeia diretamente o nome de um símbolo exportado (ex:pci_request_regions) para o caminho do arquivo.koque o contém. -
modules.alias.bin: O banco de dados binário que relaciona assinaturas de identificação de barramentos físicos (como IDs de vendor e device da especificação PCIe ou USB) aos respectivos módulos que fornecem os drivers.
Quando o sistema operacional inicializa, o utilitário modprobe ignora sumariamente o arquivo de texto plano e consome estritamente essas variantes .bin.
5. O Ciclo de Vida da Geração de Dependências
O banco de dados de dependências não é algo intrínseco à compilação individual de um módulo; ele é uma representação fotográfica do ecossistema de módulos inteiro em um dado momento. Portanto, a execução do depmod precisa ser perfeitamente orquestrada com os eventos de manutenção do sistema.
Geração em Tempo de Compilação
Durante o processo de compilação de um novo Kernel Linux a partir do código-fonte, ao invocar o alvo make modules_install, a cadeia de ferramentas de compilação executa o depmod -a (All) como sua etapa final. Ele garante que, antes mesmo do primeiro boot com o novo kernel, a árvore de dependências esteja selada e presente no disco.
Geração via DKMS e Gerenciadores de Pacotes
Em servidores de produção, a compilação dinâmica é frequente através do DKMS (Dynamic Kernel Module Support). Quando o administrador instala um driver proprietário fora da árvore principal (como drivers da NVIDIA ou módulos do ZFS), o DKMS compila o novo .ko e o insere em /lib/modules/. Imediatamente após a cópia, o script do pacote deve obrigatoriamente invocar depmod -a. Se o administrador esquecer de atualizar o índice após copiar um .ko manualmente, o módulo existirá no disco, mas o modprobe será fatalmente cego para sua existência e para suas dependências.
O Arquivo modules.builtin
Por fim, é crucial entender que nem todo código modular vira um arquivo .ko. Funções compiladas estaticamente para dentro do kernel principal (via CONFIG_*=y ao invés de =m) não precisam de resolução de dependência externa, pois seus símbolos já estão no binário mestre. O depmod gera o arquivo passivo modules.builtin que cataloga esses módulos para que ferramentas do espaço do usuário saibam que a ausência de um arquivo .ko para determinado driver não significa sua inexistência, mas sim que sua funcionalidade já está inseparavelmente fundida ao núcleo em Ring 0.