/*
 * Copyright (c) 2024, Sönke Holz <soenke.holz@serenityos.org>
 *
 * 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));
}

}
