3P by GN⁺ | ★ favorite | 댓글 2개
  • libtreeldd 출력을 트리로 바꾸고, 공유 라이브러리가 어떻게 발견됐는지 또는 왜 찾을 수 없는지 설명하는 도구임
  • 기본 출력에서는 일부 표준 의존성을 숨기며, -v, -vv, -vvv로 숨겨진 라이브러리와 이미 만난 라이브러리의 의존성까지 단계적으로 볼 수 있음
  • --path 또는 -p는 soname 대신 경로를 표시하며, --max-depth로 재귀 탐색 깊이를 제한할 수 있음
  • 설치는 v3.1.1 사전 빌드 바이너리, Fedora/RHEL/CentOS, Ubuntu 22.04+, GNU Guix를 통해 가능함
  • 소스 빌드에는 C99를 이해하는 C 컴파일러가 필요하며, make 사용 시 LDFLAGS=-static이 권장됨

libtree가 하는 일

  • libtreeldd를 트리 형태로 바꾸는 도구임
  • 공유 라이브러리가 어떻게 발견되는지, 또는 왜 위치를 찾을 수 없는지 설명함
  • README에는 doc/screenshot.png 스크린샷이 포함되어 있음

출력 옵션

  • 기본 출력에서는 일부 표준 의존성이 표시되지 않음
  • 더 자세한 출력은 verbosity 옵션으로 제어함
    • libtree -v: 기본적으로 건너뛰는 라이브러리를 표시함
    • libtree -vv: 기본적으로 건너뛰는 라이브러리의 의존성도 표시함
    • libtree -vvv: 이미 만난 라이브러리의 의존성도 표시함
  • --path 또는 -p 플래그는 soname 대신 경로를 표시함
    • 예: libtree -p $(which tar)
  • --max-depth는 재귀 깊이를 제한함

설치 방법

소스에서 빌드

  • libtreeC99를 이해하는 C 컴파일러가 필요함
  • 기본 빌드 절차는 저장소를 클론한 뒤 make를 실행하는 방식임
  • make 사용 시 LDFLAGS=-static이 권장됨
  • README에는 curllibtree.c를 받아 컴파일하는 unsafe quick install 명령도 별도 접힘 섹션으로 제공됨

댓글과 토론

