diff options
-rw-r--r-- | arm_utils.h | 4 | ||||
-rw-r--r-- | disassembler_elf.cc | 351 | ||||
-rw-r--r-- | disassembler_elf.h | 193 | ||||
-rw-r--r-- | disassembler_elf_unittest.cc | 85 | ||||
-rw-r--r-- | element_detection.cc | 16 | ||||
-rw-r--r-- | reference_bytes_mixer.cc | 104 | ||||
-rw-r--r-- | reference_bytes_mixer.h | 30 | ||||
-rw-r--r-- | rel32_finder.cc | 129 | ||||
-rw-r--r-- | rel32_finder.h | 81 | ||||
-rw-r--r-- | rel32_finder_unittest.cc | 333 | ||||
-rw-r--r-- | rel32_utils.h | 8 |
11 files changed, 1296 insertions, 38 deletions
diff --git a/arm_utils.h b/arm_utils.h index 63c7538..8664f3e 100644 --- a/arm_utils.h +++ b/arm_utils.h @@ -213,6 +213,8 @@ class AArch32Rel32Translator { // |instr_rva| as aid. static ArmAlign DecodeA24(uint32_t code32, arm_disp_t* disp); static bool EncodeA24(arm_disp_t disp, uint32_t* code32); + // TODO(huangs): Refactor the Read*() functions: These are identical + // except for Decode*() and Get*TargetRvaFromDisp(). static bool ReadA24(rva_t instr_rva, uint32_t code32, rva_t* target_rva); static bool WriteA24(rva_t instr_rva, rva_t target_rva, uint32_t* code32); @@ -362,6 +364,8 @@ class AArch64Rel32Translator { // to the counterparts in AArch32Rel32Translator. static ArmAlign DecodeImmd14(uint32_t code32, arm_disp_t* disp); static bool EncodeImmd14(arm_disp_t disp, uint32_t* code32); + // TODO(huangs): Refactor the Read*() functions: These are identical + // except for Decode*(). static bool ReadImmd14(rva_t instr_rva, uint32_t code32, rva_t* target_rva); static bool WriteImmd14(rva_t instr_rva, rva_t target_rva, uint32_t* code32); 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 diff --git a/disassembler_elf.h b/disassembler_elf.h index ffd1690..0bd11a6 100644 --- a/disassembler_elf.h +++ b/disassembler_elf.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include <algorithm> #include <deque> #include <memory> #include <string> @@ -23,6 +24,44 @@ 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 Bitness kBitness = kBit32; static constexpr elf::FileClass kIdentificationClass = elf::ELFCLASS32; @@ -44,6 +83,16 @@ struct Elf32IntelTraits : public Elf32Traits { 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 Bitness kBitness = kBit64; static constexpr elf::FileClass kIdentificationClass = elf::ELFCLASS64; @@ -64,6 +113,39 @@ struct Elf64IntelTraits : public Elf64Traits { 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 { @@ -82,7 +164,7 @@ class DisassemblerElf : public Disassembler { std::string GetExeTypeString() const override; std::vector<ReferenceGroup> MakeReferenceGroups() const override = 0; - // Find/Receive functions that are common among different architectures. + // 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); @@ -174,7 +256,7 @@ class DisassemblerElfIntel : public DisassemblerElf<TRAITS> { void ParseExecSection(const typename Traits::Elf_Shdr& section) override; void PostProcessRel32() override; - // Specialized Find/Receive functions. + // 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); @@ -189,6 +271,113 @@ class DisassemblerElfIntel : public DisassemblerElf<TRAITS> { 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); + + 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; + + // Specialized Read/Write functions for different rel32 address types. + std::unique_ptr<ReferenceReader> MakeReadRel32A24(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32A24(MutableBufferView image); + + std::unique_ptr<ReferenceReader> MakeReadRel32T8(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32T8(MutableBufferView image); + + std::unique_ptr<ReferenceReader> MakeReadRel32T11(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32T11(MutableBufferView image); + + std::unique_ptr<ReferenceReader> MakeReadRel32T20(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32T20(MutableBufferView image); + + std::unique_ptr<ReferenceReader> MakeReadRel32T24(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32T24(MutableBufferView image); +}; + +// 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; + + // Specialized Read/Write functions for different rel32 address types. + std::unique_ptr<ReferenceReader> MakeReadRel32Immd14(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32Immd14( + MutableBufferView image); + + std::unique_ptr<ReferenceReader> MakeReadRel32Immd19(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32Immd19( + MutableBufferView image); + + std::unique_ptr<ReferenceReader> MakeReadRel32Immd26(offset_t lower, + offset_t upper); + std::unique_ptr<ReferenceWriter> MakeWriteRel32Immd26( + MutableBufferView image); +}; + } // namespace zucchini #endif // COMPONENTS_ZUCCHINI_DISASSEMBLER_ELF_H_ diff --git a/disassembler_elf_unittest.cc b/disassembler_elf_unittest.cc index 2db8635..d98eb50 100644 --- a/disassembler_elf_unittest.cc +++ b/disassembler_elf_unittest.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <random> +#include <string> #include <vector> #include "components/zucchini/test_utils.h" @@ -17,6 +18,90 @@ namespace zucchini { +TEST(DisassemblerElfTest, IsTargetOffsetInElfSectionList) { + // Minimal required fields for IsTargetOffsetInElfSectionList(). + struct FakeElfShdr { + offset_t sh_offset; + offset_t sh_size; + }; + + // Calls IsTargetOffsetInElfSectionList() for fixed |sorted_list|, and sweeps + // offsets in [lo, hi). Renders results into a string consisting of '.' (not + // in list) and '*' (in list). + auto test = [&](const std::vector<FakeElfShdr>& sorted_list, offset_t lo, + offset_t hi) -> std::string { + // Ensure |sorted_list| is indeed sorted, without overlaps. + for (size_t i = 1; i < sorted_list.size(); ++i) { + if (sorted_list[i].sh_offset < + sorted_list[i - 1].sh_offset + sorted_list[i - 1].sh_size) { + return "(Bad input)"; + } + } + // The interface to IsTargetOffsetInElfSectionList() takes a list of + // pointers (since data can be casted from images), so make the conversion. + std::vector<const FakeElfShdr*> ptr_list; + for (const FakeElfShdr& header : sorted_list) + ptr_list.push_back(&header); + std::string result; + for (offset_t offset = lo; offset < hi; ++offset) { + result += IsTargetOffsetInElfSectionList(ptr_list, offset) ? '*' : '.'; + } + return result; + }; + + EXPECT_EQ("..........", test(std::vector<FakeElfShdr>(), 0, 10)); + EXPECT_EQ("*.........", test({{0, 1}}, 0, 10)); + EXPECT_EQ("...*......", test({{3, 1}}, 0, 10)); + EXPECT_EQ("...****...", test({{3, 4}}, 0, 10)); + EXPECT_EQ("...****...", test({{10003, 4}}, 10000, 10010)); + EXPECT_EQ("...********...", test({{3, 4}, {7, 4}}, 0, 14)); + EXPECT_EQ("...****.****...", test({{3, 4}, {8, 4}}, 0, 15)); + EXPECT_EQ("...****..****...", test({{3, 4}, {9, 4}}, 0, 16)); + EXPECT_EQ("..****...*****..", test({{2, 4}, {9, 5}}, 0, 16)); + EXPECT_EQ("...***......***..", test({{3, 3}, {12, 3}}, 0, 17)); + + // Many small ranges. + EXPECT_EQ("..**.**.*.*...*.*.**...**.*.**.*..", // (Comment strut). + test({{2, 2}, + {5, 2}, + {8, 1}, + {10, 1}, + {14, 1}, + {16, 1}, + {18, 2}, + {23, 2}, + {26, 1}, + {28, 2}, + {31, 1}}, + 0, 34)); + EXPECT_EQ("..*****.****.***.**.*..", + test({{137, 5}, {143, 4}, {148, 3}, {152, 2}, {155, 1}}, 135, 158)); + // Consecutive. + EXPECT_EQ("..***************..", + test({{137, 5}, {142, 4}, {146, 3}, {149, 2}, {151, 1}}, 135, 154)); + // Hover around 32 (power of 2). + EXPECT_EQ("..*******************************..", + test({{2002, 31}}, 2000, 2035)); + EXPECT_EQ("..********************************..", + test({{5002, 32}}, 5000, 5036)); + EXPECT_EQ("..*********************************..", + test({{8002, 33}}, 8000, 8037)); + // Consecutive + small gap. + EXPECT_EQ( + "..*****************.***********..", + test({{9876543, 8}, {9876551, 9}, {9876561, 11}}, 9876541, 9876574)); + // Sample internal of big range. + EXPECT_EQ("**************************************************", + test({{100, 1000000}}, 5000, 5050)); + // Sample boundaries of big range. + EXPECT_EQ(".........................*************************", + test({{100, 1000000}}, 75, 125)); + EXPECT_EQ("*************************.........................", + test({{100, 1000000}}, 1000075, 1000125)); + // 1E9 is still good. + EXPECT_EQ(".....*.....", test({{1000000000, 1}}, 999999995, 1000000006)); +} + TEST(DisassemblerElfTest, QuickDetect) { std::vector<uint8_t> image_data; ConstBufferView image; diff --git a/element_detection.cc b/element_detection.cc index 8682c78..356c0d7 100644 --- a/element_detection.cc +++ b/element_detection.cc @@ -65,6 +65,18 @@ std::unique_ptr<Disassembler> MakeDisassemblerWithoutFallback( if (disasm && disasm->size() >= kMinProgramSize) return disasm; } + + if (DisassemblerElfAArch32::QuickDetect(image)) { + auto disasm = Disassembler::Make<DisassemblerElfAArch32>(image); + if (disasm && disasm->size() >= kMinProgramSize) + return disasm; + } + + if (DisassemblerElfAArch64::QuickDetect(image)) { + auto disasm = Disassembler::Make<DisassemblerElfAArch64>(image); + if (disasm && disasm->size() >= kMinProgramSize) + return disasm; + } #endif // BUILDFLAG(ENABLE_ELF) #if BUILDFLAG(ENABLE_DEX) @@ -101,6 +113,10 @@ std::unique_ptr<Disassembler> MakeDisassemblerOfType(ConstBufferView image, return Disassembler::Make<DisassemblerElfX86>(image); case kExeTypeElfX64: return Disassembler::Make<DisassemblerElfX64>(image); + case kExeTypeElfAArch32: + return Disassembler::Make<DisassemblerElfAArch32>(image); + case kExeTypeElfAArch64: + return Disassembler::Make<DisassemblerElfAArch64>(image); #endif // BUILDFLAG(ENABLE_ELF) #if BUILDFLAG(ENABLE_DEX) case kExeTypeDex: diff --git a/reference_bytes_mixer.cc b/reference_bytes_mixer.cc index d6edd3a..6855853 100644 --- a/reference_bytes_mixer.cc +++ b/reference_bytes_mixer.cc @@ -4,9 +4,13 @@ #include "components/zucchini/reference_bytes_mixer.h" +#include <algorithm> + #include "base/check_op.h" +#include "base/logging.h" #include "base/notreached.h" #include "components/zucchini/disassembler.h" +#include "components/zucchini/disassembler_elf.h" namespace zucchini { @@ -26,7 +30,10 @@ std::unique_ptr<ReferenceBytesMixer> ReferenceBytesMixer::Create( const Disassembler& dst_dis) { ExecutableType exe_type = src_dis.GetExeType(); DCHECK_EQ(exe_type, dst_dis.GetExeType()); - // TODO(huangs): Add ARM handling code when ARM is ready. + if (exe_type == kExeTypeElfAArch32) + return std::make_unique<ReferenceBytesMixerElfArm>(exe_type); + if (exe_type == kExeTypeElfAArch64) + return std::make_unique<ReferenceBytesMixerElfArm>(exe_type); return std::make_unique<ReferenceBytesMixer>(); } @@ -45,4 +52,99 @@ ConstBufferView ReferenceBytesMixer::Mix(uint8_t type, return ConstBufferView(); } +/******** ReferenceBytesMixerElfArm ********/ + +ReferenceBytesMixerElfArm::ReferenceBytesMixerElfArm(ExecutableType exe_type) + : exe_type_(exe_type), out_buffer_(4) {} // 4 is a bound on NumBytes(). + +ReferenceBytesMixerElfArm::~ReferenceBytesMixerElfArm() = default; + +int ReferenceBytesMixerElfArm::NumBytes(uint8_t type) const { + if (exe_type_ == kExeTypeElfAArch32) { + switch (type) { + case AArch32ReferenceType::kRel32_A24: // Falls through. + case AArch32ReferenceType::kRel32_T20: + case AArch32ReferenceType::kRel32_T24: + return 4; + case AArch32ReferenceType::kRel32_T8: // Falls through. + case AArch32ReferenceType::kRel32_T11: + return 2; + } + } else if (exe_type_ == kExeTypeElfAArch64) { + switch (type) { + case AArch64ReferenceType::kRel32_Immd14: // Falls through. + case AArch64ReferenceType::kRel32_Immd19: + case AArch64ReferenceType::kRel32_Immd26: + return 4; + } + } + return 0; +} + +ConstBufferView ReferenceBytesMixerElfArm::Mix(uint8_t type, + ConstBufferView old_view, + offset_t old_offset, + ConstBufferView new_view, + offset_t new_offset) { + int num_bytes = NumBytes(type); + ConstBufferView::const_iterator new_it = new_view.begin() + new_offset; + DCHECK_LE(static_cast<size_t>(num_bytes), out_buffer_.size()); + MutableBufferView out_buffer_view(&out_buffer_[0], num_bytes); + std::copy(new_it, new_it + num_bytes, out_buffer_view.begin()); + + ArmCopyDispFun copier = GetCopier(type); + DCHECK_NE(copier, nullptr); + + if (!copier(old_view, old_offset, out_buffer_view, 0U)) { + // Failed to mix old payload bits with new operation bits. The main cause of + // of this rare failure is when BL (encoding T1) with payload bits + // representing disp % 4 == 2 transforms into BLX (encoding T2). Error + // arises because BLX requires payload bits to have disp == 0 (mod 4). + // Mixing failures are not fatal to patching; we simply fall back to direct + // copy and forgo benefits from mixing for these cases. + // TODO(huangs, etiennep): Ongoing discussion on whether we should just + // nullify all payload disp so we won't have to deal with this case, but at + // the cost of having Zucchini-apply do more work. + static int output_quota = 10; + if (output_quota > 0) { + LOG(WARNING) << "Reference byte mix failed with type = " + << static_cast<uint32_t>(type) << "." << std::endl; + --output_quota; + if (!output_quota) + LOG(WARNING) << "(Additional output suppressed)"; + } + // Fall back to direct copy. + std::copy(new_it, new_it + num_bytes, out_buffer_view.begin()); + } + return ConstBufferView(out_buffer_view); +} + +ArmCopyDispFun ReferenceBytesMixerElfArm::GetCopier(uint8_t type) const { + if (exe_type_ == kExeTypeElfAArch32) { + switch (type) { + case AArch32ReferenceType::kRel32_A24: + return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_A24>; + case AArch32ReferenceType::kRel32_T8: + return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T8>; + case AArch32ReferenceType::kRel32_T11: + return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T11>; + case AArch32ReferenceType::kRel32_T20: + return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T20>; + case AArch32ReferenceType::kRel32_T24: + return ArmCopyDisp<AArch32Rel32Translator::AddrTraits_T24>; + } + } else if (exe_type_ == kExeTypeElfAArch64) { + switch (type) { + case AArch64ReferenceType::kRel32_Immd14: + return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd14>; + case AArch64ReferenceType::kRel32_Immd19: + return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd19>; + case AArch64ReferenceType::kRel32_Immd26: + return ArmCopyDisp<AArch64Rel32Translator::AddrTraits_Immd26>; + } + } + DLOG(FATAL) << "NOTREACHED"; + return nullptr; +} + } // namespace zucchini diff --git a/reference_bytes_mixer.h b/reference_bytes_mixer.h index 3cbf187..f20b0ef 100644 --- a/reference_bytes_mixer.h +++ b/reference_bytes_mixer.h @@ -11,6 +11,7 @@ #include "components/zucchini/buffer_view.h" #include "components/zucchini/image_utils.h" +#include "components/zucchini/rel32_utils.h" namespace zucchini { @@ -83,6 +84,35 @@ class ReferenceBytesMixer { offset_t new_offset); }; +// In AArch32 and AArch64, instructions mix operation bits and payload bits in +// complex ways. This is the main use case of ReferenceBytesMixer. +class ReferenceBytesMixerElfArm : public ReferenceBytesMixer { + public: + // |exe_type| must be EXE_TYPE_ELF_ARM or EXE_TYPE_ELF_AARCH64. + explicit ReferenceBytesMixerElfArm(ExecutableType exe_type); + ReferenceBytesMixerElfArm(const ReferenceBytesMixerElfArm&) = delete; + const ReferenceBytesMixerElfArm& operator=(const ReferenceBytesMixerElfArm&) = + delete; + ~ReferenceBytesMixerElfArm() override; + + // ReferenceBytesMixer: + int NumBytes(uint8_t type) const override; + ConstBufferView Mix(uint8_t type, + ConstBufferView old_view, + offset_t old_offset, + ConstBufferView new_view, + offset_t new_offset) override; + + private: + ArmCopyDispFun GetCopier(uint8_t type) const; + + // For simplicity, 32-bit vs. 64-bit distinction is represented by state + // |exe_type_|, instead of creating derived classes. + const ExecutableType exe_type_; + + std::vector<uint8_t> out_buffer_; +}; + } // namespace zucchini #endif // COMPONENTS_ZUCCHINI_REFERENCE_BYTES_MIXER_H_ diff --git a/rel32_finder.cc b/rel32_finder.cc index ccb26f4..1ad8910 100644 --- a/rel32_finder.cc +++ b/rel32_finder.cc @@ -162,4 +162,133 @@ Rel32Finder::NextIterators Rel32FinderX64::Scan(ConstBufferView region) { return {nullptr, nullptr}; } +/******** Rel32FinderArm ********/ + +template <typename ADDR_TYPE> +Rel32FinderArm<ADDR_TYPE>::Rel32FinderArm(ConstBufferView image, + const AddressTranslator& translator) + : Rel32Finder(image, translator) {} + +template <typename ADDR_TYPE> +Rel32FinderArm<ADDR_TYPE>::~Rel32FinderArm() = default; + +template <typename ADDR_TYPE> +Rel32Finder::NextIterators Rel32FinderArm<ADDR_TYPE>::SetResult( + Result&& result, + ConstBufferView::const_iterator cursor, + int instr_size) { + rel32_ = result; + return {cursor + instr_size, cursor + instr_size}; +} + +// SetResult() for end of scan. +template <typename ADDR_TYPE> +Rel32Finder::NextIterators Rel32FinderArm<ADDR_TYPE>::SetEmptyResult() { + rel32_ = {kInvalidOffset, kInvalidOffset, ADDR_TYPE::ADDR_NONE}; + return {nullptr, nullptr}; +} + +/******** Rel32FinderAArch32 ********/ + +Rel32FinderAArch32::Rel32FinderAArch32(ConstBufferView image, + const AddressTranslator& translator, + bool is_thumb2) + : Rel32FinderArm(image, translator), is_thumb2_(is_thumb2) {} + +Rel32FinderAArch32::~Rel32FinderAArch32() = default; + +Rel32Finder::NextIterators Rel32FinderAArch32::ScanA32(ConstBufferView region) { + // Guard against alignment potentially causing |cursor > region.end()|. + if (region.size() < 4) + return SetEmptyResult(); + ConstBufferView::const_iterator cursor = region.begin(); + cursor += IncrementForAlignCeil4(cursor - image_.begin()); + for (; region.end() - cursor >= 4; cursor += 4) { + offset_t offset = base::checked_cast<offset_t>(cursor - image_.begin()); + AArch32Rel32Translator translator; + rva_t instr_rva = offset_to_rva_.Convert(offset); + uint32_t code32 = translator.FetchArmCode32(image_, offset); + rva_t target_rva = kInvalidRva; + if (translator.ReadA24(instr_rva, code32, &target_rva)) { + return SetResult({offset, target_rva, AArch32Rel32Translator::ADDR_A24}, + cursor, 4); + } + } + return SetEmptyResult(); +} + +Rel32Finder::NextIterators Rel32FinderAArch32::ScanT32(ConstBufferView region) { + // Guard against alignment potentially causing |cursor > region.end()|. + if (region.size() < 2) + return SetEmptyResult(); + ConstBufferView::const_iterator cursor = region.begin(); + cursor += IncrementForAlignCeil2(cursor - image_.begin()); + while (region.end() - cursor >= 2) { + offset_t offset = base::checked_cast<offset_t>(cursor - image_.begin()); + AArch32Rel32Translator translator; + AArch32Rel32Translator::AddrType type = AArch32Rel32Translator::ADDR_NONE; + rva_t instr_rva = offset_to_rva_.Convert(offset); + uint16_t code16 = translator.FetchThumb2Code16(image_, offset); + int instr_size = GetThumb2InstructionSize(code16); + rva_t target_rva = kInvalidRva; + if (instr_size == 2) { // 16-bit THUMB2 instruction. + if (translator.ReadT8(instr_rva, code16, &target_rva)) + type = AArch32Rel32Translator::ADDR_T8; + else if (translator.ReadT11(instr_rva, code16, &target_rva)) + type = AArch32Rel32Translator::ADDR_T11; + } else { // |instr_size == 4|: 32-bit THUMB2 instruction. + if (region.end() - cursor >= 4) { + uint32_t code32 = translator.FetchThumb2Code32(image_, offset); + if (translator.ReadT20(instr_rva, code32, &target_rva)) + type = AArch32Rel32Translator::ADDR_T20; + else if (translator.ReadT24(instr_rva, code32, &target_rva)) + type = AArch32Rel32Translator::ADDR_T24; + } + } + if (type != AArch32Rel32Translator::ADDR_NONE) + return SetResult({offset, target_rva, type}, cursor, instr_size); + cursor += instr_size; + } + return SetEmptyResult(); +} + +Rel32Finder::NextIterators Rel32FinderAArch32::Scan(ConstBufferView region) { + return is_thumb2_ ? ScanT32(region) : ScanA32(region); +} + +/******** Rel32FinderAArch64 ********/ + +Rel32FinderAArch64::Rel32FinderAArch64(ConstBufferView image, + const AddressTranslator& translator) + : Rel32FinderArm(image, translator) {} + +Rel32FinderAArch64::~Rel32FinderAArch64() = default; + +Rel32Finder::NextIterators Rel32FinderAArch64::Scan(ConstBufferView region) { + // Guard against alignment potentially causing |cursor > region.end()|. + if (region.size() < 4) + return SetEmptyResult(); + ConstBufferView::const_iterator cursor = region.begin(); + cursor += IncrementForAlignCeil4(cursor - image_.begin()); + for (; region.end() - cursor >= 4; cursor += 4) { + offset_t offset = base::checked_cast<offset_t>(cursor - image_.begin()); + // For simplicity we assume RVA fits within 32-bits. + AArch64Rel32Translator translator; + AArch64Rel32Translator::AddrType type = AArch64Rel32Translator::ADDR_NONE; + rva_t instr_rva = offset_to_rva_.Convert(offset); + uint32_t code32 = translator.FetchCode32(image_, offset); + rva_t target_rva = kInvalidRva; + if (translator.ReadImmd14(instr_rva, code32, &target_rva)) { + type = AArch64Rel32Translator::ADDR_IMMD14; + } else if (translator.ReadImmd19(instr_rva, code32, &target_rva)) { + type = AArch64Rel32Translator::ADDR_IMMD19; + } else if (translator.ReadImmd26(instr_rva, code32, &target_rva)) { + type = AArch64Rel32Translator::ADDR_IMMD26; + } + if (type != AArch64Rel32Translator::ADDR_NONE) + return SetResult({offset, target_rva, type}, cursor, 4); + } + return SetEmptyResult(); +} + } // namespace zucchini diff --git a/rel32_finder.h b/rel32_finder.h index 96a23b9..3ebeb95 100644 --- a/rel32_finder.h +++ b/rel32_finder.h @@ -10,6 +10,7 @@ #include <vector> #include "components/zucchini/address_translator.h" +#include "components/zucchini/arm_utils.h" #include "components/zucchini/buffer_view.h" #include "components/zucchini/image_utils.h" @@ -198,6 +199,86 @@ class Rel32FinderX64 : public Rel32FinderIntel { NextIterators Scan(ConstBufferView region) override; }; +// Base class for ARM (AArch32 and AArch64) instructions. +template <typename ADDR_TYPE> +class Rel32FinderArm : public Rel32Finder { + public: + struct Result { + offset_t location; + rva_t target_rva; + ADDR_TYPE type; + + // For testing. + bool operator==(const Result& other) const { + return location == other.location && target_rva == other.target_rva && + type == other.type; + } + }; + + Rel32FinderArm(ConstBufferView image, const AddressTranslator& translator); + Rel32FinderArm(const Rel32FinderArm&) = delete; + const Rel32FinderArm& operator=(const Rel32FinderArm&) = delete; + ~Rel32FinderArm() override; + + // Helper for Scan*() that also assigns |rel32_|. + NextIterators SetResult(Result&& result, + ConstBufferView::const_iterator cursor, + int instr_size); + + // SetResult() for end of scan. + NextIterators SetEmptyResult(); + + protected: + // Cached results. + Result rel32_; +}; + +// AArch32 instructions. +class Rel32FinderAArch32 + : public Rel32FinderArm<AArch32Rel32Translator::AddrType> { + public: + Rel32FinderAArch32(ConstBufferView image, + const AddressTranslator& translator, + bool is_thumb2); + Rel32FinderAArch32(const Rel32FinderAArch32&) = delete; + const Rel32FinderAArch32& operator=(const Rel32FinderAArch32&) = delete; + ~Rel32FinderAArch32() override; + + const Result& GetRel32() const { return rel32_; } + + private: + // Rel32 extraction, assuming segment is in ARM mode. + NextIterators ScanA32(ConstBufferView region); + + // Rel32 extraction, assuming segment is in THUMB2 mode. + NextIterators ScanT32(ConstBufferView region); + + // Rel32Finder: + NextIterators Scan(ConstBufferView region) override; + + // Indicates whether segment is in THUMB2 or ARM mod. In general this can + // change throughout a section. However, currently we assume that this is + // constant for an entire section. + const bool is_thumb2_; +}; + +// AArch64 instructions. +class Rel32FinderAArch64 + : public Rel32FinderArm<AArch64Rel32Translator::AddrType> { + public: + Rel32FinderAArch64(ConstBufferView image, + const AddressTranslator& translator); + Rel32FinderAArch64(const Rel32FinderAArch64&) = delete; + const Rel32FinderAArch64& operator=(const Rel32FinderAArch64&) = delete; + ~Rel32FinderAArch64() override; + + const Result& GetRel32() const { return rel32_; } + + private: + // Rel32Finder: + NextIterators Scan(ConstBufferView region) override; +}; + } // namespace zucchini #endif // COMPONENTS_ZUCCHINI_REL32_FINDER_H_ diff --git a/rel32_finder_unittest.cc b/rel32_finder_unittest.cc index 69ea42a..7e4a21e 100644 --- a/rel32_finder_unittest.cc +++ b/rel32_finder_unittest.cc @@ -15,8 +15,11 @@ #include "base/check_op.h" #include "base/format_macros.h" +#include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" +#include "components/zucchini/arm_utils.h" #include "components/zucchini/buffer_view.h" +#include "components/zucchini/disassembler_elf.h" #include "components/zucchini/image_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -42,23 +45,23 @@ TEST(Abs32GapFinderTest, All) { std::string out_str; while (gap_finder.FindNext()) { ConstBufferView gap = gap_finder.GetGap(); - size_t lo = static_cast<size_t>(gap.begin() - image.begin()); - size_t hi = static_cast<size_t>(gap.end() - image.begin()); + size_t lo = base::checked_cast<size_t>(gap.begin() - image.begin()); + size_t hi = base::checked_cast<size_t>(gap.end() - image.begin()); out_str.append(base::StringPrintf("[%" PRIuS ",%" PRIuS ")", lo, hi)); } return out_str; }; // Empty regions yield empty segments. - EXPECT_EQ("", run_test(0, 0, std::vector<offset_t>(), 4)); - EXPECT_EQ("", run_test(9, 9, std::vector<offset_t>(), 4)); + EXPECT_EQ("", run_test(0, 0, {}, 4)); + EXPECT_EQ("", run_test(9, 9, {}, 4)); EXPECT_EQ("", run_test(8, 8, {8}, 4)); EXPECT_EQ("", run_test(8, 8, {0, 12}, 4)); // If no abs32 locations exist then the segment is the main range. - EXPECT_EQ("[0,99)", run_test(0, 99, std::vector<offset_t>(), 4)); - EXPECT_EQ("[20,21)", run_test(20, 21, std::vector<offset_t>(), 4)); - EXPECT_EQ("[51,55)", run_test(51, 55, std::vector<offset_t>(), 4)); + EXPECT_EQ("[0,99)", run_test(0, 99, {}, 4)); + EXPECT_EQ("[20,21)", run_test(20, 21, {}, 4)); + EXPECT_EQ("[51,55)", run_test(51, 55, {}, 4)); // abs32 locations found near start of main range. EXPECT_EQ("[10,20)", run_test(10, 20, {5}, 4)); @@ -132,8 +135,8 @@ class TestRel32Finder : public Rel32Finder { AddressTranslator GetTrivialTranslator(size_t size) { AddressTranslator translator; EXPECT_EQ(AddressTranslator::kSuccess, - translator.Initialize({{0, static_cast<offset_t>(size), 0U, - static_cast<rva_t>(size)}})); + translator.Initialize({{0, base::checked_cast<offset_t>(size), 0U, + base::checked_cast<rva_t>(size)}})); return translator; } @@ -220,8 +223,8 @@ constexpr uint8_t kDataX86[] = { }; // Abs32 locations corresponding to |kDataX86|, with width = 4. -constexpr uint8_t kAbs32X86[] = {0x08, 0x17, 0x26, 0x2A, - 0x40, 0x51, 0x5F, 0x63}; +constexpr offset_t kAbs32X86[] = {0x08, 0x17, 0x26, 0x2A, + 0x40, 0x51, 0x5F, 0x63}; } // namespace @@ -256,28 +259,28 @@ TEST(Rel32FinderX86Test, FindNext) { TEST(Rel32FinderX86Test, Integrated) { // Truncated form of Rel32FinderIntel::Result. - typedef std::pair<offset_t, rva_t> TruncatedResults; + using TruncatedResults = std::pair<offset_t, rva_t>; ConstBufferView image = ConstBufferView::FromRange(std::begin(kDataX86), std::end(kDataX86)); std::vector<offset_t> abs32_locations(std::begin(kAbs32X86), std::end(kAbs32X86)); - std::vector<TruncatedResults> rel32_results; + std::vector<TruncatedResults> results; - Abs32GapFinder gap_finder(image, image, abs32_locations, 4U); + Abs32GapFinder gap_finder(image, image, abs32_locations, + DisassemblerElfX86::Traits::kVAWidth); AddressTranslator translator(GetTrivialTranslator(image.size())); Rel32FinderX86 rel_finder(image, translator); while (gap_finder.FindNext()) { - auto gap = gap_finder.GetGap(); rel_finder.SetRegion(gap_finder.GetGap()); while (rel_finder.FindNext()) { auto rel32 = rel_finder.GetRel32(); - rel32_results.emplace_back( - TruncatedResults{rel32.location, rel32.target_rva}); + rel_finder.Accept(); + results.emplace_back(TruncatedResults{rel32.location, rel32.target_rva}); } } - std::vector<TruncatedResults> expected_rel32_results = { + std::vector<TruncatedResults> expected_results = { {0x04, 0x08}, /* {0x09, 0x0D}, */ {0x0F, 0x13}, /* {0x15, 0x19}, */ /*{0x1B, 0x1F}, */ @@ -288,7 +291,7 @@ TEST(Rel32FinderX86Test, Integrated) { /* {0x51, 0x55}, */ {0x57, 0x5B}, /* {0x5D, 0x61}, */ /* {0x63, 0x67}, */ {0x69, 0x6D}, }; - EXPECT_EQ(expected_rel32_results, rel32_results); + EXPECT_EQ(expected_results, results); } TEST(Rel32FinderX86Test, Accept) { @@ -367,8 +370,8 @@ constexpr uint8_t kDataX64[] = { }; // Abs32 locations corresponding to |kDataX64|, with width = 8. -constexpr uint8_t kAbs32X64[] = {0x0C, 0x1F, 0x33, 0x3B, 0x49, - 0x6A, 0x99, 0xB3, 0xCC}; +constexpr offset_t kAbs32X64[] = {0x0C, 0x1F, 0x33, 0x3B, 0x49, + 0x6A, 0x99, 0xB3, 0xCC}; } // namespace @@ -420,31 +423,31 @@ TEST(Rel32FinderX64Test, FindNext) { TEST(Rel32FinderX64Test, Integrated) { // Truncated form of Rel32FinderIntel::Result. - typedef std::pair<offset_t, rva_t> TruncatedResults; + using TruncatedResults = std::pair<offset_t, rva_t>; ConstBufferView image = ConstBufferView::FromRange(std::begin(kDataX64), std::end(kDataX64)); std::vector<offset_t> abs32_locations(std::begin(kAbs32X64), std::end(kAbs32X64)); - std::vector<TruncatedResults> rel32_results; + std::vector<TruncatedResults> results; - Abs32GapFinder gap_finder(image, image, abs32_locations, 8U); + Abs32GapFinder gap_finder(image, image, abs32_locations, + DisassemblerElfX64::Traits::kVAWidth); AddressTranslator translator(GetTrivialTranslator(image.size())); Rel32FinderX64 rel_finder(image, translator); while (gap_finder.FindNext()) { - auto gap = gap_finder.GetGap(); rel_finder.SetRegion(gap_finder.GetGap()); while (rel_finder.FindNext()) { auto rel32 = rel_finder.GetRel32(); - rel32_results.emplace_back( - TruncatedResults{rel32.location, rel32.target_rva}); + rel_finder.Accept(); + results.emplace_back(TruncatedResults{rel32.location, rel32.target_rva}); } } - std::vector<TruncatedResults> expected_rel32_results = { + std::vector<TruncatedResults> expected_results = { {0x04, 0x08}, /* {0x09, 0x0D}, */ - /*{0x0F, 0x13}, */ /* {0x15, 0x19}, */ {0x1B, 0x1F}, + /* {0x0F, 0x13}, */ /* {0x15, 0x19}, */ {0x1B, 0x1F}, /* {0x21, 0x25}, */ /* {0x27, 0x2B}, */ {0x2D, 0x31}, /* {0x33, 0x37}, */ /* {0x39, 0x3D}, */ /* {0x3F, 0x43}, */ {0x45, 0x49}, @@ -464,7 +467,277 @@ TEST(Rel32FinderX64Test, Integrated) { /* {0xCC, 0xD0}, */ /* {0xD3, 0xD7}, */ {0xDA, 0xDE}, {0xE1, 0xE5}, }; - EXPECT_EQ(expected_rel32_results, rel32_results); + EXPECT_EQ(expected_results, results); +} + +namespace { + +// Runs the ARM rel32 extraction (nested) loop on |image| using |rel32_finder|, +// given |abs32_locations| for abs32 references each having |abs32_width|. +// Returns the list of extracted references. +template <class REL32_FINDER> +std::vector<typename REL32_FINDER::Result> ArmExtractRel32( + ConstBufferView image, + const std::vector<offset_t>& abs32_locations, + int abs32_width, + REL32_FINDER&& rel32_finder) { + std::vector<typename REL32_FINDER::Result> results; + Abs32GapFinder gap_finder(image, image, abs32_locations, abs32_width); + while (gap_finder.FindNext()) { + rel32_finder.SetRegion(gap_finder.GetGap()); + while (rel32_finder.FindNext()) { + typename REL32_FINDER::Result rel32 = rel32_finder.GetRel32(); + rel32_finder.Accept(); + results.emplace_back(rel32); + } + } + return results; +} + +} // namespace + +namespace { + +// AArch32 ARM mode test data. (x) and +x entries are covered by abs32 +// references (if used), which have width = 4. +constexpr uint8_t kDataAarch32ArmMode[] = { + 0x00, 0x01, 0x02, 0xEA, // 00: B 00080408 ; B encoding A1 + 0x00, 0x01, (0x02), +0xEA, // 04: B 0008040C ; B encoding A1 + +0x00, +0x01, 0x02, 0xEA, // 08: B 00080410 ; B encoding A1 + 0x00, 0x01, 0x02, 0xEA, // 0C: B 00080414 ; B encoding A1 + 0x00, 0x01, 0x02, (0xEA), // 10: B 00080418 ; B encoding A1 + +0x00, +0x01, +0x02, 0xEA, // 14: B 0008041C ; B encoding A1 + 0x00, 0x01, 0x02, 0xEA, // 18: B 00080420 ; B encoding A1 +}; + +// Abs32 locations corresponding to |kDataAarch32ArmMode|, with width = 4. +constexpr offset_t kAbs32Aarch32ArmMode[] = {0x6, 0x13}; + +} // namespace + +TEST(Rel32FinderAArch32Test, IntegratedArmModeWithoutAbs32) { + using AddrType = AArch32Rel32Translator::AddrType; + using Result = Rel32FinderAArch32::Result; + std::vector<Result> expected_results = { + {0x00, 0x80408, AddrType::ADDR_A24}, {0x04, 0x8040C, AddrType::ADDR_A24}, + {0x08, 0x80410, AddrType::ADDR_A24}, {0x0C, 0x80414, AddrType::ADDR_A24}, + {0x10, 0x80418, AddrType::ADDR_A24}, {0x14, 0x8041C, AddrType::ADDR_A24}, + {0x18, 0x80420, AddrType::ADDR_A24}, + }; + + ConstBufferView image = ConstBufferView::FromRange( + std::begin(kDataAarch32ArmMode), std::end(kDataAarch32ArmMode)); + AddressTranslator translator(GetTrivialTranslator(image.size())); + Rel32FinderAArch32 rel32_finder(image, translator, /* is_thumb2 */ false); + + std::vector<Result> results = ArmExtractRel32( + image, /* abs32_locations */ {}, DisassemblerElfAArch32::Traits::kVAWidth, + std::move(rel32_finder)); + + EXPECT_EQ(expected_results, results); +} + +TEST(Rel32FinderAArch32Test, IntegratedArmModeWithAbs32) { + using AddrType = AArch32Rel32Translator::AddrType; + using Result = Rel32FinderAArch32::Result; + std::vector<Result> expected_results = { + {0x00, 0x80408, AddrType::ADDR_A24}, + /* {0x04, 0x8040C, AddrType::ADDR_A24}, */ + /* {0x08, 0x80410, AddrType::ADDR_A24}, */ + {0x0C, 0x80414, AddrType::ADDR_A24}, + /* {0x10, 0x80418, AddrType::ADDR_A24}, */ + /* {0x14, 0x8041C, AddrType::ADDR_A24}, */ + {0x18, 0x80420, AddrType::ADDR_A24}, + }; + + ConstBufferView image = ConstBufferView::FromRange( + std::begin(kDataAarch32ArmMode), std::end(kDataAarch32ArmMode)); + std::vector<offset_t> abs32_locations(std::begin(kAbs32Aarch32ArmMode), + std::end(kAbs32Aarch32ArmMode)); + AddressTranslator translator(GetTrivialTranslator(image.size())); + Rel32FinderAArch32 rel32_finder(image, translator, /* is_thumb2 */ false); + + std::vector<Result> results = ArmExtractRel32( + image, abs32_locations, DisassemblerElfAArch32::Traits::kVAWidth, + std::move(rel32_finder)); + + EXPECT_EQ(expected_results, results); +} + +namespace { + +// AArch32 THUMB2 mode test data. (x) and +x entries are covered by abs32 +// references (if used), which have width = 4. +constexpr uint8_t kDataAarch32Thumb2Mode[] = { + 0x00, 0xDE, // 00: B.AL 00000004 ; B encoding T1 + 0x00, 0xDE, // 02: B.AL 00000006 ; B encoding T1 + 0x00, (0xDE), // 04: B.AL 00000008 ; B encoding T1 + +0x00, +0xDE, // 06: B.AL 0000000A ; B encoding T1 + +0x00, 0xE0, // 08: B 0000000C ; B encoding T2 + 0x00, 0xE0, // 0A: B 0000000E ; B encoding T2 + 0x00, 0xE0, // 0C: B 00000010 ; B encoding T2 + (0x00), +0xE0, // 0E: B 00000012 ; B encoding T2 + +0x00, +0xF0, 0x00, 0x80, // 10: B 00000014 ; B encoding T3 + 0x00, 0xF0, 0x00, 0x80, // 14: B 00000018 ; B encoding T3 + (0x00), +0xF0, +0x00, +0x80, // 18: B 0000001C ; B encoding T3 + 0x00, 0xF0, 0x00, 0x80, // 1C: B 00000020 ; B encoding T3 + 0x00, 0xF0, 0x00, 0xB8, // 20: B 00000024 ; B encoding T4 + 0x00, 0xF0, 0x00, (0xB8), // 24: B 00000028 ; B encoding T4 + +0xFE, +0xDE, // 28: B.AL 00000028 ; B encoding T1 + +0x00, 0xF0, 0x00, 0xF8, // 2A: BL 0000002E ; BL encoding T1 + 0x00, 0xF0, 0x00, 0xE8, // 2E: BLX 00000030 ; BLX encoding T2 + 0x00, 0x0B, // 32: NOP + 0x00, 0xF0, 0x00, 0xE8, // 34: BLX 00000038 ; BLX encoding T2 + 0x00, 0xF0, 0x00, 0xB8, // 38: B 0000003C ; B encoding T4 +}; + +// Abs32 locations corresponding to |kDataAarch32Thumb2Mode|, with width = 4. +constexpr offset_t kAbs32Aarch32Thumb2Mode[] = {0x05, 0x0E, 0x18, 0x27}; + +} // namespace + +TEST(Rel32FinderAArch32Test, IntegratedThumb2ModeWithoutAbs32) { + using AddrType = AArch32Rel32Translator::AddrType; + using Result = Rel32FinderAArch32::Result; + std::vector<Result> expected_results = { + {0x00, 0x04, AddrType::ADDR_T8}, {0x02, 0x06, AddrType::ADDR_T8}, + {0x04, 0x08, AddrType::ADDR_T8}, {0x06, 0x0A, AddrType::ADDR_T8}, + {0x08, 0x0C, AddrType::ADDR_T11}, {0x0A, 0x0E, AddrType::ADDR_T11}, + {0x0C, 0x10, AddrType::ADDR_T11}, {0x0E, 0x12, AddrType::ADDR_T11}, + {0x10, 0x14, AddrType::ADDR_T20}, {0x14, 0x18, AddrType::ADDR_T20}, + {0x18, 0x1C, AddrType::ADDR_T20}, {0x1C, 0x20, AddrType::ADDR_T20}, + {0x20, 0x24, AddrType::ADDR_T24}, {0x24, 0x28, AddrType::ADDR_T24}, + {0x28, 0x28, AddrType::ADDR_T8}, {0x2A, 0x2E, AddrType::ADDR_T24}, + {0x2E, 0x30, AddrType::ADDR_T24}, {0x34, 0x38, AddrType::ADDR_T24}, + {0x38, 0x3C, AddrType::ADDR_T24}, + }; + + ConstBufferView image = ConstBufferView::FromRange( + std::begin(kDataAarch32Thumb2Mode), std::end(kDataAarch32Thumb2Mode)); + AddressTranslator translator(GetTrivialTranslator(image.size())); + Rel32FinderAArch32 rel32_finder(image, translator, /* is_thumb2 */ true); + + std::vector<Result> results = ArmExtractRel32( + image, /* abs32_locations */ {}, DisassemblerElfAArch32::Traits::kVAWidth, + std::move(rel32_finder)); + + EXPECT_EQ(expected_results, results); +} + +TEST(Rel32FinderAArch32Test, IntegratedThumb2ModeWithAbs32) { + using AddrType = AArch32Rel32Translator::AddrType; + using Result = Rel32FinderAArch32::Result; + std::vector<Result> expected_results = { + {0x00, 0x04, AddrType::ADDR_T8}, + {0x02, 0x06, AddrType::ADDR_T8}, + /* {0x04, 0x08, AddrType::ADDR_T8}, */ + /* {0x06, 0x0A, AddrType::ADDR_T8}, */ + /* {0x08, 0x0C, AddrType::ADDR_T11}, */ + {0x0A, 0x0E, AddrType::ADDR_T11}, + {0x0C, 0x10, AddrType::ADDR_T11}, + /* {0x0E, 0x12, AddrType::ADDR_T11}, */ + /* {0x10, 0x14, AddrType::ADDR_T20}, */ + {0x14, 0x18, AddrType::ADDR_T20}, + /* {0x18, 0x1C, AddrType::ADDR_T20}, */ + {0x1C, 0x20, AddrType::ADDR_T20}, + {0x20, 0x24, AddrType::ADDR_T24}, + /* {0x24, 0x28, AddrType::ADDR_T24}, */ + /* {0x28, 0x28, AddrType::ADDR_T8}, */ + /* {0x2A, 0x2E, AddrType::ADDR_T24}, */ + // Abs32 reference 0x27 disrupts alignment, and THUMB2 disassembly starts + // at 0x2C, causing the following to be excluded! + /* {0x2E, 0x30, AddrType::ADDR_T24}, */ + {0x34, 0x38, AddrType::ADDR_T24}, + {0x38, 0x3C, AddrType::ADDR_T24}, + }; + + ConstBufferView image = ConstBufferView::FromRange( + std::begin(kDataAarch32Thumb2Mode), std::end(kDataAarch32Thumb2Mode)); + std::vector<offset_t> abs32_locations(std::begin(kAbs32Aarch32Thumb2Mode), + std::end(kAbs32Aarch32Thumb2Mode)); + AddressTranslator translator(GetTrivialTranslator(image.size())); + Rel32FinderAArch32 rel32_finder(image, translator, /* is_thumb2 */ true); + + std::vector<Result> results = ArmExtractRel32( + image, abs32_locations, DisassemblerElfAArch32::Traits::kVAWidth, + std::move(rel32_finder)); + + EXPECT_EQ(expected_results, results); +} + +namespace { + +// AArch32 THUMB2 mode test data. (x) and +x entries are covered by abs32 +// references (if used), which have width = 8. +constexpr uint8_t kDataAarch64[] = { + 0x0E, 0x00, 0x00, 0x36, // 00: TBZ X0,#0,00000000 ; Immd14 + 0x0E, 0x00, 0x00, (0x36), // 04: TBZ X0,#0,00000004 ; Immd14 + +0x0E, +0x00, +0x00, +0x36, // 08: TBZ X0,#0,00000008 ; Immd14 + +0x0E, +0x00, +0x00, 0x54, // 0C: B.AL 0000000C ; Immd19 + 0x0E, 0x00, 0x00, 0x54, // 10: B.AL 00000010 ; Immd19 + (0x0E), +0x00, +0x00, +0x54, // 14: B.AL 00000014 ; Immd19 + +0x00, +0x00, +0x00, +0x94, // 18: BL 00000018 ; Immd26 + 0x00, 0x00, 0x00, 0x14, // 1C: B 0000001C ; Immd26 + 0x00, 0x00, 0x00, 0x94, // 20: BL 00000020 ; Immd26 + 0x00, 0x00, 0x00, 0x14, // 24: B 00000024 ; Immd26 +}; + +// Abs32 locations corresponding to |kDataAarch64|, with width = 8. +constexpr offset_t kAbs32Aarch64[] = {0x07, 0x14}; + +} // namespace + +TEST(Rel32FinderAArch64Test, IntegratedWithoutAbs32) { + using AddrType = AArch64Rel32Translator::AddrType; + using Result = Rel32FinderAArch64::Result; + std::vector<Result> expected_results = { + {0x00, 0x00, AddrType::ADDR_IMMD14}, {0x04, 0x04, AddrType::ADDR_IMMD14}, + {0x08, 0x08, AddrType::ADDR_IMMD14}, {0x0C, 0x0C, AddrType::ADDR_IMMD19}, + {0x10, 0x10, AddrType::ADDR_IMMD19}, {0x14, 0x14, AddrType::ADDR_IMMD19}, + {0x18, 0x18, AddrType::ADDR_IMMD26}, {0x1C, 0x1C, AddrType::ADDR_IMMD26}, + {0x20, 0x20, AddrType::ADDR_IMMD26}, {0x24, 0x24, AddrType::ADDR_IMMD26}, + }; + + ConstBufferView image = ConstBufferView::FromRange(std::begin(kDataAarch64), + std::end(kDataAarch64)); + AddressTranslator translator(GetTrivialTranslator(image.size())); + Rel32FinderAArch64 rel32_finder(image, translator); + + std::vector<Result> results = ArmExtractRel32( + image, /* abs32_locations */ {}, DisassemblerElfAArch64::Traits::kVAWidth, + std::move(rel32_finder)); + + EXPECT_EQ(expected_results, results); +} + +TEST(Rel32FinderAArch64Test, IntegratedWithAbs32) { + using AddrType = AArch64Rel32Translator::AddrType; + using Result = Rel32FinderAArch64::Result; + std::vector<Result> expected_results = { + {0x00, 0x00, AddrType::ADDR_IMMD14}, + /* {0x04, 0x04, AddrType::ADDR_IMMD14}, */ + /* {0x08, 0x08, AddrType::ADDR_IMMD14}, */ + /* {0x0C, 0x0C, AddrType::ADDR_IMMD19}, */ + {0x10, 0x10, AddrType::ADDR_IMMD19}, + /* {0x14, 0x14, AddrType::ADDR_IMMD19}, */ + /* {0x18, 0x18, AddrType::ADDR_IMMD26}, */ + {0x1C, 0x1C, AddrType::ADDR_IMMD26}, + {0x20, 0x20, AddrType::ADDR_IMMD26}, + {0x24, 0x24, AddrType::ADDR_IMMD26}, + }; + + ConstBufferView image = ConstBufferView::FromRange(std::begin(kDataAarch64), + std::end(kDataAarch64)); + std::vector<offset_t> abs32_locations(std::begin(kAbs32Aarch64), + std::end(kAbs32Aarch64)); + AddressTranslator translator(GetTrivialTranslator(image.size())); + Rel32FinderAArch64 rel32_finder(image, translator); + + std::vector<Result> results = ArmExtractRel32( + image, abs32_locations, DisassemblerElfAArch64::Traits::kVAWidth, + std::move(rel32_finder)); + + EXPECT_EQ(expected_results, results); } } // namespace zucchini diff --git a/rel32_utils.h b/rel32_utils.h index db41d4a..f54c5cd 100644 --- a/rel32_utils.h +++ b/rel32_utils.h @@ -151,10 +151,10 @@ class Rel32WriterArm : public ReferenceWriter { // Type for specialized versions of ArmCopyDisp(). // TODO(etiennep/huangs): Fold ReferenceByteMixer into Disassembler and remove // direct function pointer usage. -typedef bool (*ArmCopyDispFun)(ConstBufferView src_view, - offset_t src_idx, - MutableBufferView dst_view, - offset_t dst_idx); +using ArmCopyDispFun = bool (*)(ConstBufferView src_view, + offset_t src_idx, + MutableBufferView dst_view, + offset_t dst_idx); // Copier that makes |*dst_it| similar to |*src_it| (both assumed to point to // rel32 instructions of type ADDR_TRAITS) by copying the displacement (i.e., |