在Android平台上使用dlclose(…)时的分段错误

Android上使用动态加载API(< dlfcn.h&gt ;:dlopen(),dlclose()等)时,我有一些问题.
我使用NDK独立工具链(版本8)来编译应用程序和库.
Android版本是2.2.1 Froyo.

以下是简单共享库的源代码.

#include <stdio.h>

int iii = 0;
int *ptr = NULL;

__attribute__((constructor))
static void init()
{
    iii = 653;
}

__attribute__((destructor))
static void cleanup()
{
}

int aaa(int i)
{
    printf("aaa %d\n",iii);
}

这是使用上述库的程序源代码.

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

int main()
{
    void *handle;
    typedef int (*func)(int);
    func bbb;

    printf("start...\n");

    handle = dlopen("/data/testt/test.so",RTLD_LAZY);
    if (!handle)
    {
        return 0;
    }

    bbb = (func)dlsym(handle,"aaa");
    if (bbb == NULL)
    {
        return 0;
    }

    bbb(1);

    dlclose(handle);
    printf("exit...\n");

    return 0;
}

有了这些资源,一切都运行正常,但是当我尝试使用一些STL函数或类时,当main()函数退出时,程序会崩溃,例如当使用该源代码进行共享库时.

#include <iostream>

using namespace std;

int iii = 0;
int *ptr = NULL;

__attribute__((constructor))
static void init()
{
    iii = 653;
}

__attribute__((destructor))
static void cleanup()
{
}

int aaa(int i)
{
    cout << iii << endl;
}

使用此代码,程序会在分段故障后或在main()函数退出时崩溃.
我已经尝试过几次测试,并发现以下结果.

>没有使用STL,一切都正常.
>当使用STL并且最后不要调用dlclose()时,一切都正常.
>我尝试使用-fno-use-cxa-atexit或-fuse-cxa-atexit等各种编译标记进行编译,结果是一样的.

在使用STL的代码中有什么问题?

解决方法

看起来我发现了这个bug的原因.我已经尝试过另一个例子,其中包含以下源文件
这是简单类的源代码
myclass.h
class MyClass
{
public:
    MyClass();
    ~MyClass();
    void Set();
    void Show();
private:
    int *pArray;
};

myclass.cpp

#include <stdio.h>
#include <stdlib.h>
#include "myclass.h"

MyClass::MyClass()
{
    pArray = (int *)malloc(sizeof(int) * 5);
}

MyClass::~MyClass()
{
    free(pArray);
    pArray = NULL;
}

void MyClass::Set()
{
    if (pArray != NULL)
    {
        pArray[0] = 0;
        pArray[1] = 1;
        pArray[2] = 2;
        pArray[3] = 3;
        pArray[4] = 4;
    }
}

void MyClass::Show()
{
    if (pArray != NULL)
    {
        for (int i = 0; i < 5; i++)
        {
            printf("pArray[%d] = %d\n",i,pArray[i]);
        }
    }
}

代码中可以看出,我没有使用任何与STL相关的东西.
这是函数库导出的源文件.
func.h

#ifdef __cplusplus
extern "C" {
#endif

int SetBabe(int);
int ShowBabe(int);

#ifdef __cplusplus
}
#endif

func.cpp

#include <stdio.h>
#include "myclass.h"
#include "func.h"

MyClass cls;

__attribute__((constructor))
static void init()
{

}

__attribute__((destructor))
static void cleanup()
{

}

int SetBabe(int i)
{
    cls.Set();
    return i;
}

int ShowBabe(int i)
{
    cls.Show();
    return i;
}

最后这是使用库的程序的源代码.
main.cpp中

#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include "../simple_lib/func.h"

int main()
{
    void *handle;
    typedef int (*func)(int);
    func bbb;

    printf("start...\n");

    handle = dlopen("/data/testt/test.so",RTLD_LAZY);
    if (!handle)
    {
        printf("%s\n",dlerror());
        return 0;
    }

    bbb = (func)dlsym(handle,"SetBabe");
    if (bbb == NULL)
    {
        printf("%s\n",dlerror());
        return 0;
    }
    bbb(1);

    bbb = (func)dlsym(handle,"ShowBabe");
    if (bbb == NULL)
    {
        printf("%s\n",dlerror());
        return 0;
    }
    bbb(1);

    dlclose(handle);
    printf("exit...\n");

    return 0;
}

再次,你可以看到程序使用库也没有使用任何STL相关的东西,但在程序运行后,我得到相同的分段错误在主(…)功能退出.所以这个问题与STL本身没有联系,它隐藏在别的地方.经过长时间的研究,我发现了这个bug.通常,静态C变量的析构函数会在main(…)函数退出之前立即被调用,如果它们在main程序中定义,或者如果它们在某个库中定义并且正在使用它,那么析构函数应该立即被调用dlclose(…).在Android操作系统上,在main(…)函数退出调用静态C变量的所有析构函数(在主程序中定义或在某些库中使用).那么在我们的情况下会发生什么呢?我们在我们使用的库中定义了cls static C变量.然后在main(…)函数退出之前,我们调用dlclose(…)函数,结果库关闭,cls变为无效.但是cls的指针存储在某个地方,在main(…)函数退出时应该调用析构函数,因为在调用时它已经无效了,我们得到了分段错误.所以解决方案是不要调用dlclose(…),一切都应该是好的.不幸的是,使用这个解决方案,我们不能使用attribute((destructor))来对要重新初始化的东西进行初始化,因为它被调用为dlclose(…)调用的结果.

相关文章

这篇“android轻量级无侵入式管理数据库自动升级组件怎么实现...
今天小编给大家分享一下Android实现自定义圆形进度条的常用方...
这篇文章主要讲解了“Android如何解决字符对齐问题”,文中的...
这篇文章主要介绍“Android岛屿数量算法怎么使用”的相关知识...
本篇内容主要讲解“Android如何开发MQTT协议的模型及通信”,...
本文小编为大家详细介绍“Android数据压缩的方法是什么”,内...