[libc-commits] [libc] 8c3bde8 - [libc][threads] Add architecture-specific Thread Control Block definitions (#220607)

via libc-commits libc-commits at lists.llvm.org
Thu Sep 3 23:54:11 PDT 2026


Author: Pavel Labath
Date: 2026-09-04T08:54:06+02:00
New Revision: 8c3bde8c34e086f8e75e21f0293f507798f868d4

URL: https://github.com/llvm/llvm-project/commit/8c3bde8c34e086f8e75e21f0293f507798f868d4
DIFF: https://github.com/llvm/llvm-project/commit/8c3bde8c34e086f8e75e21f0293f507798f868d4.diff

LOG: [libc][threads] Add architecture-specific Thread Control Block definitions (#220607)

Add TCB structures and low-level thread-pointer accessor primitives for
Linux (x86_64, aarch64, riscv), and update init_tls code to use the
struct fields instead of manually computing raw pointer offsets.

I've set aside space for the ThreadAttributes pointer inside the
ABI-reserved slot for the thread library, but I'm not actually using
that value yet. I'll do that in the next patch, which will replace the
`thread_local self` object with this pointer.

Added: 
    libc/src/__support/threads/linux/aarch64/tcb.h
    libc/src/__support/threads/linux/riscv/tcb.h
    libc/src/__support/threads/linux/tcb.h
    libc/src/__support/threads/linux/x86_64/tcb.h
    libc/src/__support/threads/tcb.h

Modified: 
    libc/src/__support/threads/CMakeLists.txt
    libc/src/__support/threads/linux/CMakeLists.txt
    libc/startup/linux/aarch64/CMakeLists.txt
    libc/startup/linux/aarch64/tls.cpp
    libc/startup/linux/riscv/CMakeLists.txt
    libc/startup/linux/riscv/tls.cpp
    libc/startup/linux/x86_64/CMakeLists.txt
    libc/startup/linux/x86_64/tls.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index 67a176f87a9fd..1f14e3aab4768 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -36,6 +36,17 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
   add_subdirectory(${LIBC_TARGET_OS})
 endif()
 
+if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.tcb)
+  add_header_library(
+    tcb
+    HDRS
+      tcb.h
+    DEPENDS
+      .${LIBC_TARGET_OS}.tcb
+      libc.src.__support.macros.properties.os
+  )
+endif()
+
 if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils)
   add_header_library(
     futex_utils

diff  --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 7d349f6d9bd00..51e9d4005ad0d 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -1,3 +1,19 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE}/tcb.h)
+  add_header_library(
+    tcb
+    HDRS
+      tcb.h
+      ${LIBC_TARGET_ARCHITECTURE}/tcb.h
+    DEPENDS
+      libc.hdr.offsetof_macros
+      libc.hdr.stdint_proxy
+      libc.src.__support.threads.thread_attributes
+      libc.src.__support.macros.attributes
+      libc.src.__support.macros.config
+      libc.src.__support.macros.properties.architectures
+  )
+endif()
+
 add_header_library(
   futex_word_type
   HDRS

diff  --git a/libc/src/__support/threads/linux/aarch64/tcb.h b/libc/src/__support/threads/linux/aarch64/tcb.h
new file mode 100644
index 0000000000000..74bdf7ffc9bfe
--- /dev/null
+++ b/libc/src/__support/threads/linux/aarch64/tcb.h
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines the Thread Control Block (TCB) for Linux AArch64.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_AARCH64_TCB_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_AARCH64_TCB_H
+
+#include "hdr/stdint_proxy.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/threads/thread_attributes.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct ThreadControlBlock {
+  uintptr_t dtv;
+  ThreadAttributes *attrib;
+};
+
+LIBC_INLINE ThreadControlBlock *get_tcb() {
+  ThreadControlBlock *tp;
+  asm("mrs %0, tpidr_el0" : "=r"(tp));
+  return tp;
+}
+
+LIBC_INLINE ThreadAttributes *get_current_thread_attrib() {
+  return get_tcb()->attrib;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_AARCH64_TCB_H

diff  --git a/libc/src/__support/threads/linux/riscv/tcb.h b/libc/src/__support/threads/linux/riscv/tcb.h
new file mode 100644
index 0000000000000..a3d09e894c930
--- /dev/null
+++ b/libc/src/__support/threads/linux/riscv/tcb.h
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines the Thread Control Block (TCB) for Linux RISC-V.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_RISCV_TCB_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_RISCV_TCB_H
+
+#include "hdr/stdint_proxy.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/threads/thread_attributes.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct ThreadControlBlock {
+  uintptr_t dtv;
+  ThreadAttributes *attrib;
+};
+
+LIBC_INLINE ThreadControlBlock *get_tcb() {
+  ThreadControlBlock *tp;
+  asm("mv %0, tp" : "=r"(tp));
+  return tp - 1;
+}
+
+LIBC_INLINE ThreadAttributes *get_current_thread_attrib() {
+  return get_tcb()->attrib;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_RISCV_TCB_H

diff  --git a/libc/src/__support/threads/linux/tcb.h b/libc/src/__support/threads/linux/tcb.h
new file mode 100644
index 0000000000000..e9abd07423aec
--- /dev/null
+++ b/libc/src/__support/threads/linux/tcb.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines Linux architecture-level Thread Control Block dispatcher.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_TCB_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_TCB_H
+
+#include "src/__support/macros/properties/architectures.h"
+
+#if defined(LIBC_TARGET_ARCH_IS_X86_64)
+#include "src/__support/threads/linux/x86_64/tcb.h"
+#elif defined(LIBC_TARGET_ARCH_IS_AARCH64)
+#include "src/__support/threads/linux/aarch64/tcb.h"
+#elif defined(LIBC_TARGET_ARCH_IS_ANY_RISCV)
+#include "src/__support/threads/linux/riscv/tcb.h"
+#else
+#error "Unsupported architecture for Linux Thread Control Block"
+#endif
+
+#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_TCB_H

diff  --git a/libc/src/__support/threads/linux/x86_64/tcb.h b/libc/src/__support/threads/linux/x86_64/tcb.h
new file mode 100644
index 0000000000000..c4ff19afb5fb5
--- /dev/null
+++ b/libc/src/__support/threads/linux/x86_64/tcb.h
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines the Thread Control Block (TCB) for Linux x86_64.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_X86_64_TCB_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_X86_64_TCB_H
+
+#include "hdr/offsetof_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/threads/thread_attributes.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct ThreadControlBlock {
+  uintptr_t self;
+  uintptr_t dtv;
+  ThreadAttributes *attrib;
+  uintptr_t reserved[2];
+  uintptr_t stack_guard;
+};
+static_assert(offsetof(ThreadControlBlock, stack_guard) == 0x28,
+              "Offset defined by the ABI");
+
+LIBC_INLINE ThreadControlBlock *get_tcb() {
+  ThreadControlBlock *tcb;
+  asm("mov %%fs:0, %0" : "=r"(tcb));
+  return tcb;
+}
+
+LIBC_INLINE ThreadAttributes *get_current_thread_attrib() {
+  ThreadAttributes *attrib;
+  asm("mov %%fs:%c1, %0"
+      : "=r"(attrib)
+      : "i"(offsetof(ThreadControlBlock, attrib)));
+  return attrib;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_X86_64_TCB_H

diff  --git a/libc/src/__support/threads/tcb.h b/libc/src/__support/threads/tcb.h
new file mode 100644
index 0000000000000..04af422c35df4
--- /dev/null
+++ b/libc/src/__support/threads/tcb.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines OS-level Thread Control Block dispatcher.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_TCB_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_TCB_H
+
+#include "src/__support/macros/properties/os.h"
+
+#if defined(LIBC_TARGET_OS_IS_LINUX)
+#include "src/__support/threads/linux/tcb.h"
+#else
+#error "Unsupported OS for Thread Control Block"
+#endif
+
+#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_TCB_H

diff  --git a/libc/startup/linux/aarch64/CMakeLists.txt b/libc/startup/linux/aarch64/CMakeLists.txt
index e733010a0a509..512ce24ba1658 100644
--- a/libc/startup/linux/aarch64/CMakeLists.txt
+++ b/libc/startup/linux/aarch64/CMakeLists.txt
@@ -9,6 +9,7 @@ add_startup_object(
     libc.src.__support.OSUtil.linux.syscall_wrappers.mmap
     libc.src.__support.OSUtil.linux.syscall_wrappers.munmap
     libc.src.__support.OSUtil.osutil
+    libc.src.__support.threads.tcb
     libc.src.string.memory_utils.inline_memcpy
   COMPILE_OPTIONS
     -fno-omit-frame-pointer

diff  --git a/libc/startup/linux/aarch64/tls.cpp b/libc/startup/linux/aarch64/tls.cpp
index bc35b85bdec46..f16e833014ccb 100644
--- a/libc/startup/linux/aarch64/tls.cpp
+++ b/libc/startup/linux/aarch64/tls.cpp
@@ -12,7 +12,7 @@
 #include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
 #include "src/__support/OSUtil/syscall.h"
 #include "src/__support/macros/config.h"
-#include "src/__support/threads/thread.h"
+#include "src/__support/threads/tcb.h"
 #include "src/string/memory_utils/inline_memcpy.h"
 
 #include <arm_acle.h>
@@ -33,20 +33,20 @@ void init_tls(TLSDescriptor &tls_descriptor) {
   // aarch64 follows the variant 1 TLS layout:
   //
   // 1. First entry is the dynamic thread vector pointer
-  // 2. Second entry is a 8-byte reserved word.
+  // 2. Second entry is a 8-byte reserved word (used for attrib).
   // 3. Padding for alignment.
   // 4. The TLS data from the ELF image.
   //
   // The thread pointer points to the first entry.
 
-  const uintptr_t size_of_pointers = 2 * sizeof(uintptr_t);
+  const uintptr_t TCB_SIZE = sizeof(ThreadControlBlock);
   uintptr_t padding = 0;
   const uintptr_t ALIGNMENT_MASK = app.tls.align - 1;
-  uintptr_t 
diff  = size_of_pointers & ALIGNMENT_MASK;
+  uintptr_t 
diff  = TCB_SIZE & ALIGNMENT_MASK;
   if (
diff  != 0)
     padding += (ALIGNMENT_MASK - 
diff ) + 1;
 
-  uintptr_t alloc_size = size_of_pointers + padding + app.tls.size;
+  uintptr_t alloc_size = TCB_SIZE + padding + app.tls.size;
 
   ErrorOr<void *> mmap_ret =
       linux_syscalls::mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE,
@@ -54,7 +54,7 @@ void init_tls(TLSDescriptor &tls_descriptor) {
   if (!mmap_ret.has_value())
     syscall_impl<long>(SYS_exit, 1);
   uintptr_t thread_ptr = uintptr_t(mmap_ret.value());
-  uintptr_t tls_addr = thread_ptr + size_of_pointers + padding;
+  uintptr_t tls_addr = thread_ptr + TCB_SIZE + padding;
   inline_memcpy(reinterpret_cast<char *>(tls_addr),
                 reinterpret_cast<const char *>(app.tls.address),
                 app.tls.init_size);

diff  --git a/libc/startup/linux/riscv/CMakeLists.txt b/libc/startup/linux/riscv/CMakeLists.txt
index 28654aef44c7c..be09b7e620d04 100644
--- a/libc/startup/linux/riscv/CMakeLists.txt
+++ b/libc/startup/linux/riscv/CMakeLists.txt
@@ -9,6 +9,7 @@ add_startup_object(
     libc.src.__support.OSUtil.linux.syscall_wrappers.mmap
     libc.src.__support.OSUtil.linux.syscall_wrappers.munmap
     libc.src.__support.OSUtil.osutil
+    libc.src.__support.threads.tcb
     libc.src.string.memory_utils.inline_memcpy
   COMPILE_OPTIONS
     -fno-omit-frame-pointer

diff  --git a/libc/startup/linux/riscv/tls.cpp b/libc/startup/linux/riscv/tls.cpp
index 87edca12c5b87..9e0721195499b 100644
--- a/libc/startup/linux/riscv/tls.cpp
+++ b/libc/startup/linux/riscv/tls.cpp
@@ -12,7 +12,7 @@
 #include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
 #include "src/__support/OSUtil/syscall.h"
 #include "src/__support/macros/config.h"
-#include "src/__support/threads/thread.h"
+#include "src/__support/threads/tcb.h"
 #include "src/string/memory_utils/inline_memcpy.h"
 #include <sys/syscall.h>
 
@@ -25,15 +25,15 @@ void init_tls(TLSDescriptor &tls_descriptor) {
     return;
   }
 
-  // riscv64 follows the variant 1 TLS layout:
-  const uintptr_t size_of_pointers = 2 * sizeof(uintptr_t);
+  // riscv follows the variant 1 TLS layout:
+  const uintptr_t TCB_SIZE = sizeof(ThreadControlBlock);
   uintptr_t padding = 0;
   const uintptr_t ALIGNMENT_MASK = app.tls.align - 1;
-  uintptr_t 
diff  = size_of_pointers & ALIGNMENT_MASK;
+  uintptr_t 
diff  = TCB_SIZE & ALIGNMENT_MASK;
   if (
diff  != 0)
     padding += (ALIGNMENT_MASK - 
diff ) + 1;
 
-  uintptr_t alloc_size = size_of_pointers + padding + app.tls.size;
+  uintptr_t alloc_size = TCB_SIZE + padding + app.tls.size;
 
   ErrorOr<void *> mmap_ret =
       linux_syscalls::mmap(nullptr, alloc_size, PROT_READ | PROT_WRITE,
@@ -41,7 +41,7 @@ void init_tls(TLSDescriptor &tls_descriptor) {
   if (!mmap_ret.has_value())
     syscall_impl<long>(SYS_exit, 1);
   uintptr_t thread_ptr = uintptr_t(mmap_ret.value());
-  uintptr_t tls_addr = thread_ptr + size_of_pointers + padding;
+  uintptr_t tls_addr = thread_ptr + TCB_SIZE + padding;
   inline_memcpy(reinterpret_cast<char *>(tls_addr),
                 reinterpret_cast<const char *>(app.tls.address),
                 app.tls.init_size);

diff  --git a/libc/startup/linux/x86_64/CMakeLists.txt b/libc/startup/linux/x86_64/CMakeLists.txt
index d458d9ac89316..a0938d403411e 100644
--- a/libc/startup/linux/x86_64/CMakeLists.txt
+++ b/libc/startup/linux/x86_64/CMakeLists.txt
@@ -10,6 +10,7 @@ add_startup_object(
     libc.src.__support.OSUtil.linux.syscall_wrappers.getrandom
     libc.src.__support.OSUtil.linux.syscall_wrappers.mmap
     libc.src.__support.OSUtil.linux.syscall_wrappers.munmap
+    libc.src.__support.threads.tcb
     libc.src.string.memory_utils.inline_memcpy
   COMPILE_OPTIONS
     -fno-stack-protector

diff  --git a/libc/startup/linux/x86_64/tls.cpp b/libc/startup/linux/x86_64/tls.cpp
index 13f6491295fe8..5810955c5d24d 100644
--- a/libc/startup/linux/x86_64/tls.cpp
+++ b/libc/startup/linux/x86_64/tls.cpp
@@ -12,6 +12,7 @@
 #include "src/__support/OSUtil/linux/syscall_wrappers/mmap.h"
 #include "src/__support/OSUtil/linux/syscall_wrappers/munmap.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/threads/tcb.h"
 #include "src/string/memory_utils/inline_memcpy.h"
 
 #include <asm/prctl.h>
@@ -32,15 +33,10 @@ void init_tls(TLSDescriptor &tls_descriptor) {
   if (tls_size != app.tls.size)
     tls_size += app.tls.align;
 
-  // Per the x86_64 TLS ABI, the entry pointed to by the thread pointer is the
-  // address of the TLS block. So, we add more size to accomodate this address
-  // entry.
-  // We also need to include space for the stack canary. The canary is at
-  // offset 0x28 (40) and is of size uintptr_t.
-  uintptr_t tls_size_with_addr = tls_size + sizeof(uintptr_t) + 40;
+  uintptr_t tls_size_with_tcb = tls_size + sizeof(ThreadControlBlock);
 
   ErrorOr<void *> mmap_ret =
-      linux_syscalls::mmap(nullptr, tls_size_with_addr, PROT_READ | PROT_WRITE,
+      linux_syscalls::mmap(nullptr, tls_size_with_tcb, PROT_READ | PROT_WRITE,
                            MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
   if (!mmap_ret.has_value())
     syscall_impl<long>(SYS_exit, 1);
@@ -49,23 +45,23 @@ void init_tls(TLSDescriptor &tls_descriptor) {
   // x86_64 TLS faces down from the thread pointer with the first entry
   // pointing to the address of the first real TLS byte.
   uintptr_t end_ptr = reinterpret_cast<uintptr_t>(tls_addr) + tls_size;
-  *reinterpret_cast<uintptr_t *>(end_ptr) = end_ptr;
+  auto *tcb = reinterpret_cast<ThreadControlBlock *>(end_ptr);
+  tcb->self = end_ptr;
 
   inline_memcpy(reinterpret_cast<char *>(tls_addr),
                 reinterpret_cast<const char *>(app.tls.address),
                 app.tls.init_size);
-  uintptr_t *stack_guard_addr = reinterpret_cast<uintptr_t *>(end_ptr + 40);
   // Setting the stack guard to a random value.
   // We cannot call the get_random function here as the function sets errno on
   // failure. Since errno is implemented via a thread local variable, we cannot
   // use errno before TLS is setup. The linux_syscalls wrapper is safe as it
   // reports errors via ErrorOr instead of errno.
   ErrorOr<ssize_t> stack_guard_retval =
-      linux_syscalls::getrandom(stack_guard_addr, sizeof(uint64_t), 0);
+      linux_syscalls::getrandom(&tcb->stack_guard, sizeof(tcb->stack_guard), 0);
   if (!stack_guard_retval.has_value())
     syscall_impl(SYS_exit, 1);
 
-  tls_descriptor = {tls_size_with_addr, reinterpret_cast<uintptr_t>(tls_addr),
+  tls_descriptor = {tls_size_with_tcb, reinterpret_cast<uintptr_t>(tls_addr),
                     end_ptr};
   return;
 }


        


More information about the libc-commits mailing list