Hacker News 의견들
  • 이 도구도 ldd가 검사 대상 라이브러리 일부를 실제로 실행해버리는 예상 밖 동작을 그대로 따라 하나?
    https://catonmat.net/ldd-arbitrary-code-execution

    • 최근, 대략 5년 이상 된 버전의 ldd 는 대상 바이너리를 실행하지 않음
      참고: https://manpages.debian.org/unstable/manpages/ldd.1.en.html#...
    • 모바일에서 코드를 빠르게 훑어보니 그렇게 하지는 않는 것 같음
      실제로는 ELF 파일을 직접 파싱하고, 의존성도 재귀적으로 파싱하는 듯해서 꽤 멋짐
    • Python용으로 어렴풋이 비슷한 걸 만든 적이 있는데, 이 문제를 끝내 우회하지 못했음 https://github.com/google/importlab/issues/69
    • objdump를 쓰면 ELF 파일에 인코딩된 그대로 데이터를 출력해 줌
      vdso가 검색할 라이브러리 목록까지 포함됨
  • 비슷한 도구로 lddtree가 있음
    https://github.com/gentoo/pax-utils/tree/master

  • ldd로 빠진 의존성을 재귀적으로 반복 추적해야 하는 건 꽤 지루함
    그래서 이건 좋은 개선처럼 보이고, 다음에 애매한 not found를 만나면 써볼 생각임

  • 기본적으로 Linux CLI 버전의 depends.exe 같음

    • 그 특정 도구는 최신 Windows 버전에서는 더 이상 잘 동작하지 않음
      대신 https://github.com/lucasg/Dependencies를 쓰는 편이 나음. 이것도 완전히 최신 상태는 아니지만…
      Visual Studio를 설치했고 설치 관리자에서 x64/x86 build tools (latest)를 선택했다면, VS Developer Command Prompt에서 dumpbin /dependents를 실행하는 게 여전히 가장 믿을 만한 선택지임
    • 맞음, 나도 딱 그 생각이었음
      [1] https://www.dependencywalker.com/
  • 색상이 뭘 의미하는지 궁금한 사람을 위해 적어두면, manpage/README에서는 찾지 못했음
    자홍색: 제외 목록에 있음, -v[v[v]]에서만 표시됨
    파란색: 이전에 본 항목이라서, 여러 번 등장하는 의존성을 찾을 수 있음

  • “라이브러리가 발견되거나 안 되는 이유”가 무슨 뜻이지? LD_LIBRARY_PATH에 있거나 없거나 둘 중 하나 아닌가?
    스크린샷만 봐서는 이게 뭘 가리키는지 잘 모르겠음

    • 이 도구의 맥락에서는 정확히 모르겠지만, 라이브러리 검색은 단일 환경 변수보다 훨씬 복잡함
      시스템 검색 경로, runpath, rpath, LD_LIBRARY_PATH 등 서로 다른 디렉터리 검색 방식이 여럿 있음
      라이브러리는 보통 foo.so 같은 짧은 이름으로 링크되지만, 라이브러리의 전체 경로로 동적 링크될 수도 있음
      덧붙이면 일반적으로 LD_LIBRARY_PATH 설정은 피할 수 있으면 피하는 게 훨씬 나음. 항상 가능한 건 아니지만, 설정하면 모든 실행에서 검색 우선순위 맨 위로 올라감. 어떤 것이 라이브러리 전체 경로로 동적 링크되어 있어도 LD_LIBRARY_PATH가 우선하며, 검색 방식을 완전히 평평하게 만들어버림
    • 라이브러리가 발견되기 위해 반드시 LD_LIBRARY_PATH 안에 있을 필요는 없음
      제목의 요지는 libtree가 실행 파일에서 모든 직접·간접 의존성까지 이어지는 경로를 쉽게 찾게 해준다는 것임. 그 용도 중 하나가 누락된 의존성 문제를 파악하는 데 도움을 주는 것임
      실제로 패키지 시스템을 쓰고 있다면 의존성이 빠지는 일은 별로 없을 테니, libtree는 다른 이유로 쓰게 될 가능성이 큼
    • LD_LIBRARY_PATH에 있거나 없거나만 있는 게 아니라, 각 라이브러리별로 적재 시점에 평가되는 RPATH도 있음
      더 큰 요점은 의존성이 그래프를 이루며, 이를 트리처럼 표시할 수 있다는 것임. 어떤 라이브러리를 필요로 한 라이브러리 때문에 특정 라이브러리를 못 찾았는지 아는 게 유용함
    • LD_LIBRARY_PATH만으로 라이브러리를 찾는 게 아님. 로더는 라이브러리를 찾기 위해 여러 다른 출처도 고려함
      정상적인 설정이라면 로드되는 각 바이너리의 일반적인 ELF 필드와 로더가 알고 있는 다른 경로의 조합이 됨
      같은 라이브러리의 여러 버전이나 같은 이름의 여러 라이브러리가 있는 시스템에서 LD_LIBRARY_PATH에 의존하는 건 매우 근시안적일 수 있음. 로더는 각 바이너리에 대해 LD_LIBRARY_PATH의 경로를 순서대로 검색하고, 일치하는 첫 라이브러리를 고름. 다른 방식으로 더 높은 우선순위 경로를 설정하지 않았다면 그 라이브러리가 실제로 원하는 것이 아닐 수 있고, 런타임에서 예상치 못한 오류로 이어질 수 있음
      더 나은 방법은 해당 바이너리가 필요한 라이브러리가 있는 위치로 RPATH를 설정하는 것임
      환경과 RPATH 설정이 일관되지 않으면 같은 라이브러리의 여러 버전을 동시에 로드할 수도 있음. 이 도구는 문제가 있는지, 왜 그런지 파악하는 데 도움을 줌
    • 최소한 RPATH, RUNPATH, LD_LIBRARY_PATH가 있음
      이 도구는 ldd 기반이므로 아마 DYLD_LIBRARY_PATH, DYLD_FALLBACK_FRAMEWORK_PATH, DYLD_FALLBACK_LIBRARY_PATH, @executable_path, @loader_path, @rpath도 해석할 것임
  • 정말 유용함. 보통은 실제 요구 사항이 뭔지 알아내려고 readelf섹션을 읽어봄

  • LD_DEBUG=libs로는 부족한가?

    • 그건 라이브러리 의존성에 대한 정적 평가가 아니라 로더 디버그 플래그라서, 완전히 같은 건 아님
  • 버그인지는 모르겠지만, vim 예제에서 ldd와 libtree가 서로 다른 라이브러리를 보여줌
    예를 들어 linux-vdso.so.1ldd에서는 목록 맨 위에 나오는데 libtree에는 전혀 안 나옴

    • linux-vdso.so.1은 파일 시스템 어딘가에서 찾을 수 있는 실제 라이브러리가 아니고 ELF 파일 안에서도 참조되지 않으므로, libtree는 알 수 없음
      대신 새로 시작한 프로세스의 주소 공간에 커널이 자동으로 매핑함. gettimeofday 같은 함수에서 시스템 호출 오버헤드를 피하기 위한 최적화 기능임. 참고: https://man7.org/linux/man-pages/man7/vdso.7.html
  • NixOS에서 닫힌 소스 바이너리를 실행하려면 뭘 포함해야 하는지 알아내려고 ldd를 재귀적으로 돌리는 엉망인 작은 스크립트를 만든 적이 있음
    다시 그런 걸 하게 되면 이 도구를 한번 써볼 생각임

좋아보이네요!!