3 points par GN⁺ 2024-06-10 | 2 commentaires | Partager sur WhatsApp
  • libtree est un outil qui transforme la sortie de ldd en arbre et explique comment les bibliothèques partagées sont trouvées, ou pourquoi elles sont introuvables
  • La sortie par défaut masque certaines dépendances standard ; avec -v, -vv et -vvv, on peut afficher progressivement les bibliothèques masquées ainsi que les dépendances des bibliothèques déjà rencontrées
  • --path ou -p affiche les chemins au lieu des sonames, et --max-depth permet de limiter la profondeur de l’exploration récursive
  • L’installation est possible via les binaires précompilés v3.1.1, Fedora/RHEL/CentOS, Ubuntu 22.04+ et GNU Guix
  • La compilation depuis les sources nécessite un compilateur C compatible C99 ; avec make, l’utilisation de LDFLAGS=-static est recommandée

Ce que fait libtree

  • libtree est un outil qui transforme ldd en une représentation arborescente
  • Il explique comment les bibliothèques partagées sont trouvées, ou pourquoi leur emplacement ne peut pas être déterminé
  • Le README inclut une capture d’écran doc/screenshot.png

Options de sortie

  • La sortie par défaut n’affiche pas certaines dépendances standard
  • Une sortie plus détaillée se contrôle avec les options de verbosité
    • libtree -v : affiche les bibliothèques ignorées par défaut
    • libtree -vv : affiche aussi les dépendances des bibliothèques ignorées par défaut
    • libtree -vvv : affiche aussi les dépendances des bibliothèques déjà rencontrées
  • L’option --path ou -p affiche les chemins au lieu des sonames
    • Exemple : libtree -p $(which tar)
  • --max-depth limite la profondeur de récursion

Méthodes d’installation

Compiler depuis les sources

  • libtree nécessite un compilateur C compatible C99
  • La procédure de compilation de base consiste à cloner le dépôt puis à exécuter make
  • Avec make, l’utilisation de LDFLAGS=-static est recommandée
  • Le README propose aussi, dans une section repliable séparée, une commande d’installation rapide non sécurisée qui récupère libtree.c avec curl puis le compile

