使用libhybris,glibc和bionic共存时的TLS冲突的问题

如无特殊说明,系统为linux,架构为x86 32bit,使用glibc,通过libhybris调用android bionic的驱动。一、什么是TLSTLS的全称是Thread Local Storage,是指进程中每一个线程都独有的变量,名字相同,但是读写互不影响。最常见的TLS之一就是errno,每一个线程都有自己的errno,保存着该线程的最近一次函数调用错误原因,别的线程
摘要由CSDN通过智能技术生成

如无特殊说明,系统为linux,架构为x86 32bit,使用glibc,通过libhybris调用android bionic的驱动。android版本5.1.0_r1。


一、什么是TLS

TLS的全称是Thread Local Storage,是指进程中每一个线程都独有的变量,名字相同,但是读写互不影响。最常见的TLS之一就是errno,每一个线程都有自己的errno,保存着该线程的最近一次函数调用错误原因,别的线程干啥都不会影响到这个线程的errno,防止别的线程覆盖该线程的errno。


PS:

tid是线程的id,保证同一个进程中是不重复的,但是不同进程之间可以重复。

真想修改其他线程的TLS也可以,glibc中获取其他线程的tid,强制转换为struct pthread结构体,就可以干很多事了。



1、如何使用TLS

声明变量时,添加关键字__thread(glibc支持,android bionic不支持),或者通过pthread_key_create, pthread_setspecific和pthread_getspecific三个函数去申请和读写TLS:

__thread int x = 3;
printf("%d\n", x);

pthread_key_t key;
pthread_key_create(&key, NULL);
pthread_setspecific(key,"hello world");
printf("%s\n", pthread_getspecific(key));

2、TLS的原理

linux内核对线程进行切换时,会保存和恢复一些寄存器,这是操作系统的基础知识。

有一个比较特殊的寄存器,叫做gs,没见过的话也没事,它和cs,ds,es,ss差不多,都是段寄存器。

只是CPU厂商并没有规定gs的作用,可以由操作系统自己发挥,与此类似的还有fs寄存器。


先说明下保护模式和实模式下段寄存器的含义是不同的。

实模式,也就是古老的dos时期的那种东西,地址总线16根,最大访问空间1M的。cs:ip表示的地址就是cs*16+ip。

保护模式,现在的cpu为了兼容老东西,开机时是实模式的,然后打开A20,以及其他的一些东西,就进入了保护模式。

保护模式下的段寄存器,我觉得叫做选择符更形象些,它本身并不保存真正的地址信息,而保存了一个索引,一个描述表选择,一个特权级。


比如gs=0x33,需要按照二进制来看

high low

110     0               11

idx  gdt/ldt       privilege

最低位的11b,也就是3,表示特权级,一般内核为特权级0,用户态为3。

最高位的110b,也就是6,表示在gdt或者ldt中的下标。

中间的0表示使用gdt,如果为1,表示使用ldt。


那么什么是gdt和ldt呢?

gdt是全局描述符表,ldt是局部描述符表。他们都是表格,表中的每项都包含了一个地址,以及其他一些东西。

gdt就是系统全局的一个表,每个线程都会在gdt中占据一些位置,用于存放线程的tss和ldt地址。当然gdt中还有其他的东西。

每个线程都有自己的ldt,存放线程自己的一些信息,比如数据段和代码段的地址。


比如gs=0x33时,gs:4指的就是gdt[6]中的地址,加上偏移量4。


linux内核中有个set_thread_area系统调用,就是用来设置线程的gs寄存器以及对应的gdt描述符的内容:

int do_set_thread_area(struct task_struct *p, int idx,
            struct user_desc __user *u_info,
            int can_allocate)
{
  struct user_desc info;

  if (copy_from_user(&info, u_info, sizeof(info)))
     return -EFAULT;

  if (!tls_desc_okay(&info))
     return -EINVAL;

  if (idx == -1)
     idx = info.entry_number;

  /*
   * index -1 means the kernel should try to find and
   * allocate an empty descriptor:
   */
  if (idx == -1 && can_allocate) {
     idx = get_free_idx();
     if (idx < 0)
        return idx;
     if (put_user(idx, &u_info->entry_number))
        return -EFAULT;
  }

  if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
     return -EINVAL;

  set_tls_desc(p, idx, &info, 1);

  return 0;
}

glibc或者bionic都会调用set_thread_area来设置线程的数据的,bionic是通过__set_tls来调用的。

其实线程有很大一部分是在glibc/bionic中

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值