为什么调用dlopen有时会破坏类变量的内容,从而破坏我的应用程序



我正在尝试用dlopen((加载库。但调用这个dlopen((函数有时(并不总是(会损坏我的类变量,然后应用程序就会出现分段错误。

以下不是精确的代码(伪代码(,但解释了发生了什么:

class MyClass {
public:
int MyVar;
void Print() { printf("Simply breakpointn"); };
void LoadLibrary() { dlopen("/usr/lib/x86_64-linux-gnu/libavcodec.so.58.54.100",RTLD_LAZY); };
MyClass() {
MyVar = 12345;
printf("MyVar address %pn",&MyVar);
Print();
LoadLibrary();
};
}
void main()
{
MyClass obj;
}

我用gdb调试它的方式如下:

>gdb MyApp
>break Print
>run

当它停止在打印函数断点时,我看到变量MyVar的打印地址。

MyVar address 0x7fff900bc2bc

我也可以检查它的内容。然后我做

>watch *0x7fff900bc2bc
Hardware watchpoint 2: *0x7fff900bc2bc
>cont

当它继续时,它会中断对我的变量MyVar:的意外写入

Thread 1 "MyApp" hit Hardware watchpoint 2: *0x7fff900bc2bc
Old value = 12345
New value = 32767
memmove () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:356
356 ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: No such file or directory.
(gdb) backtrace
#0  memmove () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:356
#1  0x00007ffff7fde759 in _dl_map_object_deps (map=map@entry=0x7fff90145110, preloads=preloads@entry=0x0, 
npreloads=npreloads@entry=0, trace_mode=trace_mode@entry=0, open_mode=open_mode@entry=-2147483648)
at dl-deps.c:446
#2  0x00007ffff7fe4db0 in dl_open_worker (a=a@entry=0x7fffa6fd80f0) at dl-open.c:571
#3  0x00007ffff53dd928 in __GI__dl_catch_exception (exception=<optimized out>, operate=<optimized out>, 
args=<optimized out>) at dl-error-skeleton.c:208
#4  0x00007ffff7fe460a in _dl_open (file=0x42d8ee0 "/usr/lib/x86_64-linux-gnu/libavcodec.so.58.54.100", 
mode=-2147483646, caller_dlopen=<optimized out>, nsid=-2, argc=2, argv=0x7fffffffea88, env=0x54037d0)
at dl-open.c:837
#5  0x00007ffff57bc34c in dlopen_doit (a=a@entry=0x7fffa6fd8310) at dlopen.c:66
#6  0x00007ffff53dd928 in __GI__dl_catch_exception (exception=exception@entry=0x7fffa6fd82b0, 
operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:208
#7  0x00007ffff53dd9f3 in __GI__dl_catch_error (objname=0x7fff900d8770, errstring=0x7fff900d8778, 
mallocedp=0x7fff900d8768, operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:227
#8  0x00007ffff57bcb59 in _dlerror_run (operate=operate@entry=0x7ffff57bc2f0 <dlopen_doit>, 
args=args@entry=0x7fffa6fd8310) at dlerror.c:170
#9  0x00007ffff57bc3da in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
#10 0x000000000209ec5b in MyClass::LoadLibrary() ()
.......

从堆栈回溯中,我看到MyVar被调用dlopen((损坏了但为什么呢?我做错了什么?如何解决?

不幸的是,我无法显示所有的源代码,因为它庞大,涉及许多不同的组件、许多线程和许多第三方库。我不能简单地将我的应用程序与libavcodec动态链接,因为它已经在第三方库中静态链接,但不幸的是,第三方图书馆是在没有所需功能的情况下构建的(没有VAAPI支持(。动态链接会导致符号冲突。这就是为什么我决定尝试通过dlopen((手动加载libavcodec,并从dlsym((获取所有所需的函数指针。

但为什么?我做错了什么?如何解决?

您没有说明您正在使用哪个版本的GLIBC(或哪个发行版(。

GLIBC-27dl-deps.c中的代码为:

struct link_map **l_initfini = (struct link_map **)
malloc ((2 * nneeded + 1) * sizeof needed[0]);
if (l_initfini == NULL)
_dl_signal_error (ENOMEM, map->l_name, NULL,
N_("cannot allocate dependency list"));
l_initfini[0] = l;
memcpy (&l_initfini[1], needed, nneeded * sizeof needed[0]);
memcpy (&l_initfini[nneeded + 1], l_initfini,
nneeded * sizeof needed[0]);                      // line 446

您也没有说明MyClass是堆分配的还是堆栈分配的。

GLIBC代码重写变量的一种方法是,当已经损坏了堆时。如果MyClass实际上是堆分配的(它似乎被赋予了0x7fff900bc2bc地址(,则这种情况尤其可能发生。

事实上;重写";只发生一部分时间也是堆损坏的症状。

作为第一步,我将在Valgrind下运行程序,并确保在LoadLibrary()运行之前没有检测到堆损坏(堆缓冲区溢出、空闲未分配、双重空闲等(。

最新更新