357 lines
12 KiB
C++
357 lines
12 KiB
C++
// Copyright 2018 The Chromium 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 COMPONENTS_ZUCCHINI_DISASSEMBLER_ELF_H_
|
|
#define COMPONENTS_ZUCCHINI_DISASSEMBLER_ELF_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <algorithm>
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "components/zucchini/address_translator.h"
|
|
#include "components/zucchini/buffer_view.h"
|
|
#include "components/zucchini/disassembler.h"
|
|
#include "components/zucchini/image_utils.h"
|
|
#include "components/zucchini/rel32_finder.h"
|
|
#include "components/zucchini/rel32_utils.h"
|
|
#include "components/zucchini/reloc_elf.h"
|
|
#include "components/zucchini/type_elf.h"
|
|
|
|
namespace zucchini {
|
|
|
|
struct ArmReferencePool {
|
|
enum : uint8_t {
|
|
kPoolReloc,
|
|
kPoolAbs32,
|
|
kPoolRel32,
|
|
};
|
|
};
|
|
|
|
struct AArch32ReferenceType {
|
|
enum : uint8_t {
|
|
kReloc, // kPoolReloc
|
|
|
|
kAbs32, // kPoolAbs32
|
|
|
|
kRel32_A24, // kPoolRel32
|
|
kRel32_T8,
|
|
kRel32_T11,
|
|
kRel32_T20,
|
|
kRel32_T24,
|
|
|
|
kTypeCount
|
|
};
|
|
};
|
|
|
|
struct AArch64ReferenceType {
|
|
enum : uint8_t {
|
|
kReloc, // kPoolReloc
|
|
|
|
kAbs32, // kPoolAbs32
|
|
|
|
kRel32_Immd14, // kPoolRel32
|
|
kRel32_Immd19,
|
|
kRel32_Immd26,
|
|
|
|
kTypeCount
|
|
};
|
|
};
|
|
|
|
struct Elf32Traits {
|
|
static constexpr uint16_t kVersion = 1;
|
|
static constexpr Bitness kBitness = kBit32;
|
|
static constexpr elf::FileClass kIdentificationClass = elf::ELFCLASS32;
|
|
using Elf_Shdr = elf::Elf32_Shdr;
|
|
using Elf_Phdr = elf::Elf32_Phdr;
|
|
using Elf_Ehdr = elf::Elf32_Ehdr;
|
|
using Elf_Rel = elf::Elf32_Rel;
|
|
using Elf_Rela = elf::Elf32_Rela;
|
|
};
|
|
|
|
// Architecture-specific definitions.
|
|
|
|
struct Elf32IntelTraits : public Elf32Traits {
|
|
static constexpr ExecutableType kExeType = kExeTypeElfX86;
|
|
static const char kExeTypeString[];
|
|
static constexpr elf::MachineArchitecture kMachineValue = elf::EM_386;
|
|
static constexpr uint32_t kRelType = elf::R_386_RELATIVE;
|
|
enum : uint32_t { kVAWidth = 4 };
|
|
using Rel32FinderUse = Rel32FinderX86;
|
|
};
|
|
|
|
struct ElfAArch32Traits : public Elf32Traits {
|
|
static constexpr ExecutableType kExeType = kExeTypeElfAArch32;
|
|
static const char kExeTypeString[];
|
|
static constexpr elf::MachineArchitecture kMachineValue = elf::EM_ARM;
|
|
static constexpr uint32_t kRelType = elf::R_ARM_RELATIVE;
|
|
enum : uint32_t { kVAWidth = 4 };
|
|
using ArmReferenceType = AArch32ReferenceType;
|
|
using Rel32FinderUse = Rel32FinderAArch32;
|
|
};
|
|
|
|
struct Elf64Traits {
|
|
static constexpr uint16_t kVersion = 1;
|
|
static constexpr Bitness kBitness = kBit64;
|
|
static constexpr elf::FileClass kIdentificationClass = elf::ELFCLASS64;
|
|
using Elf_Shdr = elf::Elf64_Shdr;
|
|
using Elf_Phdr = elf::Elf64_Phdr;
|
|
using Elf_Ehdr = elf::Elf64_Ehdr;
|
|
using Elf_Rel = elf::Elf64_Rel;
|
|
using Elf_Rela = elf::Elf64_Rela;
|
|
};
|
|
|
|
// Architecture-specific definitions.
|
|
struct Elf64IntelTraits : public Elf64Traits {
|
|
static constexpr ExecutableType kExeType = kExeTypeElfX64;
|
|
static const char kExeTypeString[];
|
|
static constexpr elf::MachineArchitecture kMachineValue = elf::EM_X86_64;
|
|
static constexpr uint32_t kRelType = elf::R_X86_64_RELATIVE;
|
|
enum : uint32_t { kVAWidth = 8 };
|
|
using Rel32FinderUse = Rel32FinderX64;
|
|
};
|
|
|
|
struct ElfAArch64Traits : public Elf64Traits {
|
|
static constexpr ExecutableType kExeType = kExeTypeElfAArch64;
|
|
static const char kExeTypeString[];
|
|
static constexpr elf::MachineArchitecture kMachineValue = elf::EM_AARCH64;
|
|
// TODO(huangs): See if R_AARCH64_GLOB_DAT and R_AARCH64_JUMP_SLOT should be
|
|
// used.
|
|
static constexpr uint32_t kRelType = elf::R_AARCH64_RELATIVE;
|
|
enum : uint32_t { kVAWidth = 8 };
|
|
using ArmReferenceType = AArch64ReferenceType;
|
|
using Rel32FinderUse = Rel32FinderAArch64;
|
|
};
|
|
|
|
// Decides whether target |offset| is covered by a section in |sorted_headers|.
|
|
template <class ELF_SHDR>
|
|
bool IsTargetOffsetInElfSectionList(
|
|
const std::vector<const ELF_SHDR*>& sorted_headers,
|
|
offset_t offset) {
|
|
// Use binary search to search in a list of intervals, in a fashion similar to
|
|
// AddressTranslator::OffsetToUnit().
|
|
auto comp = [](offset_t offset, const ELF_SHDR* header) -> bool {
|
|
return offset < header->sh_offset;
|
|
};
|
|
auto it = std::upper_bound(sorted_headers.begin(), sorted_headers.end(),
|
|
offset, comp);
|
|
if (it == sorted_headers.begin())
|
|
return false;
|
|
--it;
|
|
// Just check offset without worrying about width, since this is a target.
|
|
// Not using RangeCovers() because |sh_offset| and |sh_size| can be 64-bit.
|
|
return offset >= (*it)->sh_offset &&
|
|
offset - (*it)->sh_offset < (*it)->sh_size;
|
|
}
|
|
|
|
// Disassembler for ELF.
|
|
template <class TRAITS>
|
|
class DisassemblerElf : public Disassembler {
|
|
public:
|
|
using Traits = TRAITS;
|
|
static constexpr uint16_t kVersion = Traits::kVersion;
|
|
// Applies quick checks to determine whether |image| *may* point to the start
|
|
// of an executable. Returns true iff the check passes.
|
|
static bool QuickDetect(ConstBufferView image);
|
|
|
|
DisassemblerElf(const DisassemblerElf&) = delete;
|
|
const DisassemblerElf& operator=(const DisassemblerElf&) = delete;
|
|
~DisassemblerElf() override;
|
|
|
|
// Disassembler:
|
|
ExecutableType GetExeType() const override;
|
|
std::string GetExeTypeString() const override;
|
|
std::vector<ReferenceGroup> MakeReferenceGroups() const override = 0;
|
|
|
|
// Read/Write functions that are common among different architectures.
|
|
std::unique_ptr<ReferenceReader> MakeReadRelocs(offset_t lo, offset_t hi);
|
|
std::unique_ptr<ReferenceWriter> MakeWriteRelocs(MutableBufferView image);
|
|
|
|
const AddressTranslator& translator() const { return translator_; }
|
|
|
|
protected:
|
|
friend Disassembler;
|
|
|
|
DisassemblerElf();
|
|
|
|
bool Parse(ConstBufferView image) override;
|
|
|
|
// Returns the supported Elf_Ehdr::e_machine enum.
|
|
static constexpr elf::MachineArchitecture supported_architecture() {
|
|
return Traits::kMachineValue;
|
|
}
|
|
|
|
// Returns the type to look for in the reloc section.
|
|
static constexpr uint32_t supported_relocation_type() {
|
|
return Traits::kRelType;
|
|
}
|
|
|
|
// Performs architecture-specific parsing of an executable section, to extract
|
|
// rel32 references.
|
|
virtual void ParseExecSection(const typename Traits::Elf_Shdr& section) = 0;
|
|
|
|
// Processes rel32 data after they are extracted from executable sections.
|
|
virtual void PostProcessRel32() = 0;
|
|
|
|
// Parses ELF header and section headers, and performs basic validation.
|
|
// Returns whether parsing was successful.
|
|
bool ParseHeader();
|
|
|
|
// Extracts and stores section headers that we need.
|
|
void ExtractInterestingSectionHeaders();
|
|
|
|
// Parsing functions that extract references from various sections.
|
|
void GetAbs32FromRelocSections();
|
|
void GetRel32FromCodeSections();
|
|
void ParseSections();
|
|
|
|
// Main ELF header.
|
|
const typename Traits::Elf_Ehdr* header_ = nullptr;
|
|
|
|
// Section header table, ordered by section id.
|
|
elf::Elf32_Half sections_count_ = 0;
|
|
const typename Traits::Elf_Shdr* sections_ = nullptr;
|
|
|
|
// Program header table.
|
|
elf::Elf32_Half segments_count_ = 0;
|
|
const typename Traits::Elf_Phdr* segments_ = nullptr;
|
|
|
|
// Bit fields to store the role each section may play.
|
|
std::vector<int> section_judgements_;
|
|
|
|
// Translator between offsets and RVAs.
|
|
AddressTranslator translator_;
|
|
|
|
// Identity translator for abs32 translation.
|
|
AddressTranslator identity_translator_;
|
|
|
|
// Extracted relocation section dimensions data, sorted by file offsets.
|
|
std::vector<SectionDimensionsElf> reloc_section_dims_;
|
|
|
|
// Headers of executable sections, sorted by file offsets of the data each
|
|
// header points to.
|
|
std::vector<const typename Traits::Elf_Shdr*> exec_headers_;
|
|
|
|
// Sorted file offsets of abs32 locations.
|
|
std::vector<offset_t> abs32_locations_;
|
|
};
|
|
|
|
// Disassembler for ELF with Intel architectures.
|
|
template <class TRAITS>
|
|
class DisassemblerElfIntel : public DisassemblerElf<TRAITS> {
|
|
public:
|
|
using Traits = TRAITS;
|
|
enum ReferenceType : uint8_t { kReloc, kAbs32, kRel32, kTypeCount };
|
|
|
|
DisassemblerElfIntel();
|
|
DisassemblerElfIntel(const DisassemblerElfIntel&) = delete;
|
|
const DisassemblerElfIntel& operator=(const DisassemblerElfIntel&) = delete;
|
|
~DisassemblerElfIntel() override;
|
|
|
|
// Disassembler:
|
|
std::vector<ReferenceGroup> MakeReferenceGroups() const override;
|
|
|
|
// DisassemblerElf:
|
|
void ParseExecSection(const typename Traits::Elf_Shdr& section) override;
|
|
void PostProcessRel32() override;
|
|
|
|
// Specialized Read/Write functions.
|
|
std::unique_ptr<ReferenceReader> MakeReadAbs32(offset_t lo, offset_t hi);
|
|
std::unique_ptr<ReferenceWriter> MakeWriteAbs32(MutableBufferView image);
|
|
std::unique_ptr<ReferenceReader> MakeReadRel32(offset_t lo, offset_t hi);
|
|
std::unique_ptr<ReferenceWriter> MakeWriteRel32(MutableBufferView image);
|
|
|
|
private:
|
|
// Sorted file offsets of rel32 locations.
|
|
// Using std::deque to reduce peak memory footprint.
|
|
std::deque<offset_t> rel32_locations_;
|
|
};
|
|
|
|
using DisassemblerElfX86 = DisassemblerElfIntel<Elf32IntelTraits>;
|
|
using DisassemblerElfX64 = DisassemblerElfIntel<Elf64IntelTraits>;
|
|
|
|
// Disassembler for ELF with ARM architectures.
|
|
template <class TRAITS>
|
|
class DisassemblerElfArm : public DisassemblerElf<TRAITS> {
|
|
public:
|
|
using Traits = TRAITS;
|
|
DisassemblerElfArm();
|
|
DisassemblerElfArm(const DisassemblerElfArm&) = delete;
|
|
const DisassemblerElfArm& operator=(const DisassemblerElfArm&) = delete;
|
|
~DisassemblerElfArm() override;
|
|
|
|
// Determines whether target |offset| is in an executable section.
|
|
bool IsTargetOffsetInExecSection(offset_t offset) const;
|
|
|
|
// Creates an architecture-specific Rel32Finder for ParseExecSection.
|
|
virtual std::unique_ptr<typename Traits::Rel32FinderUse> MakeRel32Finder(
|
|
const typename Traits::Elf_Shdr& section) = 0;
|
|
|
|
// DisassemblerElf:
|
|
void ParseExecSection(const typename Traits::Elf_Shdr& section) override;
|
|
void PostProcessRel32() override;
|
|
|
|
// Specialized Read/Write functions.
|
|
std::unique_ptr<ReferenceReader> MakeReadAbs32(offset_t lo, offset_t hi);
|
|
std::unique_ptr<ReferenceWriter> MakeWriteAbs32(MutableBufferView image);
|
|
|
|
// Specialized Read/Write functions for different rel32 address types.
|
|
template <class ADDR_TRAITS>
|
|
std::unique_ptr<ReferenceReader> MakeReadRel32(offset_t lower,
|
|
offset_t upper);
|
|
template <class ADDR_TRAITS>
|
|
std::unique_ptr<ReferenceWriter> MakeWriteRel32(MutableBufferView image);
|
|
|
|
protected:
|
|
// Sorted file offsets of rel32 locations for each rel32 address type.
|
|
std::deque<offset_t>
|
|
rel32_locations_table_[Traits::ArmReferenceType::kTypeCount];
|
|
};
|
|
|
|
// Disassembler for ELF with AArch32 (AKA ARM32).
|
|
class DisassemblerElfAArch32 : public DisassemblerElfArm<ElfAArch32Traits> {
|
|
public:
|
|
DisassemblerElfAArch32();
|
|
DisassemblerElfAArch32(const DisassemblerElfAArch32&) = delete;
|
|
const DisassemblerElfAArch32& operator=(const DisassemblerElfAArch32&) =
|
|
delete;
|
|
~DisassemblerElfAArch32() override;
|
|
|
|
// Disassembler:
|
|
std::vector<ReferenceGroup> MakeReferenceGroups() const override;
|
|
|
|
// DisassemblerElfArm:
|
|
std::unique_ptr<typename Traits::Rel32FinderUse> MakeRel32Finder(
|
|
const typename Traits::Elf_Shdr& section) override;
|
|
|
|
// Under the naive assumption that an executable section is entirely ARM mode
|
|
// or THUMB2 mode, this function implements heuristics to distinguish between
|
|
// the two. Returns true if section is THUMB2 mode; otherwise return false.
|
|
bool IsExecSectionThumb2(const typename Traits::Elf_Shdr& section) const;
|
|
};
|
|
|
|
// Disassembler for ELF with AArch64 (AKA ARM64).
|
|
class DisassemblerElfAArch64 : public DisassemblerElfArm<ElfAArch64Traits> {
|
|
public:
|
|
DisassemblerElfAArch64();
|
|
DisassemblerElfAArch64(const DisassemblerElfAArch64&) = delete;
|
|
const DisassemblerElfAArch64& operator=(const DisassemblerElfAArch64&) =
|
|
delete;
|
|
~DisassemblerElfAArch64() override;
|
|
|
|
// Disassembler:
|
|
std::vector<ReferenceGroup> MakeReferenceGroups() const override;
|
|
|
|
// DisassemblerElfArm:
|
|
std::unique_ptr<typename Traits::Rel32FinderUse> MakeRel32Finder(
|
|
const typename Traits::Elf_Shdr& section) override;
|
|
};
|
|
|
|
} // namespace zucchini
|
|
|
|
#endif // COMPONENTS_ZUCCHINI_DISASSEMBLER_ELF_H_
|