blob: 5aaf292bfa8b36f1722e7de9967b292c14c1397e [file] [log] [blame]
// 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_