diff options
author | Samuel Huang <huangs@chromium.org> | 2021-08-05 16:46:38 +0000 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2021-08-05 10:05:02 -0700 |
commit | fa10b05c4854c6d8a603ee47c2a213cbc23f8646 (patch) | |
tree | 94ad9d794dedc26bd0e0be4b18511d45026a0b98 /disassembler_elf.cc | |
parent | 3e1f64d1395c53a730475d930b663d5f6006099e (diff) | |
download | zucchini-fa10b05c4854c6d8a603ee47c2a213cbc23f8646.tar.gz |
[Zucchini] Add ARM support for ELF files.
This CL enables ARM-ELF (AArch32 and AArch64) support in Zucchini.
* Define ARM {AArch32, AArch64}ReferenceType.
* Add Rel32Finder{Arm, AArch32, AArch64} (with tests) to use
previously-added ARM disassembly code to extract rel32 references.
* Add DisassemblerElf{Arm, AArch32, AArch64} to parse ARM ELF files and
create reference readers / writers, and reference groups.
* For AArch32: Add heuristic detection of ARM vs. Thumb2 mode.
* Add IsTargetOffsetInElfSectionList() (with tests) to help ARM reject
false positive references.
* Add ReferenceBytesMixerElfArm to remove redundant reference target
information from bytewise correction data.
Bug: 918867
Change-Id: I1e6d3d8b8d174c85a3d44ca6d642b7ff0bd6a6a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2922822
Commit-Queue: Samuel Huang <huangs@chromium.org>
Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/master@{#908913}
NOKEYCHECK=True
GitOrigin-RevId: 85cc8a596f183487b395a59e80b2f654f241ab2c
Diffstat (limited to 'disassembler_elf.cc')
-rw-r--r-- | disassembler_elf.cc | 351 |
1 files changed, 350 insertions, 1 deletions
diff --git a/disassembler_elf.cc b/disassembler_elf.cc index 9c9ebb8..94dc12a 100644 --- a/disassembler_elf.cc +++ b/disassembler_elf.cc @@ -6,7 +6,6 @@ #include <stddef.h> -#include <algorithm> #include <utility> #include "base/logging.h" @@ -14,6 +13,7 @@ #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 { @@ -23,6 +23,9 @@ 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. @@ -132,6 +135,14 @@ 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 @@ -146,6 +157,14 @@ 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. @@ -503,4 +522,334 @@ template bool DisassemblerElf<Elf32IntelTraits>::QuickDetect( 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_); +} + +/******** 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::MakeReadRel32A24, + &DisassemblerElfAArch32::MakeWriteRel32A24}, + {ReferenceTypeTraits{2, TypeTag(AArch32ReferenceType::kRel32_T8), + PoolTag(ArmReferencePool::kPoolRel32)}, + &DisassemblerElfAArch32::MakeReadRel32T8, + &DisassemblerElfAArch32::MakeWriteRel32T8}, + {ReferenceTypeTraits{2, TypeTag(AArch32ReferenceType::kRel32_T11), + PoolTag(ArmReferencePool::kPoolRel32)}, + &DisassemblerElfAArch32::MakeReadRel32T11, + &DisassemblerElfAArch32::MakeWriteRel32T11}, + {ReferenceTypeTraits{4, TypeTag(AArch32ReferenceType::kRel32_T20), + PoolTag(ArmReferencePool::kPoolRel32)}, + &DisassemblerElfAArch32::MakeReadRel32T20, + &DisassemblerElfAArch32::MakeWriteRel32T20}, + {ReferenceTypeTraits{4, TypeTag(AArch32ReferenceType::kRel32_T24), + PoolTag(ArmReferencePool::kPoolRel32)}, + &DisassemblerElfAArch32::MakeReadRel32T24, + &DisassemblerElfAArch32::MakeWriteRel32T24}, + }; +} + +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; +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch32::MakeReadRel32A24( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch32Rel32Translator::AddrTraits_A24>>( + translator_, image_, + rel32_locations_table_[AArch32Rel32Translator::ADDR_A24], lower, upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch32::MakeWriteRel32A24( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch32Rel32Translator::AddrTraits_A24>>(translator_, + image); +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch32::MakeReadRel32T8( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch32Rel32Translator::AddrTraits_T8>>( + translator_, image_, + rel32_locations_table_[AArch32Rel32Translator::ADDR_T8], lower, upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch32::MakeWriteRel32T8( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch32Rel32Translator::AddrTraits_T8>>(translator_, + image); +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch32::MakeReadRel32T11( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch32Rel32Translator::AddrTraits_T11>>( + translator_, image_, + rel32_locations_table_[AArch32Rel32Translator::ADDR_T11], lower, upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch32::MakeWriteRel32T11( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch32Rel32Translator::AddrTraits_T11>>(translator_, + image); +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch32::MakeReadRel32T20( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch32Rel32Translator::AddrTraits_T20>>( + translator_, image_, + rel32_locations_table_[AArch32Rel32Translator::ADDR_T20], lower, upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch32::MakeWriteRel32T20( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch32Rel32Translator::AddrTraits_T20>>(translator_, + image); +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch32::MakeReadRel32T24( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch32Rel32Translator::AddrTraits_T24>>( + translator_, image_, + rel32_locations_table_[AArch32Rel32Translator::ADDR_T24], lower, upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch32::MakeWriteRel32T24( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch32Rel32Translator::AddrTraits_T24>>(translator_, + image); +} + +/******** 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::MakeReadRel32Immd14, + &DisassemblerElfAArch64::MakeWriteRel32Immd14}, + {ReferenceTypeTraits{4, TypeTag(AArch64ReferenceType::kRel32_Immd19), + PoolTag(ArmReferencePool::kPoolRel32)}, + &DisassemblerElfAArch64::MakeReadRel32Immd19, + &DisassemblerElfAArch64::MakeWriteRel32Immd19}, + {ReferenceTypeTraits{4, TypeTag(AArch64ReferenceType::kRel32_Immd26), + PoolTag(ArmReferencePool::kPoolRel32)}, + &DisassemblerElfAArch64::MakeReadRel32Immd26, + &DisassemblerElfAArch64::MakeWriteRel32Immd26}, + }; +} + +std::unique_ptr<DisassemblerElfAArch64::Traits::Rel32FinderUse> +DisassemblerElfAArch64::MakeRel32Finder( + const typename Traits::Elf_Shdr& section) { + return std::make_unique<Rel32FinderAArch64>(image_, translator_); +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch64::MakeReadRel32Immd14( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch64Rel32Translator::AddrTraits_Immd14>>( + translator_, this->image_, + rel32_locations_table_[AArch64Rel32Translator::ADDR_IMMD14], lower, + upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch64::MakeWriteRel32Immd14( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch64Rel32Translator::AddrTraits_Immd14>>(translator_, + image); +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch64::MakeReadRel32Immd19( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch64Rel32Translator::AddrTraits_Immd19>>( + translator_, this->image_, + rel32_locations_table_[AArch64Rel32Translator::ADDR_IMMD19], lower, + upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch64::MakeWriteRel32Immd19( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch64Rel32Translator::AddrTraits_Immd19>>(translator_, + image); +} + +std::unique_ptr<ReferenceReader> DisassemblerElfAArch64::MakeReadRel32Immd26( + offset_t lower, + offset_t upper) { + return std::make_unique< + Rel32ReaderArm<AArch64Rel32Translator::AddrTraits_Immd26>>( + translator_, this->image_, + rel32_locations_table_[AArch64Rel32Translator::ADDR_IMMD26], lower, + upper); +} + +std::unique_ptr<ReferenceWriter> DisassemblerElfAArch64::MakeWriteRel32Immd26( + MutableBufferView image) { + return std::make_unique< + Rel32WriterArm<AArch64Rel32Translator::AddrTraits_Immd26>>(translator_, + image); +} + +// 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 |