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

前端之家收集整理的这篇文章主要介绍了在Android平台上使用dlclose(…)时的分段错误前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
Android上使用动态加载API(< dlfcn.h&gt ;:dlopen(),dlclose()等)时,我有一些问题.
我使用NDK独立工具链(版本8)来编译应用程序和库.
Android版本是2.2.1 Froyo.

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

  1. #include <stdio.h>
  2.  
  3. int iii = 0;
  4. int *ptr = NULL;
  5.  
  6. __attribute__((constructor))
  7. static void init()
  8. {
  9. iii = 653;
  10. }
  11.  
  12. __attribute__((destructor))
  13. static void cleanup()
  14. {
  15. }
  16.  
  17. int aaa(int i)
  18. {
  19. printf("aaa %d\n",iii);
  20. }

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

  1. #include <dlfcn.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4.  
  5. int main()
  6. {
  7. void *handle;
  8. typedef int (*func)(int);
  9. func bbb;
  10.  
  11. printf("start...\n");
  12.  
  13. handle = dlopen("/data/testt/test.so",RTLD_LAZY);
  14. if (!handle)
  15. {
  16. return 0;
  17. }
  18.  
  19. bbb = (func)dlsym(handle,"aaa");
  20. if (bbb == NULL)
  21. {
  22. return 0;
  23. }
  24.  
  25. bbb(1);
  26.  
  27. dlclose(handle);
  28. printf("exit...\n");
  29.  
  30. return 0;
  31. }

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

  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. int iii = 0;
  6. int *ptr = NULL;
  7.  
  8. __attribute__((constructor))
  9. static void init()
  10. {
  11. iii = 653;
  12. }
  13.  
  14. __attribute__((destructor))
  15. static void cleanup()
  16. {
  17. }
  18.  
  19. int aaa(int i)
  20. {
  21. cout << iii << endl;
  22. }

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

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

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

解决方法

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

myclass.cpp

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "myclass.h"
  4.  
  5. MyClass::MyClass()
  6. {
  7. pArray = (int *)malloc(sizeof(int) * 5);
  8. }
  9.  
  10. MyClass::~MyClass()
  11. {
  12. free(pArray);
  13. pArray = NULL;
  14. }
  15.  
  16. void MyClass::Set()
  17. {
  18. if (pArray != NULL)
  19. {
  20. pArray[0] = 0;
  21. pArray[1] = 1;
  22. pArray[2] = 2;
  23. pArray[3] = 3;
  24. pArray[4] = 4;
  25. }
  26. }
  27.  
  28. void MyClass::Show()
  29. {
  30. if (pArray != NULL)
  31. {
  32. for (int i = 0; i < 5; i++)
  33. {
  34. printf("pArray[%d] = %d\n",i,pArray[i]);
  35. }
  36. }
  37. }

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

  1. #ifdef __cplusplus
  2. extern "C" {
  3. #endif
  4.  
  5. int SetBabe(int);
  6. int ShowBabe(int);
  7.  
  8. #ifdef __cplusplus
  9. }
  10. #endif

func.cpp

  1. #include <stdio.h>
  2. #include "myclass.h"
  3. #include "func.h"
  4.  
  5. MyClass cls;
  6.  
  7. __attribute__((constructor))
  8. static void init()
  9. {
  10.  
  11. }
  12.  
  13. __attribute__((destructor))
  14. static void cleanup()
  15. {
  16.  
  17. }
  18.  
  19. int SetBabe(int i)
  20. {
  21. cls.Set();
  22. return i;
  23. }
  24.  
  25. int ShowBabe(int i)
  26. {
  27. cls.Show();
  28. return i;
  29. }

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

  1. #include <dlfcn.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include "../simple_lib/func.h"
  5.  
  6. int main()
  7. {
  8. void *handle;
  9. typedef int (*func)(int);
  10. func bbb;
  11.  
  12. printf("start...\n");
  13.  
  14. handle = dlopen("/data/testt/test.so",RTLD_LAZY);
  15. if (!handle)
  16. {
  17. printf("%s\n",dlerror());
  18. return 0;
  19. }
  20.  
  21. bbb = (func)dlsym(handle,"SetBabe");
  22. if (bbb == NULL)
  23. {
  24. printf("%s\n",dlerror());
  25. return 0;
  26. }
  27. bbb(1);
  28.  
  29. bbb = (func)dlsym(handle,"ShowBabe");
  30. if (bbb == NULL)
  31. {
  32. printf("%s\n",dlerror());
  33. return 0;
  34. }
  35. bbb(1);
  36.  
  37. dlclose(handle);
  38. printf("exit...\n");
  39.  
  40. return 0;
  41. }

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

猜你在找的Android相关文章