1./hardware/libhardware/hardware.c
static int load(const char *id,const char *path,
const struct hw_module_t **pHmi){
int status;
void *handle;
struct hw_module_t *hmi;
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
//sym为HMI;从handle指针所指向的库中找如下宏定义
//hardware/libhardware/include/hardware.h
//#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
//#define HAL_MODULE_INFO_SYM HMI
if (hmi == NULL) {
LOGE("load: couldn't find symbol %s", sym);
status = -EINVAL; goto done;
}
if (strcmp(id, hmi->id) != 0) {
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL; goto done;
}
hmi->dso = handle;
status = 0;
done: if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
}
else {
LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p", id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
}
2./sdk/emulator/sensors/Sensors_qemu.c
struct sensors_module_t HAL_MODULE_INFO_SYM = {
//注意:Android4.0里边是这样定义的:const struct sensors_module_t
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = SENSORS_HARDWARE_MODULE_ID,
.name = "Goldfish SENSORS Module",
.author = "The Android Open Source Project",
.methods = &sensors_module_methods,
},
.get_sensors_list = sensors__get_sensors_list
};
3.现象:
Android4.0 上述2中HAL代码无论在4.0还是4.1环境下编译后,放置Android4.1开发板运行;结果都会在上述1中“hmi->dso = handle”处挂掉,即出现SEGV_ACCERR段错误。
4.原因:
Android4.1修改了/bionic/linker(增加了relro支持,这部分会被编译成/system/bin/linker),而该部分即为动态链接库加载时所用dlopen、dlsym等函数代码;修改后的linker会把dlsym时对应的const变量地址映射为只读(类似mprotect函数:mprotect设置内存访问权限)。所以,当你调用hmi->dso(const型) =handle给只读地址赋值时,程序就会段错误退出。
5. 解决办法(Android4.1下)
要么换掉linker(/system/bin/linker);或者prelink处理,这部分我还没有研究。
要么用最简单的办法:把上述2中的const去掉。
下面通过一个简单的例子看下,说明在没有linker即动态库文件加载时、上述问题并不存在:test.c
#include <stdio.h>
struct my_test{
int a;
void *b;
};
int main(){
char c[50];
printf("c addr is %lx\n",c);
const struct my_test std1 = {2,NULL};
printf("std1.a = %d\n",std1.a);
printf("std1.b is %lx\n",std1.b);
struct my_test *hmi;
hmi = &std1;
printf("&std1 is %lx\n",&std1);
printf("hmi is %lx\n",hmi);
hmi->b = c;
hmi->a = 3;
printf("std1.a = %d\n",std1.a);
printf("hmi->b is %lx\n",hmi->b);
printf("std1.b is %lx\n",std1.b);
return 0;
}
编译:gcc -o test test.c
执行:./test
结果:
c addr is bfa50aba
std1.a = 2
std1.b is 0
&std1 is bfa50aac
hmi is bfa50aac
std1.a = 3
hmi->b is bfa50aba
std1.b is bfa50aba
====================================================================================================================================
顺便说下const,要修改const修饰变量的值、可以指针强转来实现(这在上边两处都有体现):
test1.c
#include <stdio.h>
int main(){
const int a = 3;
printf("a is %d\n",a);
//a = 2; //这句放通则编译不过
int *b;
b = &a;
*b = 2;
printf("a is %d\n",a);
return 0;
}
编译:gcc -o test1 test1.c
结果:
a is 3
a is 2
说明:
上述所有在g++编译器是编译不过的,因为C++语言强制要求指向const对象的指针也必须具有const特性!
C++没有给const常量分配内存,意思就是它只是一个编译期间的常量(但声明一个extern const会分配内存);
而C分配了(因此姑且叫变量),C可以通过指针“消除”(专业术语叫指针强转)const内存的“只读”属性;以上都是编译阶段的术语。原理是const只是编译阶段起作用(即编译阶段假定了这段内存是只读的),只要编译通过、程序运行过程中是可以修改的;在实际运行阶段const修饰变量在的内存区域并不是只读内存。而linker连接器却可以在运行时指定真正的只读内存。
总结一句话:C中const修饰的是只读变量,C++中const修饰才是真正意义上的常量!
所以,Android4.1可能也是为了防止上述修改(因为hardware.c由gcc编译器编译)对linker部分做了改动,即在linker连接器加载动态库时对其中的const类型真正给予只读内存保护、以此来提高安全性。