| // 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 <utility> |
| |
| #include "base/logging.h" |
| #include "base/numerics/checked_math.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "components/zucchini/abs32_utils.h" |
| #include "components/zucchini/algorithm.h" |
| #include "components/zucchini/arm_utils.h" |
| #include "components/zucchini/buffer_source.h" |
| |
| namespace zucchini { |
| |
| namespace { |
| |
| constexpr uint64_t kElfImageBase = 0; |
| constexpr size_t kSizeBound = 0x7FFF0000; |
| |
| // Threshold value for heuristics to detect THUMB2 code. |
| constexpr double kAArch32BitCondAlwaysDensityThreshold = 0.4; |
| |
| // Bit fields for JudgeSection() return value. |
| enum SectionJudgement : int { |
| // Bit: Section does not invalidate ELF, but may or may not be useful. |
| SECTION_BIT_SAFE = 1 << 0, |
| // Bit: Section useful for AddressTranslator, to map between offsets and RVAs. |
| SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR = 1 << 1, |
| // Bit: Section useful for |offset_bound|, to estimate ELF size. |
| SECTION_BIT_USEFUL_FOR_OFFSET_BOUND = 1 << 2, |
| // Bit: Section potentially useful for pointer extraction. |
| SECTION_BIT_MAYBE_USEFUL_FOR_POINTERS = 1 << 3, |
| |
| // The following are verdicts from combining bits, to improve semantics. |
| // Default value: A section is malformed and invalidates ELF. |
| SECTION_IS_MALFORMED = 0, |
| // Section does not invalidate ELF, but is also not used for anything. |
| SECTION_IS_USELESS = SECTION_BIT_SAFE, |
| }; |
| |
| // Decides how a section affects ELF parsing, and returns a bit field composed |
| // from SectionJudgement values. |
| template <class TRAITS> |
| int JudgeSection(size_t image_size, const typename TRAITS::Elf_Shdr* section) { |
| // BufferRegion uses |size_t| this can be 32-bit in some cases. For Elf64 |
| // |sh_addr|, |sh_offset| and |sh_size| are 64-bit this can result in |
| // overflows in the subsequent validation steps. |
| if (!base::IsValueInRangeForNumericType<size_t>(section->sh_addr) || |
| !base::IsValueInRangeForNumericType<size_t>(section->sh_offset) || |
| !base::IsValueInRangeForNumericType<size_t>(section->sh_size)) { |
| return SECTION_IS_MALFORMED; |
| } |
| |
| // Examine RVA range: Reject if numerical overflow may happen. |
| if (!BufferRegion{static_cast<size_t>(section->sh_addr), |
| static_cast<size_t>(section->sh_size)} |
| .FitsIn(kSizeBound)) |
| return SECTION_IS_MALFORMED; |
| |
| // Examine offset range: If section takes up |image| data then be stricter. |
| size_t offset_bound = |
| (section->sh_type == elf::SHT_NOBITS) ? kSizeBound : image_size; |
| if (!BufferRegion{static_cast<size_t>(section->sh_offset), |
| static_cast<size_t>(section->sh_size)} |
| .FitsIn(offset_bound)) |
| return SECTION_IS_MALFORMED; |
| |
| // Empty sections don't contribute to offset-RVA mapping. For consistency, it |
| // should also not affect |offset_bounds|. |
| if (section->sh_size == 0) |
| return SECTION_IS_USELESS; |
| |
| // Sections with |sh_addr == 0| are ignored because these tend to duplicates |
| // (can cause problems for lookup) and uninteresting. For consistency, it |
| // should also not affect |offset_bounds|. |
| if (section->sh_addr == 0) |
| return SECTION_IS_USELESS; |
| |
| if (section->sh_type == elf::SHT_NOBITS) { |
| // Special case for .tbss sections: These should be ignored because they may |
| // have offset-RVA map that don't match other sections. |
| if (section->sh_flags & elf::SHF_TLS) |
| return SECTION_IS_USELESS; |
| |
| // Section is useful for offset-RVA translation, but does not affect |
| // |offset_bounds| since it can have large virtual size (e.g., .bss). |
| return SECTION_BIT_SAFE | SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR; |
| } |
| |
| return SECTION_BIT_SAFE | SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR | |
| SECTION_BIT_USEFUL_FOR_OFFSET_BOUND | |
| SECTION_BIT_MAYBE_USEFUL_FOR_POINTERS; |
| } |
| |
| // Determines whether |section| is a reloc section. |
| template <class TRAITS> |
| bool IsRelocSection(const typename TRAITS::Elf_Shdr& section) { |
| DCHECK_GT(section.sh_size, 0U); |
| 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) { |
| DCHECK_GT(section.sh_size, 0U); |
| return section.sh_type == elf::SHT_PROGBITS && |
| (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; |
| |
| /******** ElfAArch32Traits ********/ |
| |
| // static |
| constexpr ExecutableType ElfAArch32Traits::kExeType; |
| const char ElfAArch32Traits::kExeTypeString[] = "ELF ARM"; |
| constexpr elf::MachineArchitecture ElfAArch32Traits::kMachineValue; |
| constexpr uint32_t ElfAArch32Traits::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; |
| |
| /******** ElfAArch64Traits ********/ |
| |
| // static |
| constexpr ExecutableType ElfAArch64Traits::kExeType; |
| const char ElfAArch64Traits::kExeTypeString[] = "ELF ARM64"; |
| constexpr elf::MachineArchitecture ElfAArch64Traits::kMachineValue; |
| constexpr uint32_t ElfAArch64Traits::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_); |
| // Ensure any offsets will fit within the |image_|'s bounds. |
| if (!base::IsValueInRangeForNumericType<offset_t>(image_.size())) |
| return false; |
| |
| // 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); |
| |
| // Visits |segments_| to get estimate on |offset_bound|. |
| for (const typename Traits::Elf_Phdr* segment = segments_; |
| segment != segments_ + segments_count_; ++segment) { |
| // |image_.covers()| is a sufficient check except when size_t is 32 bit and |
| // parsing ELF64. In such cases a value-in-range check is needed on the |
| // segment. This fixes crbug/1035603. |
| offset_t segment_end; |
| base::CheckedNumeric<offset_t> checked_segment_end = segment->p_offset; |
| checked_segment_end += segment->p_filesz; |
| if (!checked_segment_end.AssignIfValid(&segment_end) || |
| !image_.covers({static_cast<size_t>(segment->p_offset), |
| static_cast<size_t>(segment->p_filesz)})) { |
| return false; |
| } |
| offset_bound = std::max(offset_bound, segment_end); |
| } |
| |
| // Visit and validate each section; add address translation data to |units|. |
| std::vector<AddressTranslator::Unit> units; |
| units.reserve(sections_count_); |
| section_judgements_.reserve(sections_count_); |
| |
| for (int i = 0; i < sections_count_; ++i) { |
| const typename Traits::Elf_Shdr* section = §ions_[i]; |
| int judgement = JudgeSection<Traits>(image_.size(), section); |
| section_judgements_.push_back(judgement); |
| if ((judgement & SECTION_BIT_SAFE) == 0) |
| return false; |
| |
| 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); |
| if ((judgement & SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR) != 0) { |
| // Store mappings between RVA and offset. |
| units.push_back({sh_offset, sh_size, sh_addr, sh_size}); |
| } |
| if ((judgement & SECTION_BIT_USEFUL_FOR_OFFSET_BOUND) != 0) { |
| offset_t section_end = base::checked_cast<offset_t>(sh_offset + sh_size); |
| offset_bound = std::max(offset_bound, section_end); |
| } |
| } |
| |
| // 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; |
| |
| DCHECK_LE(offset_bound, image_.size()); |
| 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 ((section_judgements_[i] & SECTION_BIT_MAYBE_USEFUL_FOR_POINTERS) != 0) { |
| 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) { |
| constexpr int kAbs32Width = Traits::kVAWidth; |
| |
| // |this->| is needed to access protected members of templated base class. To |
| // reduce noise, use local references for these. |
| ConstBufferView& image_ = this->image_; |
| const AddressTranslator& translator_ = this->translator_; |
| auto& abs32_locations_ = this->abs32_locations_; |
| |
| // 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(translator_); |
| |
| ConstBufferView region(image_.begin() + section.sh_offset, section.sh_size); |
| Abs32GapFinder gap_finder(image_, region, abs32_locations_, kAbs32Width); |
| typename TRAITS::Rel32FinderUse rel_finder(image_, translator_); |
| // Iterate over gaps between abs32 references, to avoid collision. |
| while (gap_finder.FindNext()) { |
| rel_finder.SetRegion(gap_finder.GetGap()); |
| while (rel_finder.FindNext()) { |
| auto rel32 = rel_finder.GetRel32(); |
| if (target_rva_checker.IsValid(rel32.target_rva) && |
| (rel32.can_point_outside_section || |
| (start_rva <= rel32.target_rva && rel32.target_rva < end_rva))) { |
| rel_finder.Accept(); |
| rel32_locations_.push_back(rel32.location); |
| } |
| } |
| } |
| } |
| |
| 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); |
| |
| /******** DisassemblerElfArm ********/ |
| |
| template <class Traits> |
| DisassemblerElfArm<Traits>::DisassemblerElfArm() = default; |
| |
| template <class Traits> |
| DisassemblerElfArm<Traits>::~DisassemblerElfArm() = default; |
| |
| template <class Traits> |
| bool DisassemblerElfArm<Traits>::IsTargetOffsetInExecSection( |
| offset_t offset) const { |
| // Executable sections can appear in large numbers in .o files and in |
| // pathological cases. Since this function may be called for each reference |
| // candidate, linear search may be too slow (so use binary search). |
| return IsTargetOffsetInElfSectionList(this->exec_headers_, offset); |
| } |
| |
| template <class Traits> |
| void DisassemblerElfArm<Traits>::ParseExecSection( |
| const typename Traits::Elf_Shdr& section) { |
| ConstBufferView& image_ = this->image_; |
| const AddressTranslator& translator_ = this->translator_; |
| auto& abs32_locations_ = this->abs32_locations_; |
| |
| ConstBufferView region(image_.begin() + section.sh_offset, section.sh_size); |
| Abs32GapFinder gap_finder(image_, region, abs32_locations_, Traits::kVAWidth); |
| std::unique_ptr<typename Traits::Rel32FinderUse> rel_finder = |
| MakeRel32Finder(section); |
| AddressTranslator::RvaToOffsetCache rva_to_offset(translator_); |
| while (gap_finder.FindNext()) { |
| rel_finder->SetRegion(gap_finder.GetGap()); |
| while (rel_finder->FindNext()) { |
| auto rel32 = rel_finder->GetRel32(); |
| offset_t target_offset = rva_to_offset.Convert(rel32.target_rva); |
| if (target_offset != kInvalidOffset) { |
| // For robustness, reject illegal offsets, which can arise from, e.g., |
| // misidentify ARM vs. THUMB2 mode, or even misidentifying data as code! |
| if (IsTargetOffsetInExecSection(target_offset)) { |
| rel_finder->Accept(); |
| rel32_locations_table_[rel32.type].push_back(rel32.location); |
| } |
| } |
| } |
| } |
| } |
| |
| template <class Traits> |
| void DisassemblerElfArm<Traits>::PostProcessRel32() { |
| for (int type = 0; type < AArch32Rel32Translator::NUM_ADDR_TYPE; ++type) { |
| std::sort(rel32_locations_table_[type].begin(), |
| rel32_locations_table_[type].end()); |
| rel32_locations_table_[type].shrink_to_fit(); |
| } |
| } |
| |
| template <class Traits> |
| std::unique_ptr<ReferenceReader> DisassemblerElfArm<Traits>::MakeReadAbs32( |
| offset_t lo, |
| offset_t hi) { |
| // TODO(huangs): Reconcile the use of Win32-specific classes in ARM code! |
| Abs32RvaExtractorWin32 abs_rva_extractor(this->image_, |
| AbsoluteAddress(Traits::kBitness, 0), |
| this->abs32_locations_, lo, hi); |
| return std::make_unique<Abs32ReaderWin32>(std::move(abs_rva_extractor), |
| this->translator_); |
| } |
| |
| template <class Traits> |
| std::unique_ptr<ReferenceWriter> DisassemblerElfArm<Traits>::MakeWriteAbs32( |
| MutableBufferView image) { |
| return std::make_unique<Abs32WriterWin32>( |
| image, AbsoluteAddress(Traits::kBitness, 0), this->translator_); |
| } |
| |
| template <class TRAITS> |
| template <class ADDR_TRAITS> |
| std::unique_ptr<ReferenceReader> DisassemblerElfArm<TRAITS>::MakeReadRel32( |
| offset_t lower, |
| offset_t upper) { |
| return std::make_unique<Rel32ReaderArm<ADDR_TRAITS>>( |
| this->translator_, this->image_, |
| this->rel32_locations_table_[ADDR_TRAITS::addr_type], lower, upper); |
| } |
| |
| template <class TRAITS> |
| template <class ADDR_TRAITS> |
| std::unique_ptr<ReferenceWriter> DisassemblerElfArm<TRAITS>::MakeWriteRel32( |
| MutableBufferView image) { |
| return std::make_unique<Rel32WriterArm<ADDR_TRAITS>>(this->translator_, |
| image); |
| } |
| |
| /******** DisassemblerElfAArch32 ********/ |
| |
| DisassemblerElfAArch32::DisassemblerElfAArch32() = default; |
| DisassemblerElfAArch32::~DisassemblerElfAArch32() = default; |
| |
| std::vector<ReferenceGroup> DisassemblerElfAArch32::MakeReferenceGroups() |
| const { |
| return { |
| {ReferenceTypeTraits{sizeof(Traits::Elf_Rel::r_offset), |
| TypeTag(AArch32ReferenceType::kReloc), |
| PoolTag(ArmReferencePool::kPoolReloc)}, |
| &DisassemblerElfAArch32::MakeReadRelocs, |
| &DisassemblerElfAArch32::MakeWriteRelocs}, |
| {ReferenceTypeTraits{Traits::kVAWidth, |
| TypeTag(AArch32ReferenceType::kAbs32), |
| PoolTag(ArmReferencePool::kPoolAbs32)}, |
| &DisassemblerElfAArch32::MakeReadAbs32, |
| &DisassemblerElfAArch32::MakeWriteAbs32}, |
| {ReferenceTypeTraits{4, TypeTag(AArch32ReferenceType::kRel32_A24), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch32::MakeReadRel32< |
| AArch32Rel32Translator::AddrTraits_A24>, |
| &DisassemblerElfAArch32::MakeWriteRel32< |
| AArch32Rel32Translator::AddrTraits_A24>}, |
| {ReferenceTypeTraits{2, TypeTag(AArch32ReferenceType::kRel32_T8), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch32::MakeReadRel32< |
| AArch32Rel32Translator::AddrTraits_T8>, |
| &DisassemblerElfAArch32::MakeWriteRel32< |
| AArch32Rel32Translator::AddrTraits_T8>}, |
| {ReferenceTypeTraits{2, TypeTag(AArch32ReferenceType::kRel32_T11), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch32::MakeReadRel32< |
| AArch32Rel32Translator::AddrTraits_T11>, |
| &DisassemblerElfAArch32::MakeWriteRel32< |
| AArch32Rel32Translator::AddrTraits_T11>}, |
| {ReferenceTypeTraits{4, TypeTag(AArch32ReferenceType::kRel32_T20), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch32::MakeReadRel32< |
| AArch32Rel32Translator::AddrTraits_T20>, |
| &DisassemblerElfAArch32::MakeWriteRel32< |
| AArch32Rel32Translator::AddrTraits_T20>}, |
| {ReferenceTypeTraits{4, TypeTag(AArch32ReferenceType::kRel32_T24), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch32::MakeReadRel32< |
| AArch32Rel32Translator::AddrTraits_T24>, |
| &DisassemblerElfAArch32::MakeWriteRel32< |
| AArch32Rel32Translator::AddrTraits_T24>}, |
| }; |
| } |
| |
| std::unique_ptr<DisassemblerElfAArch32::Traits::Rel32FinderUse> |
| DisassemblerElfAArch32::MakeRel32Finder( |
| const typename Traits::Elf_Shdr& section) { |
| return std::make_unique<Rel32FinderAArch32>(image_, translator_, |
| IsExecSectionThumb2(section)); |
| } |
| |
| bool DisassemblerElfAArch32::IsExecSectionThumb2( |
| const typename Traits::Elf_Shdr& section) const { |
| // ARM mode requires 4-byte alignment. |
| if (section.sh_addr % 4 != 0 || section.sh_size % 4 != 0) |
| return true; |
| const uint8_t* first = image_.begin() + section.sh_offset; |
| const uint8_t* end = first + section.sh_size; |
| // Each instruction in 32-bit ARM (little-endian) looks like |
| // ?? ?? ?? X?, |
| // where X specifies conditional execution. X = 0xE represents AL = "ALways |
| // execute", and tends to appear very often. We use this as our main indicator |
| // to discern 32-bit ARM mode from THUMB2 mode. |
| size_t num = 0; |
| size_t den = 0; |
| for (const uint8_t* cur = first; cur < end; cur += 4) { |
| // |cur[3]| is within bounds because |end - cur| is a multiple of 4. |
| uint8_t maybe_cond = cur[3] & 0xF0; |
| if (maybe_cond == 0xE0) |
| ++num; |
| ++den; |
| } |
| |
| if (den > 0) { |
| LOG(INFO) << "Section scan: " << num << " / " << den << " => " |
| << base::StringPrintf("%.2f", num * 100.0 / den) << "%"; |
| } |
| return num < den * kAArch32BitCondAlwaysDensityThreshold; |
| } |
| |
| /******** DisassemblerElfAArch64 ********/ |
| |
| DisassemblerElfAArch64::DisassemblerElfAArch64() = default; |
| |
| DisassemblerElfAArch64::~DisassemblerElfAArch64() = default; |
| |
| std::vector<ReferenceGroup> DisassemblerElfAArch64::MakeReferenceGroups() |
| const { |
| return { |
| {ReferenceTypeTraits{sizeof(Traits::Elf_Rel::r_offset), |
| TypeTag(AArch64ReferenceType::kReloc), |
| PoolTag(ArmReferencePool::kPoolReloc)}, |
| &DisassemblerElfAArch64::MakeReadRelocs, |
| &DisassemblerElfAArch64::MakeWriteRelocs}, |
| {ReferenceTypeTraits{Traits::kVAWidth, |
| TypeTag(AArch64ReferenceType::kAbs32), |
| PoolTag(ArmReferencePool::kPoolAbs32)}, |
| &DisassemblerElfAArch64::MakeReadAbs32, |
| &DisassemblerElfAArch64::MakeWriteAbs32}, |
| {ReferenceTypeTraits{4, TypeTag(AArch64ReferenceType::kRel32_Immd14), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch64::MakeReadRel32< |
| AArch64Rel32Translator::AddrTraits_Immd14>, |
| &DisassemblerElfAArch64::MakeWriteRel32< |
| AArch64Rel32Translator::AddrTraits_Immd14>}, |
| {ReferenceTypeTraits{4, TypeTag(AArch64ReferenceType::kRel32_Immd19), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch64::MakeReadRel32< |
| AArch64Rel32Translator::AddrTraits_Immd19>, |
| &DisassemblerElfAArch64::MakeWriteRel32< |
| AArch64Rel32Translator::AddrTraits_Immd19>}, |
| {ReferenceTypeTraits{4, TypeTag(AArch64ReferenceType::kRel32_Immd26), |
| PoolTag(ArmReferencePool::kPoolRel32)}, |
| &DisassemblerElfAArch64::MakeReadRel32< |
| AArch64Rel32Translator::AddrTraits_Immd26>, |
| &DisassemblerElfAArch64::MakeWriteRel32< |
| AArch64Rel32Translator::AddrTraits_Immd26>}, |
| }; |
| } |
| |
| std::unique_ptr<DisassemblerElfAArch64::Traits::Rel32FinderUse> |
| DisassemblerElfAArch64::MakeRel32Finder( |
| const typename Traits::Elf_Shdr& section) { |
| return std::make_unique<Rel32FinderAArch64>(image_, translator_); |
| } |
| |
| // Explicit instantiation for supported classes. |
| template class DisassemblerElfArm<ElfAArch32Traits>; |
| template class DisassemblerElfArm<ElfAArch64Traits>; |
| template bool DisassemblerElf<ElfAArch32Traits>::QuickDetect( |
| ConstBufferView image); |
| template bool DisassemblerElf<ElfAArch64Traits>::QuickDetect( |
| ConstBufferView image); |
| |
| } // namespace zucchini |