Libtree: ldd ที่อธิบายการค้นพบไลบรารีในรูปแบบต้นไม้
(github.com/haampie)- libtree คือเครื่องมือที่เปลี่ยนเอาต์พุตของ
lddให้เป็นรูปแบบต้นไม้ และอธิบายว่าไลบรารีแบบแชร์ถูกค้นพบได้อย่างไร หรือเหตุใดจึงหาไม่พบ - เอาต์พุตพื้นฐานจะซ่อน dependency มาตรฐาน บางส่วนไว้ และสามารถใช้
-v,-vv,-vvvเพื่อดูไลบรารีที่ถูกซ่อน รวมถึง dependency ของไลบรารีที่เคยพบแล้วได้แบบไล่ระดับ --pathหรือ-pแสดง พาธ แทน soname และสามารถใช้--max-depthเพื่อจำกัดความลึกของการค้นหาแบบ recursive ได้- การติดตั้งทำได้ผ่าน ไบนารีที่ build ไว้ล่วงหน้า v3.1.1, Fedora/RHEL/CentOS, Ubuntu 22.04+ และ GNU Guix
- การ build จากซอร์สต้องใช้คอมไพเลอร์ C ที่เข้าใจ C99 และเมื่อใช้
makeแนะนำให้ใช้LDFLAGS=-static
สิ่งที่ libtree ทำ
- libtree คือเครื่องมือที่เปลี่ยน
lddให้อยู่ในรูปแบบต้นไม้ - อธิบายว่าไลบรารีแบบแชร์ถูก ค้นพบได้อย่างไร หรือเหตุใดจึงระบุตำแหน่งไม่ได้
- README มีสกรีนช็อต
doc/screenshot.pngรวมอยู่ด้วย
ตัวเลือกเอาต์พุต
- เอาต์พุตพื้นฐานจะไม่แสดง dependency มาตรฐาน บางส่วน
- เอาต์พุตที่ละเอียดขึ้นควบคุมได้ด้วยตัวเลือก verbosity
libtree -v: แสดงไลบรารีที่โดยปกติจะถูกข้ามlibtree -vv: แสดง dependency ของไลบรารีที่โดยปกติจะถูกข้ามด้วยlibtree -vvv: แสดง dependency ของไลบรารีที่เคยพบแล้วด้วย
- แฟล็ก
--pathหรือ-pแสดง พาธ แทน soname- ตัวอย่าง:
libtree -p $(which tar)
- ตัวอย่าง:
--max-depthจำกัดความลึกของการ recursive
วิธีติดตั้ง
- ไบนารีที่ build ไว้ล่วงหน้าสำหรับ v3.1.1: มีไบนารีที่ build ไว้ล่วงหน้าสำหรับ Linux
- บน Fedora / RHEL / CentOS ติดตั้งด้วย
dnf- สำหรับ RHEL และดิสทริบิวชันที่สืบทอดมา ให้เปิดใช้
epel-releaseก่อน dnf install libtree-ldd
- สำหรับ RHEL และดิสทริบิวชันที่สืบทอดมา ให้เปิดใช้
- บน Ubuntu 22.04+ ติดตั้งด้วย
apt-get install libtree - บน GNU Guix ติดตั้งด้วย
guix install libtree - มี รีลีสเก่า v2.0.0 ให้ใช้งานด้วย
Build จากซอร์ส
libtreeต้องใช้คอมไพเลอร์ C ที่เข้าใจ C99- ขั้นตอน build พื้นฐานคือ clone repository แล้วรัน
makegit clone https://github.com/haampie/libtree.gitcd libtreemake
- เมื่อใช้
makeแนะนำให้ใช้LDFLAGS=-static - README ยังมีคำสั่ง unsafe quick install สำหรับดาวน์โหลด
libtree.cด้วยcurlแล้วคอมไพล์ แยกไว้ในส่วนที่พับได้
2 ความคิดเห็น
ความคิดเห็นจาก Hacker News
เครื่องมือนี้ก็ทำตาม พฤติกรรมที่คาดไม่ถึง ของ
lddที่รันบางส่วนของไลบรารีที่ถูกตรวจสอบจริง ๆ ด้วยหรือเปล่า?https://catonmat.net/ldd-arbitrary-code-execution
lddเวอร์ชันที่เก่ากว่าประมาณ 5 ปีขึ้นไป จะไม่รันไบนารีเป้าหมายแล้วอ้างอิง: https://manpages.debian.org/unstable/manpages/ldd.1.en.html#...
ที่จริงดูเหมือนมัน พาร์สไฟล์ ELF โดยตรง และพาร์ส dependency แบบ recursive ด้วย ค่อนข้างเจ๋งทีเดียว
objdumpมันจะพิมพ์ข้อมูลออกมาตามที่เข้ารหัสอยู่ในไฟล์ ELF เลยรวมถึงรายการไลบรารีที่ vdso จะค้นหาด้วย
มีเครื่องมือคล้ายกันชื่อ lddtree
https://github.com/gentoo/pax-utils/tree/master
การต้อง ไล่ตาม dependency ที่หายไปแบบ recursive ซ้ำ ๆ ด้วย
lddนั้นค่อนข้างน่าเบื่อดังนั้นอันนี้ดูเป็นการปรับปรุงที่ดี และคราวหน้าถ้าเจอ
not foundแบบคลุมเครือจะลองใช้ดูโดยพื้นฐานแล้วเหมือน depends.exe เวอร์ชัน Linux CLI
ใช้ https://github.com/lucasg/Dependencies แทนจะดีกว่า ถึงอันนี้ก็ไม่ได้ทันสมัยสุด ๆ เช่นกัน…
ถ้าคุณติดตั้ง Visual Studio และเลือก
x64/x86 build tools (latest)ในตัวติดตั้งไว้ การรันdumpbin /dependentsใน VS Developer Command Prompt ยังคงเป็นตัวเลือกที่น่าเชื่อถือที่สุด[1] https://www.dependencywalker.com/
สำหรับคนที่สงสัยว่าสีต่าง ๆ หมายถึงอะไร ขอจดไว้ตรงนี้ เพราะหาไม่เจอใน manpage/README
สีม่วงแดง: อยู่ในรายการยกเว้น จะแสดงเฉพาะใน
-v[v[v]]สีน้ำเงิน: เป็นรายการที่เคยเห็นมาก่อน จึงช่วยหา dependency ที่ปรากฏหลายครั้งได้
“เหตุผลที่ไลบรารีถูกพบหรือไม่ถูกพบ” หมายความว่าอะไร? มันก็แค่อยู่หรือไม่อยู่ใน
LD_LIBRARY_PATHไม่ใช่เหรอ?ดูจากสกรีนช็อตอย่างเดียวก็ไม่ค่อยเข้าใจว่าหมายถึงอะไร
มีวิธีค้นหาไดเรกทอรีหลายแบบ เช่น เส้นทางค้นหาของระบบ, runpath, rpath,
LD_LIBRARY_PATHฯลฯไลบรารีมักถูกลิงก์ด้วยชื่อสั้น ๆ อย่าง
foo.soแต่ก็อาจถูก dynamic link ด้วยพาธเต็มของไลบรารีได้เช่นกันเสริมอีกอย่าง โดยทั่วไปถ้าเลี่ยงได้ควรเลี่ยงการตั้ง
LD_LIBRARY_PATHจะดีกว่ามาก แม้จะไม่ได้ทำได้เสมอไป แต่ถ้าตั้งแล้วมันจะขึ้นไปอยู่บนสุดของลำดับความสำคัญในการค้นหาสำหรับทุกการรัน แม้บางอย่างจะถูก dynamic link ด้วยพาธเต็มของไลบรารีLD_LIBRARY_PATHก็ยังมีลำดับความสำคัญสูงกว่า และทำให้วิธีค้นหาถูกทำให้แบนราบไปหมดLD_LIBRARY_PATHเสมอไปถึงจะถูกพบใจความของชื่อคือ libtree ช่วยให้หาเส้นทางจากไฟล์ executable ไปจนถึง dependency ทั้งแบบตรงและอ้อมทั้งหมดได้ง่าย หนึ่งในกรณีใช้งานคือช่วยวิเคราะห์ปัญหา dependency ที่ขาดหาย
ในทางปฏิบัติ ถ้าคุณใช้ระบบแพ็กเกจ dependency ก็คงไม่ค่อยหาย ดังนั้นมีแนวโน้มว่าจะใช้ libtree ด้วยเหตุผลอื่นมากกว่า
LD_LIBRARY_PATHเท่านั้น แต่ยังมี RPATH ที่ประเมินตอนโหลดสำหรับแต่ละไลบรารีด้วยประเด็นที่ใหญ่กว่าคือ dependency ก่อตัวเป็นกราฟ และสามารถแสดงเหมือนต้นไม้ได้ การรู้ว่าไลบรารีใดเป็นต้นเหตุให้ต้องการไลบรารีบางตัวจนหาไม่เจอมีประโยชน์
LD_LIBRARY_PATHเพื่อหาไลบรารีเท่านั้น loader ยังพิจารณาแหล่งอื่น ๆ อีกหลายแห่งเพื่อค้นหาไลบรารีในการตั้งค่าปกติ จะเป็นการผสมกันระหว่าง ฟิลด์ ELF ทั่วไปของแต่ละไบนารีที่ถูกโหลด กับพาธอื่น ๆ ที่ loader รู้จัก
บนระบบที่มีไลบรารีเดียวกันหลายเวอร์ชัน หรือมีไลบรารีหลายตัวที่ชื่อเดียวกัน การพึ่งพา
LD_LIBRARY_PATHอาจเป็นมุมมองที่สั้นมาก loader จะค้นหาพาธในLD_LIBRARY_PATHตามลำดับสำหรับแต่ละไบนารี และเลือกไลบรารีตัวแรกที่ตรงกัน ถ้าไม่ได้ตั้งพาธที่มีลำดับความสำคัญสูงกว่าด้วยวิธีอื่น ไลบรารีนั้นอาจไม่ใช่ตัวที่ต้องการจริง ๆ และอาจนำไปสู่ข้อผิดพลาดที่ไม่คาดคิดใน runtimeวิธีที่ดีกว่าคือตั้ง RPATH ให้ชี้ไปยังตำแหน่งที่มีไลบรารีที่ไบนารีนั้นต้องการ
หาก environment และการตั้งค่า RPATH ไม่สอดคล้องกัน ก็อาจโหลดไลบรารีเดียวกันหลายเวอร์ชันพร้อมกันได้ เครื่องมือนี้ช่วยให้เข้าใจว่ามีปัญหาหรือไม่ และเพราะอะไร
LD_LIBRARY_PATHเครื่องมือนี้อิงกับ
lddดังนั้นก็น่าจะตีความDYLD_LIBRARY_PATH,DYLD_FALLBACK_FRAMEWORK_PATH,DYLD_FALLBACK_LIBRARY_PATH,@executable_path,@loader_path,@rpathด้วยมีประโยชน์มาก ปกติจะใช้
readelfอ่าน section เพื่อดูว่าความต้องการจริง ๆ คืออะไรLD_DEBUG=libsยังไม่พอเหรอ?ไม่รู้ว่าเป็นบั๊กหรือเปล่า แต่ในตัวอย่าง vim
lddกับ libtree แสดงไลบรารีคนละชุดกันเช่น
linux-vdso.so.1อยู่บนสุดของรายการในlddแต่ไม่ปรากฏใน libtree เลยlinux-vdso.so.1ไม่ใช่ไลบรารีจริง ๆ ที่จะหาเจอได้ที่ไหนสักแห่งในไฟล์ซิสเต็ม และไม่ได้ถูกอ้างอิงในไฟล์ ELF ด้วย ดังนั้น libtree จึงรู้ไม่ได้แต่เคอร์เนลจะ map มันเข้าไปใน address space ของโปรเซสที่เพิ่งเริ่มโดยอัตโนมัติแทน เป็นการเพิ่มประสิทธิภาพเพื่อหลีกเลี่ยง overhead ของ system call ในฟังก์ชันอย่าง
gettimeofdayอ้างอิง: https://man7.org/linux/man-pages/man7/vdso.7.htmlเคยทำ สคริปต์เล็ก ๆ ที่ยุ่งเหยิง เพื่อรัน
lddแบบ recursive สำหรับดูว่าต้องใส่อะไรบ้างถึงจะรันไบนารี closed source บน NixOS ได้ถ้าต้องทำแบบนั้นอีก คงจะลองใช้เครื่องมือนี้ดู
ดูดีนะ!!