Skip to content

Commit

Permalink
LibELF: Add headers describing the TLS layout of each architecture
Browse files Browse the repository at this point in the history
All of our architectures sadly use a slightly different TLS layout.
These headers try to abstract the differences in a nice way.
  • Loading branch information
spholz authored and ADKaster committed Apr 19, 2024
1 parent 243d700 commit 74a5150
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 0 deletions.
67 changes: 67 additions & 0 deletions Userland/Libraries/LibELF/Arch/aarch64/tls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2024, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <AK/BitCast.h>
#include <AK/StdLibExtraDetails.h>
#include <AK/Types.h>

namespace ELF {

struct ThreadControlBlock {
// Variant I of the ELF TLS data structures requires that the TCB has to contain a pointer to the dtv at offset 0.
// This member is unused, as we currently only support static TLS blocks.
void* dynamic_thread_vector;

FlatPtr padding;
};

// The TCB needs to have a size of 2 * sizeof(FlatPtr) on AArch64.
static_assert(AssertSize<ThreadControlBlock, 2 * sizeof(FlatPtr)>());

static constexpr size_t TLS_VARIANT = 1;
static constexpr size_t TLS_DTV_OFFSET = 0;
static constexpr size_t TLS_TP_STATIC_TLS_BLOCK_OFFSET = sizeof(ThreadControlBlock);

// AArch64 ELF TLS Layout
//
// [TCB][static TLS.....]
// ^tp (tpidr_el0)

inline size_t calculate_static_tls_region_size(size_t tls_template_size, size_t tls_alignment)
{
(void)tls_alignment;
return sizeof(ThreadControlBlock) + tls_template_size;
}

inline FlatPtr calculate_tp_value_from_static_tls_region_address(FlatPtr static_tls_region_address, size_t tls_template_size, size_t tls_alignment)
{
(void)tls_template_size;
(void)tls_alignment;
return static_tls_region_address;
}

inline ThreadControlBlock* get_tcb_pointer_from_thread_pointer(FlatPtr thread_pointer)
{
return bit_cast<ThreadControlBlock*>(thread_pointer);
}

inline void* get_pointer_to_first_static_tls_block_from_thread_pointer(FlatPtr thread_pointer, size_t tls_template_size, size_t tls_alignment)
{
(void)tls_template_size;
(void)tls_alignment;
return bit_cast<void*>(thread_pointer + sizeof(ThreadControlBlock));
}

inline void* get_pointer_to_static_tls_region_from_thread_pointer(FlatPtr thread_pointer, size_t tls_template_size, size_t tls_alignment)
{
(void)tls_template_size;
(void)tls_alignment;
return bit_cast<void*>(thread_pointer);
}

}
62 changes: 62 additions & 0 deletions Userland/Libraries/LibELF/Arch/riscv64/tls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2024, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <AK/BitCast.h>
#include <AK/Types.h>

namespace ELF {

struct ThreadControlBlock {
// NOTE: "ELF Handling For Thread-Local Storage" says that when using variant I of the data structures (which RISC-V does),
// the TCB has to have a pointer to the dtv at offset 0.
// However, that document also says that the thread pointer has to point to the TCB.
// It's probably still a good idea to put the dtv pointer at offset 0.
// This member is unused, as we currently only support static TLS blocks.
void* dynamic_thread_vector;
};

static constexpr size_t TLS_VARIANT = 1;
static constexpr size_t TLS_DTV_OFFSET = 0x800;
static constexpr size_t TLS_TP_STATIC_TLS_BLOCK_OFFSET = 0;

// RISC-V ELF TLS Layout
// The padding is needed so tp is correctly aligned.
//
// [..padding..][TCB][static TLS.....]
// ^tp

inline size_t calculate_static_tls_region_size(size_t tls_template_size, size_t tls_alignment)
{
return align_up_to(sizeof(ThreadControlBlock), tls_alignment) + tls_template_size;
}

inline FlatPtr calculate_tp_value_from_static_tls_region_address(FlatPtr static_tls_region_address, size_t tls_template_size, size_t tls_alignment)
{
(void)tls_template_size;
return static_tls_region_address + align_up_to(sizeof(ThreadControlBlock), tls_alignment);
}

inline ThreadControlBlock* get_tcb_pointer_from_thread_pointer(FlatPtr thread_pointer)
{
return bit_cast<ThreadControlBlock*>(thread_pointer - sizeof(ThreadControlBlock));
}

inline void* get_pointer_to_first_static_tls_block_from_thread_pointer(FlatPtr thread_pointer, size_t tls_template_size, size_t tls_alignment)
{
(void)tls_template_size;
(void)tls_alignment;
return bit_cast<void*>(thread_pointer);
}

inline void* get_pointer_to_static_tls_region_from_thread_pointer(FlatPtr thread_pointer, size_t tls_template_size, size_t tls_alignment)
{
(void)tls_template_size;
return bit_cast<void*>(thread_pointer - align_up_to(sizeof(ThreadControlBlock), tls_alignment));
}

}
11 changes: 11 additions & 0 deletions Userland/Libraries/LibELF/Arch/tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@

