Linux程序分析工具:ldd和nm

ldd和nm是Linux下两个非常实用的程序分析工具。其中,ldd是用来分析程序运行时需要依赖的动态链接库的工具,nm是用来查看指定程序中的符号表信息的工具。

1 ldd

格式:ldd [options] file

功能:列出file运行所需的共享库

参数:

-d 执行重定位并报告所有丢失的函数

-r 执行对函数和对象的重定位并报告丢失的任何函数或对象

首先,ldd不是一个可执行程序,而是一个shell脚本。ldd能够显示可执行模块的dependency,其原理是通过设置一系列的环境变量,如下:LD_TRACE_LOADED_OBJECTS、LD_WARN、LD_BIND_NOW、LD_LIBRARY_VERSION、LD_VERBOSE等。当LD_TRACE_LOADED_OBJECTS环境变量不为空时,任何可执行程序在运行时,它都会只显示模块的dependency,而程序并不真正执行。要不你可以在shell终端测试一下,如下:

(1) export LD_TRACE_LOADED_OBJECTS=1

(2) 再执行任何的程序,如ls等,看看程序的运行结果。

ldd显示可执行模块的dependency的工作原理,其实质是通过ld-linux.so(elf动态库的装载器)来实现的。我们知道,ld-linux.so模块会先于executable模块程序工作,并获得控制权,因此当上述的那些环境变量被设置时,ld-linux.so选择了显示可执行模块的dependency。

实际上可以直接执行ld-linux.so模块,如:/lib/ld-linux.so.2 –list program(这相当于ldd program)ldd命令使用方法(摘自ldd –help)

我们选择一段待测试的应用程序,代码如下:

//@file tooltest.c//@brief resource sharing between parent-process and sub-process#include <stdio.h>#include <stdlib.h>#include <unistd.h>main(void){pid_t pid;stack = *heap;//pointer to a heap variableheap = (int *)malloc(sizeof(int));*heap = 2;//set the heap value to 2pid = fork();(pid < 0){//errorperror();exit(-1);}else if (pid == 0){++;stack++;(*heap)++;//print all valuesprintf(, global, stack, *heap);exit(0);}else{//parent processsleep(2);//sleep 2 secends to make sure the sub-process runs firstprintf(, global, stack, *heap);}return 0;}

然后,,我们编译并运行ldd命令:

xiaomanon@xiaomanon-machine:~/Documents/c_code$ ldd tooltestlinux-gate.so.1 => (0xb775b000)libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7595000)/lib/ld-linux.so.2 (0xb775c000)

我们可以将ldd的输出结果分为3列来看:

■ 第一列:程序需要依赖什么库

■ 第二列:系统提供的与程序需要的库对应的库名称

■ 第三列:依赖库加载的开始地址

通过上面的这些信息,我们可以总结出下面的用途:

(1) 通过对比第一列和第二列,我们可以知道程序需要的动态链接库和系统实际提供的是否相比配。

(2) 通过第三列,我们可以知道当前动态链接库中的符号在进程地址空间中的起始位置。

2 nm

格式:nm [options] file

功能:列出file中的所有符号

参数:

-C 将符号转化为用户级的名字

-s 当用于.a文件即静态库时,输出把符号名映射到定义该符号的模块或成员名的索引

-u 显示在file外定义的符号或没有定义的符号

-l 显示每个符号的行号,或为定义符号的重定义项

下面是运行nm命令的输出结果:

xiaomanon@xiaomanon-machine:~/Documents/c_code$ nm tooltest0804a038 B __bss_start0804a038 b completed.65900804a02c D __data_start0804a02c W data_start08048450 t deregister_tm_clones080484c0 t __do_global_dtors_aux08049f0c t __do_global_dtors_aux_fini_array_entry0804a030 D __dso_handle08049f14 d _DYNAMIC0804a038 D _edata0804a03c B _endU exit@@GLIBC_2.008048674 T _finiU fork@@GLIBC_2.008048688 R _fp_hw080484e0 t frame_dummy08049f08 t __frame_dummy_init_array_entry080487e0 r __FRAME_END__0804a034 D global0804a000 d _GLOBAL_OFFSET_TABLE_w __gmon_start__08048354 T _init08049f0c t __init_array_end08049f08 t __init_array_start0804868c R _IO_stdin_usedw _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTable08049f10 d __JCR_END__08049f10 d __JCR_LIST__w _Jv_RegisterClasses08048670 T __libc_csu_fini08048600 T __libc_csu_initU __libc_start_main@@GLIBC_2.00804850d T mainU malloc@@GLIBC_2.0U perror@@GLIBC_2.0U printf@@GLIBC_2.008048480 t register_tm_clonesU sleep@@GLIBC_2.008048410 T _start0804a038 D __TMC_END__08048440 T __x86.get_pc_thunk.bx

上面便是tooltest这个程序中所有的符号,首先介绍一下上面输出内容的格式:

■ 第一列:当前符号的地址。

■ 第二列:当前符号的类型(关于类型的说明,可以查看手册页man nm详细阅读)。

■ 第三列:当前符号的名称。

使用nm主要有一下几个方面的帮助:

(1) 判断指定的程序中有没有指定的符号,比较常用的方式为:nm –C program | grep symbol

(2) 解决程序编译时undefined reference的错误,以及multiple definition的错误。

(3) 查看某个符号的地址,以及在进程空间的大概位置(.bss, .data, .text段,具体可以通过第二列的类型来判断)。

部分符号类型说明

A : 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。

即使是不成熟的尝试,也胜于胎死腹中的策略。

Linux程序分析工具:ldd和nm

相关文章:

你感兴趣的文章:

标签云: