LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

基于ldd快速遍历应用/库的依赖关系

对于复杂的应用或库文件,要了解其运作原理、架构,通过了解其库依赖关系不失为一种手段。

ldd可以查看单个可执行文件或库文件以来的库,但是库比较多的话不容易有个全局概念。

所以基于ldd编解Python脚本,做个小工具,提供集中查看方式:

  • 直接文本查看类似tree命令输出的属性结构。
  • xml格式的树形结构。
  • dot格式数据,然后转换成png查看调用关系。

第一种最简单,可以嵌入式设备直接查看;最后一种最直观,最有效。

1 ldd

ldd可以显示一个应用程序或者库文件以来的库文件,基于这个命令可以遍历所有被依赖的库文件。

重点看ldd结果输出的依赖库文件的路径,可以被ldd再次调用,递归遍历所有的库文件。

ldd /usr/bin/weston
        linux-vdso.so.1 (0x0000007f8f9e5000)
        libexec_weston.so.0 => /usr/lib/weston/libexec_weston.so.0 (0x0000007f8f960000)
        libc.so.6 => /lib64/libc.so.6 (0x0000007f8f7f0000)
        libweston-11.so.0 => /usr/lib64/libweston-11.so.0 (0x0000007f8f780000)
        libwayland-client.so.0 => /usr/lib64/libwayland-client.so.0 (0x0000007f8f760000)
        libwayland-server.so.0 => /usr/lib64/libwayland-server.so.0 (0x0000007f8f730000)
        libinput.so.10 => /usr/lib64/libinput.so.10 (0x0000007f8f6d0000)
        libevdev.so.2 => /usr/lib64/libevdev.so.2 (0x0000007f8f6a0000)
        /lib/ld-linux-aarch64.so.1 (0x0000007f8f9b2000)
        libpixman-1.so.0 => /usr/lib64/libpixman-1.so.0 (0x0000007f8f630000)
        libdrm.so.2 => /usr/lib64/libdrm.so.2 (0x0000007f8f600000)
        libxkbcommon.so.0 => /usr/lib64/libxkbcommon.so.0 (0x0000007f8f5a0000)
        libmali_hook.so.1 => /usr/lib64/libmali_hook.so.1 (0x0000007f8f580000)
        libmali.so.1 => /usr/lib64/libmali.so.1 (0x0000007f88b60000)
        libffi.so.8 => /usr/lib64/libffi.so.8 (0x0000007f88b40000)
        libmtdev.so.1 => /usr/lib64/libmtdev.so.1 (0x0000007f88b20000)
        libudev.so.1 => /lib64/libudev.so.1 (0x0000007f88ae0000)
        libm.so.6 => /lib64/libm.so.6 (0x0000007f88a50000)
        librga.so.2 => /usr/lib64/librga.so.2 (0x0000007f88a20000)
        libdl.so.2 => /lib64/libdl.so.2 (0x0000007f88a00000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x0000007f889e0000)
        libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000007f88850000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000007f88820000)

 2 基于ldd编写python脚本

 2.1 生成类似tree库调用关系

#!/usr/bin/python
import sys, os

def interate_elf_libraries(header, elf_name):
  result = os.popen("ldd %s"%(elf_name))
  header = header + "  "
  for library in result.readlines():
    library_split= library.split()
    if len(library_split) == 4:
      elf_name = library_split[2]
      if( not ("libm.so" in elf_name or "libc.so" in elf_name or "libgcc_s.so" in elf_name or "libdl.so" in elf_name or "libpthread.so" in elf_name or "libstdc++.so" in elf_name)):
        print(header + "|-" + elf_name)
        interate_elf_libraries(header, elf_name)
  result.close()


if __name__ == '__main__':
  if len(sys.argv) < 2:
    print("No input argument.")
    sys.exit(1)
  header = ""
  elf_name = sys.argv[1]
  print(header + elf_name)
  interate_elf_libraries(header, elf_name)

 执行./ldd-ext.py /usr/bin/weston,结果如下:

/usr/bin/weston
  |-/usr/lib/weston/libexec_weston.so.0
    |-/usr/lib64/libweston-11.so.0
      |-/usr/lib64/libwayland-server.so.0
        |-/usr/lib64/libffi.so.8
      |-/usr/lib64/libpixman-1.so.0
        |-/usr/lib64/librga.so.2
          |-/usr/lib64/libdrm.so.2
        |-/usr/lib64/libdrm.so.2
      |-/usr/lib64/libdrm.so.2
      |-/usr/lib64/libxkbcommon.so.0
      |-/usr/lib64/libmali_hook.so.1
        |-/usr/lib64/libmali.so.1
          |-/usr/lib64/libdrm.so.2
          |-/usr/lib64/libwayland-client.so.0
            |-/usr/lib64/libffi.so.8
          |-/usr/lib64/libwayland-server.so.0
            |-/usr/lib64/libffi.so.8
          |-/usr/lib64/libffi.so.8
...

 2.2 生成xml格式的库调用关系

#!/usr/bin/python
import sys, os

def interate_elf_libraries(header, elf_name):
  result = os.popen("ldd %s"%(elf_name))
  result_lines = result.readlines()
  elf_has_sub=0
  for library in result_lines:
    library_split= library.split()
    if len(library_split) == 4:
      elf_name = library_split[2]
      if( not ("libm.so" in elf_name or "libc.so" in elf_name or "libgcc_s.so" in elf_name or "libdl.so" in elf_name or "libpthread.so" in elf_name or "libstdc++.so" in elf_name)):
        elf_has_sub=1

  if( elf_has_sub == 1):
    print(header + "<sub>")
    header_sub = header + "\t"
    for library in result_lines:
      library_split= library.split()
      if len(library_split) == 4:
        elf_name = library_split[2]
        if( not ("libm.so" in elf_name or "libc.so" in elf_name or "libgcc_s.so" in elf_name or "libdl.so" in elf_name or "libpthread.so" in elf_name or "libstdc++.so" in elf_name)):
          print(header_sub + "<name>" + elf_name + "</name>")
          interate_elf_libraries(header_sub, elf_name)
    print(header + "</sub>")
  result.close()


if __name__ == '__main__':
  if len(sys.argv) < 2:
    print("No input argument.")
    sys.exit(1)
  print("<?xml version='1.0' encoding='UTF-8'?>")
  header = ""
  elf_name = sys.argv[1]
  print("<app>")
  print(header + "<name>" + elf_name + "</name>")
  interate_elf_libraries(header, elf_name)
  print("</app>")

执行./ldd-ext.py /usr/bin/weston > weston.xml,然后再浏览器中查看如下:

2.3 生成dot格式库调用关系,然后转成png图片

 大概流程如下:

  • ldd获取被调用的库文件路径。
  • python递归调用ldd,并保存成dot格式的文件。
  • 使用dot命令根据dot数据生成png等格式。
#!/usr/bin/python
import sys, os, re
#exclude_libs = ["libm.so", "libc.so", "libgcc_s.so", "libpthread.so", "libstdc++.so", "libdl.so", "libogg.so", "libffi.so", "librga.so", "libglib-2.0.so", "libz.so", "libgthread-2.0.so", "libpcre.so", "libpcre2-16.so"]
exclude_libs = ["libm.so", "libc.so", "libgcc_s.so", "libpthread.so", "libstdc++.so", "libdl.so", "libz.so"]
def interate_elf_libraries(header, elf_name):
  result = os.popen("ldd %s"%(elf_name))
  regex_pattern = '|'.join(map(re.escape, exclude_libs))
  header = header + "  "
  for library in result.readlines():
    library_split= library.split()
    if len(library_split) == 4:#这种情况才会包含库文件路径。
      sub_elf_name = library_split[2]
      if not re.search(regex_pattern, sub_elf_name, re.IGNORECASE):#排除某些不重要,或者不想查看的库文件。
        print("\"" + elf_name + "\"->\"" + sub_elf_name + "\";")
        interate_elf_libraries(header, sub_elf_name)#递归遍历被依赖的库文件。
  result.close()

if __name__ == '__main__':
  if len(sys.argv) < 2:
    print("No input argument.")
    sys.exit(1)
  header = ""
  elf_name = sys.argv[1]
  print("strict digraph lib_call {")#一定要用strict,起到去重作用;使用digraph表示需要方向。
  interate_elf_libraries(header, elf_name)
  print("}")

 执行./ldd-ext.py /usr/bin/weston > weston.dot,结果如下:

strict digraph lib_call {
"/usr/bin/weston"->"/usr/lib/weston/libexec_weston.so.0";
"/usr/lib/weston/libexec_weston.so.0"->"/usr/lib64/libweston-11.so.0";
..."/usr/bin/weston"->"/usr/lib64/librga.so.2";
"/usr/lib64/librga.so.2"->"/usr/lib64/libdrm.so.2";
}

 执行命令dot -Tpng weston.dot  -o weston.png,生成png图片:

关于Graphviz/dot更多参考《Documentation | Graphviz》。

posted on 2024-03-29 23:59  ArnoldLu  阅读(485)  评论(0编辑  收藏  举报

导航