| // 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 "constants.h" |
| #include "field.h" |
| |
| namespace elfldltl { |
| |
| // 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. |
| |
| // 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 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, |
| }; |
| }; |
| |
| // 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> { |
| 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 { |
| 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> { |
| 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 { |
| 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; |
| |
| 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 = SignedField<size_type, kSwap>; |
| |
| static constexpr auto kAddressBits = kAddrBits<Addr>; |
| |
| using typename Layout<Class, Data>::Nhdr; |
| |
| using Note = ElfNote; |
| using NoteSegment = ElfNoteSegment<kData>; |
| |
| struct Ehdr { |
| 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. |
| } |
| |
| constexpr bool Loadable(ElfMachine target = ElfMachine::kNative) const { |
| return Valid() && type == ElfType::kDyn && machine == target; |
| } |
| |
| 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; |
| }; |
| |
| struct Sym : public Layout<Class, Data>::Sym { |
| using Layout<Class, Data>::Sym::Sym; |
| using Layout<Class, Data>::Sym::operator=; |
| |
| // The layout differs completely by class, but the one-byte info field is |
| // always encoded the same way. |
| |
| constexpr ElfSymBind bind() const { return static_cast<ElfSymBind>(this->info() >> 4); } |
| |
| constexpr ElfSymType type() const { return static_cast<ElfSymType>(this->info() & 0xf); } |
| }; |
| |
| struct Rel { |
| constexpr uint32_t symndx() const { return 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; |
| |
| Addr offset; |
| Addr info; |
| }; |
| |
| struct Rela : public Rel { |
| SignedField<size_type, kSwap> addend; |
| }; |
| }; |
| |
| 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>>; |
| |
| } // namespace elfldltl |
| |
| #endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_LAYOUT_H_ |