《Android系统学习》第六章:Android4.1 HAL段错误问题—linker与prelink

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类型真正给予只读内存保护、以此来提高安全性。



你可能感兴趣的:(《Android系统学习》第六章:Android4.1 HAL段错误问题—linker与prelink)