关于Android中so的符号表导出以及C++的符号改编规则

最近比较好奇为什么dlsym要传入的函数名这么奇怪,于是就调研了一下Android中是怎么样允许So中的函数符号导出。。。

好吧有这么个东西,是用来说明这个function的visibility的。。。

__attribute__ ((visibility ("default")))

在函数定义时写上的话,当编译选项中做了如下设置时:

LOCAL_CFLAGS += -fvisiblity=hidden
没有__attribute__ ((visibility ("default")))声明的函数将不导出,即在导出中查找不到对应函数。

再看一下NDK的jni.h中有一句话:

#define JNIEXPORT  __attribute__ ((visibility ("default")))
所以声明函数时候有JNIEXPORT的函数都会导出。


C中有JNIEXPORT声明的函数会保留原名,不会被符号改编。但是C++函数导出的时候会被符号改编。

C++(GNU C++)符号改编规则如下:
a)   全局变量:
即在命名空间和类之外的变量,改编后的符号名就是变量名。
b)   全局函数:
以”_Z”开头,然后是函数名字符的个数,接着是函数名,最后是函数参数的别名,double的别名是d,int的别名是i。如全局函数int func(double d, int i)改编后的符号是_Z4funcdi。
c)   类或命名空间中的变量或函数:
以”_ZN”开头,然后是各个空间和类的名字,每个名字前是名的字符长度,然后是变量/函数名的长度和变量/函数名,后面紧跟”E”,然后如果是函数则跟参数别名,如果是变量则什么都不用加。如上面代码中的:mangling::C1::C2::func(int i)改编后的符号是_ZN8mangling2C12C24funcEi
d)   构造函数和析构函数
以”_ZN”开头,然后是各个空间和类的名字,每个名字前是名的字符长度,然后,构造函数接C1或者C2,析构函数接D1或者D2,最后以“E”接函数参数别名结束。如上面代码中的构造函数:mangling::C1::C1()改编后是_ZN8mangling2C1C1Ev 或者_ZN8mangling2C1C2Ev。(注意:个人觉得C1和C2效果是一样的,D1和D2是一样的,但是网上有不同的看法,可能由于编译环境不一样吧,该贴http://www.sourceware.org/ml/gdb/2004-07/msg00163.html就和我说的不一样。)
e)   函数参数是结构体或类:
当函数的参数中含有类或结构体时,其它参数按照上面所说规则,对于类或结构体,在类或者结构提前加上类或结构体名的字符长度。例如:int stru_func(int i, struct stru  s, double d)改编后的符号为_Z9stru_funci4strud。
f)   函数参数是指针
当函数参数是指针时,该参数的别名是P(大写字符)加上该指针指向的变量类型的别名,例如当参数是”int *i”是该参数的别名是Pi。全局函数int point_func(int *i)改编之后的符号是_Z10point_funcPi。当参数为指针的指针时,别名为PP加所指向参数类型的别名,以此类推。例如,”int **i”该参数的别名为“PPi“。
g)   函数参数是命名空间中的类或结构体
其它地方和普通函数一样,只有类或结构参数变为:以”N”开头,然后是空间名的长度加空间名,接着是类或结构名的长度加类或结构名,最后以”E”结束。例如:d nstru_fun(int i, struct stru s, double d)改编后的符号是_Z9nstru_funiN2ns4struEd。
h)   函数参数是引用
该参数以”R”开始,然后接该参数引用的变量类型的别名。例如:void re_func(int &i)改编后的符号为:/*_Z7re_funcRi*/
i)   函数参数是string类型
字符串std::string类型的别名为Ss。例如:void str_func(string s)改编后的别名是:/*_Z8str_funcSs*/
j)   函数参数是const常引用
当函数参数为const常引用时,以”RK”开始,后面接引用的参数的类型,例如:void con_func(const int &i)改编后的符号是/*_Z8str_funcRKi*/
k)   函数参数是一维数组
当函数参数是一维数组时,和参数是指针一样。
l)   函数参数是多维数组
对于多维数组,第一维可以看做是指针,其它维看做是数组。函数参数是多维数组时,以”P”(代表数组的第一维)开始,后面接”A”各维数组的长度,以”_”间隔,最后以下划线加数组的类型的别名结束。例如:void arr_func(int a[][10][20][30])改编后的符号为_Z8arr_funcPA10_A20_A30_i
m)   参数类型为ostream或istream
标准类型std::ostream(std::basic_ostream >)的别名为So,而 std:: istream(std::basic_istream >)的别名为:Si,例如:bool print_to(ostream o)改编后的符号是:/*_Z8print_toSo*/
注意:虚函数,静态函数和普通函数一样,都是按照规则c)。


为使C++兼容C语言,所以C++使用了extern “C”关键字,extern “C”的使用方法如下:

extern "C"{
void func(double d);
int var;
}

大括号里的代码,都会当做C语言管理。所以上面代码中的func和var改编之后仍然是func和var,当然大括号里的函数或变量也可以单独声明:

extern void func(double d);
extern int var;

btw:.hpp文件其实质就是将.cpp的实现代码混入.h头文件当中。


你可能感兴趣的:(移动安全,Linux技巧)