我正在尝试使用dladdr。它正确定位了库,但找不到函数名。我可以调用objdump,做一些数学运算,并获得传递给dladdr的函数的地址。如果objdump可以看到它,为什么dladdr不能?
下面是我的函数:
const char *FuncName(const void *pFunc)
{
Dl_info DlInfo;
int nRet;
// Lookup the name of the function given the function pointer
if ((nRet = dladdr(pFunc, &DlInfo)) != 0)
return DlInfo.dli_sname;
return NULL;
}
这里是一个gdb成绩单显示我得到了什么。
Program received signal SIGINT, Interrupt.
[Switching to Thread 0xf7f4c6c0 (LWP 28365)]
0xffffe410 in __kernel_vsyscall ()
(gdb) p MatchRec8Cmp
$2 = {void (TCmp *, TWork *, TThread *)} 0xf1b62e73 <MatchRec8Cmp>
(gdb) call FuncName(MatchRec8Cmp)
$3 = 0x0
(gdb) call FuncName(0xf1b62e73)
$4 = 0x0
(gdb) b FuncName
Breakpoint 1 at 0xf44bdddb: file threads.c, line 3420.
(gdb) call FuncName(MatchRec8Cmp)
Breakpoint 1, FuncName (pFunc=0xf1b62e73) at threads.c:3420
3420 {
The program being debugged stopped while in a function called from GDB.
When the function (FuncName) is done executing, GDB will silently
stop (instead of continuing to evaluate the expression containing
the function call).
(gdb) s
3426 if ((nRet = dladdr(pFunc, &DlInfo)) != 0)
(gdb)
3427 return DlInfo.dli_sname;
(gdb) p DlInfo
$5 = {dli_fname = 0x8302e08 "/xxx/libdata.so", dli_fbase = 0xf1a43000, dli_sname = 0x0, dli_saddr = 0x0}
(gdb) p nRet
$6 = 1
(gdb) p MatchRec8Cmp - 0xf1a43000
$7 = (void (*)(TCmp *, TWork *, TThread *)) 0x11fe73
(gdb) q
The program is running. Exit anyway? (y or n) y
下面是我从objdmp得到的
$ objdump --syms /xxx/libdata.so | grep MatchRec8Cmp
0011fe73 l F .text 00000a98 MatchRec8Cmp
果然,0011 fe 73 = MatchRec 8 Cmp-0xf 1a 43000。有人知道为什么dladdr不能返回dli_sname =“MatchRec 8 Cmp”吗???
Red Hat Enterprise Linux Server 5.4(Tikanga)我以前见过这种工作。可能是我的编译开关:
CFLAGS = -m32 -march=i686 -msse3 -ggdb3 -pipe -fno-common -fomit-frame-pointer \
-Ispio -fms-extensions -Wmissing-declarations -Wstrict-prototypes -Wunused -Wall \
-Wno-multichar -Wdisabled-optimization -Wmissing-prototypes -Wnested-externs \
-Wpointer-arith -Wextra -Wno-sign-compare -Wno-sequence-point \
-I../../../include -I/usr/local/include -fPIC \
-D$(Uname) -D_REENTRANT -D_GNU_SOURCE
我尝试过使用-g而不是-ggdb 3,尽管我不认为调试符号与elf有任何关系。
4条答案
按热度按时间6uxekuva1#
如果objdump可以看到它,为什么dladdr不能
dladdr
只能在动态符号表中看到 * 导出 * 的函数。很有可能什么都没有显示。事实上,你的objdump显示符号是 local,这证明这是原因。
符号是局部的,因为它具有隐藏的可见性,是静态的,或者因为您以其他方式隐藏它(例如使用链接器脚本)。
更新:
那些标有“U”的与dladdr一起工作。它们以某种方式自动“导出”。
它们工作是因为它们是从 * 其他共享库 * 导出的。
U
代表未解析,即在别处定义。更新2023/05/14:
我看到下面有几个“答案”告诉你添加
-rdynamic
或--export-dynamic
来“解决”问题。这些答案并不能解释“为什么”(即它们不是所问问题的实际答案),并且也没有解释解决方案的成本,这可能很重要。
首先,最好添加
-rdynamic
标志,因为这是一个编译器前端标志,它被翻译成 * 适当的 * 链接器标志(一些链接器理解-E
,一些理解--export-dynamic
,一些理解两者)。其次,如果你要添加一个特定于链接器的标志,你应该正确地做:
-Wl,--export-dynamic
。添加--export-dynamic
而不带-Wl,
前缀 * 碰巧工作 * 偶然- GCC不理解该标志并将其传递给链接器。但它将来可能会做别的事情。-rdynamic
的成本是多少?它会减慢可执行文件的加载速度。你会得到多少减速取决于nm -D a.out
的大小,与没有标志的情况相比。所有附加符号都被输入到加载器作用域中,并且每当需要解析共享库中的符号时,都会对其进行搜索。
如果你不想导出的符号被导出了,这也会破坏你的可执行文件。
有比
-rdynamic
更好的解决方案吗?很高兴你问了有可能是。
较新版本的链接器有
--export-dynamic-symbol SYMBOL
和--export-dynamic-symbol-list FILE
选项(如果要将这些标志传递给GCC,请使用-Wl,
前缀),这允许您控制 * 确切 * 导出哪些符号。这是一个更精确的解决方案(与导出 * 所有内容 * 的
-rdynamic
相比),并且成本(通常)显着降低。np8igboo2#
我把
-rdynamic
添加到我的LDFLAGS。man gcc
说:hs1ihplo3#
添加gcc选项“-export-dynamic”为我解决了这个问题。
mzaanser4#
hinesmr解决方案对我起作用了。我传递给gcc的确切选项是“-Wl,--export-dynamic”,所有函数都对dladdr可见