| // Copyright 2021 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_LAYOUT_H_ |
| #define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_LAYOUT_H_ |
| |
| #include <array> |
| #include <limits> |
| #include <optional> |
| #include <string_view> |
| |
| #include "constants.h" |
| #include "field.h" |
| |
| namespace elfldltl { |
| |
| // Forward declaration; see <lib/elfldltl/abi-ptr.h>. |
| struct LocalAbiTraits; |
| |
| // These types are parameterized by class (32-bit vs 64-bit) and data (byte |
| // order). The traditional ELF names Byte, Half, Word, Xword, and Addr are |
| // used for accessor types that respect the byte order and class. Note that |
| // redundant traditional names such as Offset are not used; Addr is used for |
| // all "address-sized" fields, whether they are offsets, addresses, or sizes. |
| // |
| // The Elf<Class, Data> template, abbreviated Elf32<Data> or Elf64<Data>, is |
| // the intended way to refer to all these types. The Layout* base types are |
| // just doing some implementation sharing between template instantiations. |
| // |
| // When working with actual values rather than encoded ELF metadata formats, |
| // the standard uintNN_t types should be used. The Elf::size_type type is an |
| // alias for the address-sized unsigned integer type, i.e. the host-side native |
| // type corresponding to Elf::Addr (which might be a byte-swapping type). |
| // |
| // The type and field names for struct types use the traditional terse ELF |
| // names, but without the traditional prefixes or capitalization. Each field |
| // is a byte-order-respecting accessor for the natural underlying type or an |
| // enum class with the natural underlying type, and has a simple lowercase name |
| // with no prefix or suffix. For compound fields, specific accessors are also |
| // provided to do the bit-field extraction. |
| |
| template <typename T, typename... AnyOfThese> |
| inline constexpr bool kIsAnyOf = (std::is_same_v<T, AnyOfThese> || ...); |
| |
| // The basic types and some structure layouts are identical across bit width |
| // (ElfClass). This base class handles differences in byte order (ElfData). |
| template <ElfData Data> |
| struct LayoutBase { |
| static constexpr ElfData kData = Data; |
| static constexpr bool kSwap = Data != ElfData::kNative; |
| |
| template <typename T> |
| struct UnsignedType { |
| using type = UnsignedField<T, kSwap>; |
| static_assert(sizeof(type) == sizeof(T)); |
| static_assert(alignof(type) == alignof(T)); |
| }; |
| |
| template <typename T> |
| using Unsigned = typename UnsignedType<T>::type; |
| |
| using Byte = Unsigned<uint8_t>; |
| using Half = Unsigned<uint16_t>; |
| using Word = Unsigned<uint32_t>; |
| using Xword = Unsigned<uint64_t>; |
| |
| struct Nhdr { |
| static constexpr uint32_t Align(uint32_t size) { |
| return (size + kAlign - 1) & -uint32_t{kAlign}; |
| } |
| |
| constexpr uint32_t name_offset() const { return sizeof(*this); } |
| |
| constexpr uint32_t desc_offset() const { return name_offset() + Align(namesz); } |
| |
| constexpr uint32_t size_bytes() const { return desc_offset() + Align(descsz); } |
| |
| static constexpr uint32_t kAlign = 4; |
| |
| Word namesz; |
| Word descsz; |
| Word type; |
| }; |
| static_assert(sizeof(Nhdr) == 12); |
| }; |
| |
| // A base class for the different phdr layouts, ensuring that all of the |
| // Elf<...>::Phdr definitions below use the same Flags type. |
| struct PhdrBase { |
| // These are individual bits OR'd together. |
| enum Flags : uint32_t { |
| kExecute = 1 << 0, |
| kWrite = 1 << 1, |
| kRead = 1 << 2, |
| }; |
| }; |
| |
| // A base class for the shdr layout, ensuring that all of the Elf<...>::Shdr |
| // definitions below use the same Flags type. |
| struct ShdrBase { |
| // These are individual bits OR'd together. |
| enum Flags : uint32_t { |
| kWrite = 1u << 0, |
| kAlloc = 1u << 1, |
| kExecinstr = 1u << 2, |
| kMerge = 1u << 4, |
| kStrings = 1u << 5, |
| kInfoLink = 1u << 6, |
| kLinkOrder = 1u << 7, |
| kOsNonconforming = 1u << 8, |
| kGroup = 1u << 9, |
| kTls = 1u << 10, |
| kCompressed = 1u << 11, |
| kOrdered = 1u << 30, |
| kExclude = 1u << 31, |
| }; |
| }; |
| |
| // A base class for the sym layout, allowing the Elf<...>::Sym definitions |
| // below to share these methods. |
| template <class Sym> |
| struct SymBase { |
| constexpr ElfSymBind bind() const { |
| return static_cast<ElfSymBind>(static_cast<const Sym*>(this)->info() >> 4); |
| } |
| |
| constexpr ElfSymType type() const { |
| return static_cast<ElfSymType>(static_cast<const Sym*>(this)->info() & 0xf); |
| } |
| |
| static constexpr uint8_t MakeInfo(ElfSymBind bind, ElfSymType type) { |
| return static_cast<uint8_t>((static_cast<uint8_t>(bind) << 4) | |
| (static_cast<uint8_t>(type) << 0)); |
| } |
| |
| constexpr ElfSymVisibility visibility() const { |
| return static_cast<ElfSymVisibility>(static_cast<const Sym*>(this)->other() & 0x3); |
| } |
| |
| // Returns true if this symbol as the referent of a dynamic relocation will |
| // always be resolved just to itself in the referring module. This assumes |
| // that STV_PROTECTED does not need to be resolved to ET_EXEC PLT or COPY |
| // reloc sites. Note also that the null symbol with index zero always has |
| // all zero fields and thus STB_LOCAL binding, so this returns true for it. |
| // Relocations using symbol zero are implicitly resolved as module-relative |
| // since its st_value is also zero. |
| constexpr bool runtime_local() const { |
| return bind() == ElfSymBind::kLocal || visibility() > ElfSymVisibility::kDefault; |
| } |
| }; |
| |
| // Some header layouts vary by ElfClass, i.e. address size used in ELF |
| // metadata. This is partially specialized by class below. |
| template <ElfClass Class, ElfData Data> |
| struct Layout; |
| |
| // Layout details specific to 32-bit ELF. |
| template <ElfData Data> |
| struct Layout<ElfClass::k32, Data> : public LayoutBase<Data> { |
| static constexpr ElfClass kClass = ElfClass::k32; |
| |
| using LayoutBase<Data>::kData; |
| using LayoutBase<Data>::kSwap; |
| using typename LayoutBase<Data>::Byte; |
| using typename LayoutBase<Data>::Half; |
| using typename LayoutBase<Data>::Word; |
| |
| using Addr = typename LayoutBase<Data>::template Unsigned<uint32_t>; |
| |
| struct Phdr : public PhdrBase { |
| EnumField<ElfPhdrType, kSwap> type; |
| Addr offset; |
| Addr vaddr; |
| Addr paddr; |
| Addr filesz; |
| Addr memsz; |
| Word flags; |
| Addr align; |
| }; |
| |
| struct Sym : public SymBase<Sym> { |
| Word name; |
| Addr value; |
| Addr size; |
| Byte info; |
| Byte other; |
| Half shndx; |
| }; |
| |
| static constexpr unsigned int kRelTypeBits = 8; |
| }; |
| |
| // Layout details specific to 64-bit ELF. |
| template <ElfData Data> |
| struct Layout<ElfClass::k64, Data> : public LayoutBase<Data> { |
| static constexpr ElfClass kClass = ElfClass::k64; |
| |
| using LayoutBase<Data>::kData; |
| using LayoutBase<Data>::kSwap; |
| using typename LayoutBase<Data>::Byte; |
| using typename LayoutBase<Data>::Half; |
| using typename LayoutBase<Data>::Word; |
| |
| using Addr = typename LayoutBase<Data>::template Unsigned<uint64_t>; |
| |
| struct Phdr : public PhdrBase { |
| EnumField<ElfPhdrType, kSwap> type; |
| Word flags; |
| Addr offset; |
| Addr vaddr; |
| Addr paddr; |
| Addr filesz; |
| Addr memsz; |
| Addr align; |
| }; |
| |
| struct Sym : public SymBase<Sym> { |
| Word name; |
| Byte info; |
| Byte other; |
| Half shndx; |
| Addr value; |
| Addr size; |
| }; |
| |
| static constexpr unsigned int kRelTypeBits = 32; |
| }; |
| |
| // Forward declarations (see note.h). |
| struct ElfNote; |
| template <ElfData Data> |
| class ElfNoteSegment; |
| |
| // Forward declaration (see tls-layout.h). |
| template <class Elf> |
| class TlsLayout; |
| |
| template <typename Addr> |
| constexpr auto kAddrBits = std::numeric_limits<typename Addr::value_type>::digits; |
| |
| // The various ELF data structure layouts differ by class (32-bit vs 64-bit). |
| // But many use the same layout with certain fields being either 32 or 64 bits. |
| // The layouts that actually differ in field order and the like are defined by |
| // the Layout base class; the common-by-analogy layouts are defined here. |
| template <ElfClass Class = ElfClass::kNative, ElfData Data = ElfData::kNative> |
| struct Elf : private Layout<Class, Data> { |
| static constexpr ElfClass kClass = Class; |
| static constexpr ElfData kData = Data; |
| |
| using Layout<Class, Data>::kSwap; |
| using typename Layout<Class, Data>::Byte; |
| using typename Layout<Class, Data>::Half; |
| using typename Layout<Class, Data>::Word; |
| using typename Layout<Class, Data>::Xword; |
| using typename Layout<Class, Data>::Addr; |
| |
| using size_type = typename Addr::value_type; |
| |
| using Addend = typename Addr::Signed; |
| |
| static constexpr auto kAddressBits = kAddrBits<Addr>; |
| |
| using typename Layout<Class, Data>::Nhdr; |
| |
| using Note = ElfNote; |
| using NoteSegment = ElfNoteSegment<kData>; |
| |
| struct Ehdr { |
| using ElfLayout = Elf; |
| |
| constexpr bool Valid() const { |
| return magic == kMagic && // It's ELF at all, |
| elfclass == Class && elfdata == Data && // of the right sort, |
| ident_version == ElfVersion::kCurrent && // and passes basic |
| version == ElfVersion::kCurrent && // sanity checks of |
| ehsize == sizeof(Ehdr); // various sorts. |
| } |
| |
| // This is the verbose version that uses the Diagnostics template API (see |
| // diagnostics.h) to report why it returns false when it does. |
| template <class Diagnostics> |
| constexpr bool Valid(Diagnostics& diagnostics) const { |
| using namespace std::literals::string_view_literals; |
| |
| // The diagnostics object might tell us to keep going after each error. |
| bool valid = true; |
| auto check = [&](bool ok, auto&& error) -> bool { |
| if (ok) { |
| return true; |
| } |
| valid = false; |
| return diagnostics.FormatError(std::forward<decltype(error)>(error)); |
| }; |
| |
| return check(magic == kMagic, "not an ELF file"sv) && |
| check(elfclass == Class, "wrong ELF class (bit-width)"sv) && |
| check(elfdata == Data, "wrong byte order"sv) && |
| check(version == ElfVersion::kCurrent, "wrong e_version value"sv) && |
| check(ehsize == sizeof(Ehdr), "wrong e_ehsize value"sv) && |
| check(ident_version == ElfVersion::kCurrent, "wrong EI_VERSION value"sv) && valid; |
| } |
| |
| constexpr bool Loadable(std::optional<ElfMachine> target = ElfMachine::kNative) const { |
| return Valid() && type == ElfType::kDyn && (!target || machine == target); |
| } |
| |
| // This is the verbose version that uses the Diagnostics template API (see |
| // diagnostics.h) to report why it returns false when it does. |
| template <class Diagnostics> |
| constexpr bool Loadable(Diagnostics& diagnostics, |
| std::optional<ElfMachine> target = ElfMachine::kNative) const { |
| using namespace std::literals::string_view_literals; |
| |
| if (!Valid(diagnostics)) [[unlikely]] { |
| return false; |
| } |
| |
| if (target && machine != target) [[unlikely]] { |
| diagnostics.FormatError("wrong e_machine for architecture"sv); |
| return false; |
| } |
| |
| switch (type()) { |
| case ElfType::kDyn: |
| [[likely]]; |
| break; |
| case ElfType::kExec: |
| diagnostics.FormatError( |
| "loading ET_EXEC files is not supported, only ET_DYN files;" |
| " be sure to compile and link as PIE (-fPIE, -pie)"sv); |
| return false; |
| case ElfType::kRel: |
| diagnostics.FormatError("ET_REL files cannot be loaded"sv); |
| return false; |
| case ElfType::kCore: |
| diagnostics.FormatError("ET_CORE files cannot be loaded"sv); |
| return false; |
| default: |
| diagnostics.FormatError("unrecognized e_type value"sv, static_cast<uint32_t>(type())); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static constexpr Word kMagic{std::array{'\x7f', 'E', 'L', 'F'}}; |
| |
| // phnum has this value to indicate the real number of phdrs is too large |
| // to fit and is instead stored in shdr[0].info. |
| static inline const Half kPnXnum{0xffff}; |
| |
| // These together make up the traditional unsigned char e_ident[16]. |
| Word magic; |
| ElfClass elfclass; |
| ElfData elfdata; |
| ElfVersion ident_version; |
| Byte osabi; |
| Byte abiversion; |
| Byte ident_pad[7]; |
| |
| EnumField<ElfType, kSwap> type; |
| EnumField<ElfMachine, kSwap> machine; |
| EnumField<ElfVersion, kSwap, uint32_t> version; |
| Addr entry; |
| Addr phoff; |
| Addr shoff; |
| Word flags; |
| Half ehsize; |
| Half phentsize; |
| Half phnum; |
| Half shentsize; |
| Half shnum; |
| Half shstrndx; |
| }; |
| |
| using typename Layout<Class, Data>::Phdr; |
| |
| // This is not really used at runtime except for the kPnXnum protocol. |
| // But it's useful to have all the values handy for diagnostic tools. |
| struct Shdr : public ShdrBase { |
| Word name; |
| EnumField<ElfShdrType, kSwap> type; |
| Addr flags; |
| Addr addr; |
| Addr offset; |
| Addr size; |
| Word link; |
| Word info; |
| Addr addralign; |
| Addr entsize; |
| }; |
| |
| struct Dyn { |
| EnumField<ElfDynTag, kSwap, size_type> tag; |
| |
| // Traditionally this was a union d_un of d_val and d_ptr, but both with |
| // types that are just an address-sized unsigned integer. Sometimes the |
| // value is a "pointer" (relative to load bias) to some data structure. |
| // Sometimes it's a byte size. Sometimes it's an enum constant. |
| Addr val; |
| }; |
| |
| using typename Layout<Class, Data>::Sym; |
| |
| // These two classes are copied so that these types can be both easily |
| // aggregate initialized and designated initialized. We can't have both |
| // if we opt to use inheritance to save some code duplication in these |
| // two classes. Note, Phdr and Sym types use inheritance in this way |
| // and the syntax to aggregate initialize them is ugly {{}, fields...}. |
| // In practice, those types are never aggregate initialized because their |
| // layout differs between elf classes. The Rel types do not so we wish |
| // for them to be easily aggregate initializable. Likewise, we want |
| // these types to follow Phdr and Sym and be designated initializable |
| // even if this pattern is unlikely to be used on these types. |
| |
| struct Rel { |
| constexpr uint32_t symndx() const { return static_cast<uint32_t>(info() >> kSymndxShift); } |
| constexpr uint32_t type() const { return info() & kTypeMask; } |
| |
| static constexpr auto kSymndxShift = Layout<Class, Data>::kRelTypeBits; |
| static constexpr auto kTypeMask = (size_type{1} << kSymndxShift) - 1; |
| |
| static constexpr Addr MakeInfo(Addr sym_name, uint32_t type) { |
| return (sym_name << kSymndxShift) | (static_cast<Addr>(type) & kTypeMask); |
| } |
| |
| Addr offset; |
| Addr info; |
| }; |
| |
| struct Rela { |
| constexpr uint32_t symndx() const { return static_cast<uint32_t>(info() >> kSymndxShift); } |
| constexpr uint32_t type() const { return info() & kTypeMask; } |
| |
| static constexpr auto kSymndxShift = Layout<Class, Data>::kRelTypeBits; |
| static constexpr auto kTypeMask = (size_type{1} << kSymndxShift) - 1; |
| |
| static constexpr Addr MakeInfo(Addr sym_name, uint32_t type) { |
| return (sym_name << kSymndxShift) | (static_cast<Addr>(type) & kTypeMask); |
| } |
| |
| Addr offset; |
| Addr info; |
| Addend addend; |
| }; |
| |
| // When the compiler generates a call to __tls_get_addr, the linker |
| // generates two corresponding dynamic relocation entries applying to |
| // adjacent GOT slots that form a pair describing what module and symbol |
| // resolved the reference at dynamic link time. The first slot holds the |
| // module ID, a 1-origin index. The second slot holds the offset from |
| // that module's PT_TLS segment. |
| struct TlsGetAddrGot { |
| Addr tls_modid; // R_*_DTPMOD* et al relocations set this. |
| Addr offset; // R_*_DTPOFF* et al relocations set this. |
| }; |
| |
| // When the compiler generates a TLSDESC callback, the linker generates a |
| // single corresponding dynamic relocation entry that applies to a pair of |
| // adjacent GOT slots. In DT_REL format, the addend is stored in the second |
| // slot. The first slot holds a function pointer installed by the dynamic |
| // linker. The compiler generates code to call this function pointer using a |
| // bespoke calling convention specified in each psABI; it takes a single |
| // argument of this address in the GOT. The second slot is filled by the |
| // dynamic linker with whatever value is of use to the function it installs |
| // such that it returns the thread pointer offset of the per-thread address |
| // of the relocation's symbol plus addend. (For static TLS, that offset will |
| // be the same in every thread. For dynamic TLS, it will be the difference |
| // of unrelated pointers that recovers an uncorrelated per-thread address.) |
| struct TlsDescGot { |
| Addr function; |
| Addr value; |
| }; |
| |
| // These are declared in svr4-abi.h rather than there. These are not |
| // formally parts of the ELF format, but rather de facto standard ABI types |
| // from the original SVR4 implementation that introduced ELF that have been |
| // kept compatible in other implementations historically. |
| |
| template <class AbiTraits = LocalAbiTraits> |
| struct LinkMap; |
| |
| template <class AbiTraits = LocalAbiTraits> |
| struct RDebug; |
| |
| // This is true for any T that's an layout.h or field.h type, i.e. guaranteed |
| // to use the right format and byte order for the given ELF Class and Data. |
| // Note that all <lib/elfldltl/field.h> types yield true even if they aren't |
| // sized or byte orders used in this Elf instantiation: they are still types |
| // whose exact intended target format is both well-known and represented for |
| // byte-by-byte copying directly in T. Conversely, note neither LinkMap, |
| // RDebug, nor any other type that indirectly uses AbiPtr, is considered a |
| // "layout" type; these types are specialized by an AbiTraits parameter that |
| // determines their actual memory format. All non-layout types (aside from |
| // single-byte integers and the like) require specialized transcription when |
| // copying between address spaces or pointer formats. This is defined |
| // primarily for the benefit of <lib/ld/remote-abi-transcriber.h>, which see. |
| template <typename T> |
| static constexpr bool kIsLayout = kIsAnyOf<T, Ehdr, Shdr, Nhdr, Phdr, Dyn, Sym, Rel, Rela, |
| TlsGetAddrGot, TlsDescGot, TlsLayout<Elf>>; |
| }; |
| |
| template <ElfData Data = ElfData::kNative> |
| using Elf32 = Elf<ElfClass::k32, Data>; |
| |
| template <ElfData Data = ElfData::kNative> |
| using Elf64 = Elf<ElfClass::k64, Data>; |
| |
| // This instantiates Template with Elf64<> and Elf32<> as parameters. |
| template <template <class...> typename Template> |
| using AllNativeFormats = Template<Elf64<>, Elf32<>>; |
| |
| // This instantiates Template with each Elf variant as a parameter. |
| template <template <class...> typename Template> |
| using AllFormats = Template<Elf64<ElfData::k2Lsb>, Elf32<ElfData::k2Lsb>, // |
| Elf64<ElfData::k2Msb>, Elf32<ElfData::k2Msb>>; |
| |
| template <typename T> |
| struct IsAnyLayout { |
| template <class... Elf> |
| using type = std::bool_constant<(Elf::template kIsLayout<T> || ...)>; |
| }; |
| |
| // Like Elf::kIsLayout, but across any known format. |
| template <typename T> |
| static constexpr bool kIsLayout = AllFormats<IsAnyLayout<T>::template type>{}; |
| |
| template <ElfClass Class, ElfData Data> |
| template <typename T, bool kSwap> |
| constexpr bool Elf<Class, Data>::kIsLayout<UnsignedField<T, kSwap>> = true; |
| |
| template <ElfClass Class, ElfData Data> |
| template <typename T, bool kSwap> |
| constexpr bool Elf<Class, Data>::kIsLayout<SignedField<T, kSwap>> = true; |
| |
| template <ElfClass Class, ElfData Data> |
| template <typename T, bool kSwap, typename U> |
| constexpr bool Elf<Class, Data>::kIsLayout<EnumField<T, kSwap, U>> = true; |
| |
| } // namespace elfldltl |
| |
| #endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_LAYOUT_H_ |