3 pontos por GN⁺ 2024-06-10 | 2 comentários | Compartilhar no WhatsApp
  • libtree é uma ferramenta que transforma a saída de ldd em uma árvore e explica como bibliotecas compartilhadas foram encontradas ou por que não podem ser localizadas
  • Na saída padrão, algumas dependências padrão ficam ocultas, e com -v, -vv, -vvv é possível ver gradualmente as bibliotecas ocultas e até as dependências de bibliotecas já encontradas
  • --path ou -p mostra o caminho em vez do soname, e --max-depth pode limitar a profundidade da busca recursiva
  • A instalação é possível por meio de binários pré-compilados v3.1.1, Fedora/RHEL/CentOS, Ubuntu 22.04+ e GNU Guix
  • Para compilar a partir do código-fonte, é necessário um compilador C com suporte a C99, e ao usar make, LDFLAGS=-static é recomendado

O que o libtree faz

  • libtree é uma ferramenta que transforma ldd em uma árvore
  • Explica como bibliotecas compartilhadas são encontradas ou por que sua localização não pode ser determinada
  • O README inclui uma captura de tela em doc/screenshot.png

Opções de saída

  • Na saída padrão, algumas dependências padrão não são exibidas
  • A saída mais detalhada é controlada por opções de verbosidade
    • libtree -v: mostra bibliotecas que normalmente são ignoradas
    • libtree -vv: também mostra as dependências de bibliotecas que normalmente são ignoradas
    • libtree -vvv: também mostra as dependências de bibliotecas já encontradas
  • A flag --path ou -p mostra o caminho em vez do soname
    • Exemplo: libtree -p $(which tar)
  • --max-depth limita a profundidade da recursão

Como instalar

Compilando a partir do código-fonte

  • libtree requer um compilador C com suporte a C99
  • O procedimento básico de compilação é clonar o repositório e depois executar make
  • Ao usar make, LDFLAGS=-static é recomendado
  • O README também fornece, em uma seção recolhível separada, um comando de instalação rápida insegura que baixa libtree.c com curl e o compila

