dladdr 不返回函数名

问题描述 投票:0回答:4

我正在尝试使用 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

果然,0011fe73 = MatchRec8Cmp - 0xf1a43000。有人知道为什么 dladdr 不能返回 dli_sname = "MatchRec8Cmp" 吗???

我正在运行 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 而不是 -ggdb3 尝试过,尽管我认为调试符号与 elf 没有任何关系。

c linux elf
4个回答
11
投票

objdump能看到,为什么dladdr看不到

dladdr
只能在动态符号表中看到函数exported。很有可能

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

什么也没显示。确实你的objdump显示符号是local,证明是这个原因。

符号是局部的,因为它具有隐藏的可见性,是静态的,或者因为您以其他方式隐藏它(例如使用链接描述文件)。

更新:

那些标有“U”的人与 dladdr 一起工作。他们以某种方式自动“导出”。

它们之所以有效,是因为它们是从 其他一些共享库 导出的。

U
代表未解决,即在别处定义。

更新 2023/05/14:

我看到下面有几个“答案”告诉你添加

-rdynamic
--export-dynamic
来“解决”问题。

这些答案没有解释“为什么”(即它们不是所问问题的实际答案),也没有解释解决方案的成本,这可能很重要。

首先,添加

-rdynamic
标志是更可取的,因为这是一个编译器前端标志,它被翻译成 appropriate 链接器标志(一些链接器理解
-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,
前缀),这允许您控制 exactly 哪些符号被导出。

这是一个更精确的解决方案(与导出

everything
-rdynamic相比),并且成本(通常)显着降低。


5
投票

我将

-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.

3
投票

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


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.