2 commentaires

 
GN⁺ 2024-06-10
Commentaires sur Hacker News
  • Cet outil reprend-il lui aussi le comportement inattendu de ldd, qui finit par exécuter une partie des bibliothèques inspectées ?
    https://catonmat.net/ldd-arbitrary-code-execution

    • Récemment, les versions de ldd vieilles d’environ 5 ans ou plus n’exécutent plus le binaire cible
      Référence : https://manpages.debian.org/unstable/manpages/ldd.1.en.html#...
    • J’ai parcouru rapidement le code sur mobile, et on dirait que non
      En pratique, il semble parser directement les fichiers ELF et parser aussi les dépendances récursivement, ce qui est plutôt élégant
    • J’avais déjà bricolé quelque chose de vaguement similaire pour Python, mais je n’ai jamais réussi à contourner complètement ce problème https://github.com/google/importlab/issues/69
    • Avec objdump, on peut afficher les données exactement telles qu’elles sont encodées dans le fichier ELF
      Y compris la liste des bibliothèques que vdso ira chercher
  • Il existe un outil similaire, lddtree
    https://github.com/gentoo/pax-utils/tree/master

  • Devoir suivre récursivement les dépendances manquantes avec ldd est assez pénible
    Donc ça ressemble à une bonne amélioration, et je compte l’essayer la prochaine fois que je tomberai sur un not found ambigu

  • En gros, c’est la version Linux CLI de depends.exe

    • Cet outil précis ne fonctionne plus très bien sur les versions récentes de Windows
      Mieux vaut utiliser https://github.com/lucasg/Dependencies à la place. Ce n’est pas totalement à jour non plus, mais…
      Si vous avez installé Visual Studio et choisi x64/x86 build tools (latest) dans l’installateur, lancer dumpbin /dependents depuis le VS Developer Command Prompt reste l’option la plus fiable
    • Oui, c’est exactement à ça que j’ai pensé aussi
      [1] https://www.dependencywalker.com/
  • Pour ceux qui se demandent à quoi correspondent les couleurs, je n’ai rien trouvé dans la manpage ni le README
    Magenta : présent dans la liste d’exclusion, affiché uniquement avec -v[v[v]]
    Bleu : élément déjà vu, ce qui permet de repérer les dépendances qui apparaissent plusieurs fois

  • Que signifie exactement « pourquoi une bibliothèque est trouvée ou non » ? Elle est soit dans LD_LIBRARY_PATH, soit elle n’y est pas, non ?
    Rien qu’avec la capture d’écran, je ne vois pas bien ce que ça veut dire

    • Je ne connais pas précisément le contexte de cet outil, mais la recherche de bibliothèques est bien plus complexe qu’une simple variable d’environnement
      Il existe plusieurs mécanismes de recherche de répertoires : chemin système, runpath, rpath, LD_LIBRARY_PATH, etc.
      Les bibliothèques sont généralement liées sous un nom court comme foo.so, mais elles peuvent aussi être liées dynamiquement via leur chemin complet
      D’ailleurs, il vaut généralement mieux éviter de définir LD_LIBRARY_PATH quand on le peut. Ce n’est pas toujours possible, mais une fois défini, il passe tout en haut de la priorité de recherche pour chaque exécution. Même si quelque chose est lié dynamiquement avec le chemin complet d’une bibliothèque, LD_LIBRARY_PATH passe avant et aplatit complètement le mécanisme de recherche
    • Pour qu’une bibliothèque soit trouvée, elle n’a pas besoin d’être dans LD_LIBRARY_PATH
      L’idée du titre, c’est que libtree permet de retrouver facilement le chemin depuis l’exécutable jusqu’à toutes les dépendances directes et indirectes. L’un des usages est d’aider à comprendre les problèmes de dépendances manquantes
      En pratique, si vous utilisez un système de paquets, il est peu probable qu’il manque des dépendances, donc vous utiliserez sans doute libtree pour d’autres raisons
    • Il n’y a pas juste le cas « dans LD_LIBRARY_PATH ou pas » : il y a aussi RPATH, évalué au chargement pour chaque bibliothèque
      Le point plus général, c’est que les dépendances forment un graphe, qu’on peut représenter comme un arbre. Il est utile de savoir quelle bibliothèque a entraîné l’impossibilité d’en trouver une autre
    • Les bibliothèques ne sont pas trouvées uniquement via LD_LIBRARY_PATH. Le chargeur prend aussi en compte plusieurs autres sources
      Dans une configuration normale, cela repose sur une combinaison des champs ELF habituels de chaque binaire chargé et d’autres chemins connus du chargeur
      Sur un système avec plusieurs versions d’une même bibliothèque, ou plusieurs bibliothèques portant le même nom, dépendre de LD_LIBRARY_PATH peut être très myope. Le chargeur parcourt les chemins de LD_LIBRARY_PATH dans l’ordre pour chaque binaire et choisit la première bibliothèque correspondante. À moins d’avoir défini un chemin de priorité plus élevée autrement, ce n’est peut-être pas la bibliothèque réellement voulue, ce qui peut provoquer des erreurs inattendues à l’exécution
      Une meilleure approche consiste à définir RPATH vers l’emplacement de la bibliothèque requise par ce binaire
      Si l’environnement et les réglages RPATH ne sont pas cohérents, plusieurs versions d’une même bibliothèque peuvent même être chargées simultanément. Cet outil aide à voir s’il y a un problème, et pourquoi
    • Il y a au minimum RPATH, RUNPATH et LD_LIBRARY_PATH
      Comme cet outil repose sur ldd, il interprétera probablement aussi DYLD_LIBRARY_PATH, DYLD_FALLBACK_FRAMEWORK_PATH, DYLD_FALLBACK_LIBRARY_PATH, @executable_path, @loader_path, @rpath
  • Vraiment utile. En général, je regarde les sections avec readelf pour comprendre quelles sont les exigences réelles

  • LD_DEBUG=libs, ce n’est pas suffisant ?

    • Ce n’est pas exactement la même chose, parce que c’est un flag de debug du chargeur, pas une évaluation statique des dépendances de bibliothèques
  • Je ne sais pas si c’est un bug, mais dans l’exemple de vim, ldd et libtree affichent des bibliothèques différentes
    Par exemple, linux-vdso.so.1 apparaît tout en haut dans ldd, mais pas du tout dans libtree

    • linux-vdso.so.1 n’est pas une vraie bibliothèque qu’on peut trouver quelque part dans le système de fichiers, et elle n’est pas non plus référencée dans le fichier ELF, donc libtree ne peut pas la connaître
      À la place, le noyau la mappe automatiquement dans l’espace d’adressage d’un processus nouvellement démarré. C’est une optimisation pour éviter le surcoût des appels système dans des fonctions comme gettimeofday. Référence : https://man7.org/linux/man-pages/man7/vdso.7.html
  • Sur NixOS, j’avais écrit un petit script bancal qui lançait ldd récursivement pour comprendre ce qu’il fallait inclure afin d’exécuter des binaires closed source
    Si je dois refaire ça un jour, j’essaierai probablement cet outil

 
kayws426 2024-06-11

Ça a l'air bien !!