| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_TLS_LAYOUT_H_ |
| #define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_TLS_LAYOUT_H_ |
| |
| #include <lib/stdcompat/bit.h> |
| |
| #include <algorithm> |
| #include <cassert> |
| |
| #include "constants.h" |
| #include "layout.h" |
| #include "machine.h" |
| |
| namespace elfldltl { |
| |
| // This performs static TLS layout according to the machine's ABI rules. |
| // |
| // Each call to Assign takes the PT_TLS phdr for a module and returns the |
| // thread pointer offset for that module's TLS block. If there is a main |
| // executable that might use the Local Exec TLS model, then its TLS block must |
| // be assigned first. |
| // |
| // When all the static TLS modules have been assigned, then size_bytes() and |
| // alignment() return the size and alignment for the static TLS area. The size |
| // is zero if there were no nonempty PT_TLS segments passed to Assign at all. |
| // When nonzero, the size includes the ABI reserved area, if any: it's the |
| // space that must be available immediately at (or before) the thread pointer. |
| |
| template <class Elf = Elf<>> |
| class TlsLayout { |
| public: |
| using Addr = typename Elf::Addr; |
| using size_type = typename Elf::size_type; |
| using Phdr = typename Elf::Phdr; |
| |
| template <ElfMachine Machine = ElfMachine::kNative, size_type RedZone = 0> |
| constexpr size_type Assign(const Phdr& phdr) { |
| return Assign<Machine, RedZone>(phdr.memsz, phdr.align); |
| } |
| |
| template <ElfMachine Machine = ElfMachine::kNative, size_type RedZone = 0> |
| constexpr size_type Assign(size_type segment_size, size_type segment_alignment) { |
| using Traits = TlsTraits<Elf, Machine>; |
| |
| segment_alignment = std::max<size_type>(segment_alignment, 1); |
| assert(cpp20::has_single_bit(segment_alignment)); |
| |
| // The first module gets assigned at a fixed offset. This isn't just made |
| // the initializer value for size_bytes_ for two reasons: to keep this type |
| // purely zero-initialized so it can live in .bss; so that the state when |
| // no PT_TLS segments exist at all is always simply zero size. |
| if (size_bytes_ == 0) { |
| size_bytes_ = Traits::kTlsLocalExecOffset; |
| } |
| |
| // The whole static TLS block must be at least as aligned as each segment. |
| alignment_ = std::max<size_type>(alignment_, segment_alignment); |
| |
| // Within the block, each segment must be aligned according to its p_align. |
| auto segment_aligned = [segment_alignment](size_type size) { |
| return AlignUp(size, segment_alignment); |
| }; |
| |
| // Assign an offset for this segment and update the total size. |
| if constexpr (Traits::kTlsNegative) { |
| // Below the last assignment, aligned down as needed. |
| size_type offset = segment_aligned(size_bytes_ + segment_size); |
| size_bytes_ = offset + RedZone; |
| return -offset; |
| } else { |
| // Above the last assignment, aligned up as needed. |
| size_type offset = segment_aligned(size_bytes_); |
| size_bytes_ = offset + segment_size + RedZone; |
| return offset; |
| } |
| } |
| |
| constexpr size_type size_bytes() const { return size_bytes_; } |
| |
| constexpr size_type alignment() const { return alignment_; } |
| |
| // This rounds up a size to at least alignment(); and also to at least the |
| // second argument if given. |
| constexpr size_type Align(size_type size, size_type min_alignment = 0) const { |
| return AlignUp(size, std::max<size_type>(min_alignment, alignment_)); |
| } |
| |
| private: |
| static constexpr size_type AlignUp(size_type size, size_type alignment) { |
| return (size + alignment - 1) & -alignment; |
| } |
| |
| Addr size_bytes_ = 0; |
| Addr alignment_ = 0; |
| |
| public: |
| // <lib/ld/remote-abi-transcriber.h> introspection API. These aliases must |
| // be public, but can't be defined lexically before the private: section that |
| // declares the members; so this special public: section is at the end. |
| |
| using AbiLocal = TlsLayout; |
| |
| template <template <class...> class Template> |
| using AbiBases = Template<>; |
| |
| template <template <auto...> class Template> |
| using AbiMembers = Template<&TlsLayout::size_bytes_, &TlsLayout::alignment_>; |
| }; |
| |
| } // namespace elfldltl |
| |
| #endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_TLS_LAYOUT_H_ |