c-使用"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: %sn", dlerror());
return 1;
}
if (dlsym(dlopen_res, "visible_sym") == NULL) {
printf("bb_so is NULL: %sn", dlerror());
return 1;
} else {
printf("'visible_sym' open okn");
}

if (dlsym(dlopen_res, "hidden_sym") == NULL) {
printf("bb_so is NULL: %sn", 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的解决方案。任何允许我访问隐藏符号的东西,而不修改库符号表,都将被视为可接受的解决方案。


我在现实世界中的用例非常相似——我想访问gprof在插入指令的代码中生成的评测信息。我仍然不能完全确定,但它似乎存储在声明为struct __bb *__bb_head __attribute__((visibility("hidden")));__bb_head变量中。使用<sys/gmon.h><sys/gmon_out.h>头可以访问结构定义,但我找不到任何方法来实际获取原始形式的分析数据。我知道gprof允许我在程序执行完成时转储信息,但我需要在运行时获取这些数据,而不必强制写入文件,然后重新读取。

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: %sn", dlerror());
return 1;
}
void* bb_so = dlsym(dlopen_res, "__bb_head");
if (bb_so == NULL) {
printf("bb_so is NULL: %sn", 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将能够找到它。


更新:

我确实希望这不是解决这个问题的唯一方法,但我想"隐藏意味着隐藏";在这种情况下,没有可接受的解决方法霍克斯克拉珀

AFAICT,隐藏意味着符号是全局,以便链接形成.so文件的各种.o文件可以访问该符号。然后,当.so被链接时,符号绑定被改变,使其看起来就像上面有static一样

我接受这个答案,因为它在技术上回答了我的问题,经过一整天的努力,我认为这是我能得到的最好的解决方案,尽管解决方案本身基本上意味着";没有一个合理的解决方案"霍克斯克拉珀

即使符号全局符号,您也可能无法安全访问/使用它。这是因为您要做的是在数据累积时转储数据。这可能是UB,因为它[可能]没有锁定/冻结数据。

特别是,周期性地发送信号以获得函数直方图[gprof在内部这样做]与运行的代码[通常]异步。

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

您可能可以启动跟踪,等待一段时间,停止跟踪(使用禁止的方法),转储数据。然后,重复这个过程。这不是你说的你想要的,但基于你所说的限制(即没有重建gprof库等),这可能已经足够妥协了

我想这取决于你想要什么性能数据,以及你愿意花多少精力来检测目标代码以获得它

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

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

我在每个事件条目中记录时间戳,这样我就可以看到在给定时间在每个函数中花费的确切时间。我也可以看到延迟(gprof不会提供),尤其是对于多线程应用程序。

也就是说,线程A在时间T1到达点X。它将数据排入线程B,线程A循环并等待更多数据。线程B在时间T2唤醒,将数据排成队列,对其进行处理,并在时间T3返回睡眠。线程A在时间T4唤醒。

现在,如果T2 - T1是"0";"过量";,那么我们想知道为什么。线程B是否仍在处理以前的请求。还是因为系统负载过重而延迟?T4 - T2T4 - T1时间短吗?

我使这个事件队列线程安全[而AFAICT、gprof不是]。

我以类似于dtrace的方式插入代码。

我保留了由全局主标志[或每个事件类型标志的向量]启用的指令插入调用。然后,我可以在远程系统上打开它们(例如在客户站点运行),以在性能"良好"的情况下收集数据;发行";[无论是什么]只显示在一个系统上,配置仅存在于客户的系统中。也就是说,尽管尽了最大努力,问题是在我可以访问的任何实验室/测试系统上都无法复制。

YMMV。。。

最新更新