C语言 dladdr不返回函数名

q9rjltbz  于 2023-05-16  发布在  其他
关注(0)|答案(4)|浏览(211)

我正在尝试使用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有任何关系。

6uxekuva

6uxekuva1#

如果objdump可以看到它,为什么dladdr不能
dladdr只能在动态符号表中看到 * 导出 * 的函数。很有可能

nm -D /xxx/libdata.so | grep MatchRec8Cmp

什么都没有显示。事实上,你的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相比),并且成本(通常)显着降低。

np8igboo

np8igboo2#

我把-rdynamic添加到我的LDFLAGS。
man gcc说:

-rdynamic
    Pass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the
    dynamic symbol table. This option is needed for some uses of "dlopen" or to allow obtaining backtraces from within a program.
hs1ihplo

hs1ihplo3#

添加gcc选项“-export-dynamic”为我解决了这个问题。

mzaanser

mzaanser4#

hinesmr解决方案对我起作用了。我传递给gcc的确切选项是“-Wl,--export-dynamic”,所有函数都对dladdr可见

相关问题