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 };
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; }
执行:./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; }
结果:
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类型真正给予只读内存保护、以此来提高安全性。