blob: ef050a462f7fd97c69f9fdfca5d75221bbc3a791 [file] [log] [blame]
// 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.
#include "components/zucchini/disassembler_elf.h"
#include <stddef.h>
#include <algorithm>
#include <utility>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "components/zucchini/abs32_utils.h"
#include "components/zucchini/algorithm.h"
#include "components/zucchini/buffer_source.h"
namespace zucchini {
namespace {
constexpr uint64_t kElfImageBase = 0;
// Determines whether |section| is a reloc section.
template <class Traits>
bool IsRelocSection(const typename Traits::Elf_Shdr& section) {
if (section.sh_size == 0)
return false;
if (section.sh_type == elf::SHT_REL) {
// Also validate |section.sh_entsize|, which gets used later.
return section.sh_entsize == sizeof(typename Traits::Elf_Rel);
}
if (section.sh_type == elf::SHT_RELA)
return section.sh_entsize == sizeof(typename Traits::Elf_Rela);
return false;
}
// Determines whether |section| is a section with executable code.
template <class Traits>
bool IsExecSection(const typename Traits::Elf_Shdr& section) {
return (section.sh_flags & elf::SHF_EXECINSTR) != 0;
}
} // namespace
/******** Elf32Traits ********/
// static
constexpr Bitness Elf32Traits::kBitness;
constexpr elf::FileClass Elf32Traits::kIdentificationClass;
/******** Elf32IntelTraits ********/
// static
constexpr ExecutableType Elf32IntelTraits::kExeType;
const char Elf32IntelTraits::kExeTypeString[] = "ELF x86";
constexpr elf::MachineArchitecture Elf32IntelTraits::kMachineValue;
constexpr uint32_t Elf32IntelTraits::kRelType;
/******** Elf64Traits ********/
// static
constexpr Bitness Elf64Traits::kBitness;
constexpr elf::FileClass Elf64Traits::kIdentificationClass;
/******** Elf64IntelTraits ********/
// static
constexpr ExecutableType Elf64IntelTraits::kExeType;
const char Elf64IntelTraits::kExeTypeString[] = "ELF x64";
constexpr elf::MachineArchitecture Elf64IntelTraits::kMachineValue;
constexpr uint32_t Elf64IntelTraits::kRelType;
/******** DisassemblerElf ********/
// static.
template <class Traits>
bool DisassemblerElf<Traits>::QuickDetect(ConstBufferView image) {
BufferSource source(image);
// Do not consume the bytes for the magic value, as they are part of the
// header.
if (!source.CheckNextBytes({0x7F, 'E', 'L', 'F'}))
return false;
auto* header = source.GetPointer<typename Traits::Elf_Ehdr>();
if (!header)
return false;
if (header->e_ident[elf::EI_CLASS] != Traits::kIdentificationClass)
return false;
if (header->e_ident[elf::EI_DATA] != 1) // Only ELFDATA2LSB is supported.
return false;
if (header->e_type != elf::ET_EXEC && header->e_type != elf::ET_DYN)
return false;
if (header->e_version != 1 || header->e_ident[elf::EI_VERSION] != 1)
return false;
if (header->e_machine != supported_architecture())
return false;
if (header->e_shentsize != sizeof(typename Traits::Elf_Shdr))
return false;
return true;
}
template <class Traits>
DisassemblerElf<Traits>::~DisassemblerElf() = default;
template <class Traits>
ExecutableType DisassemblerElf<Traits>::GetExeType() const {
return Traits::kExeType;
}
template <class Traits>
std::string DisassemblerElf<Traits>::GetExeTypeString() const {
return Traits::kExeTypeString;
}
// |num_equivalence_iterations_| = 2 for reloc -> abs32.
template <class Traits>
DisassemblerElf<Traits>::DisassemblerElf() : Disassembler(2) {}
template <class Traits>
bool DisassemblerElf<Traits>::Parse(ConstBufferView image) {
image_ = image;
if (!ParseHeader())
return false;
ParseSections();
return true;
}
template <class Traits>
std::unique_ptr<ReferenceReader> DisassemblerElf<Traits>::MakeReadRelocs(
offset_t lo,
offset_t hi) {
DCHECK_LE(lo, hi);
DCHECK_LE(hi, image_.size());
if (reloc_section_dims_.empty())
return std::make_unique<EmptyReferenceReader>();
return std::make_unique<RelocReaderElf>(
image_, Traits::kBitness, reloc_section_dims_,
supported_relocation_type(), lo, hi, translator_);
}
template <class Traits>
std::unique_ptr<ReferenceWriter> DisassemblerElf<Traits>::MakeWriteRelocs(
MutableBufferView image) {
return std::make_unique<RelocWriterElf>(image, Traits::kBitness, translator_);
}
template <class Traits>
bool DisassemblerElf<Traits>::ParseHeader() {
BufferSource source(image_);
// Ensures |header_| is valid later on.
if (!QuickDetect(image_))
return false;
header_ = source.GetPointer<typename Traits::Elf_Ehdr>();
sections_count_ = header_->e_shnum;
source = std::move(BufferSource(image_).Skip(header_->e_shoff));
sections_ = source.GetArray<typename Traits::Elf_Shdr>(sections_count_);
if (!sections_)
return false;
offset_t section_table_end =
base::checked_cast<offset_t>(source.begin() - image_.begin());
segments_count_ = header_->e_phnum;
source = std::move(BufferSource(image_).Skip(header_->e_phoff));
segments_ = source.GetArray<typename Traits::Elf_Phdr>(segments_count_);
if (!segments_)
return false;
offset_t segment_table_end =
base::checked_cast<offset_t>(source.begin() - image_.begin());
// Check string section -- even though we've stopped using them.
elf::Elf32_Half string_section_id = header_->e_shstrndx;
if (string_section_id >= sections_count_)
return false;
size_t section_names_size = sections_[string_section_id].sh_size;
if (section_names_size > 0) {
// If nonempty, then last byte of string section must be null.
const char* section_names = nullptr;
source = std::move(
BufferSource(image_).Skip(sections_[string_section_id].sh_offset));
section_names = source.GetArray<char>(section_names_size);
if (!section_names || section_names[section_names_size - 1] != '\0')
return false;
}
// Establish bound on encountered offsets.
offset_t offset_bound = std::max(section_table_end, segment_table_end);
// Visit each section, validate, and add address translation data to |units|.
std::vector<AddressTranslator::Unit> units;
units.reserve(sections_count_);
for (int i = 0; i < sections_count_; ++i) {
const typename Traits::Elf_Shdr* section = &sections_[i];
// Skip empty sections. These don't affect |offset_bound|, and don't
// contribute to RVA-offset mapping.
if (section->sh_size == 0)
continue;
// Be lax with RVAs: Assume they fit in int32_t, even for 64-bit. If
// assumption fails, simply skip the section with warning.
if (!RangeIsBounded(section->sh_addr, section->sh_size, kRvaBound) ||
!RangeIsBounded(section->sh_offset, section->sh_size, kOffsetBound)) {
LOG(WARNING) << "Section " << i << " does not fit in int32_t.";
continue;
}
// Extract dimensions to 32-bit integers to facilitate conversion. Range of
// values was ensured above when checking that the section is bounded.
uint32_t sh_size = base::checked_cast<uint32_t>(section->sh_size);
offset_t sh_offset = base::checked_cast<offset_t>(section->sh_offset);
rva_t sh_addr = base::checked_cast<rva_t>(section->sh_addr);
// Update |offset_bound|.
if (section->sh_type != elf::SHT_NOBITS) {
// Be strict with offsets: Any size overflow invalidates the file.
if (!image_.covers({sh_offset, sh_size}))
return false;
offset_t section_end = sh_offset + sh_size;
offset_bound = std::max(offset_bound, section_end);
}
// Compute mappings to translate between RVA and offset. As a heuristic,
// sections with RVA == 0 (i.e., |sh_addr == 0|) are ignored because these
// tend to be duplicates (which cause problems during lookup), and tend to
// be uninteresting.
if (section->sh_addr > 0) {
// Add |section| data for offset-RVA translation.
units.push_back({sh_offset, sh_size, sh_addr, sh_size});
}
}
// Initialize |translator_| for offset-RVA translations. Any inconsistency
// (e.g., 2 offsets correspond to the same RVA) would invalidate the ELF file.
if (translator_.Initialize(std::move(units)) != AddressTranslator::kSuccess)
return false;
// Visits |segments_| to get better estimate on |offset_bound|.
for (const typename Traits::Elf_Phdr* segment = segments_;
segment != segments_ + segments_count_; ++segment) {
if (!RangeIsBounded(segment->p_offset, segment->p_filesz, kOffsetBound))
return false;
offset_t segment_end =
base::checked_cast<offset_t>(segment->p_offset + segment->p_filesz);
offset_bound = std::max(offset_bound, segment_end);
}
if (offset_bound > image_.size())
return false;
image_.shrink(offset_bound);
return true;
}
template <class Traits>
void DisassemblerElf<Traits>::ExtractInterestingSectionHeaders() {
DCHECK(reloc_section_dims_.empty());
DCHECK(exec_headers_.empty());
for (elf::Elf32_Half i = 0; i < sections_count_; ++i) {
const typename Traits::Elf_Shdr* section = sections_ + i;
if (IsRelocSection<Traits>(*section))
reloc_section_dims_.emplace_back(*section);
else if (IsExecSection<Traits>(*section))
exec_headers_.push_back(section);
}
auto comp = [](const typename Traits::Elf_Shdr* a,
const typename Traits::Elf_Shdr* b) {
return a->sh_offset < b->sh_offset;
};
std::sort(reloc_section_dims_.begin(), reloc_section_dims_.end());
std::sort(exec_headers_.begin(), exec_headers_.end(), comp);
}
template <class Traits>
void DisassemblerElf<Traits>::GetAbs32FromRelocSections() {
constexpr int kAbs32Width = Traits::kVAWidth;
DCHECK(abs32_locations_.empty());
// Read reloc targets to get preliminary abs32 locations.
std::unique_ptr<ReferenceReader> relocs = MakeReadRelocs(0, offset_t(size()));
for (auto ref = relocs->GetNext(); ref.has_value(); ref = relocs->GetNext())
abs32_locations_.push_back(ref->target);
std::sort(abs32_locations_.begin(), abs32_locations_.end());
// Abs32 references must have targets translatable to offsets. Remove those
// that are unable to do so.
size_t num_untranslatable =
RemoveUntranslatableAbs32(image_, {Traits::kBitness, kElfImageBase},
translator_, &abs32_locations_);
LOG_IF(WARNING, num_untranslatable) << "Removed " << num_untranslatable
<< " untranslatable abs32 references.";
// Abs32 reference bodies must not overlap. If found, simply remove them.
size_t num_overlapping =
RemoveOverlappingAbs32Locations(kAbs32Width, &abs32_locations_);
LOG_IF(WARNING, num_overlapping)
<< "Removed " << num_overlapping
<< " abs32 references with overlapping bodies.";
abs32_locations_.shrink_to_fit();
}
template <class Traits>
void DisassemblerElf<Traits>::GetRel32FromCodeSections() {
for (const typename Traits::Elf_Shdr* section : exec_headers_)
ParseExecSection(*section);
PostProcessRel32();
}
template <class Traits>
void DisassemblerElf<Traits>::ParseSections() {
ExtractInterestingSectionHeaders();
GetAbs32FromRelocSections();
GetRel32FromCodeSections();
}
/******** DisassemblerElfIntel ********/
template <class Traits>
DisassemblerElfIntel<Traits>::DisassemblerElfIntel() = default;
template <class Traits>
DisassemblerElfIntel<Traits>::~DisassemblerElfIntel() = default;
template <class Traits>
std::vector<ReferenceGroup> DisassemblerElfIntel<Traits>::MakeReferenceGroups()
const {
return {
{ReferenceTypeTraits{sizeof(Traits::Elf_Rel::r_offset), TypeTag(kReloc),
PoolTag(kReloc)},
&DisassemblerElfIntel<Traits>::MakeReadRelocs,
&DisassemblerElfIntel<Traits>::MakeWriteRelocs},
{ReferenceTypeTraits{Traits::kVAWidth, TypeTag(kAbs32), PoolTag(kAbs32)},
&DisassemblerElfIntel<Traits>::MakeReadAbs32,
&DisassemblerElfIntel<Traits>::MakeWriteAbs32},
// N.B.: Rel32 |width| is 4 bytes, even for x64.
{ReferenceTypeTraits{4, TypeTag(kRel32), PoolTag(kRel32)},
&DisassemblerElfIntel<Traits>::MakeReadRel32,
&DisassemblerElfIntel<Traits>::MakeWriteRel32}};
}
template <class Traits>
void DisassemblerElfIntel<Traits>::ParseExecSection(
const typename Traits::Elf_Shdr& section) {
ConstBufferView& image_ = this->image_;
auto& abs32_locations_ = this->abs32_locations_;
std::ptrdiff_t from_offset_to_rva = section.sh_addr - section.sh_offset;
// Range of values was ensured in ParseHeader().
rva_t start_rva = base::checked_cast<rva_t>(section.sh_addr);
rva_t end_rva = base::checked_cast<rva_t>(start_rva + section.sh_size);
AddressTranslator::RvaToOffsetCache target_rva_checker(this->translator_);
ConstBufferView region(image_.begin() + section.sh_offset, section.sh_size);
Abs32GapFinder gap_finder(image_, region, abs32_locations_, 4);
std::unique_ptr<Rel32FinderIntel> finder =
std::make_unique<typename Traits::Rel32FinderUse>(image_);
for (auto gap = gap_finder.GetNext(); gap.has_value();
gap = gap_finder.GetNext()) {
finder->Reset(gap.value());
for (auto rel32 = finder->GetNext(); rel32.has_value();
rel32 = finder->GetNext()) {
offset_t rel32_offset =
base::checked_cast<offset_t>(rel32->location - image_.begin());
rva_t rel32_rva = rva_t(rel32_offset + from_offset_to_rva);
rva_t target_rva = rel32_rva + 4 + image_.read<uint32_t>(rel32_offset);
if (target_rva_checker.IsValid(target_rva) &&
(rel32->can_point_outside_section ||
(start_rva <= target_rva && target_rva < end_rva))) {
finder->Accept();
rel32_locations_.push_back(rel32_offset);
}
}
}
}
template <class Traits>
void DisassemblerElfIntel<Traits>::PostProcessRel32() {
rel32_locations_.shrink_to_fit();
std::sort(rel32_locations_.begin(), rel32_locations_.end());
}
template <class Traits>
std::unique_ptr<ReferenceReader> DisassemblerElfIntel<Traits>::MakeReadAbs32(
offset_t lo,
offset_t hi) {
// TODO(huangs): Don't use Abs32RvaExtractorWin32 here; use new class that
// caters to different ELF architectures.
Abs32RvaExtractorWin32 abs_rva_extractor(
this->image_, AbsoluteAddress(Traits::kBitness, kElfImageBase),
this->abs32_locations_, lo, hi);
return std::make_unique<Abs32ReaderWin32>(std::move(abs_rva_extractor),
this->translator_);
}
template <class Traits>
std::unique_ptr<ReferenceWriter> DisassemblerElfIntel<Traits>::MakeWriteAbs32(
MutableBufferView image) {
return std::make_unique<Abs32WriterWin32>(
image, AbsoluteAddress(Traits::kBitness, kElfImageBase),
this->translator_);
}
template <class Traits>
std::unique_ptr<ReferenceReader> DisassemblerElfIntel<Traits>::MakeReadRel32(
offset_t lo,
offset_t hi) {
return std::make_unique<Rel32ReaderX86>(this->image_, lo, hi,
&rel32_locations_, this->translator_);
}
template <class Traits>
std::unique_ptr<ReferenceWriter> DisassemblerElfIntel<Traits>::MakeWriteRel32(
MutableBufferView image) {
return std::make_unique<Rel32WriterX86>(image, this->translator_);
}
// Explicit instantiation for supported classes.
template class DisassemblerElfIntel<Elf32IntelTraits>;
template class DisassemblerElfIntel<Elf64IntelTraits>;
template bool DisassemblerElf<Elf32IntelTraits>::QuickDetect(
ConstBufferView image);
template bool DisassemblerElf<Elf64IntelTraits>::QuickDetect(
ConstBufferView image);
} // namespace zucchini