当链接动态库时出现下面这样的错误时,一般是动态库生成的编译选项问题
found local symbol '__bss_start' in global part of symbol table
found local symbol '_edata' in global part of symbol table
found local symbol '_end' in global part of symbol table
解决方法:
Android.mk :: APP_LDFLAGS := -fuse-ld=gold
CMakeLists.txt:
/home/user/cmake-3.12.1/bin/cmake -DCMAKE_SHARED_LINKER_FLAGS=-fuse-ld=gold .. -DANDROID_ABI=arm64-v8a 后面省略了
查看确认方法:readelf -s *.so |grep "__bss_start"
参考链接:
https://stackoverflow.com/questions/55014879/local-symbol-bss-start-in-global-part-of-symbol-table-only-in-android-ndk
当链接动态库时出现下面这样的错误时,一般是动态库生成的编译选项问题found local symbol '__bss_start' in global part of symbol tablefound local symbol '_edata' in global part of symbol tablefound local symbol '_end' in global par...
如何使用dlopen API动态地加载C++函数和类,是Unix C++程序员经常碰到的
问题
。
事实上,情况偶尔有些复杂,需要一些解释。这正是写这篇mini HOWTO的缘由。
理解这篇文档的前提是对C/C++语言中dlopen API有基本的了解。
这篇HOWTO的维护链接是:
http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/
二、
问题
所在
有时你想在运行时加载一个库(并使用其中的函数),这在你为你的程序写一些插件或模块架构的时候经常发生。
在C语言中,加载一个库轻而易举(调用dlopen、dlsym和dlclose就够了),但对C++来说,情况稍微复杂。
动态加载一个C++库的困难一部分是因为C++的name mangling
(译者注:也有人把它翻译为“名字毁坏”,我觉得还是不翻译好),
另一部分是因为dlopen API是用C语言实现的,因而没有提供一个合适的方式来装载类。
在解释如何装载C++库之前,最好再详细了解一下name mangling。
我推荐您了解一下它,即使您对它不感兴趣。因为这有助于您理解
问题
是如何产生的,如何才能解决它们。
1. Name Mangling
在每个C++程序(或库、目标文件)中,
所有非静态(non-static)函数在二进制文件中都是以“符号(symbol)”形式出现的。
这些符号都是唯一的字符串,从而把各个函数在程序、库、目标文件中区分开来。
在C中,符号名正是函数名:strcpy函数的符号名就是“strcpy”,等等。
这可能是因为两个非静态函数的名字一定各不相同的缘故。
而C++允许重载(不同的函数有相同的名字但不同的参数),
并且有很多C所没有的特性──比如类、成员函数、异常说明──几乎不可能直接用函数名作符号名。
为了解决这个
问题
,C++采用了所谓的name mangling。它把函数名和一些信息(如参数数量和大小)杂糅在一起,
改造成奇形怪状,只有编译器才懂的符号名。
例如,被mangle后的foo可能看起来像foo@4%6^,或者,符号名里头甚至不包括“foo”。
其中一个
问题
是,C++标准(目前是[ISO14882])并没有定义名字必须如何被mangle,
所以每个编译器都按自己的方式来进行name mangling。
有些编译器甚至在不同版本间更换mangling算法(尤其是g++ 2.x和3.x)。
即使您搞清楚了您的编译器到底怎么进行mangling的,从而可以用dlsym调用函数了,
但可能仅仅限于您手头的这个编译器而已,而无法在下一版编译器下工作。
使用dlopen API的另一个
问题
是,它只支持加载函数。
但在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。
四、解决方案
1. extern "C"
C++有个特定的关键字用来声明采用C binding的函数:
extern "C" 。
用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。
因此,只有非成员函数才能被声明为extern "C",并且不能被重载。
尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。
冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,
相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。
在用AndroidStudio开发APP时,因重新拉取git仓库代码,导致以下错误:
[164/164] Linking CXX shared library ..\..\..\..\build\intermediates\cmake\debug\obj\arm64-v8a\libailiving.so
FAILED: cmd.exe /C "cd . && C:\Users\LENOVO\App
Data
\Local\Android\Sdk\ndk\22.1.7171670\toolcha
类型选择dll
2. IDE自动生成的代码已经把整个架构弄好了,其中和项目同名的.h和.cpp文件就是我们自己写代码的地方了。我想写的dll是导出一个类,在这里我就直接在它自动生成的CmyDll类上面改了。
myDll.h
myDll.cpp
在mydll.h和mydll.cpp中给类添加成员函数
//mydll.h
class MYDLL_API CmyDll {
public:
CmyDll(void);
// TODO: 在此添加您的方法。
int myFunction(int a, int b);
//mydll.cpp
int CmyDll::myFunction(int a, int b) {
return a*b;
3.编译的时候我选择了release,这里可以用默认的debug也行
在mydll.h和mydll.cpp中给类添加成员函数
最后生成解决方案后产生的mydll.lib和mydll.dll就是我们需要的二进制文件了。lib文件是编译是要用的,而dll是调用这个库的程序运行时需要的。
调用dll
1.重新建立一个工程
这回选择普通的控制台程序就行了。我建了个名为myDllCall的工程。
2.把库的头文件include进来,以及连接lib文件
其中 include进来的 myDll.h 和 **#pragma comment()**的lib根据自己的路径写。
#include "stdafx.h"
#include "../../myDll/myDll/myDll.h" //头文件
#pragma comment(lib,"../../myDll/Release/myDll.lib") //调用自己写的外部库
#include
int main()
CmyDll mydll;
int a, b;
std::cin >> a >> b;
std::cout << mydll.myFunction(a, b) <> a >> b;
std::cout << mydll.myFunction(a, b) << std::
end
l;
return 0;
3.dll放到可执行文件同一目录下面
刚刚的代码直接编译没
问题
,运行会报错.
直接编译没
问题
,运行会报错
原因是dll要和生成的可执行文件在同一个目录下,我把mydll.dll放进去之后就解决了。
我们成功的在自己的工程里调用了外部的类
可以看到我们成功的在自己的工程里调用了外部的类。
Gradle sync failed: com.android.tools.idea.gradle.project.sync.idea.issues.SdkPlatformNotFoundException: Module: ‘ble
data
control’ platform ‘android-24’ not found. (12 s 199 ms)
sdk版本不对应 下载对应的sdk
java.lang.ClassNotFoundExce
在centos上装了两个版本的gcc,4.8和7.3,编译程序遇到链接
问题
。找不到xx的引用。“undefined reference to ...”。明明刚才编译出来的库一链接就是找不到符号。
找不到符号那就先查看一下动态(静态)链接库的符号。用命令
nm -g libcryptopp-arm64.a | c++filt | grep BufferedTransformation::Chan...
静态链接中有一个专门的段叫
符号表
-- “.symtab”(Symbol Table),里面保存了所有关于该目标文件的符号的定义和引用。
动态链接中同样有一个段叫 动态
符号表
-- “.dynsym”(Dynamic Symbol) , 但.dynsym 相对于 .symtab 只保存了与动态链接相关的导入导出符号。
so中同样有.symtab,其中保存着所有的符号
.symt...
2. 使用gcc编译器将源代码文件编译成目标文件,例如test.o。
3. 使用gcc编译器将目标文件链接成
动态链接库
,例如libtest.so。在链接时需要使用共享库选项“-shared”。
4. 将生成的
动态链接库
复制到系统库路径中,例如/usr/lib。
5. 在需要使用
动态链接库
的程序中,使用“-l库名”选项来链接
动态链接库
,例如gcc -o main main.c -ltest。
6. 运行程序,
动态链接库
会在运行时被加载并链接。
需要注意的是,在编写源代码文件时需要使用导出函数的方式来定义
动态链接库
的接口,例如使用“__attribute__((visibility("default")))”来声明函数可见性。同时,还需要注意
动态链接库
的版本号和
符号表
等
问题
,以确保
动态链接库
的兼容性和稳定性。