Libtree: ldd giải thích việc phát hiện thư viện dưới dạng cây
(github.com/haampie)- libtree là công cụ chuyển đầu ra của
lddthành dạng cây, đồng thời giải thích thư viện dùng chung được tìm thấy như thế nào hoặc vì sao không thể tìm thấy - Ở đầu ra mặc định, công cụ ẩn một số phụ thuộc tiêu chuẩn; có thể dùng
-v,-vv,-vvvđể lần lượt xem các thư viện bị ẩn và cả phụ thuộc của những thư viện đã gặp --pathhoặc-phiển thị đường dẫn thay vì soname, và có thể giới hạn độ sâu tìm kiếm đệ quy bằng--max-depth- Có thể cài đặt qua binary dựng sẵn v3.1.1, Fedora/RHEL/CentOS, Ubuntu 22.04+ và GNU Guix
- Khi build từ mã nguồn, cần trình biên dịch C hiểu C99; khi dùng
make, khuyến nghị dùngLDFLAGS=-static
libtree làm gì
- libtree là công cụ chuyển
lddsang dạng cây - Giải thích thư viện dùng chung được tìm thấy như thế nào, hoặc vì sao không thể xác định vị trí của chúng
- README có kèm ảnh chụp màn hình
doc/screenshot.png
Tùy chọn đầu ra
- Ở đầu ra mặc định, một số phụ thuộc tiêu chuẩn sẽ không được hiển thị
- Đầu ra chi tiết hơn được điều khiển bằng các tùy chọn verbosity
libtree -v: hiển thị các thư viện bị bỏ qua theo mặc địnhlibtree -vv: cũng hiển thị phụ thuộc của các thư viện bị bỏ qua theo mặc địnhlibtree -vvv: cũng hiển thị phụ thuộc của các thư viện đã gặp trước đó
- Cờ
--pathhoặc-phiển thị đường dẫn thay vì soname- Ví dụ:
libtree -p $(which tar)
- Ví dụ:
--max-depthgiới hạn độ sâu đệ quy
Cách cài đặt
- Binary dựng sẵn cho v3.1.1: cung cấp binary dựng sẵn cho Linux
- Trên Fedora / RHEL / CentOS, cài bằng
dnf- Với RHEL và các bản phân phối dẫn xuất, trước tiên hãy kích hoạt
epel-release dnf install libtree-ldd
- Với RHEL và các bản phân phối dẫn xuất, trước tiên hãy kích hoạt
- Trên Ubuntu 22.04+, cài bằng
apt-get install libtree - Trên GNU Guix, cài bằng
guix install libtree - Cũng có bản phát hành cũ hơn v2.0.0
Build từ mã nguồn
libtreecần trình biên dịch C hiểu C99- Quy trình build cơ bản là clone repository rồi chạy
makegit clone https://github.com/haampie/libtree.gitcd libtreemake
- Khi dùng
make, khuyến nghị dùngLDFLAGS=-static - README cũng cung cấp riêng trong một mục gập lệnh unsafe quick install để tải
libtree.cbằngcurlrồi biên dịch
2 bình luận
Ý kiến trên Hacker News
Công cụ này có lặp lại đúng hành vi ngoài dự kiến của
ldd, tức là thực sự chạy một phần thư viện đang được kiểm tra không?https://catonmat.net/ldd-arbitrary-code-execution
lddkhoảng hơn 5 năm tuổi trở lại đây không chạy binary đíchTham khảo: https://manpages.debian.org/unstable/manpages/ldd.1.en.html#...
Trên thực tế, có vẻ nó tự parse trực tiếp file ELF, rồi cũng parse đệ quy các dependency, nên khá ấn tượng
objdumpthì nó sẽ in ra dữ liệu đúng như được mã hóa trong file ELFBao gồm cả danh sách thư viện mà vdso sẽ tìm kiếm
Có một công cụ tương tự là lddtree
https://github.com/gentoo/pax-utils/tree/master
Việc phải lần theo đệ quy lặp đi lặp lại các dependency bị thiếu bằng
lddkhá là chánNên cái này trông như một cải tiến tốt, và lần tới nếu gặp một lỗi
not foundmơ hồ thì tôi sẽ thử dùngVề cơ bản nó giống phiên bản Linux CLI của depends.exe
Thay vào đó nên dùng https://github.com/lucasg/Dependencies. Dù nó cũng không hoàn toàn cập nhật nhất...
Nếu bạn đã cài Visual Studio và chọn
x64/x86 build tools (latest)trong trình cài đặt, thì chạydumpbin /dependentstrong VS Developer Command Prompt vẫn là lựa chọn đáng tin cậy nhất[1] https://www.dependencywalker.com/
Ghi chú cho ai thắc mắc màu sắc có ý nghĩa gì, vì tôi không tìm thấy trong manpage/README
Màu tím hồng: nằm trong danh sách loại trừ, chỉ hiển thị với
-v[v[v]]Màu xanh dương: mục đã thấy trước đó, nên có thể tìm ra các dependency xuất hiện nhiều lần
“Giải thích lý do thư viện được tìm thấy hoặc không được tìm thấy” nghĩa là gì? Chẳng phải chỉ là có nằm trong
LD_LIBRARY_PATHhay không thôi sao?Chỉ nhìn ảnh chụp màn hình thì tôi không rõ nó đang ám chỉ điều gì
Có nhiều cơ chế tìm thư mục khác nhau như đường dẫn tìm kiếm của hệ thống, runpath, rpath,
LD_LIBRARY_PATH, v.v.Thư viện thường được liên kết bằng tên ngắn như
foo.so, nhưng cũng có thể được liên kết động bằng đường dẫn đầy đủ của thư việnNói thêm thì thông thường nên tránh cấu hình
LD_LIBRARY_PATHnếu có thể. Không phải lúc nào cũng làm được, nhưng nếu đặt nó thì nó sẽ nhảy lên đầu thứ tự ưu tiên tìm kiếm cho mọi lần thực thi. Kể cả khi thứ gì đó được liên kết động bằng đường dẫn đầy đủ tới thư viện thìLD_LIBRARY_PATHvẫn được ưu tiên, làm phẳng hoàn toàn cơ chế tìm kiếmLD_LIBRARY_PATHmới được tìm thấyÝ chính của tiêu đề là libtree giúp dễ dàng lần ra đường đi từ file thực thi tới toàn bộ dependency trực tiếp và gián tiếp của nó. Một trong những công dụng là hỗ trợ chẩn đoán vấn đề dependency bị thiếu
Trên thực tế, nếu bạn dùng hệ thống quản lý gói thì dependency thường hiếm khi bị thiếu, nên có lẽ bạn sẽ dùng libtree vì các lý do khác
LD_LIBRARY_PATH, mà còn có RPATH được đánh giá lúc load cho từng thư việnĐiểm lớn hơn là các dependency tạo thành một đồ thị, và có thể hiển thị nó dưới dạng cây. Biết được thư viện nào không tìm thấy do một thư viện nào khác cần đến là điều hữu ích
LD_LIBRARY_PATH. Loader còn xét nhiều nguồn khác để tìm thư việnTrong một cấu hình bình thường, đó sẽ là tổ hợp giữa các trường ELF thông dụng của từng binary được tải và các đường dẫn khác mà loader biết
Trên hệ thống có nhiều phiên bản của cùng một thư viện hoặc nhiều thư viện trùng tên, việc phụ thuộc vào
LD_LIBRARY_PATHcó thể rất thiển cận. Loader sẽ duyệt các đường dẫn trongLD_LIBRARY_PATHtheo thứ tự cho từng binary và chọn thư viện khớp đầu tiên. Nếu bạn không thiết lập đường dẫn ưu tiên cao hơn bằng cách khác thì thư viện đó có thể không phải thứ bạn thực sự muốn, và điều này có thể dẫn tới lỗi bất ngờ lúc runtimeCách tốt hơn là thiết lập RPATH trỏ tới nơi chứa thư viện mà binary đó cần
Nếu cấu hình môi trường và RPATH không nhất quán, bạn thậm chí có thể tải đồng thời nhiều phiên bản của cùng một thư viện. Công cụ này giúp xác định có vấn đề hay không, và vì sao
LD_LIBRARY_PATHVì công cụ này dựa trên
ldd, nên có lẽ nó cũng sẽ diễn giảiDYLD_LIBRARY_PATH,DYLD_FALLBACK_FRAMEWORK_PATH,DYLD_FALLBACK_LIBRARY_PATH,@executable_path,@loader_path,@rpathThật sự hữu ích. Bình thường tôi sẽ dùng
readelfđể đọc các section hòng tìm ra yêu cầu thực tế là gìLD_DEBUG=libslà chưa đủ sao?Không rõ đây có phải bug không, nhưng trong ví dụ với vim thì
lddvà libtree hiển thị các thư viện khác nhauVí dụ,
linux-vdso.so.1xuất hiện ở đầu danh sách tronglddnhưng hoàn toàn không có trong libtreelinux-vdso.so.1không phải là thư viện thực có thể tìm thấy đâu đó trong hệ thống file, và cũng không được tham chiếu bên trong file ELF, nên libtree không thể biết về nóThay vào đó, kernel tự động map nó vào không gian địa chỉ của tiến trình mới khởi chạy. Đây là một tối ưu để tránh overhead của system call cho các hàm như
gettimeofday. Tham khảo: https://man7.org/linux/man-pages/man7/vdso.7.htmlTrên NixOS, tôi từng viết một script nhỏ khá tệ để chạy
lddđệ quy nhằm tìm ra cần đưa vào những gì để chạy được binary đóng nguồnNếu lại phải làm chuyện đó, tôi sẽ thử công cụ này
Trông có vẻ hay đấy!!