ld、ldd、nm、readelf、objdump在代码调试中的使用, 查看库文件的内容

符号表symbol:不包括函数内部定义的变量,其无法通过readelf、nm、objdump等命令查询

1. 查看静态库里包含的符号:readelf -c libxxx.a  readelf -s libxxx.a 或 nm -g --defined-only libxxx.a 或 objdump -x xxx.a

动态库:readelf -a libxxx.so 或 objdump -tT xxx.so 或 nm -D xxx.so

nm -A libxxx.xx | grep objname

# 查看动态库有哪些符号,包括数据段、导出的函数和引用其他库的函数
objdump -tT xxx.so        objdump -x xxx.a

# 查看动态库依赖项
objdump -x xxx.so | grep "NEEDED"

# 查看动态符号表
objdump -T xxx.so
## 假如想知道 xxx.so 中是否导出了符号 yyy ,那么命令为 objdump -T xxx.so | grep "yyy" 。

# 查看动态符号表
objdump -t xxx.so## -T 和 -t 选项在于 -T 只能查看动态符号,如库导出的函数和引用其他库的函数,而 -t 可以查看所有的符号,包括数据段的符号。

 

nm命令:分析二进制文件、库文件、可执行文件中的符号表,返回二进制文件中各段的信息。

-A 或-o或 --print-file-name:打印出每个符号属于的文件
-a或--debug-syms:打印出所有符号,包括debug符号
-D 或--dynamic:显示动态符号而不显示普通符号,一般用于动态库。
-g或--extern-only:仅显示外部符号。外部符号是可以被其他目标文件或程序访问和使用的符号。在库文件中定义的函数、全局变量等都被视为外部符号。
--defined-only:仅显示定义的符号。已定义的符号是在目标文件中有具体实现或定义的符号。例如,如果目标文件包含函数的实现或全局变量的定义。

 

小写字母:本地符号the symbol is usually local。大写字母:全局符号he symbol is global (external)。u", "v" and "w" shown for special global symbols (").

                       p: 位于堆栈展开部分

符号  类型 说明
A 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
B 未被初始化的全局数据,该符号的值出现在非初始化数据段(bss)中。例如在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM中
C 该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B。
D 已经初始化的全局数据。该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中。
G 该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。
I 该符号是对另一个符号的间接引用。
N 该符号是一个debugging符号。
R 该符号位于只读数据区。例如定义全局const int test[] = {123, 123};则test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,test在rodata section中,大小为4。
S 符号位于非初始化数据区,用于small object。
T 该符号位于代码区text section。
U 该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。
V 该符号是一个weak object。
W The symbol is a weak symbol that has not been specifically tagged as a weak object symbol.符号为弱符号,当系统有定义符号时,使用定义符号,当系统未定义符号且定义了弱符号时,使用弱符号。
- 该符号是a.out格式文件中的stabs symbol。
? 该符号类型没有定义

objdump命令:用查看目标文件或者可执行的目标文件的构成。

  • -a--archive-headers :显示档案库的成员信息,类似 ls -l 将 lib*.a 的信息列出。
  • -e--debugging-tags :类似 -g 选项,但是生成的信息是和 ctags 工具相兼容的格式。
  • -f--file-headers :显示文件头信息。
  • -g--debugging :显示调试信息。企图解析保存在文件中的调试信息并以 C 语言的语法显示出来。仅仅支持某些类型的调试信息。有些其他的格式被 readelf -w 支持。
  • -h--[section-]headers :显示目标文件各个 section 的头部摘要信息。
  • -s--full-contents :显示指定 section 的完整内容。默认所有的非空 section 都会被显示。
  • -t--syms :显示文件的符号表入口。类似于 nm -s 提供的信息。
  • -T--dynamic-syms :显示文件的动态符号表入口,仅仅对动态目标文件意义,比如某些共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。
  • -x--all-headers :显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。

readelf命令:用来显示一个或者多个elf格式的目标文件的信息

readelf -s libxx.a/libxx.so/可执行文件, 打印库或可执行文件里包含的符号(函数、变量、常量或其他命名实体)

readelf -c libxxx.a 打印静态库里包含的符号。不可跟动态库、可执行文件,archive文件一般是指静态库文件 --archive-index     Display the symbol/file index in an archive

 

2. 系统中存在名字相同的两个库时,libA.so,  libB.so, CMakeLists.txt 在动态编译链接时指定了链接libB.so,但如果执行程序时 LD_LIBRARY_PATH环境变量里libA.so路径在libB.so之前,还是会优先使用libA.so。

echo $LD_LIBRARY_PATH查看搜索路径。   

 export LD_LIBRARY_PATH=path/to/lib:$LD_LIBRARY_PATH  可将新设置了路径放到已有LD_LIBRARY_PATH路劲之前

在调试程序时,出现库文件的问题,可使用ldd -u定位到没有找到的库文件,或ldd -d显示重定位的库和没找到的库,-r还可以显示出现问题的函数

ld xxx.so 可以找到库文件在链接时缺少的库文件。

3. ldd命令:-v:详细信息模式,打印所有相关信息;
-u:打印未使用的直接依赖;
-d:执行重定位和报告任何丢失的对象;
-r:执行数据对象和函数的重定位,并且报告任何丢失的对象和函数;
–help:显示帮助信息。

ldd命令可以打印可执行文件使用到的所有隐式调用的动态库,因为这些库的引用信息已经被编译到可执行文件中了。并且即使这些库又隐式调用了其它的库,ldd命令无论有多少层,都可以将其打印出来。但如果可执行文件或其隐式调用的库中显式调用了动态链接库,ldd无法识别,打印的结果中,“=>”左边的表示该程序需要连接的共享库的名字,右边表示由 Linux 的共享库系统找到的对应的共享库在文件系统中的具体位置。

显式调用的库是在执行过程中被加载到内存或卸除的,ldd命令并不实际执行文件,因此无法识别。

 

ldd targetfile | grep objname :查看targetfile中是否使用了objname对象 及其

 

ld命令是GNU的连接器,将目标文件连接为可执行程序。用于链接程序中的目标文件和它们之间的关系。它能够将一个可执行文件与模块的定义连接起来,也可以根据它们之间的依赖关系将多个可执行文件或者库文件连接在一起。

LD命令可以把多个源文件编译后生成的目标文件,结合成一个可执行文件。它需要接受一个或多个目标文件作为输入,并且可以添加额外的库文件,这些库文件可能会定义附加的函数和变量的引用。最终的可执行文件可以用来直接执行,也可以用来生成共享库文件。

1. 可以使用-E参数让LD命令只负责连接的任务,而不编译源文件。

2. 可以使用-r参数来合并多个源文件或者库文件到一个可执行文件或者共享库文件中。

3. 可以使用-d参数来指定LD命令是否要包括符号调试信息到输出文件中。

 

LD_DEBUG=libs ./program1

用于在 Linux 系统上调试动态库加载过程。通过设置 LD_DEBUG=libs,你可以让动态链接器在加载共享库时输出详细的调试信息,帮助你了解系统如何解析库路径、查找和加载共享库。

使用场景:

  • 当程序无法找到或加载特定的动态库时,LD_DEBUG=libs 可以帮助你追踪加载过程并识别问题。
  • 它可以帮助调试 LD_LIBRARY_PATHld.so.conf 或其他与动态库加载相关的问题
posted @ 2023-06-27 09:28  wieneralan  阅读(508)  评论(0编辑  收藏  举报