linux程序分析命令(一)
- **ldd:**用于打印共享库依赖。这个命令会显示出一个可执行文件所依赖的所有共享库(动态链接库),这对于解决运行时库依赖问题非常有用。
- **nm:**用于列出对象文件的符号表。这个命令可以显示出定义和引用的符号,对于理解程序结构和调试非常有帮助。
- objdump:显示二进制文件的信息。这个命令可以用来显示程序的汇编代码、段信息等,对于底层分析和调试很有用。
- strace:跟踪系统调用。通过这个命令,你可以看到一个程序执行时所有的系统调用,这对于理解程序如何与操作系统交互非常重要。
- ltrace:跟踪库函数调用。与strace类似,但是ltrace专注于跟踪程序调用库函数的情况。
- gdb:GNU调试器。这是一个功能强大的调试工具,可以让你看到程序执行时的内部情况,比如变量的值、程序的执行流程等。
- valgrind:内存调试工具。这个工具主要用于检测内存泄漏、内存损坏等问题,对于提高程序稳定性非常有帮助。
- readelf:显示ELF格式文件的信息。这个命令可以显示出ELF格式的二进制文件(如Linux下的可执行文件和共享库)的详细信息,包括段、节、符号等。
- file:确定文件类型。这个命令可以帮助你识别一个文件是二进制可执行文件、文本文件还是其他类型的文件。
- size:显示二进制文件的段大小。这个命令会列出二进制文件各个段(如文本段、数据段)的大小,对于评估程序占用空间有一定帮助。
ldd命令
ldd命令是Linux下一个非常实用的工具,它用于显示一个可执行文件或共享库文件的依赖关系。基本上,它会列出程序运行所需要的所有共享库。
下面是ldd的基本用法和一些常见的使用场景。
基本用法
#命令格式:
ldd [选项] 文件...
#文件...:指定要检查的可执行文件或共享库文件。
最简单的用法是直接跟上你想要检查的文件名:
ldd /path/to/your/program
#这条命令会列出/path/to/your/program这个程序所依赖的所有共享库。
常见用法
检查可执行文件的依赖库:
ldd /usr/bin/ls
#这会显示ls命令所依赖的所有共享库。
检查动态库的依赖:
#如果你有一个共享库文件(例如,libexample.so),你可以使用ldd来查看它依赖哪些其他库:
ldd libexample.so
过滤输出:
#如果你只对特定的依赖感兴趣,可以使用管道和grep命令来过滤输出。例如,如果你只想看看是否依赖于libc.so.6:
ldd /path/to/your/program | grep libc.so.6
解决“不是动态可执行文件”错误:
如果你尝试对静态链接的可执行文件使用ldd,你可能会收到一个错误消息说“不是动态可执行文件”。这意味着该文件没有使用动态链接。在这种情况下,没有依赖关系可以显示。
使用ldd调试加载问题:
当你的程序因为缺少某个共享库而不能启动时,ldd可以帮助你快速识别缺少了哪个库。然后,你可以安装缺少的库或调整环境变量来解决问题。
高级用法
- 使用LD_TRACE_LOADED_OBJECTS环境变量
ldd实际上是通过设置LD_TRACE_LOADED_OBJECTS=1环境变量来工作的。你可以直接使用这个环境变量来获取相同的输出,这在某些特殊情况下可能会有用:
LD_TRACE_LOADED_OBJECTS=1 /path/to/your/program
- 检查程序如何使用特定的共享库
如果你想要检查一个程序是如何使用特定的共享库的,可以结合使用ldd和nm命令。首先使用ldd找出依赖,然后用nm检查符号信息:
ldd /path/to/your/program | grep libexample.so
nm -D /path/to/libexample.so
- 使用ldd进行安全检查
虽然ldd主要用于依赖检查,但它也可以帮助识别潜在的安全风险。例如,通过检查程序依赖的库是否都来自可信路径,可以帮助识别可能的库劫持或路径注入问题。
ldd /path/to/your/program | grep -v "^/"
解决依赖问题
当你遇到因缺少共享库而导致的程序启动问题时,ldd可以帮助你快速定位缺少哪个库。通过比较不同环境(例如,开发和生产)下的ldd输出,你可以找出缺失的依赖。结合使用strace
虽然不是ldd的直接用法,但结合使用strace可以帮助你深入了解程序在运行时的行为,包括它如何加载共享库。通过观察程序启动过程中的系统调用strace /path/to/your/program 2>&1 | grep openat
nm 命令
nm命令在Linux中是一个非常有用的工具,它用于列出目标文件的符号表内容。符号表主要包含了程序中各种变量、函数的名称、类型、地址等信息。
下面是nm命令的一些基本用法:
查看目标文件的符号表:
nm 目标文件名
这将列出目标文件中所有符号的名称、类型和地址。
只显示未定义的符号:
nm -u 目标文件名
这个选项 (-u) 只会列出那些未定义的符号,这对于找出缺失的链接非常有用。
按大小排序显示符号:
nm --size-sort 目标文件名
使用 --size-sort 选项可以按照符号大小进行排序显示,这有助于分析哪些符号占用了较多空间。
只显示特定类型的符号:
nm --defined-only 目标文件名
通过 --defined-only 选项,可以只列出已定义的符号,忽略未定义的符号。
查看动态链接库中的符号:
nm -D 动态链接库文件名
-D 或 --dynamic 选项用于查看动态链接库(.so 文件)中的符号。
过滤输出结果:
nm 目标文件名 | grep 符号名称
使用管道 (|) 和 grep 命令可以过滤输出结果,仅显示与特定符号名称相关的行。
查看C++程序的符号:
nm -C 目标文件名
-C 或 --demangle 选项用于显示C++程序中更易读的符号名称,因为C++编译器通常会改变函数名(名字修饰)以支持重载等特性。
解析C++的符号名:
C++程序中的符号名经过编译器处理后会变得难以阅读。使用c++filt工具可以将这些符号名解析成更易于理解的形式。
nm 目标文件名 | c++filt
这样可以使C++的复杂符号名变得可读。
使用正则表达式过滤符号:
nm命令支持使用正则表达式来过滤显示的符号,这在你想要查找符合特定模式的符号时非常有用。
nm 目标文件名 | grep '正则表达式'
例如,使用grep '^T’可以过滤出所有类型为T(即在文本段中的符号)的符号。
比较不同版本的二进制文件:
通过比较不同版本的二进制文件中的符号表,可以快速了解两个版本之间增加或删除了哪些功能。
nm -an 旧版本文件名 > old_symbols.txt
nm -an 新版本文件名 > new_symbols.txt
diff old_symbols.txt new_symbols.txt
这里,-a选项表示列出所有符号,包括调试符号;-n选项表示按照地址排序。
查看符号的大小:
#使用--print-size选项可以打印每个符号的大小,这对于分析程序占用空间非常有帮助。
nm --print-size 目标文件名
输出格式化的信息:
#nm命令允许通过--format或者-f选项指定输出格式,支持的格式包括bsd、sysv、posix等,这有助于根据需求调整输出信息的格式。
nm --format=sysv 目标文件名
查找静态库中未使用的函数:
#创建一个包含所有符号的列表,然后使用nm检查静态库(.a文件)中哪些函数未被使用。
nm --undefined-only 静态库文件名
这将列出所有未定义的符号,即可能未被使用的函数。