#pragma once

#include <AK/Platform.h>
#include <AK/Types.h>

namespace ELF {

void set_thread_pointer_register(FlatPtr);

}

#if ARCH(AARCH64)
# include <LibELF/Arch/aarch64/tls.h>
#elif ARCH(RISCV64)
# include <LibELF/Arch/riscv64/tls.h>
#elif ARCH(X86_64)
# include <LibELF/Arch/x86_64/tls.h>
#else
# error Unknown architecture
#endif
62 changes: 62 additions & 0 deletions Userland/Libraries/LibELF/Arch/x86_64/tls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2024, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <AK/BitCast.h>
#include <AK/Types.h>

namespace ELF {

struct ThreadControlBlock {
// The %fs segment register is the thread pointer register on x86-64.
// x86-64 uses variant II of the TLS data structures described in "ELF Handling For Thread-Local Storage",
// which requires the thread pointer to point to the TCB.
// That document also requires that the pointer shall be accessible with "movq %fs:0, %<reg>",
// so the first member in the TCB has to be a copy of the thread pointer.
void* thread_pointer;

// Variant II requires that the TCB has to contain a pointer to the dtv at an unspecified offset.
// This member is unused, as we currently only support static TLS blocks.
void* dynamic_thread_vector;
};

static constexpr size_t TLS_VARIANT = 2;
static constexpr size_t TLS_DTV_OFFSET = 0;
static constexpr size_t TLS_TP_STATIC_TLS_BLOCK_OFFSET = 0;

// x86-64 ELF TLS Layout
// The padding is needed so tp (fs_base) is correctly aligned.
//
// [.....static TLS][..padding..][TCB]
// ^tp (fs_base)

inline size_t calculate_static_tls_region_size(size_t tls_template_size, size_t tls_alignment)
{
return align_up_to(tls_template_size, tls_alignment) + sizeof(ThreadControlBlock);
}

inline FlatPtr calculate_tp_value_from_static_tls_region_address(FlatPtr static_tls_region_address, size_t tls_template_size, size_t tls_alignment)
{
return static_tls_region_address + align_up_to(tls_template_size, tls_alignment);
}

inline ThreadControlBlock* get_tcb_pointer_from_thread_pointer(FlatPtr thread_pointer)
{
return bit_cast<ThreadControlBlock*>(thread_pointer);
}

inline void* get_pointer_to_first_static_tls_block_from_thread_pointer(FlatPtr thread_pointer, size_t tls_template_size, size_t tls_alignment)
{
return bit_cast<void*>(thread_pointer - align_up_to(tls_template_size, tls_alignment));
}

inline void* get_pointer_to_static_tls_region_from_thread_pointer(FlatPtr thread_pointer, size_t tls_template_size, size_t tls_alignment)
{
return bit_cast<void*>(thread_pointer - align_up_to(tls_template_size, tls_alignment));
}

}

0 comments on commit 74a5150

Please sign in to comment.