使用“dlsym”访问具有隐藏可见性属性的动态符号

Access dynamic symbol with hidden visibility attribute using `dlsym`

我有一个动态链接库,它定义了我需要访问的 __attribute__((visibility("hidden"))) 符号。这是一个简化的代码

shared.c

__attribute__((visibility("hidden"))) int hidden_sym[12];
int                                       visible_sym[12];

shared_user.c

#include <dlfcn.h>
#include <stdio.h>

int main() {
    void* dlopen_res = dlopen("./libnogcc.so", RTLD_LAZY);
    if (dlopen_res == NULL) {
        printf("dlopen_res is NULL: %s\n", dlerror());
        return 1;
    }

    if (dlsym(dlopen_res, "visible_sym") == NULL) {
        printf("bb_so is NULL: %s\n", dlerror());
        return 1;
    } else {
        printf("'visible_sym' open ok\n");
    }


    if (dlsym(dlopen_res, "hidden_sym") == NULL) {
        printf("bb_so is NULL: %s\n", dlerror());
        return 1;
    }
}

compilation and execution

gcc shared.c -fpic -shared -olibnogcc.so
gcc -ldl shared_user.c -o shared_main
./shared_main

它正确加载 visible_symbol,但预计无法解析隐藏符号:

'visible_sym' open ok
bb_so is NULL: ./libnogcc.so: undefined symbol: hidden_sym

我想知道是否有任何解决方法可以让我访问隐藏的符号。

请注意,它不需要是基于 dlsym 的解决方案。任何能让我访问隐藏符号而不修改库符号 table 的方法都将被视为可接受的解决方案。


我的 real-world 用例非常相似 - 我想访问由 gprof 在检测代码中生成的分析信息。我仍然不确定,但它似乎存储在声明为 struct __bb *__bb_head __attribute__((visibility("hidden")));__bb_head 变量中。可以使用 <sys/gmon.h><sys/gmon_out.h> headers 访问结构定义,但我无法找到任何方法以原始形式实际获取分析数据。我知道 gprof 允许我在程序完成执行时转储信息,但我需要在运行时获取此数据,而不必强制写入文件然后 re-reading 返回。

code for accessing libc data

#include <dlfcn.h>
#include <stdio.h>
#include <sys/gmon.h>
#include <sys/gmon_out.h>

int main() {
    void* dlopen_res = dlopen("libc.so.6", RTLD_LAZY);
    if (dlopen_res == NULL) {
        printf("dlopen_res is NULL: %s\n", dlerror());
        return 1;
    }

    void* bb_so = dlsym(dlopen_res, "__bb_head");
    if (bb_so == NULL) {
        printf("bb_so is NULL: %s\n", dlerror());
        return 1;
    }
}

我刚刚对你的简单 .so 进行了测试。

hidden_sym确实出现在.symtab

如果你这样做 readelf -a,你将能够看到它:

     6: 0000000000004040    48 OBJECT  GLOBAL DEFAULT   21 visible_sym
    39: 0000000000004080    48 OBJECT  LOCAL  DEFAULT   21 hidden_sym
    47: 0000000000004040    48 OBJECT  GLOBAL DEFAULT   21 visible_sym

dlsym 可能找不到它,但您可以解析 readelf 输出或使用 libelf 来获取符号。

您可以使用 /proc/self/maps 找到库的加载地址,然后应用偏移量。

或者,您可以复制 .so 文件。然后,通过编辑副本将绑定从 LOCAL 更改为 GLOBAL。可能有现成的工具可以让您执行此操作。

那么dlsym能找到


更新:

I sure do hope that is not the only way to get around this, but I guess "hidden means hidden" in this case, and there is no acceptable workaround. – haxscramper

AFAICT,隐藏意味着符号是 global,因此 link 的各种 .o 文件组成 .so 文件可以访问符号。然后,当 .so 被 link 编辑时,符号绑定被更改,就好像它上面有 static

I'm accepting this answer because it technically answers my question, and after a whole day of trying to find a workaround, I think that's as good of a solution I can get, even though the solution itself basically means "there is no sane solution". – haxscramper

即使符号 全局的,您也可能无法安全地 access/utilize 它。那是因为您要做的是在累积数据时转储数据。这可能是 UB,因为它 [可能] 没有 lock/freeze 数据。

特别是,定期发送信号以获得函数直方图 [gprof 在内部执行] [通常] 与 运行 代码异步。

您必须对其进行测试以确认您可以安全地访问数据。

您也许可以开始跟踪,稍等片刻,停止跟踪 [使用被禁止的方法],转储数据。并且,重复这个过程。这不是你所说的你想要的,但它可能足以妥协,基于你规定的限制(即不重建 gprof 库等)。

我想这取决于您想要什么样的性能数据以及您愿意花费多少精力来检测目标代码以获取它。

对于您当前的用例来说它可能不值得,但如果您要对其他项目进行性能分析,您可以重用您在未来开发的自定义方法(即)它成为您的一部分个人编程“技巧包”。

当我需要性能数据时,我通常会自己滚动。我维护一个“事件”结构的环形队列。事件可以是任何内容(例如)enter_func、exit_func、func_is_at_line_X 等

我在每个事件条目中记录时间戳,因此我可以看到在给定时间在每个函数中花费的确切时间。我也可以看到 latency [gprof 不会提供],特别是对于多线程应用程序。

即线程A在时间T1到达X点。它将数据排队到线程 B 和线程 A 循环并等待更多数据。线程 B 在时间 T2 醒来,从队列中取出数据,对其进行处理,然后在时间 T3 返回睡眠状态。线程 A 在时间 T4 醒来。

现在,如果 T2 - T1 是“过度”,那么我们想知道为什么。线程 B 是否仍在处理先前的请求。或者,是否因为系统负载过重而延迟? T4 - T2 是不是比时间 T4 - T1 [这就是我们所希望的]?

我使这个事件队列线程安全[据我所知,gprof 不是]。

我以类似于 dtrace 的方式检测代码。

我将检测调用留在原地,由全局主标志 [或每个事件类型标志的向量] 启用。然后,我可以在远程系统(例如客户站点的 运行)上打开它们,以在性能“问题”[无论是什么] 仅出现在 的情况下收集数据one 系统配置仅存在于客户系统中。也就是说,尽管尽了最大努力,但问题无法在我可以访问的任何lab/test系统上重现。

YMMV ...