aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Huang <huangs@chromium.org>2021-08-05 16:46:38 +0000
committerCopybara-Service <copybara-worker@google.com>2021-08-05 10:05:02 -0700
commitfa10b05c4854c6d8a603ee47c2a213cbc23f8646 (patch)
tree94ad9d794dedc26bd0e0be4b18511d45026a0b98
parent3e1f64d1395c53a730475d930b663d5f6006099e (diff)
downloadzucchini-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
-rw-r--r--arm_utils.h4
-rw-r--r--disassembler_elf.cc351
-rw-r--r--disassembler_elf.h193
-rw-r--r--disassembler_elf_unittest.cc85
-rw-r--r--element_detection.cc16
-rw-r--r--reference_bytes_mixer.cc104
-rw-r--r--reference_bytes_mixer.h30
-rw-r--r--rel32_finder.cc129
-rw-r--r--rel32_finder.h81
-rw-r--r--rel32_finder_unittest.cc333
-rw-r--r--rel32_utils.h8
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.,