Solaris 链接程序附带有调试库和 mdb(1) 模块。使用调试库,可以更详细地跟踪运行时链接过程。使用 mdb(1) 模块,可以进行交互式进程调试。
此调试库有助于了解或调试应用程序和依赖项的执行。使用此库显示的信息类型应保持不变。不过,信息的确切格式可能随发行版的不同而有所变化。
不了解运行时链接程序的用户可能不熟悉某些调试输出。不过,也许您希望大概了解其中许多方面。
可使用环境变量 LD_DEBUG 来启用调试。所有调试输出都使用进程标识符作为前缀,并且在缺省情况下会指示为标准错误。必须使用一个或多个标记来扩充此环境变量,以指示所需调试的类型。
使用 LD_DEBUG=help,可以显示 LD_DEBUG 中可用的标记。当进程在显示信息后终止时,可以使用任何动态可执行文件请求此信息。
$ LD_DEBUG=help prog 11693: 11693: For debugging the runtime linking of an application: 11693: LD_DEBUG=token1,token2 prog 11693: enables diagnostics to the stderr. The additional 11693: option: 11693: LD_DEBUG_OUTPUT=file 11693: redirects the diagnostics to an output file created 11593: using the specified name and the process id as a 11693: suffix. All diagnostics are prepended with the 11693: process id. 11693: 11693: 11693: audit display runtime link-audit processing 11693: basic provide basic trace information/warnings 11693: bindings display symbol binding; detail flag shows 11693: absolute:relative addresses 11693: cap display hardware/software capability processing 11693: detail provide more information in conjunction with other 11693: options 11693: files display input file processing (files and libraries) 11693: help display this help message 11693: init display init and fini processing 11693: libs display library search paths 11693: move display move section processing 11693: reloc display relocation processing 11693: symbols display symbol table processing 11693: tls display TLS processing info 11693: unused display unused/unreferenced files 11693: versions display version processing |
此示例显示对运行时链接程序有意义的选项。确切选项可能随发行版的不同而有所变化。
环境变量 LD_DEBUG_OUTPUT 可用来指定使用输出文件来代替标准错误。进程标识符将作为后缀添加到输出文件。
对于安全的应用程序,不允许进行调试。
其中一个最有用的调试选项是显示运行时进行的符号绑定。以下示例使用很简单的动态可执行文件,该可执行文件依赖于两个局部共享库。
$ cat bar.c int bar = 10; $ cc -o bar.so.1 -K pic -G bar.c $ cat foo.c foo(int data) { return (data); } $ cc -o foo.so.1 -K pic -G foo.c $ cat main.c extern int foo(); extern int bar; main() { return (foo(bar)); } $ cc -o prog main.c -R/tmp:. foo.so.1 bar.so.1 |
通过设置 LD_DEBUG=bindings,可以显示运行时符号绑定。
$ LD_DEBUG=bindings prog 11753: ....... 11753: binding file=prog to file=./bar.so.1: symbol bar 11753: ....... 11753: transferring control: prog 11753: ....... 11753: binding file=prog to file=./foo.so.1: symbol foo 11753: ....... |
符号 bar 是即时重定位所需的,将在应用程序获取控制权之前绑定。然而,符号 foo 是延迟重定位所需的,将在应用程序获得第一次调用函数的控制权之后绑定。此重定位说明了延迟绑定的缺省模式。如果设置了环境变量 LD_BIND_NOW,则所有符号绑定都会在应用程序获取控制权之前进行。
通过设置 LD_DEBUG=bindings,detail,可提供有关实际绑定位置的实际地址和相对地址的其他信息。
当运行时链接程序执行函数重定位时,将重写与函数 .plt 关联的数据。通过 .plt 进行的后续调用将直接转至该函数。环境变量 LD_BIND_NOT 可设置为任何值以免更新此数据。通过将此变量与对详细绑定的调试请求一起使用,可以全盘了解所有函数绑定的运行时情况。此组合可能会产生大量输出,从而导致应用程序性能下降。
可以使用 LD_DEBUG 来显示使用的各个搜索路径。例如,通过设置 LD_DEBUG=libs,可以显示用于查找所有依赖项的搜索路径机制。
$ LD_DEBUG=libs prog 11775: 11775: find object=foo.so.1; searching 11775: search path=/tmp:. (RPATH from file prog) 11775: trying path=/tmp/foo.so.1 11775: trying path=./foo.so.1 11775: 11775: find object=bar.so.1; searching 11775: search path=/tmp:. (RPATH from file prog) 11775: trying path=/tmp/bar.so.1 11775: trying path=./bar.so.1 11775: ....... |
应用程序 prog 中记录的运行路径将影响两个依赖项 foo.so.1 和 bar.so.1 的搜索。
同样,可通过设置 LD_DEBUG=symbols 来显示每个符号查找的搜索路径。组合使用 symbols 和 bindings 可生成符号重定位进程的完整信息。
$ LD_DEBUG=bindings,symbols prog 11782: ....... 11782: symbol=bar; lookup in file=./foo.so.1 [ ELF ] 11782: symbol=bar; lookup in file=./bar.so.1 [ ELF ] 11782: binding file=prog to file=./bar.so.1: symbol bar 11782: ....... 11782: transferring control: prog 11782: ....... 11782: symbol=foo; lookup in file=prog [ ELF ] 11782: symbol=foo; lookup in file=./foo.so.1 [ ELF ] 11782: binding file=prog to file=./foo.so.1: symbol foo 11782: ....... |
在前面的示例中,未在应用程序 prog 中搜索符号 bar。省略数据引用查找是因为在处理复制重定位时使用了优化。有关此重定位类型的更多详细信息,请参见复制重定位。
调试器模块提供了一组可在 mdb(1) 下装入的 dcmds 和 walkers。此模块可用于检查运行时链接程序的各种内部数据结构。许多调试信息都要求您熟悉运行时链接程序的内部构造,并且该信息会随发行版的不同而有所变化。但是,这些数据结构中的某些元素显示了动态链接进程的基本组件,有助于进行一般调试。
以下示例显示了一些将 mdb(1) 与运行时链接程序调试器模块一起使用的简单情况。
$ cat main.c #include <dlfnc.h> int main() { void * handle; void (* fptr)(); if ((handle = dlopen("foo.so.1", RTLD_LAZY)) == NULL) return (1); if ((fptr = (void (*)())dlsym(handle, "foo")) == NULL) return (1); (*fptr)(); return (0); } $ cc -o main main.c -R. |
如果 mdb(1) 尚未自动装入调试器模块 ld.so,则显式装入该模块。之后可以检查调试器模块的功能。
$ mdb main > ::load ld.so > ::dmods -l ld.so ld.so ----------------------------------------------------------------- dcmd Bind - Display a Binding descriptor dcmd Callers - Display Rt_map CALLERS binding descriptors dcmd Depends - Display Rt_map DEPENDS binding descriptors dcmd ElfDyn - Display Elf_Dyn entry dcmd ElfEhdr - Display Elf_Ehdr entry dcmd ElfPhdr - Display Elf_Phdr entry dcmd Groups - Display Rt_map GROUPS group handles dcmd GrpDesc - Display a Group Descriptor dcmd GrpHdl - Display a Group Handle dcmd Handles - Display Rt_map HANDLES group descriptors .... > ::bp main > :r |
进程中的每个动态库都表示为链接映射 Rt_map,该映射在链接映射列表中对其进行维护。可使用 Rt_maps 显示进程的所有链接映射。
> ::Rt_maps Link-map lists (dynlm_list): 0xffbfe0d0 ---------------------------------------------- Lm_list: 0xff3f6f60 (LM_ID_BASE) ---------------------------------------------- lmco rtmap ADDR() NAME() ---------------------------------------------- [0xc] 0xff3f0fdc 0x00010000 main [0xc] 0xff3f1394 0xff280000 /lib/libc.so.1 ---------------------------------------------- Lm_list: 0xff3f6f88 (LM_ID_LDSO) ---------------------------------------------- [0xc] 0xff3f0c78 0xff3b0000 /lib/ld.so.1 |
可使用 Rt_map 显示单个链接映射。
> 0xff3f9040::Rt_map Rt_map located at: 0xff3f9040 NAME: main PATHNAME: /export/home/user/main ADDR: 0x00010000 DYN: 0x000207bc NEXT: 0xff3f9460 PREV: 0x00000000 FCT: 0xff3f6f18 TLSMODID: 0 INIT: 0x00010710 FINI: 0x0001071c GROUPS: 0x00000000 HANDLES: 0x00000000 DEPENDS: 0xff3f96e8 CALLERS: 0x00000000 ..... |
可使用 ElfDyn dcmd 显示目标文件的 .dynamic 节。以下示例显示了前 4 项。
> 0x000207bc,4::ElfDyn Elf_Dyn located at: 0x207bc 0x207bc NEEDED 0x0000010f Elf_Dyn located at: 0x207c4 0x207c4 NEEDED 0x00000124 Elf_Dyn located at: 0x207cc 0x207cc INIT 0x00010710 Elf_Dyn located at: 0x207d4 0x207d4 FINI 0x0001071c |
mdb(1) 在设置推迟断点时也很有用。在此示例中,函数 foo() 中的断点可能会很有用。但是,在对 foo.so.1 执行 dlopen(3C) 之前,调试器不知道此符号。推迟断点会指示调试器在装入动态库时设置实际断点。
> ::bp foo.so.1`foo > :c > mdb: You've got symbols! > mdb: stop at foo.so.1`foo mdb: target stopped at: foo.so.1`foo: save %sp, -0x68, %sp |
此时,已经装入了新目标文件:
> *ld.so`lml_main::Rt_maps lmco rtmap ADDR() NAME() ---------------------------------------------- [0xc] 0xff3f0fdc 0x00010000 main [0xc] 0xff3f1394 0xff280000 /lib/libc.so.1 [0xc] 0xff3f9ca4 0xff380000 ./foo.so.1 [0xc] 0xff37006c 0xff260000 ./bar.so.1 |
foo.so.1 的链接映射显示了 dlopen(3C) 返回的句柄。可使用 Handles 来扩展此结构。
> 0xff3f9ca4::Handles -v HANDLES for ./foo.so.1 ---------------------------------------------- HANDLE: 0xff3f9f60 Alist[used 1: total 1] ---------------------------------------------- Group Handle located at: 0xff3f9f28 ---------------------------------------------- owner: ./foo.so.1 flags: 0x00000000 [ 0 ] refcnt: 1 depends: 0xff3f9fa0 Alist[used 2: total 4] ---------------------------------------------- Group Descriptor located at: 0xff3f9fac depend: 0xff3f9ca4 ./foo.so.1 flags: 0x00000003 [ AVAIL-TO-DLSYM,ADD-DEPENDENCIES ] ---------------------------------------------- Group Descriptor located at: 0xff3f9fd8 depend: 0xff37006c ./bar.so.1 flags: 0x00000003 [ AVAIL-TO-DLSYM,ADD-DEPENDENCIES ] |
句柄的依赖项由一组链接映射组成,这些链接映射表示可满足 dlsym(3C) 请求的句柄的目标文件。在此情况下,依赖项为 foo.so.1 和 bar.so.1。
前面的示例提供了调试器模块功能的基本指南,但确切的命令、用法和输出可能会随发行版的不同而有所变化。有关系统中可用的确切功能,请参阅用法和帮助信息。