3 puntos por GN⁺ 2024-06-10 | 2 comentarios | Compartir por WhatsApp
  • libtree es una herramienta que convierte la salida de ldd en un árbol y explica cómo se encontró una biblioteca compartida, o por qué no se pudo encontrar
  • En la salida predeterminada oculta algunas dependencias estándar, y con -v, -vv, -vvv se pueden ver de forma gradual las bibliotecas ocultas e incluso las dependencias de bibliotecas ya encontradas
  • --path o -p muestra la ruta en lugar del soname, y con --max-depth se puede limitar la profundidad de la exploración recursiva
  • La instalación está disponible mediante binarios precompilados de v3.1.1, Fedora/RHEL/CentOS, Ubuntu 22.04+ y GNU Guix
  • Para compilar desde el código fuente se requiere un compilador de C que entienda C99, y al usar make se recomienda LDFLAGS=-static

Qué hace libtree

  • libtree es una herramienta que convierte ldd a una forma de árbol
  • Explica cómo se encuentran las bibliotecas compartidas, o por qué no se puede ubicar su ruta
  • El README incluye una captura de pantalla en doc/screenshot.png

Opciones de salida

  • En la salida predeterminada no se muestran algunas dependencias estándar
  • La salida más detallada se controla con opciones de verbosity
    • libtree -v: muestra las bibliotecas que se omiten de forma predeterminada
    • libtree -vv: también muestra las dependencias de las bibliotecas que se omiten de forma predeterminada
    • libtree -vvv: también muestra las dependencias de las bibliotecas ya encontradas
  • La bandera --path o -p muestra la ruta en lugar del soname
    • Ejemplo: libtree -p $(which tar)
  • --max-depth limita la profundidad de recursión

Métodos de instalación

Compilar desde el código fuente

  • libtree requiere un compilador de C que entienda C99
  • El procedimiento básico de compilación consiste en clonar el repositorio y luego ejecutar make
  • Al usar make se recomienda LDFLAGS=-static
  • El README también ofrece, en una sección plegable aparte, comandos de instalación rápida insegura para descargar libtree.c con curl y compilarlo

2 comentarios

 
GN⁺ 2024-06-10
Opiniones de Hacker News
  • ¿Esta herramienta también conserva el comportamiento inesperado de ldd de ejecutar realmente parte de las bibliotecas que está inspeccionando?
    https://catonmat.net/ldd-arbitrary-code-execution

    • Últimamente, las versiones de ldd de aproximadamente más de 5 años ya no ejecutan el binario objetivo
      Referencia: https://manpages.debian.org/unstable/manpages/ldd.1.en.html#...
    • Revisando rápido el código desde el celular, parece que no hace eso
      En realidad parece que parsea directamente el archivo ELF y también parsea las dependencias de forma recursiva, así que está bastante bueno
    • Alguna vez hice algo vagamente parecido para Python, pero nunca logré esquivar este problema https://github.com/google/importlab/issues/69
    • Si usas objdump, te imprime los datos tal como están codificados en el archivo ELF
      Incluso incluye la lista de bibliotecas que buscará vdso
  • Hay una herramienta similar llamada lddtree
    https://github.com/gentoo/pax-utils/tree/master

  • Tener que rastrear recursivamente las dependencias faltantes con ldd se vuelve bastante tedioso
    Así que esto parece una buena mejora, y pienso probarlo la próxima vez que me encuentre con un not found ambiguo

  • Básicamente es como la versión de CLI en Linux de depends.exe

    • Esa herramienta en particular ya no funciona muy bien en versiones modernas de Windows
      Es mejor usar https://github.com/lucasg/Dependencies. Tampoco está totalmente al día, pero…
      Si instalaste Visual Studio y seleccionaste x64/x86 build tools (latest) en el instalador, ejecutar dumpbin /dependents desde VS Developer Command Prompt sigue siendo la opción más confiable
    • Sí, pensé exactamente lo mismo
      [1] https://www.dependencywalker.com/
  • Para quienes se pregunten qué significan los colores, lo dejo anotado; no lo encontré en la manpage/README
    Magenta: está en la lista de exclusión, solo se muestra con -v[v[v]]
    Azul: es un elemento visto antes, así que permite encontrar dependencias que aparecen varias veces

  • ¿Qué significa “por qué se encuentra o no se encuentra una biblioteca”? ¿No es simplemente que está o no está en LD_LIBRARY_PATH?
    Solo con la captura de pantalla no me queda claro a qué se refiere

    • No sé exactamente en el contexto de esta herramienta, pero la búsqueda de bibliotecas es mucho más compleja que una sola variable de entorno
      Hay varias formas distintas de buscar directorios, como rutas de búsqueda del sistema, runpath, rpath, LD_LIBRARY_PATH, etc.
      Las bibliotecas normalmente se enlazan con un nombre corto como foo.so, pero también pueden enlazarse dinámicamente usando la ruta completa de la biblioteca
      Además, en general es mucho mejor evitar configurar LD_LIBRARY_PATH si se puede. No siempre es posible, pero al configurarlo sube al primer lugar en la prioridad de búsqueda para todas las ejecuciones. Aunque algo esté enlazado dinámicamente con la ruta completa de una biblioteca, LD_LIBRARY_PATH tiene prioridad y termina aplanando por completo la forma de búsqueda
    • Una biblioteca no necesariamente tiene que estar dentro de LD_LIBRARY_PATH para ser encontrada
      La idea del título es que libtree facilita encontrar la ruta desde un ejecutable hacia todas sus dependencias directas e indirectas. Uno de sus usos es ayudar a diagnosticar problemas de dependencias faltantes
      En la práctica, si usas un sistema de paquetes, no suele faltar una dependencia, así que probablemente uses libtree por otros motivos
    • No es solo estar o no estar en LD_LIBRARY_PATH; también existe RPATH, que se evalúa en el momento de carga para cada biblioteca
      El punto más importante es que las dependencias forman un grafo y que este puede mostrarse como un árbol. Es útil saber por culpa de qué biblioteca que requería otra biblioteca no se pudo encontrar una biblioteca específica
    • Las bibliotecas no se buscan solo con LD_LIBRARY_PATH. El loader también considera varias otras fuentes para encontrarlas
      En una configuración normal, es una combinación de los campos ELF habituales de cada binario cargado y otras rutas conocidas por el loader
      En un sistema con varias versiones de la misma biblioteca o varias bibliotecas con el mismo nombre, depender de LD_LIBRARY_PATH puede ser muy cortoplacista. Para cada binario, el loader busca en orden las rutas de LD_LIBRARY_PATH y elige la primera biblioteca que coincide. Si no configuraste una ruta de mayor prioridad por otro medio, puede que esa biblioteca no sea la que realmente quieres, y eso puede terminar en errores inesperados en runtime
      Una mejor forma es configurar el RPATH del binario hacia la ubicación donde están las bibliotecas que necesita
      Si el entorno y la configuración de RPATH no son consistentes, incluso puedes terminar cargando varias versiones de la misma biblioteca al mismo tiempo. Esta herramienta ayuda a entender si hay un problema y por qué
    • Como mínimo están RPATH, RUNPATH y LD_LIBRARY_PATH
      Como esta herramienta se basa en ldd, probablemente también interprete DYLD_LIBRARY_PATH, DYLD_FALLBACK_FRAMEWORK_PATH, DYLD_FALLBACK_LIBRARY_PATH, @executable_path, @loader_path y @rpath
  • Realmente útil. Normalmente termino leyendo las secciones con readelf para averiguar cuáles son los requisitos reales

  • ¿No alcanza con LD_DEBUG=libs?

    • Eso es una bandera de depuración del loader, no una evaluación estática de las dependencias de bibliotecas, así que no es exactamente lo mismo
  • No sé si es un bug, pero en el ejemplo de vim ldd y libtree muestran bibliotecas distintas
    Por ejemplo, linux-vdso.so.1 aparece al principio de la lista en ldd, pero no aparece para nada en libtree

    • linux-vdso.so.1 no es una biblioteca real que puedas encontrar en algún lugar del sistema de archivos y tampoco está referenciada dentro del archivo ELF, así que libtree no puede conocerla
      En cambio, el kernel la mapea automáticamente en el espacio de direcciones de un proceso recién iniciado. Es una optimización para evitar el overhead de llamadas al sistema en funciones como gettimeofday. Referencia: https://man7.org/linux/man-pages/man7/vdso.7.html
  • Alguna vez hice un scriptcito desprolijo que ejecutaba ldd de forma recursiva para averiguar qué tenía que incluir para correr binarios de código cerrado en NixOS
    Si vuelvo a tener que hacer algo así, pienso probar esta herramienta

 
kayws426 2024-06-11

¡¡Se ve bien!!