跳过导航链接 | |
退出打印视图 | |
![]() |
Oracle Solaris 11.1 链接程序和库指南 Oracle Solaris 11.1 Information Library (简体中文) |
Oracle Solaris 运行时链接程序附带有调试库和调试 mdb(1) 模块。使用调试库,可以更详细地跟踪运行时链接过程。使用 mdb(1) 模块,可以进行交互式进程调试。
运行时链接程序提供了调试功能,允许您详细地跟踪应用程序的运行时链接及其依赖项情况。使用此功能显示的信息类型应保持不变。不过,信息的确切格式可能随发行版的不同而有所变化。
不了解运行时链接程序的用户可能不熟悉某些调试输出。不过,您可能对其中许多方面不太感兴趣。
可使用环境变量 LD_DEBUG 来启用调试。所有调试输出都使用进程标识符作为前缀。必须使用一个或多个标记来扩充此环境变量,以指示所需调试的类型。
使用 LD_DEBUG=help,可以显示 LD_DEBUG 中可用的标记。
$ LD_DEBUG=help prog
prog 可以是任何动态可执行文件。在将控制权转交给 prog 之前,进程终止并随后显示帮助信息。可执行文件的选择并不重要。
缺省情况下,会将所有调试输出发送到标准错误输出文件 stderr。可以使用 output 标记将调试输出定向到其他文件。例如,可以在名为 rtld-debug.txt 的文件中捕获帮助文本。
$ LD_DEBUG=help,output=rtld-debug.txt prog
此外,还可以通过设置环境变量 LD_DEBUG_OUTPUT 来重定向调试输出。使用 LD_DEBUG_OUTPUT 时,进程标识符作为后缀添加到输出文件名。
如果同时指定 LD_DEBUG_OUTPUT 和 output 标记,则 LD_DEBUG_OUTPUT 优先。如果同时指定 LD_DEBUG_OUTPUT 和 output 标记,则 LD_DEBUG_OUTPUT 优先。将 output 标记与调用 fork(2) 的程序结合使用会导致每个进程都将调试输出写入同一文件。调试输出因此会变得混乱且不完整。在这种情况下应使用 LD_DEBUG_OUTPUT,以将每个进程的调试输出定向到唯一的文件。
对于安全的应用程序,不允许进行调试。
其中一个最有用的调试选项是显示运行时进行的符号绑定。以下示例使用很简单的动态可执行文件,该可执行文件依赖于两个局部共享目标文件。
$ cat bar.c int bar = 10; $ cc -o bar.so.1 -K pic -G bar.c $ cat foo.c int foo(int data) { return (data); } $ cc -o foo.so.1 -K pic -G foo.c $ cat main.c extern int foo(); extern int bar; int 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,可提供有关实际绑定位置的实际地址和相对地址的其他信息。
可以使用 LD_DEBUG 来显示使用的各个搜索路径。例如,通过设置 LD_DEBUG=libs,可以显示用于查找所有依赖项的搜索路径机制。
$ LD_DEBUG=libs prog 11775: 11775: find object=foo.so.1; searching 11775: search path=/tmp:. (RUNPATH/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:. (RUNPATH/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。