gpt4 book ai didi

c - 在自定义 libc 中实现线程本地存储

转载 作者:IT王子 更新时间:2023-10-29 00:58:07 26 4
gpt4 key购买 nike

我正在为非常小的静态链接程序实现 libc 的一个小子集,我认为添加 TLS 支持将是一个很好的学习体验。我用 Ulrich Drepper's TLS document作为引用。

我设置了两个字符串来尝试这个:

static __thread const char msg1[] = "TLS (1).\n"; /* 10 bytes */
static __thread const char msg2[] = "TLS (2).\n"; /* 10 bytes */

编译器生成以下指令来访问它们:

mov    rbx, QWORD PTR fs:0x0 ; Load TLS.
lea rsi, [rbx-0x14] ; Get a pointer to 'msg1'. 20 byte offset.
lea rsi, [rbx-0xa] ; Get a pointer to 'msg2'. 10 byte offset.

假设我将 TCB 放在堆栈的某处:

struct tcb {
void* self; /* Points to self. I read that this was necessary somewhere. */
int errno; /* Per-thread errno variable. */
int padding;
};

然后将 TLS 区域放在它旁边 tls = &tcb - tls_size。然后我将 FS 寄存器设置为指向 fs = tls + tls_size,并将 TLS 初始化镜像复制到 tls

但是,这是行不通的。我已通过将 tls_image 中的 20 个字节写入 stdout 来验证我是否正确定位了 TLS 初始化镜像。这让我相信我错误地放置了 TCB 和/或 TLS 区域,或者我不符合 ABI。

  • 我使用 arch_prctl(2) 设置 FS 寄存器。我是否需要以某种方式使用 set_thread_area(2)
  • 我没有dtv。我假设这不是必需的,因为我是静态链接。

关于我做错了什么有什么想法吗?非常感谢!

最佳答案

I'm implementing a small subset of libc for very small and statically linked programs, and I figured that adding TLS support would be a good learning experience.

好主意!我不得不在一个项目中实现我自己的 TLS,因为我不能使用任何像 pthread 这样的通用线程库。我没有完全解决您的问题的方法,但分享我的经验可能会有用。

I set the FS register using arch_prctl(2). Do I need to use set_thread_area(2) somehow?

答案取决于您实际使用的架构。如果您使用的是 x86-64 位,则应专门使用 arch_prctl将 FS 寄存器设置为要用作 TLS 的内存区域(它允许您寻址大于 4GB 的内存区域)。而对于 x86-32,您必须使用 set_thread_area因为它是内核支持的唯一系统调用。

我的实现背后的想法是为每个线程分配一个私有(private)内存区域并将其地址保存到 %GS 寄存器中。这是一种相当简单的方法,但就我而言,它非常有效。每次你想访问一个线程的私有(private)区域时,你只需要将保存在 %GS 中的值和一个标识内存位置的偏移量用作基地址。我通常为每个线程分配一个内存页 (4096),并将其分成 8 个字节的 block 。所以,我为每个线程分配了 512 个私有(private)内存槽,可以像索引从 0 到 511 的数组一样访问。

这是我使用的代码:

#define _GNU_SOURCE 1 

#include "tls.h"
#include <asm/ldt.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <asm/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

void * install_tls() {
void *addr = mmap(0, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (syscall(SYS_arch_prctl,ARCH_SET_GS, addr) < 0)
return NULL;

return addr;
}

void freeTLS() {
void *addr;
syscall(SYS_arch_prctl,ARCH_GET_GS, &addr);
munmap(addr, 4096);
}

bool set_tls_value(int idx, unsigned long val) {
if (idx < 0 || idx >= 4096/8) {
return false;
}
asm volatile(
"movq %0, %%gs:(%1)\n"
:
: "q"((void *)val), "q"(8ll * idx));
return true;
}


unsigned long get_tls_value(int idx) {
long long rc;
if (idx < 0 || idx >= 4096/8) {
return 0;
}
asm volatile(
"movq %%gs:(%1), %0\n"
: "=q"(rc)
: "q"(8ll * idx));
return rc;
}

这是带有一些宏的标题:

#ifndef TLS_H
#define TLS_H

#include <stdbool.h>

void *install_tls();
void freeTLS();
bool set_tls_value (int, unsigned long);
unsigned long get_tls_value(int );

/*
*macros used to set and retrieve the values
from the tls area
*/

#define TLS_TID 0x0
#define TLS_FD 0x8
#define TLS_MONITORED 0x10

#define set_local_tid(_x) \
set_tls_value(TLS_TID, (unsigned long)_x)

#define set_local_fd(_x) \
set_tls_value(TLS_FD, (unsigned long)_x)

#define set_local_monitored(_x) \
set_tls_value(TLS_MONITORED, (unsigned long)_x)

#define get_local_tid() \
get_tls_value(TLS_TID)

#define get_local_fd() \
get_tls_value(TLS_FD)

#define get_local_monitored() \
get_tls_value(TLS_MONITORED)



#endif /* end of include guard: TLS_H */

每个线程要完成的第一个 Action 是安装 TLS 内存区域。一旦 TLS 被初始化,每个线程就可以开始使用这个区域作为私有(private) TLS。

关于c - 在自定义 libc 中实现线程本地存储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14530590/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com