2 comentários

 
GN⁺ 2024-06-10
Opiniões no Hacker News
  • Esta ferramenta também replica o comportamento inesperado do ldd de acabar executando de fato parte da biblioteca que está sendo inspecionada?
    https://catonmat.net/ldd-arbitrary-code-execution

    • Recentemente, versões do ldd com aproximadamente mais de 5 anos não executam o binário alvo
      Referência: https://manpages.debian.org/unstable/manpages/ldd.1.en.html#...
    • Dei uma olhada rápida no código pelo celular e parece que ele não faz isso
      Na verdade, parece fazer o parse direto do arquivo ELF e também analisar recursivamente as dependências, o que é bem legal
    • Já fiz algo vagamente parecido para Python, mas nunca consegui contornar esse problema https://github.com/google/importlab/issues/69
    • Se usar objdump, ele imprime os dados exatamente como codificados no arquivo ELF
      Inclui até a lista de bibliotecas que o vdso vai procurar
  • Existe uma ferramenta parecida chamada lddtree
    https://github.com/gentoo/pax-utils/tree/master

  • Ter que rastrear recursivamente dependências faltantes com ldd é bem tedioso
    Então isso parece uma boa melhoria, e pretendo usar da próxima vez que encontrar um not found ambíguo

  • Basicamente, parece uma versão Linux CLI do depends.exe

    • Essa ferramenta específica não funciona mais muito bem nas versões modernas do Windows
      É melhor usar https://github.com/lucasg/Dependencies. Ela também não está totalmente atualizada, mas…
      Se você instalou o Visual Studio e selecionou x64/x86 build tools (latest) no instalador, executar dumpbin /dependents no VS Developer Command Prompt ainda é a opção mais confiável
    • Sim, foi exatamente isso que pensei
      [1] https://www.dependencywalker.com/
  • Para quem está curioso sobre o que as cores significam, não encontrei isso na manpage/README
    Magenta: está na lista de exclusão, exibido apenas com -v[v[v]]
    Azul: item já visto antes, para encontrar dependências que aparecem várias vezes

  • O que significa “por que uma biblioteca foi encontrada ou não”? Ou ela está em LD_LIBRARY_PATH, ou não está, certo?
    Só pela captura de tela, não entendi bem a que isso se refere

    • Não sei exatamente no contexto desta ferramenta, mas a busca de bibliotecas é muito mais complexa do que uma única variável de ambiente
      Há vários mecanismos diferentes para buscar diretórios, como caminhos de busca do sistema, runpath, rpath, LD_LIBRARY_PATH etc.
      Bibliotecas normalmente são vinculadas por um nome curto, como foo.so, mas também podem ser vinculadas dinamicamente pelo caminho completo da biblioteca
      Além disso, em geral é muito melhor evitar configurar LD_LIBRARY_PATH quando possível. Nem sempre é possível, mas, ao configurá-lo, ele vai para o topo da prioridade de busca em todas as execuções. Mesmo que algo tenha sido vinculado dinamicamente pelo caminho completo da biblioteca, LD_LIBRARY_PATH tem precedência e acaba achatando completamente o mecanismo de busca
    • Uma biblioteca não precisa estar em LD_LIBRARY_PATH para ser encontrada
      A ideia do título é que o libtree facilita encontrar o caminho de um executável até todas as suas dependências diretas e indiretas. Um dos usos disso é ajudar a entender problemas de dependências ausentes
      Na prática, se você usa um sistema de pacotes, provavelmente não verá dependências faltando com frequência, então é mais provável que use o libtree por outros motivos
    • Não existe apenas “estar ou não em LD_LIBRARY_PATH”; há também o RPATH, avaliado no momento do carregamento para cada biblioteca
      O ponto maior é que dependências formam um grafo, e isso pode ser exibido como uma árvore. É útil saber por causa de qual biblioteca que precisava de outra uma determinada biblioteca não foi encontrada
    • Bibliotecas não são encontradas apenas via LD_LIBRARY_PATH. O loader também considera várias outras fontes para encontrá-las
      Em uma configuração normal, isso é uma combinação dos campos ELF usuais de cada binário carregado com outros caminhos conhecidos pelo loader
      Em sistemas com várias versões da mesma biblioteca ou várias bibliotecas com o mesmo nome, depender de LD_LIBRARY_PATH pode ser uma visão muito curta. Para cada binário, o loader procura os caminhos de LD_LIBRARY_PATH em ordem e escolhe a primeira biblioteca que corresponde. Se você não tiver definido um caminho de prioridade maior por outro mecanismo, aquela biblioteca pode não ser a que você realmente quer, levando a erros inesperados em runtime
      Uma abordagem melhor é definir o RPATH do binário para o local onde estão as bibliotecas de que ele precisa
      Se o ambiente e a configuração de RPATH não forem consistentes, você pode até carregar várias versões da mesma biblioteca ao mesmo tempo. Esta ferramenta ajuda a descobrir se há problema e por quê
    • No mínimo há RPATH, RUNPATH e LD_LIBRARY_PATH
      Como esta ferramenta é baseada em ldd, ela provavelmente também interpreta DYLD_LIBRARY_PATH, DYLD_FALLBACK_FRAMEWORK_PATH, DYLD_FALLBACK_LIBRARY_PATH, @executable_path, @loader_path e @rpath
  • Muito útil. Normalmente acabo lendo as seções com readelf para descobrir quais são os requisitos reais

  • LD_DEBUG=libs não é suficiente?

    • Isso é uma flag de debug do loader, não uma avaliação estática das dependências de bibliotecas, então não é exatamente a mesma coisa
  • Não sei se é bug, mas no exemplo do vim, ldd e libtree mostram bibliotecas diferentes
    Por exemplo, linux-vdso.so.1 aparece no topo da lista no ldd, mas não aparece em lugar nenhum no libtree

    • linux-vdso.so.1 não é uma biblioteca real que você possa encontrar em algum lugar no sistema de arquivos e também não é referenciada dentro do arquivo ELF, então o libtree não consegue saber dela
      Em vez disso, o kernel a mapeia automaticamente no espaço de endereçamento de um processo recém-iniciado. É uma otimização para evitar o overhead de chamadas de sistema em funções como gettimeofday. Referência: https://man7.org/linux/man-pages/man7/vdso.7.html
  • Certa vez fiz um scriptzinho bagunçado que rodava ldd recursivamente para descobrir o que eu precisava incluir para executar binários de código fechado no NixOS
    Se eu tiver que fazer isso de novo, pretendo experimentar esta ferramenta

 
kayws426 2024-06-11

Parece muito bom!!