| // Copyright 2018 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "components/zucchini/disassembler_dex.h" | 
 |  | 
 | #include <stddef.h> | 
 | #include <stdlib.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <cctype> | 
 | #include <cmath> | 
 | #include <iterator> | 
 | #include <set> | 
 | #include <utility> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/callback.h" | 
 | #include "base/logging.h" | 
 | #include "base/numerics/safe_conversions.h" | 
 | #include "base/optional.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "components/zucchini/buffer_source.h" | 
 | #include "components/zucchini/buffer_view.h" | 
 | #include "components/zucchini/io_utils.h" | 
 |  | 
 | namespace zucchini { | 
 |  | 
 | namespace { | 
 |  | 
 | // A DEX item specified by an offset, if absent, has a sentinel value of 0 since | 
 | // 0 is never a valid item offset (it points to magic at start of DEX). | 
 | constexpr offset_t kDexSentinelOffset = 0U; | 
 |  | 
 | // A DEX item specified by an index, if absent, has a sentinel value of | 
 | // NO_INDEX = 0xFFFFFFFF. This is represented as an offset_t for uniformity. | 
 | constexpr offset_t kDexSentinelIndexAsOffset = 0xFFFFFFFFU; | 
 |  | 
 | static_assert(kDexSentinelIndexAsOffset != kInvalidOffset, | 
 |               "Sentinel should not be confused with invalid offset."); | 
 |  | 
 | // Size of a Dalvik instruction unit. Need to cast to signed int because | 
 | // sizeof() gives size_t, which dominates when operated on ptrdiff_t, then | 
 | // wrecks havoc for base::checked_cast<int16_t>(). | 
 | constexpr int kInstrUnitSize = static_cast<int>(sizeof(uint16_t)); | 
 |  | 
 | // Checks if |offset| is byte aligned to 32 bits or 4 bytes. | 
 | bool Is32BitAligned(offset_t offset) { | 
 |   return offset % 4 == 0; | 
 | } | 
 |  | 
 | // Returns a lower bound for the size of an item of type |type_item_code|. | 
 | // - For fixed-length items (e.g., kTypeFieldIdItem) this is the exact size. | 
 | // - For variant-length items (e.g., kTypeCodeItem), returns a value that is | 
 | //   known to be less than the item length (e.g., header size). | 
 | // - For items not handled by this function, returns 1 for sanity check. | 
 | size_t GetItemBaseSize(uint16_t type_item_code) { | 
 |   switch (type_item_code) { | 
 |     case dex::kTypeStringIdItem: | 
 |       return sizeof(dex::StringIdItem); | 
 |     case dex::kTypeTypeIdItem: | 
 |       return sizeof(dex::TypeIdItem); | 
 |     case dex::kTypeProtoIdItem: | 
 |       return sizeof(dex::ProtoIdItem); | 
 |     case dex::kTypeFieldIdItem: | 
 |       return sizeof(dex::FieldIdItem); | 
 |     case dex::kTypeMethodIdItem: | 
 |       return sizeof(dex::MethodIdItem); | 
 |     case dex::kTypeClassDefItem: | 
 |       return sizeof(dex::ClassDefItem); | 
 |     // No need to handle dex::kTypeMapList. | 
 |     case dex::kTypeTypeList: | 
 |       return sizeof(uint32_t);  // Variable-length. | 
 |     case dex::kTypeAnnotationSetRefList: | 
 |       return sizeof(uint32_t);  // Variable-length. | 
 |     case dex::kTypeAnnotationSetItem: | 
 |       return sizeof(uint32_t);  // Variable-length. | 
 |     case dex::kTypeCodeItem: | 
 |       return sizeof(dex::CodeItem);  // Variable-length. | 
 |     case dex::kTypeAnnotationsDirectoryItem: | 
 |       return sizeof(dex::AnnotationsDirectoryItem);  // Variable-length. | 
 |     default: | 
 |       return 1U;  // Unhandled item. For sanity check assume size >= 1. | 
 |   } | 
 | } | 
 |  | 
 | /******** CodeItemParser ********/ | 
 |  | 
 | // A parser to extract successive code items from a DEX image whose header has | 
 | // been parsed. | 
 | class CodeItemParser { | 
 |  public: | 
 |   using size_type = BufferSource::size_type; | 
 |  | 
 |   explicit CodeItemParser(ConstBufferView image) : image_(image) {} | 
 |  | 
 |   // Initializes the parser, returns true on success and false on error. | 
 |   bool Init(const dex::MapItem& code_map_item) { | 
 |     // Sanity check to quickly fail if |code_map_item.offset| or | 
 |     // |code_map_item.size| is too large. This is a heuristic because code item | 
 |     // sizes need to be parsed (sizeof(dex::CodeItem) is a lower bound). | 
 |     if (!image_.covers_array(code_map_item.offset, code_map_item.size, | 
 |                              sizeof(dex::CodeItem))) { | 
 |       return false; | 
 |     } | 
 |     source_ = std::move(BufferSource(image_).Skip(code_map_item.offset)); | 
 |     return true; | 
 |   } | 
 |  | 
 |   // Extracts the header of the next code item, and skips the variable-length | 
 |   // data. Returns the offset of the code item if successful. Otherwise returns | 
 |   // kInvalidOffset, and thereafter the parser becomes valid. For reference, | 
 |   // here's a pseudo-struct of a complete code item: | 
 |   // | 
 |   // struct code_item { | 
 |   //   // 4-byte aligned here. | 
 |   //   // 16-byte header defined (dex::CodeItem). | 
 |   //   uint16_t registers_size; | 
 |   //   uint16_t ins_size; | 
 |   //   uint16_t outs_size; | 
 |   //   uint16_t tries_size; | 
 |   //   uint32_t debug_info_off; | 
 |   //   uint32_t insns_size; | 
 |   // | 
 |   //   // Variable-length data follow. | 
 |   //   uint16_t insns[insns_size];  // Instruction bytes. | 
 |   //   uint16_t padding[(tries_size > 0 && insns_size % 2 == 1) ? 1 : 0]; | 
 |   // | 
 |   //   if (tries_size > 0) { | 
 |   //     // 4-byte aligned here. | 
 |   //     struct try_item {  // dex::TryItem. | 
 |   //       uint32_t start_addr; | 
 |   //       uint16_t insn_count; | 
 |   //       uint16_t handler_off; | 
 |   //     } tries[tries_size]; | 
 |   // | 
 |   //     struct encoded_catch_handler_list { | 
 |   //       uleb128 handlers_size; | 
 |   //       struct encoded_catch_handler { | 
 |   //         sleb128 encoded_catch_handler_size; | 
 |   //         struct encoded_type_addr_pair { | 
 |   //           uleb128 type_idx; | 
 |   //           uleb128 addr; | 
 |   //         } handlers[abs(encoded_catch_handler_size)]; | 
 |   //         if (encoded_catch_handler_size <= 0) { | 
 |   //           uleb128 catch_all_addr; | 
 |   //         } | 
 |   //       } handlers_list[handlers_size]; | 
 |   //     } handlers_group;  // Confusingly called "handlers" in DEX doc. | 
 |   //   } | 
 |   // | 
 |   //   // Padding to 4-bytes align next code_item *only if more exist*. | 
 |   // } | 
 |   offset_t GetNext() { | 
 |     // Read header CodeItem. | 
 |     if (!source_.AlignOn(image_, 4U)) | 
 |       return kInvalidOffset; | 
 |     const offset_t code_item_offset = | 
 |         base::checked_cast<offset_t>(source_.begin() - image_.begin()); | 
 |     const auto* code_item = source_.GetPointer<const dex::CodeItem>(); | 
 |     if (!code_item) | 
 |       return kInvalidOffset; | 
 |     DCHECK(Is32BitAligned(code_item_offset)); | 
 |  | 
 |     // TODO(huangs): Fail if |code_item->insns_size == 0| (Constraint A1). | 
 |     // Skip instruction bytes. | 
 |     if (!source_.GetArray<uint16_t>(code_item->insns_size)) | 
 |       return kInvalidOffset; | 
 |     // Skip padding if present. | 
 |     if (code_item->tries_size > 0 && !source_.AlignOn(image_, 4U)) | 
 |       return kInvalidOffset; | 
 |  | 
 |     // Skip tries[] and handlers_group to arrive at the next code item. Parsing | 
 |     // is nontrivial due to use of uleb128 / sleb128. | 
 |     if (code_item->tries_size > 0) { | 
 |       // Skip (try_item) tries[]. | 
 |       if (!source_.GetArray<dex::TryItem>(code_item->tries_size)) | 
 |         return kInvalidOffset; | 
 |  | 
 |       // Skip handlers_group. | 
 |       uint32_t handlers_size = 0; | 
 |       if (!source_.GetUleb128(&handlers_size)) | 
 |         return kInvalidOffset; | 
 |       // Sanity check to quickly reject excessively large |handlers_size|. | 
 |       if (source_.Remaining() < static_cast<size_type>(handlers_size)) | 
 |         return kInvalidOffset; | 
 |  | 
 |       // Skip (encoded_catch_handler) handlers_list[]. | 
 |       for (uint32_t k = 0; k < handlers_size; ++k) { | 
 |         int32_t encoded_catch_handler_size = 0; | 
 |         if (!source_.GetSleb128(&encoded_catch_handler_size)) | 
 |           return kInvalidOffset; | 
 |         const size_type abs_size = std::abs(encoded_catch_handler_size); | 
 |         if (source_.Remaining() < abs_size)  // Sanity check. | 
 |           return kInvalidOffset; | 
 |         // Skip (encoded_type_addr_pair) handlers[]. | 
 |         for (size_type j = 0; j < abs_size; ++j) { | 
 |           if (!source_.SkipLeb128() || !source_.SkipLeb128()) | 
 |             return kInvalidOffset; | 
 |         } | 
 |         // Skip catch_all_addr. | 
 |         if (encoded_catch_handler_size <= 0) { | 
 |           if (!source_.SkipLeb128()) | 
 |             return kInvalidOffset; | 
 |         } | 
 |       } | 
 |     } | 
 |     // Success! |code_item->insns_size| is validated, but its content is still | 
 |     // considered unsafe and requires validation. | 
 |     return code_item_offset; | 
 |   } | 
 |  | 
 |   // Given |code_item_offset| that points to the start of a valid code item in | 
 |   // |image|, returns |insns| bytes as ConstBufferView. | 
 |   static ConstBufferView GetCodeItemInsns(ConstBufferView image, | 
 |                                           offset_t code_item_offset) { | 
 |     BufferSource source(BufferSource(image).Skip(code_item_offset)); | 
 |     const auto* code_item = source.GetPointer<const dex::CodeItem>(); | 
 |     DCHECK(code_item); | 
 |     BufferRegion insns{0, code_item->insns_size * kInstrUnitSize}; | 
 |     DCHECK(source.covers(insns)); | 
 |     return source[insns]; | 
 |   } | 
 |  | 
 |  private: | 
 |   ConstBufferView image_; | 
 |   BufferSource source_; | 
 | }; | 
 |  | 
 | /******** InstructionParser ********/ | 
 |  | 
 | // A class that successively reads |code_item| for Dalvik instructions, which | 
 | // are found at |insns|, spanning |insns_size| uint16_t "units". These units | 
 | // store instructions followed by optional non-instruction "payload". Finding | 
 | // payload boundary requires parsing: On finding an instruction that uses (and | 
 | // points to) payload, the boundary is updated. | 
 | class InstructionParser { | 
 |  public: | 
 |   struct Value { | 
 |     offset_t instr_offset; | 
 |     const dex::Instruction* instr = nullptr;  // null for unknown instructions. | 
 |   }; | 
 |  | 
 |   // Returns pointer to DEX Instruction data for |opcode|, or null if |opcode| | 
 |   // is unknown. An internal initialize-on-first-use table is used for fast | 
 |   // lookup. | 
 |   const dex::Instruction* FindDalvikInstruction(uint8_t opcode) { | 
 |     static bool is_init = false; | 
 |     static const dex::Instruction* instruction_table[256]; | 
 |     if (!is_init) { | 
 |       is_init = true; | 
 |       std::fill(std::begin(instruction_table), std::end(instruction_table), | 
 |                 nullptr); | 
 |       for (const dex::Instruction& instr : dex::kByteCode) { | 
 |         std::fill(instruction_table + instr.opcode, | 
 |                   instruction_table + instr.opcode + instr.variant, &instr); | 
 |       } | 
 |     } | 
 |     return instruction_table[opcode]; | 
 |   } | 
 |  | 
 |   InstructionParser() = default; | 
 |  | 
 |   InstructionParser(ConstBufferView image, offset_t base_offset) | 
 |       : image_begin_(image.begin()), | 
 |         insns_(CodeItemParser::GetCodeItemInsns(image, base_offset)), | 
 |         payload_boundary_(insns_.end()) {} | 
 |  | 
 |   // Reads the next instruction. On success, makes the data read available via | 
 |   // value() and returns true. Otherwise (done or found error) returns false. | 
 |   bool ReadNext() { | 
 |     // Do not scan past payload boundary. | 
 |     if (insns_.begin() >= payload_boundary_) | 
 |       return false; | 
 |  | 
 |     const offset_t instr_offset = | 
 |         base::checked_cast<offset_t>(insns_.begin() - image_begin_); | 
 |     const uint8_t op = insns_.read<uint8_t>(0); | 
 |     const dex::Instruction* instr = FindDalvikInstruction(op); | 
 |  | 
 |     // Stop on finding unknown instructions. ODEX files might trigger this. | 
 |     if (!instr) { | 
 |       LOG(WARNING) << "Unknown Dalvik instruction detected at " | 
 |                    << AsHex<8>(instr_offset) << "."; | 
 |       return false; | 
 |     } | 
 |  | 
 |     const int instr_length_units = instr->layout; | 
 |     const size_t instr_length_bytes = instr_length_units * kInstrUnitSize; | 
 |     if (insns_.size() < instr_length_bytes) | 
 |       return false; | 
 |  | 
 |     // Handle instructions with variable-length data payload (31t). | 
 |     if (instr->opcode == 0x26 ||  // fill-array-data | 
 |         instr->opcode == 0x2B ||  // packed-switch | 
 |         instr->opcode == 0x2C) {  // sparse-switch | 
 |       const int32_t unsafe_payload_rel_units = insns_.read<int32_t>(2); | 
 |       // Payload must be in current code item, after current instruction. | 
 |       if (unsafe_payload_rel_units < instr_length_units || | 
 |           static_cast<uint32_t>(unsafe_payload_rel_units) >= | 
 |               insns_.size() / kInstrUnitSize) { | 
 |         LOG(WARNING) << "Invalid payload found."; | 
 |         return false; | 
 |       } | 
 |       // Update boundary between instructions and payload. | 
 |       const ConstBufferView::const_iterator payload_it = | 
 |           insns_.begin() + unsafe_payload_rel_units * kInstrUnitSize; | 
 |       payload_boundary_ = std::min(payload_boundary_, payload_it); | 
 |     } | 
 |  | 
 |     insns_.remove_prefix(instr_length_bytes); | 
 |     value_ = {instr_offset, instr}; | 
 |     return true; | 
 |   } | 
 |  | 
 |   const Value& value() const { return value_; } | 
 |  | 
 |  private: | 
 |   ConstBufferView::const_iterator image_begin_; | 
 |   ConstBufferView insns_; | 
 |   ConstBufferView::const_iterator payload_boundary_; | 
 |   Value value_; | 
 | }; | 
 |  | 
 | /******** InstructionReferenceReader ********/ | 
 |  | 
 | // A class to visit |code_items|, parse instructions, and emit embedded | 
 | // References of a type determined by |filter_| and |mapper_|. Only References | 
 | // located in |[lo, hi)| are emitted. |lo| and |hi| are assumed to never | 
 | // straddle the body of a Reference. | 
 | class InstructionReferenceReader : public ReferenceReader { | 
 |  public: | 
 |   // A function that takes a parsed Dalvik instruction and decides whether it | 
 |   // contains a specific type of Reference. If true, then returns the Reference | 
 |   // location. Otherwise returns kInvalidOffset. | 
 |   using Filter = | 
 |       base::RepeatingCallback<offset_t(const InstructionParser::Value&)>; | 
 |   // A function that takes Reference location from |filter_| to extract the | 
 |   // stored target. If valid, returns it. Otherwise returns kInvalidOffset. | 
 |   using Mapper = base::RepeatingCallback<offset_t(offset_t)>; | 
 |  | 
 |   InstructionReferenceReader(ConstBufferView image, | 
 |                              offset_t lo, | 
 |                              offset_t hi, | 
 |                              const std::vector<offset_t>& code_item_offsets, | 
 |                              Filter&& filter, | 
 |                              Mapper&& mapper) | 
 |       : image_(image), | 
 |         lo_(lo), | 
 |         hi_(hi), | 
 |         end_it_(code_item_offsets.end()), | 
 |         filter_(std::move(filter)), | 
 |         mapper_(std::move(mapper)) { | 
 |     const auto begin_it = code_item_offsets.begin(); | 
 |     // Use binary search to find the code item that contains |lo_|. | 
 |     auto comp = [](offset_t test_offset, offset_t code_item_offset) { | 
 |       return test_offset < code_item_offset; | 
 |     }; | 
 |     cur_it_ = std::upper_bound(begin_it, end_it_, lo_, comp); | 
 |     if (cur_it_ != begin_it) | 
 |       --cur_it_; | 
 |     parser_ = InstructionParser(image_, *cur_it_); | 
 |   } | 
 |  | 
 |   // ReferenceReader: | 
 |   base::Optional<Reference> GetNext() override { | 
 |     while (true) { | 
 |       while (parser_.ReadNext()) { | 
 |         const auto& v = parser_.value(); | 
 |         DCHECK_NE(v.instr, nullptr); | 
 |         if (v.instr_offset >= hi_) | 
 |           return base::nullopt; | 
 |         const offset_t location = filter_.Run(v); | 
 |         if (location == kInvalidOffset || location < lo_) | 
 |           continue; | 
 |         // The general check is |location + reference_width > hi_|. However, by | 
 |         // assumption |hi_| and |lo_| do not straddle the body of a Reference. | 
 |         // So |reference_width| is unneeded. | 
 |         if (location >= hi_) | 
 |           return base::nullopt; | 
 |         offset_t target = mapper_.Run(location); | 
 |         if (target != kInvalidOffset) | 
 |           return Reference{location, target}; | 
 |         else | 
 |           LOG(WARNING) << "Invalid target at " << AsHex<8>(location) << "."; | 
 |       } | 
 |       ++cur_it_; | 
 |       if (cur_it_ == end_it_) | 
 |         return base::nullopt; | 
 |       parser_ = InstructionParser(image_, *cur_it_); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   const ConstBufferView image_; | 
 |   const offset_t lo_; | 
 |   const offset_t hi_; | 
 |   const std::vector<offset_t>::const_iterator end_it_; | 
 |   const Filter filter_; | 
 |   const Mapper mapper_; | 
 |   std::vector<offset_t>::const_iterator cur_it_; | 
 |   InstructionParser parser_; | 
 | }; | 
 |  | 
 | /******** ItemReferenceReader ********/ | 
 |  | 
 | // A class to visit fixed-size item elements (determined by |item_size|) and | 
 | // emit a "member variable of interest" (MVI, determined by |rel_location| and | 
 | // |mapper|) as Reference. Only MVIs lying in |[lo, hi)| are emitted. |lo| and | 
 | // |hi| are assumed to never straddle the body of a Reference. | 
 | class ItemReferenceReader : public ReferenceReader { | 
 |  public: | 
 |   // A function that takes an MVI's location and emit its target offset. | 
 |   using Mapper = base::RepeatingCallback<offset_t(offset_t)>; | 
 |  | 
 |   // |item_size| is the size of a fixed-size item. |rel_location| is the | 
 |   // relative location of MVI from the start of the item containing it. | 
 |   ItemReferenceReader(offset_t lo, | 
 |                       offset_t hi, | 
 |                       const dex::MapItem& map_item, | 
 |                       size_t item_size, | 
 |                       size_t rel_location, | 
 |                       Mapper&& mapper) | 
 |       : hi_(hi), | 
 |         item_base_offset_(base::checked_cast<offset_t>(map_item.offset)), | 
 |         num_items_(base::checked_cast<uint32_t>(map_item.size)), | 
 |         item_size_(base::checked_cast<uint32_t>(item_size)), | 
 |         rel_location_(base::checked_cast<uint32_t>(rel_location)), | 
 |         mapper_(std::move(mapper)) { | 
 |     static_assert(sizeof(decltype(map_item.offset)) <= sizeof(offset_t), | 
 |                   "map_item.offset too large."); | 
 |     static_assert(sizeof(decltype(map_item.size)) <= sizeof(offset_t), | 
 |                   "map_item.size too large."); | 
 |     if (!item_base_offset_) { | 
 |       // Empty item: Assign |cur_idx| to |num_items_| to skip everything. | 
 |       cur_idx_ = num_items_; | 
 |     } else if (lo < item_base_offset_) { | 
 |       cur_idx_ = 0; | 
 |     } else if (lo < OffsetOfIndex(num_items_)) { | 
 |       cur_idx_ = (lo - item_base_offset_) / item_size_; | 
 |       // Fine-tune: Advance if |lo| lies beyond the MVI. | 
 |       if (lo > OffsetOfIndex(cur_idx_) + rel_location_) | 
 |         ++cur_idx_; | 
 |     } else { | 
 |       cur_idx_ = num_items_; | 
 |     } | 
 |   } | 
 |  | 
 |   // ReferenceReader: | 
 |   base::Optional<Reference> GetNext() override { | 
 |     while (cur_idx_ < num_items_) { | 
 |       const offset_t item_offset = OffsetOfIndex(cur_idx_); | 
 |       const offset_t location = item_offset + rel_location_; | 
 |       // The general check is |location + reference_width > hi_|. However, by | 
 |       // assumption |hi_| and |lo_| do not straddle the body of a Reference. So | 
 |       // |reference_width| is unneeded. | 
 |       if (location >= hi_) | 
 |         break; | 
 |       const offset_t target = mapper_.Run(location); | 
 |  | 
 |       // kDexSentinelOffset (0) may appear for the following: | 
 |       // - ProtoIdItem: parameters_off. | 
 |       // - ClassDefItem: interfaces_off, annotations_off, class_data_off, | 
 |       //   static_values_off. | 
 |       // - AnnotationsDirectoryItem: class_annotations_off. | 
 |       // - AnnotationSetRefItem: annotations_off. | 
 |       // kDexSentinelIndexAsOffset (0xFFFFFFFF) may appear for the following: | 
 |       // - ClassDefItem: superclass_idx, source_file_idx. | 
 |       if (target == kDexSentinelOffset || target == kDexSentinelIndexAsOffset) { | 
 |         ++cur_idx_; | 
 |         continue; | 
 |       } | 
 |  | 
 |       if (target == kInvalidOffset) { | 
 |         LOG(WARNING) << "Invalid item target at " << AsHex<8>(location) << "."; | 
 |         break; | 
 |       } | 
 |       ++cur_idx_; | 
 |       return Reference{location, target}; | 
 |     } | 
 |     return base::nullopt; | 
 |   } | 
 |  | 
 |  private: | 
 |   offset_t OffsetOfIndex(uint32_t idx) { | 
 |     return base::checked_cast<uint32_t>(item_base_offset_ + idx * item_size_); | 
 |   } | 
 |  | 
 |   const offset_t hi_; | 
 |   const offset_t item_base_offset_; | 
 |   const uint32_t num_items_; | 
 |   const uint32_t item_size_; | 
 |   const uint32_t rel_location_; | 
 |   const Mapper mapper_; | 
 |   offset_t cur_idx_ = 0; | 
 | }; | 
 |  | 
 | // Parses a flattened jagged list of lists of items that looks like: | 
 | //   NTTT|NTT|NTTTT|N|NTT... | 
 | // where |N| is an uint32_t representing the number of items in each sub-list, | 
 | // and "T" is a fixed-size item (|item_width|) of type "T". On success, stores | 
 | // the offset of each |T| into |item_offsets|, and returns true. Otherwise | 
 | // (e.g., on finding any structural problem) returns false. | 
 | bool ParseItemOffsets(ConstBufferView image, | 
 |                       const dex::MapItem& map_item, | 
 |                       size_t item_width, | 
 |                       std::vector<offset_t>* item_offsets) { | 
 |   // Sanity check: |image| should at least fit |map_item.size| copies of "N". | 
 |   if (!image.covers_array(map_item.offset, map_item.size, sizeof(uint32_t))) | 
 |     return false; | 
 |   BufferSource source = std::move(BufferSource(image).Skip(map_item.offset)); | 
 |   item_offsets->clear(); | 
 |   for (uint32_t i = 0; i < map_item.size; ++i) { | 
 |     if (!source.AlignOn(image, 4U)) | 
 |       return false; | 
 |     uint32_t unsafe_size; | 
 |     if (!source.GetValue<uint32_t>(&unsafe_size)) | 
 |       return false; | 
 |     DCHECK(Is32BitAligned( | 
 |         base::checked_cast<offset_t>(source.begin() - image.begin()))); | 
 |     if (!source.covers_array(0, unsafe_size, item_width)) | 
 |       return false; | 
 |     for (uint32_t j = 0; j < unsafe_size; ++j) { | 
 |       item_offsets->push_back( | 
 |           base::checked_cast<offset_t>(source.begin() - image.begin())); | 
 |       source.Skip(item_width); | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | // Parses AnnotationDirectoryItems of the format (using RegEx) "(AF*M*P*)*", | 
 | // where: | 
 | //   A = AnnotationsDirectoryItem (contains class annotation), | 
 | //   F = FieldAnnotation, | 
 | //   M = MethodAnnotation, | 
 | //   P = ParameterAnnotation. | 
 | // On success, stores the offsets of each class, field, method and parameter | 
 | // annotation for each item into |*_annotation_offsets|. Otherwise on finding | 
 | // structural issues returns false. | 
 | bool ParseAnnotationsDirectoryItems( | 
 |     ConstBufferView image, | 
 |     const dex::MapItem& annotations_directory_map_item, | 
 |     std::vector<offset_t>* annotations_directory_item_offsets, | 
 |     std::vector<offset_t>* field_annotation_offsets, | 
 |     std::vector<offset_t>* method_annotation_offsets, | 
 |     std::vector<offset_t>* parameter_annotation_offsets) { | 
 |   // Sanity check: |image| should at least fit | 
 |   // |annotations_directory_map_item.size| copies of "A". | 
 |   if (!image.covers_array(annotations_directory_map_item.offset, | 
 |                           annotations_directory_map_item.size, | 
 |                           sizeof(dex::AnnotationsDirectoryItem))) { | 
 |     return false; | 
 |   } | 
 |   BufferSource source = std::move( | 
 |       BufferSource(image).Skip(annotations_directory_map_item.offset)); | 
 |   annotations_directory_item_offsets->clear(); | 
 |   field_annotation_offsets->clear(); | 
 |   method_annotation_offsets->clear(); | 
 |   parameter_annotation_offsets->clear(); | 
 |  | 
 |   // Helper to process sublists. | 
 |   auto parse_list = [&source, image](uint32_t unsafe_size, size_t item_width, | 
 |                                      std::vector<offset_t>* item_offsets) { | 
 |     DCHECK(Is32BitAligned( | 
 |         base::checked_cast<offset_t>(source.begin() - image.begin()))); | 
 |     if (!source.covers_array(0, unsafe_size, item_width)) | 
 |       return false; | 
 |     item_offsets->reserve(item_offsets->size() + unsafe_size); | 
 |     for (uint32_t i = 0; i < unsafe_size; ++i) { | 
 |       item_offsets->push_back( | 
 |           base::checked_cast<offset_t>(source.begin() - image.begin())); | 
 |       source.Skip(item_width); | 
 |     } | 
 |     return true; | 
 |   }; | 
 |  | 
 |   annotations_directory_item_offsets->reserve( | 
 |       annotations_directory_map_item.size); | 
 |   for (uint32_t i = 0; i < annotations_directory_map_item.size; ++i) { | 
 |     if (!source.AlignOn(image, 4U)) | 
 |       return false; | 
 |     // Parse header. | 
 |     annotations_directory_item_offsets->push_back( | 
 |         base::checked_cast<offset_t>(source.begin() - image.begin())); | 
 |     dex::AnnotationsDirectoryItem unsafe_annotations_directory_item; | 
 |     if (!source.GetValue(&unsafe_annotations_directory_item)) | 
 |       return false; | 
 |     // Parse sublists. | 
 |     if (!(parse_list(unsafe_annotations_directory_item.fields_size, | 
 |                      sizeof(dex::FieldAnnotation), field_annotation_offsets) && | 
 |           parse_list(unsafe_annotations_directory_item.annotated_methods_size, | 
 |                      sizeof(dex::MethodAnnotation), | 
 |                      method_annotation_offsets) && | 
 |           parse_list( | 
 |               unsafe_annotations_directory_item.annotated_parameters_size, | 
 |               sizeof(dex::ParameterAnnotation), | 
 |               parameter_annotation_offsets))) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | /******** CachedItemListReferenceReader ********/ | 
 |  | 
 | // A class that takes sorted |item_offsets|, and emits all member variable of | 
 | // interest (MVIs) that fall inside |[lo, hi)|. The MVI of each item has | 
 | // location of |rel_location| from item offset, and has target extracted with | 
 | // |mapper| (which performs validation). By the "atomicity assumption", | 
 | // [|lo, hi)| never cut across an MVI. | 
 | class CachedItemListReferenceReader : public ReferenceReader { | 
 |  public: | 
 |   // A function that takes an MVI's location and emit its target offset. | 
 |   using Mapper = base::RepeatingCallback<offset_t(offset_t)>; | 
 |  | 
 |   CachedItemListReferenceReader(offset_t lo, | 
 |                                 offset_t hi, | 
 |                                 uint32_t rel_location, | 
 |                                 const std::vector<offset_t>& item_offsets, | 
 |                                 Mapper&& mapper) | 
 |       : hi_(hi), | 
 |         rel_location_(rel_location), | 
 |         end_it_(item_offsets.cend()), | 
 |         mapper_(mapper) { | 
 |     cur_it_ = std::upper_bound(item_offsets.cbegin(), item_offsets.cend(), lo); | 
 |     // Adding |rel_location_| is necessary as references can be offset from the | 
 |     // start of the item. | 
 |     if (cur_it_ != item_offsets.begin() && *(cur_it_ - 1) + rel_location_ >= lo) | 
 |       --cur_it_; | 
 |   } | 
 |  | 
 |   // ReferenceReader: | 
 |   base::Optional<Reference> GetNext() override { | 
 |     while (cur_it_ < end_it_) { | 
 |       const offset_t location = *cur_it_ + rel_location_; | 
 |       if (location >= hi_)  // Check is simplified by atomicity assumption. | 
 |         break; | 
 |       const offset_t target = mapper_.Run(location); | 
 |       if (target == kInvalidOffset) { | 
 |         LOG(WARNING) << "Invalid item target at " << AsHex<8>(location) << "."; | 
 |         break; | 
 |       } | 
 |       ++cur_it_; | 
 |  | 
 |       // kDexSentinelOffset is a sentinel for; | 
 |       // - AnnotationsDirectoryItem: class_annotations_off | 
 |       if (target == kDexSentinelOffset) | 
 |         continue; | 
 |       return Reference{location, target}; | 
 |     } | 
 |     return base::nullopt; | 
 |   } | 
 |  | 
 |  private: | 
 |   const offset_t hi_; | 
 |   const uint32_t rel_location_; | 
 |   const std::vector<offset_t>::const_iterator end_it_; | 
 |   const Mapper mapper_; | 
 |   std::vector<offset_t>::const_iterator cur_it_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CachedItemListReferenceReader); | 
 | }; | 
 |  | 
 | // Reads an INT index at |location| in |image| and translates the index to the | 
 | // offset of a fixed-size item specified by |target_map_item| and | 
 | // |target_item_size|. Returns the target offset if valid, or kInvalidOffset | 
 | // otherwise. This is compatible with | 
 | // CachedReferenceListReferenceReader::Mapper, | 
 | // InstructionReferenceReader::Mapper, and ItemReferenceReader::Mapper. | 
 | template <typename INT> | 
 | static offset_t ReadTargetIndex(ConstBufferView image, | 
 |                                 const dex::MapItem& target_map_item, | 
 |                                 size_t target_item_size, | 
 |                                 offset_t location) { | 
 |   static_assert(sizeof(INT) <= sizeof(offset_t), | 
 |                 "INT may not fit into offset_t."); | 
 |   const offset_t unsafe_idx = image.read<INT>(location); | 
 |   // kDexSentinalIndexAsOffset (0xFFFFFFFF) is a sentinel for | 
 |   // - ClassDefItem: superclass_idx, source_file_idx. | 
 |   if (unsafe_idx == kDexSentinelIndexAsOffset) | 
 |     return unsafe_idx; | 
 |   if (unsafe_idx >= target_map_item.size) | 
 |     return kInvalidOffset; | 
 |   return target_map_item.offset + | 
 |          base::checked_cast<offset_t>(unsafe_idx * target_item_size); | 
 | } | 
 |  | 
 | // Reads uint32_t value in |image| at (valid) |location| and checks whether it | 
 | // is a safe offset of a fixed-size item. Returns the target offset (possibly a | 
 | // sentinel) if valid, or kInvalidOffset otherwise. This is compatible with | 
 | // CachedReferenceListReferenceReader::Mapper, | 
 | // InstructionReferenceReader::Mapper, and ItemReferenceReader::Mapper. | 
 | static offset_t ReadTargetOffset32(ConstBufferView image, offset_t location) { | 
 |   const offset_t unsafe_target = | 
 |       static_cast<offset_t>(image.read<uint32_t>(location)); | 
 |   // Skip and don't validate kDexSentinelOffset as it is indicative of an | 
 |   // empty reference. | 
 |   if (unsafe_target == kDexSentinelOffset) | 
 |     return unsafe_target; | 
 |  | 
 |   // TODO(huangs): Check that |unsafe_target| is within the correct data | 
 |   // section. | 
 |   if (unsafe_target >= image.size()) | 
 |     return kInvalidOffset; | 
 |   return unsafe_target; | 
 | } | 
 |  | 
 | /******** ReferenceWriterAdaptor ********/ | 
 |  | 
 | // A ReferenceWriter that adapts a callback that performs type-specific | 
 | // Reference writes. | 
 | class ReferenceWriterAdaptor : public ReferenceWriter { | 
 |  public: | 
 |   using Writer = base::RepeatingCallback<void(Reference, MutableBufferView)>; | 
 |  | 
 |   ReferenceWriterAdaptor(MutableBufferView image, Writer&& writer) | 
 |       : image_(image), writer_(std::move(writer)) {} | 
 |  | 
 |   // ReferenceWriter: | 
 |   void PutNext(Reference ref) override { writer_.Run(ref, image_); } | 
 |  | 
 |  private: | 
 |   MutableBufferView image_; | 
 |   Writer writer_; | 
 | }; | 
 |  | 
 | // Helper that's compatible with ReferenceWriterAdaptor::Writer. | 
 | // Given that |ref.target| points to the start of a fixed size DEX item (e.g., | 
 | // FieldIdItem), translates |ref.target| to item index, and writes the result to | 
 | // |ref.location| as |INT|. | 
 | template <typename INT> | 
 | static void WriteTargetIndex(const dex::MapItem& target_map_item, | 
 |                              size_t target_item_size, | 
 |                              Reference ref, | 
 |                              MutableBufferView image) { | 
 |   const size_t unsafe_idx = | 
 |       (ref.target - target_map_item.offset) / target_item_size; | 
 |   // Verify that index is within bound. | 
 |   if (unsafe_idx >= target_map_item.size) { | 
 |     LOG(ERROR) << "Target index out of bounds at: " << AsHex<8>(ref.location) | 
 |                << "."; | 
 |     return; | 
 |   } | 
 |   // Verify that |ref.target| points to start of item. | 
 |   DCHECK_EQ(ref.target, target_map_item.offset + unsafe_idx * target_item_size); | 
 |   image.write<INT>(ref.location, base::checked_cast<INT>(unsafe_idx)); | 
 | } | 
 |  | 
 | // Buffer for ReadDexHeader() to optionally return results. | 
 | struct ReadDexHeaderResults { | 
 |   BufferSource source; | 
 |   const dex::HeaderItem* header; | 
 |   int dex_version; | 
 | }; | 
 |  | 
 | // Returns whether |image| points to a DEX file. If this is a possibility and | 
 | // |opt_results| is not null, then uses it to pass extracted data to enable | 
 | // further parsing. | 
 | bool ReadDexHeader(ConstBufferView image, ReadDexHeaderResults* opt_results) { | 
 |   // This part needs to be fairly efficient since it may be called many times. | 
 |   BufferSource source(image); | 
 |   const dex::HeaderItem* header = source.GetPointer<dex::HeaderItem>(); | 
 |   if (!header) | 
 |     return false; | 
 |   if (header->magic[0] != 'd' || header->magic[1] != 'e' || | 
 |       header->magic[2] != 'x' || header->magic[3] != '\n' || | 
 |       header->magic[7] != '\0') { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Magic matches: More detailed tests can be conducted. | 
 |   int dex_version = 0; | 
 |   for (int i = 4; i < 7; ++i) { | 
 |     if (!isdigit(header->magic[i])) | 
 |       return false; | 
 |     dex_version = dex_version * 10 + (header->magic[i] - '0'); | 
 |   } | 
 |  | 
 |   // Only support DEX versions 35 and 37. | 
 |   // TODO(huangs): Handle version 38. | 
 |   if (dex_version != 35 && dex_version != 37) | 
 |     return false; | 
 |  | 
 |   if (header->file_size > image.size() || | 
 |       header->file_size < sizeof(dex::HeaderItem) || | 
 |       header->map_off < sizeof(dex::HeaderItem)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (opt_results) | 
 |     *opt_results = {source, header, dex_version}; | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | /******** DisassemblerDex ********/ | 
 |  | 
 | DisassemblerDex::DisassemblerDex() : Disassembler(4) {} | 
 |  | 
 | DisassemblerDex::~DisassemblerDex() = default; | 
 |  | 
 | // static. | 
 | bool DisassemblerDex::QuickDetect(ConstBufferView image) { | 
 |   return ReadDexHeader(image, nullptr); | 
 | } | 
 |  | 
 | ExecutableType DisassemblerDex::GetExeType() const { | 
 |   return kExeTypeDex; | 
 | } | 
 |  | 
 | std::string DisassemblerDex::GetExeTypeString() const { | 
 |   return base::StringPrintf("DEX (version %d)", dex_version_); | 
 | } | 
 |  | 
 | std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const { | 
 |   // Must follow DisassemblerDex::ReferenceType order. Initialized on first use. | 
 |   return { | 
 |       {{4, TypeTag(kTypeIdToDescriptorStringId), PoolTag(kStringId)}, | 
 |        &DisassemblerDex::MakeReadTypeIdToDescriptorStringId32, | 
 |        &DisassemblerDex::MakeWriteStringId32}, | 
 |       {{4, TypeTag(kProtoIdToShortyStringId), PoolTag(kStringId)}, | 
 |        &DisassemblerDex::MakeReadProtoIdToShortyStringId32, | 
 |        &DisassemblerDex::MakeWriteStringId32}, | 
 |       {{4, TypeTag(kFieldIdToNameStringId), PoolTag(kStringId)}, | 
 |        &DisassemblerDex::MakeReadFieldToNameStringId32, | 
 |        &DisassemblerDex::MakeWriteStringId32}, | 
 |       {{4, TypeTag(kMethodIdToNameStringId), PoolTag(kStringId)}, | 
 |        &DisassemblerDex::MakeReadMethodIdToNameStringId32, | 
 |        &DisassemblerDex::MakeWriteStringId32}, | 
 |       {{4, TypeTag(kClassDefToSourceFileStringId), PoolTag(kStringId)}, | 
 |        &DisassemblerDex::MakeReadClassDefToSourceFileStringId32, | 
 |        &DisassemblerDex::MakeWriteStringId32}, | 
 |       {{2, TypeTag(kCodeToStringId16), PoolTag(kStringId)}, | 
 |        &DisassemblerDex::MakeReadCodeToStringId16, | 
 |        &DisassemblerDex::MakeWriteStringId16}, | 
 |       {{4, TypeTag(kCodeToStringId32), PoolTag(kStringId)}, | 
 |        &DisassemblerDex::MakeReadCodeToStringId32, | 
 |        &DisassemblerDex::MakeWriteStringId32}, | 
 |       {{4, TypeTag(kProtoIdToReturnTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadProtoIdToReturnTypeId32, | 
 |        &DisassemblerDex::MakeWriteTypeId32}, | 
 |       {{2, TypeTag(kFieldIdToClassTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadFieldToClassTypeId16, | 
 |        &DisassemblerDex::MakeWriteTypeId16}, | 
 |       {{2, TypeTag(kFieldIdToTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadFieldToTypeId16, | 
 |        &DisassemblerDex::MakeWriteTypeId16}, | 
 |       {{2, TypeTag(kMethodIdToClassTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadMethodIdToClassTypeId16, | 
 |        &DisassemblerDex::MakeWriteTypeId16}, | 
 |       {{4, TypeTag(kClassDefToClassTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadClassDefToClassTypeId32, | 
 |        &DisassemblerDex::MakeWriteTypeId32}, | 
 |       {{4, TypeTag(kClassDefToSuperClassTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadClassDefToSuperClassTypeId32, | 
 |        &DisassemblerDex::MakeWriteTypeId32}, | 
 |       {{2, TypeTag(kTypeListToTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadTypeListToTypeId16, | 
 |        &DisassemblerDex::MakeWriteTypeId16}, | 
 |       {{2, TypeTag(kCodeToTypeId), PoolTag(kTypeId)}, | 
 |        &DisassemblerDex::MakeReadCodeToTypeId16, | 
 |        &DisassemblerDex::MakeWriteTypeId16}, | 
 |       {{2, TypeTag(kMethodIdToProtoId), PoolTag(kProtoId)}, | 
 |        &DisassemblerDex::MakeReadMethodIdToProtoId16, | 
 |        &DisassemblerDex::MakeWriteProtoId16}, | 
 |       {{2, TypeTag(kCodeToFieldId), PoolTag(kFieldId)}, | 
 |        &DisassemblerDex::MakeReadCodeToFieldId16, | 
 |        &DisassemblerDex::MakeWriteFieldId16}, | 
 |       {{4, TypeTag(kAnnotationsDirectoryToFieldId), PoolTag(kFieldId)}, | 
 |        &DisassemblerDex::MakeReadAnnotationsDirectoryToFieldId32, | 
 |        &DisassemblerDex::MakeWriteFieldId32}, | 
 |       {{2, TypeTag(kCodeToMethodId), PoolTag(kMethodId)}, | 
 |        &DisassemblerDex::MakeReadCodeToMethodId16, | 
 |        &DisassemblerDex::MakeWriteMethodId16}, | 
 |       {{4, TypeTag(kAnnotationsDirectoryToMethodId), PoolTag(kMethodId)}, | 
 |        &DisassemblerDex::MakeReadAnnotationsDirectoryToMethodId32, | 
 |        &DisassemblerDex::MakeWriteMethodId32}, | 
 |       {{4, TypeTag(kAnnotationsDirectoryToParameterMethodId), | 
 |         PoolTag(kMethodId)}, | 
 |        &DisassemblerDex::MakeReadAnnotationsDirectoryToParameterMethodId32, | 
 |        &DisassemblerDex::MakeWriteMethodId32}, | 
 |       {{4, TypeTag(kProtoIdToParametersTypeList), PoolTag(kTypeList)}, | 
 |        &DisassemblerDex::MakeReadProtoIdToParametersTypeList, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kClassDefToInterfacesTypeList), PoolTag(kTypeList)}, | 
 |        &DisassemblerDex::MakeReadClassDefToInterfacesTypeList, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kAnnotationsDirectoryToParameterAnnotationSetRef), | 
 |         PoolTag(kAnnotationSetRefList)}, | 
 |        &DisassemblerDex:: | 
 |            MakeReadAnnotationsDirectoryToParameterAnnotationSetRef, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kAnnotationSetRefListToAnnotationSet), | 
 |         PoolTag(kAnnotionSet)}, | 
 |        &DisassemblerDex::MakeReadAnnotationSetRefListToAnnotationSet, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kAnnotationsDirectoryToClassAnnotationSet), | 
 |         PoolTag(kAnnotionSet)}, | 
 |        &DisassemblerDex::MakeReadAnnotationsDirectoryToClassAnnotationSet, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kAnnotationsDirectoryToFieldAnnotationSet), | 
 |         PoolTag(kAnnotionSet)}, | 
 |        &DisassemblerDex::MakeReadAnnotationsDirectoryToFieldAnnotationSet, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kAnnotationsDirectoryToMethodAnnotationSet), | 
 |         PoolTag(kAnnotionSet)}, | 
 |        &DisassemblerDex::MakeReadAnnotationsDirectoryToMethodAnnotationSet, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kClassDefToClassData), PoolTag(kClassData)}, | 
 |        &DisassemblerDex::MakeReadClassDefToClassData, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{1, TypeTag(kCodeToRelCode8), PoolTag(kCode)}, | 
 |        &DisassemblerDex::MakeReadCodeToRelCode8, | 
 |        &DisassemblerDex::MakeWriteRelCode8}, | 
 |       {{2, TypeTag(kCodeToRelCode16), PoolTag(kCode)}, | 
 |        &DisassemblerDex::MakeReadCodeToRelCode16, | 
 |        &DisassemblerDex::MakeWriteRelCode16}, | 
 |       {{4, TypeTag(kCodeToRelCode32), PoolTag(kCode)}, | 
 |        &DisassemblerDex::MakeReadCodeToRelCode32, | 
 |        &DisassemblerDex::MakeWriteRelCode32}, | 
 |       {{4, TypeTag(kStringIdToStringData), PoolTag(kStringData)}, | 
 |        &DisassemblerDex::MakeReadStringIdToStringData, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kAnnotationSetToAnnotation), PoolTag(kAnnotation)}, | 
 |        &DisassemblerDex::MakeReadAnnotationSetToAnnotation, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kClassDefToStaticValuesEncodedArray), | 
 |         PoolTag(kEncodedArray)}, | 
 |        &DisassemblerDex::MakeReadClassDefToStaticValuesEncodedArray, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |       {{4, TypeTag(kClassDefToAnnotationDirectory), | 
 |         PoolTag(kAnnotationsDirectory)}, | 
 |        &DisassemblerDex::MakeReadClassDefToAnnotationDirectory, | 
 |        &DisassemblerDex::MakeWriteAbs32}, | 
 |   }; | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadStringIdToStringData( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   // dex::StringIdItem::string_data_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, string_map_item_, sizeof(dex::StringIdItem), | 
 |       offsetof(dex::StringIdItem, string_data_off), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadTypeIdToDescriptorStringId32(offset_t lo, | 
 |                                                       offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::TypeIdItem::descriptor_idx)>, image_, | 
 |       string_map_item_, sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, type_map_item_, sizeof(dex::TypeIdItem), | 
 |       offsetof(dex::TypeIdItem, descriptor_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadProtoIdToShortyStringId32(offset_t lo, offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::ProtoIdItem::shorty_idx)>, image_, | 
 |       string_map_item_, sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, proto_map_item_, sizeof(dex::ProtoIdItem), | 
 |       offsetof(dex::ProtoIdItem, shorty_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadProtoIdToReturnTypeId32(offset_t lo, offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::ProtoIdItem::return_type_idx)>, image_, | 
 |       type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, proto_map_item_, sizeof(dex::ProtoIdItem), | 
 |       offsetof(dex::ProtoIdItem, return_type_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadProtoIdToParametersTypeList(offset_t lo, offset_t hi) { | 
 |   // dex::ProtoIdItem::parameters_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, proto_map_item_, sizeof(dex::ProtoIdItem), | 
 |       offsetof(dex::ProtoIdItem, parameters_off), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadFieldToClassTypeId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::FieldIdItem::class_idx)>, image_, | 
 |       type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, field_map_item_, sizeof(dex::FieldIdItem), | 
 |       offsetof(dex::FieldIdItem, class_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadFieldToTypeId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto mapper = | 
 |       base::BindRepeating(ReadTargetIndex<decltype(dex::FieldIdItem::type_idx)>, | 
 |                           image_, type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, field_map_item_, sizeof(dex::FieldIdItem), | 
 |       offsetof(dex::FieldIdItem, type_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadFieldToNameStringId32( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto mapper = | 
 |       base::BindRepeating(ReadTargetIndex<decltype(dex::FieldIdItem::name_idx)>, | 
 |                           image_, string_map_item_, sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, field_map_item_, sizeof(dex::FieldIdItem), | 
 |       offsetof(dex::FieldIdItem, name_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadMethodIdToClassTypeId16(offset_t lo, offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::MethodIdItem::class_idx)>, image_, | 
 |       type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, method_map_item_, sizeof(dex::MethodIdItem), | 
 |       offsetof(dex::MethodIdItem, class_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadMethodIdToProtoId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::MethodIdItem::proto_idx)>, image_, | 
 |       proto_map_item_, sizeof(dex::ProtoIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, method_map_item_, sizeof(dex::MethodIdItem), | 
 |       offsetof(dex::MethodIdItem, proto_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadMethodIdToNameStringId32(offset_t lo, offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::MethodIdItem::name_idx)>, image_, | 
 |       string_map_item_, sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, method_map_item_, sizeof(dex::MethodIdItem), | 
 |       offsetof(dex::MethodIdItem, name_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadClassDefToClassTypeId32(offset_t lo, offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::ClassDefItem::superclass_idx)>, image_, | 
 |       type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem), | 
 |       offsetof(dex::ClassDefItem, class_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadClassDefToSuperClassTypeId32(offset_t lo, | 
 |                                                       offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::ClassDefItem::superclass_idx)>, image_, | 
 |       type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem), | 
 |       offsetof(dex::ClassDefItem, superclass_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadClassDefToInterfacesTypeList(offset_t lo, | 
 |                                                       offset_t hi) { | 
 |   // dex::ClassDefItem::interfaces_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem), | 
 |       offsetof(dex::ClassDefItem, interfaces_off), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadClassDefToSourceFileStringId32(offset_t lo, | 
 |                                                         offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::ClassDefItem::source_file_idx)>, image_, | 
 |       string_map_item_, sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem), | 
 |       offsetof(dex::ClassDefItem, source_file_idx), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadClassDefToAnnotationDirectory(offset_t lo, | 
 |                                                        offset_t hi) { | 
 |   // dex::ClassDefItem::annotations_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem), | 
 |       offsetof(dex::ClassDefItem, annotations_off), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadClassDefToClassData( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   // dex::ClassDefItem::class_data_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem), | 
 |       offsetof(dex::ClassDefItem, class_data_off), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadClassDefToStaticValuesEncodedArray(offset_t lo, | 
 |                                                             offset_t hi) { | 
 |   // dex::ClassDefItem::static_values_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<ItemReferenceReader>( | 
 |       lo, hi, class_def_map_item_, sizeof(dex::ClassDefItem), | 
 |       offsetof(dex::ClassDefItem, static_values_off), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadTypeListToTypeId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto mapper = | 
 |       base::BindRepeating(ReadTargetIndex<decltype(dex::TypeItem::type_idx)>, | 
 |                           image_, type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::TypeItem, type_idx), type_list_offsets_, | 
 |       std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationSetToAnnotation(offset_t lo, offset_t hi) { | 
 |   // dex::AnnotationOffItem::annotation_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::AnnotationOffItem, annotation_off), | 
 |       annotation_set_offsets_, std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationSetRefListToAnnotationSet(offset_t lo, | 
 |                                                              offset_t hi) { | 
 |   // dex::AnnotationSetRefItem::annotations_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::AnnotationSetRefItem, annotations_off), | 
 |       annotation_set_ref_list_offsets_, std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationsDirectoryToClassAnnotationSet(offset_t lo, | 
 |                                                                   offset_t hi) { | 
 |   // dex::AnnotationsDirectoryItem::class_annotations_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::AnnotationsDirectoryItem, class_annotations_off), | 
 |       annotations_directory_item_offsets_, std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationsDirectoryToFieldId32(offset_t lo, | 
 |                                                          offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::FieldAnnotation::field_idx)>, image_, | 
 |       field_map_item_, sizeof(dex::FieldIdItem)); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::FieldAnnotation, field_idx), | 
 |       annotations_directory_item_field_annotation_offsets_, std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationsDirectoryToFieldAnnotationSet(offset_t lo, | 
 |                                                                   offset_t hi) { | 
 |   // dex::FieldAnnotation::annotations_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::FieldAnnotation, annotations_off), | 
 |       annotations_directory_item_field_annotation_offsets_, std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationsDirectoryToMethodId32(offset_t lo, | 
 |                                                           offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::MethodAnnotation::method_idx)>, image_, | 
 |       method_map_item_, sizeof(dex::MethodIdItem)); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::MethodAnnotation, method_idx), | 
 |       annotations_directory_item_method_annotation_offsets_, std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationsDirectoryToMethodAnnotationSet( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   // dex::MethodAnnotation::annotations_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::MethodAnnotation, annotations_off), | 
 |       annotations_directory_item_method_annotation_offsets_, std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationsDirectoryToParameterMethodId32( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto mapper = base::BindRepeating( | 
 |       ReadTargetIndex<decltype(dex::ParameterAnnotation::method_idx)>, image_, | 
 |       method_map_item_, sizeof(dex::MethodIdItem)); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::ParameterAnnotation, method_idx), | 
 |       annotations_directory_item_parameter_annotation_offsets_, | 
 |       std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> | 
 | DisassemblerDex::MakeReadAnnotationsDirectoryToParameterAnnotationSetRef( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   // dex::ParameterAnnotation::annotations_off mapper. | 
 |   auto mapper = base::BindRepeating(ReadTargetOffset32, image_); | 
 |   return std::make_unique<CachedItemListReferenceReader>( | 
 |       lo, hi, offsetof(dex::ParameterAnnotation, annotations_off), | 
 |       annotations_directory_item_parameter_annotation_offsets_, | 
 |       std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToStringId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::c && | 
 |             (value.instr->opcode == 0x1A)) {  // const-string | 
 |           // BBBB from e.g., const-string vAA, string@BBBB. | 
 |           return value.instr_offset + 2; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = | 
 |       base::BindRepeating(ReadTargetIndex<uint16_t>, image_, string_map_item_, | 
 |                           sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToStringId32( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::c && | 
 |             (value.instr->opcode == 0x1B)) {  // const-string/jumbo | 
 |           // BBBBBBBB from e.g., const-string/jumbo vAA, string@BBBBBBBB. | 
 |           return value.instr_offset + 2; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = | 
 |       base::BindRepeating(ReadTargetIndex<uint32_t>, image_, string_map_item_, | 
 |                           sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToTypeId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::c && | 
 |             (value.instr->opcode == 0x1C ||   // const-class | 
 |              value.instr->opcode == 0x1F ||   // check-cast | 
 |              value.instr->opcode == 0x20 ||   // instance-of | 
 |              value.instr->opcode == 0x22 ||   // new-instance | 
 |              value.instr->opcode == 0x23 ||   // new-array | 
 |              value.instr->opcode == 0x24 ||   // filled-new-array | 
 |              value.instr->opcode == 0x25)) {  // filled-new-array/range | 
 |           // BBBB from e.g., const-class vAA, type@BBBB. | 
 |           return value.instr_offset + 2; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = base::BindRepeating(ReadTargetIndex<uint16_t>, image_, | 
 |                                     type_map_item_, sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToFieldId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::c && | 
 |             (value.instr->opcode == 0x52 ||   // iinstanceop (iget-*, iput-*) | 
 |              value.instr->opcode == 0x60)) {  // sstaticop (sget-*, sput-*) | 
 |           // CCCC from e.g., iget vA, vB, field@CCCC. | 
 |           return value.instr_offset + 2; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = base::BindRepeating(ReadTargetIndex<uint16_t>, image_, | 
 |                                     field_map_item_, sizeof(dex::FieldIdItem)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToMethodId16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::c && | 
 |             (value.instr->opcode == 0x6E ||   // invoke-kind | 
 |              value.instr->opcode == 0x74)) {  // invoke-kind/range | 
 |           // BBBB from e.g., invoke-virtual {vC, vD, vE, vF, vG}, meth@BBBB. | 
 |           return value.instr_offset + 2; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = | 
 |       base::BindRepeating(ReadTargetIndex<uint16_t>, image_, method_map_item_, | 
 |                           sizeof(dex::MethodIdItem)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToRelCode8( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::t && | 
 |             value.instr->opcode == 0x28) {  // goto | 
 |           // +AA from e.g., goto +AA. | 
 |           return value.instr_offset + 1; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = base::BindRepeating( | 
 |       [](DisassemblerDex* dis, offset_t location) { | 
 |         // Address is relative to the current instruction, which begins 1 unit | 
 |         // before |location|. This needs to be subtracted out. Also, store as | 
 |         // int32_t so |unsafe_delta - 1| won't underflow! | 
 |         int32_t unsafe_delta = dis->image_.read<int8_t>(location); | 
 |         offset_t unsafe_target = static_cast<offset_t>( | 
 |             location + (unsafe_delta - 1) * kInstrUnitSize); | 
 |         // TODO(huangs): Check that |unsafe_target| stays within code item. | 
 |         return unsafe_target; | 
 |       }, | 
 |       base::Unretained(this)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToRelCode16( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::t && | 
 |             (value.instr->opcode == 0x29 ||   // goto/16 | 
 |              value.instr->opcode == 0x32 ||   // if-test | 
 |              value.instr->opcode == 0x38)) {  // if-testz | 
 |           // +AAAA from e.g., goto/16 +AAAA. | 
 |           return value.instr_offset + 2; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = base::BindRepeating( | 
 |       [](DisassemblerDex* dis, offset_t location) { | 
 |         // Address is relative to the current instruction, which begins 1 unit | 
 |         // before |location|. This needs to be subtracted out. Also, store as | 
 |         // int32_t so |unsafe_delta - 1| won't underflow! | 
 |         int32_t unsafe_delta = dis->image_.read<int16_t>(location); | 
 |         offset_t unsafe_target = static_cast<offset_t>( | 
 |             location + (unsafe_delta - 1) * kInstrUnitSize); | 
 |         // TODO(huangs): Check that |unsafe_target| stays within code item. | 
 |         return unsafe_target; | 
 |       }, | 
 |       base::Unretained(this)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToRelCode32( | 
 |     offset_t lo, | 
 |     offset_t hi) { | 
 |   auto filter = base::BindRepeating( | 
 |       [](const InstructionParser::Value& value) -> offset_t { | 
 |         if (value.instr->format == dex::FormatId::t && | 
 |             (value.instr->opcode == 0x26 ||   // fill-array-data | 
 |              value.instr->opcode == 0x2A ||   // goto/32 | 
 |              value.instr->opcode == 0x2B ||   // packed-switch | 
 |              value.instr->opcode == 0x2C)) {  // sparse-switch | 
 |           // +BBBBBBBB from e.g., fill-array-data vAA, +BBBBBBBB. | 
 |           // +AAAAAAAA from e.g., goto/32 +AAAAAAAA. | 
 |           return value.instr_offset + 2; | 
 |         } | 
 |         return kInvalidOffset; | 
 |       }); | 
 |   auto mapper = base::BindRepeating( | 
 |       [](DisassemblerDex* dis, offset_t location) { | 
 |         // Address is relative to the current instruction, which begins 1 unit | 
 |         // before |location|. This needs to be subtracted out. Use int64_t to | 
 |         // avoid underflow and overflow. | 
 |         int64_t unsafe_delta = dis->image_.read<int32_t>(location); | 
 |         int64_t unsafe_target = location + (unsafe_delta - 1) * kInstrUnitSize; | 
 |  | 
 |         // TODO(huangs): Check that |unsafe_target| stays within code item. | 
 |         offset_t checked_unsafe_target = | 
 |             static_cast<offset_t>(base::CheckedNumeric<offset_t>(unsafe_target) | 
 |                                       .ValueOrDefault(kInvalidOffset)); | 
 |         return checked_unsafe_target < kOffsetBound ? checked_unsafe_target | 
 |                                                     : kInvalidOffset; | 
 |       }, | 
 |       base::Unretained(this)); | 
 |   return std::make_unique<InstructionReferenceReader>( | 
 |       image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteStringId16( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating( | 
 |       WriteTargetIndex<uint16_t>, string_map_item_, sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteStringId32( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating( | 
 |       WriteTargetIndex<uint32_t>, string_map_item_, sizeof(dex::StringIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteTypeId16( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating(WriteTargetIndex<uint16_t>, type_map_item_, | 
 |                                     sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteTypeId32( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating(WriteTargetIndex<uint32_t>, type_map_item_, | 
 |                                     sizeof(dex::TypeIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteProtoId16( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating(WriteTargetIndex<uint16_t>, proto_map_item_, | 
 |                                     sizeof(dex::ProtoIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteFieldId16( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating(WriteTargetIndex<uint16_t>, field_map_item_, | 
 |                                     sizeof(dex::FieldIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteFieldId32( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating(WriteTargetIndex<uint32_t>, field_map_item_, | 
 |                                     sizeof(dex::FieldIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteMethodId16( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating( | 
 |       WriteTargetIndex<uint16_t>, method_map_item_, sizeof(dex::MethodIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteMethodId32( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating( | 
 |       WriteTargetIndex<uint32_t>, method_map_item_, sizeof(dex::MethodIdItem)); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteRelCode8( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating([](Reference ref, MutableBufferView image) { | 
 |     ptrdiff_t unsafe_byte_diff = | 
 |         static_cast<ptrdiff_t>(ref.target) - ref.location; | 
 |     DCHECK_EQ(0, unsafe_byte_diff % kInstrUnitSize); | 
 |     // |delta| is relative to start of instruction, which is 1 unit before | 
 |     // |ref.location|. The subtraction above removed too much, so +1 to fix. | 
 |     base::CheckedNumeric<int8_t> delta((unsafe_byte_diff / kInstrUnitSize) + 1); | 
 |     if (!delta.IsValid()) { | 
 |       LOG(ERROR) << "Invalid reference at: " << AsHex<8>(ref.location) << "."; | 
 |       return; | 
 |     } | 
 |     image.write<int8_t>(ref.location, delta.ValueOrDie()); | 
 |   }); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteRelCode16( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating([](Reference ref, MutableBufferView image) { | 
 |     ptrdiff_t unsafe_byte_diff = | 
 |         static_cast<ptrdiff_t>(ref.target) - ref.location; | 
 |     DCHECK_EQ(0, unsafe_byte_diff % kInstrUnitSize); | 
 |     // |delta| is relative to start of instruction, which is 1 unit before | 
 |     // |ref.location|. The subtraction above removed too much, so +1 to fix. | 
 |     base::CheckedNumeric<int16_t> delta((unsafe_byte_diff / kInstrUnitSize) + | 
 |                                         1); | 
 |     if (!delta.IsValid()) { | 
 |       LOG(ERROR) << "Invalid reference at: " << AsHex<8>(ref.location) << "."; | 
 |       return; | 
 |     } | 
 |     image.write<int16_t>(ref.location, delta.ValueOrDie()); | 
 |   }); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteRelCode32( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating([](Reference ref, MutableBufferView image) { | 
 |     ptrdiff_t unsafe_byte_diff = | 
 |         static_cast<ptrdiff_t>(ref.target) - ref.location; | 
 |     DCHECK_EQ(0, unsafe_byte_diff % kInstrUnitSize); | 
 |     // |delta| is relative to start of instruction, which is 1 unit before | 
 |     // |ref.location|. The subtraction above removed too much, so +1 to fix. | 
 |     base::CheckedNumeric<int32_t> delta((unsafe_byte_diff / kInstrUnitSize) + | 
 |                                         1); | 
 |     if (!delta.IsValid()) { | 
 |       LOG(ERROR) << "Invalid reference at: " << AsHex<8>(ref.location) << "."; | 
 |       return; | 
 |     } | 
 |     image.write<int32_t>(ref.location, delta.ValueOrDie()); | 
 |   }); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteAbs32( | 
 |     MutableBufferView image) { | 
 |   auto writer = base::BindRepeating([](Reference ref, MutableBufferView image) { | 
 |     image.write<uint32_t>(ref.location, ref.target); | 
 |   }); | 
 |   return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer)); | 
 | } | 
 |  | 
 | bool DisassemblerDex::Parse(ConstBufferView image) { | 
 |   image_ = image; | 
 |   return ParseHeader(); | 
 | } | 
 |  | 
 | bool DisassemblerDex::ParseHeader() { | 
 |   ReadDexHeaderResults results; | 
 |   if (!ReadDexHeader(image_, &results)) | 
 |     return false; | 
 |  | 
 |   header_ = results.header; | 
 |   dex_version_ = results.dex_version; | 
 |   BufferSource source = results.source; | 
 |  | 
 |   // DEX header contains file size, so use it to resize |image_| right away. | 
 |   image_.shrink(header_->file_size); | 
 |  | 
 |   // Read map list. This is not a fixed-size array, so instead of reading | 
 |   // MapList directly, read |MapList::size| first, then visit elements in | 
 |   // |MapList::list|. | 
 |   static_assert( | 
 |       offsetof(dex::MapList, list) == sizeof(decltype(dex::MapList::size)), | 
 |       "MapList size error."); | 
 |   source = std::move(BufferSource(image_).Skip(header_->map_off)); | 
 |   decltype(dex::MapList::size) list_size = 0; | 
 |   if (!source.GetValue(&list_size) || list_size > dex::kMaxItemListSize) | 
 |     return false; | 
 |   const auto* item_list = source.GetArray<const dex::MapItem>(list_size); | 
 |   if (!item_list) | 
 |     return false; | 
 |  | 
 |   // Read and validate map list, ensuring that required item types are present. | 
 |   // - GetItemBaseSize() should have an entry for each item. | 
 |   // - dex::kTypeCodeItem is actually not required; it's possible to have a DEX | 
 |   //   file with classes that have no code. However, this is unlikely to appear | 
 |   //   in application, so for simplicity we require DEX files to have code. | 
 |   std::set<uint16_t> required_item_types = { | 
 |       dex::kTypeStringIdItem, dex::kTypeTypeIdItem,   dex::kTypeProtoIdItem, | 
 |       dex::kTypeFieldIdItem,  dex::kTypeMethodIdItem, dex::kTypeClassDefItem, | 
 |       dex::kTypeTypeList,     dex::kTypeCodeItem, | 
 |   }; | 
 |   for (offset_t i = 0; i < list_size; ++i) { | 
 |     const dex::MapItem* item = &item_list[i]; | 
 |     // Reject unreasonably large |item->size|. | 
 |     size_t item_size = GetItemBaseSize(item->type); | 
 |     // Confusing name: |item->size| is actually the number of items. | 
 |     if (!image_.covers_array(item->offset, item->size, item_size)) | 
 |       return false; | 
 |     if (!map_item_map_.insert(std::make_pair(item->type, item)).second) | 
 |       return false;  // A given type must appear at most once. | 
 |     required_item_types.erase(item->type); | 
 |   } | 
 |   // TODO(huangs): Replace this with guards throughout file. | 
 |   if (!required_item_types.empty()) | 
 |     return false; | 
 |  | 
 |   // Make local copies of main map items. | 
 |   string_map_item_ = *map_item_map_[dex::kTypeStringIdItem]; | 
 |   type_map_item_ = *map_item_map_[dex::kTypeTypeIdItem]; | 
 |   proto_map_item_ = *map_item_map_[dex::kTypeProtoIdItem]; | 
 |   field_map_item_ = *map_item_map_[dex::kTypeFieldIdItem]; | 
 |   method_map_item_ = *map_item_map_[dex::kTypeMethodIdItem]; | 
 |   class_def_map_item_ = *map_item_map_[dex::kTypeClassDefItem]; | 
 |   type_list_map_item_ = *map_item_map_[dex::kTypeTypeList]; | 
 |   code_map_item_ = *map_item_map_[dex::kTypeCodeItem]; | 
 |  | 
 |   // The following types are optional and may not be present in every DEX file. | 
 |   if (map_item_map_.count(dex::kTypeAnnotationSetRefList)) { | 
 |     annotation_set_ref_list_map_item_ = | 
 |         *map_item_map_[dex::kTypeAnnotationSetRefList]; | 
 |   } | 
 |   if (map_item_map_.count(dex::kTypeAnnotationSetItem)) | 
 |     annotation_set_map_item_ = *map_item_map_[dex::kTypeAnnotationSetItem]; | 
 |   if (map_item_map_.count(dex::kTypeAnnotationsDirectoryItem)) { | 
 |     annotations_directory_map_item_ = | 
 |         *map_item_map_[dex::kTypeAnnotationsDirectoryItem]; | 
 |   } | 
 |  | 
 |   // Iteratively parse variable length lists, annotations directory items, and | 
 |   // code items blocks. Any failure would indicate invalid DEX. Success | 
 |   // indicates that no structural problem is found. However, contained | 
 |   // references data read from parsed items still require validation. | 
 |   if (!(ParseItemOffsets(image_, type_list_map_item_, sizeof(dex::TypeItem), | 
 |                          &type_list_offsets_) && | 
 |         ParseItemOffsets(image_, annotation_set_ref_list_map_item_, | 
 |                          sizeof(dex::AnnotationSetRefItem), | 
 |                          &annotation_set_ref_list_offsets_) && | 
 |         ParseItemOffsets(image_, annotation_set_map_item_, | 
 |                          sizeof(dex::AnnotationOffItem), | 
 |                          &annotation_set_offsets_) && | 
 |         ParseAnnotationsDirectoryItems( | 
 |             image_, annotations_directory_map_item_, | 
 |             &annotations_directory_item_offsets_, | 
 |             &annotations_directory_item_field_annotation_offsets_, | 
 |             &annotations_directory_item_method_annotation_offsets_, | 
 |             &annotations_directory_item_parameter_annotation_offsets_))) { | 
 |     return false; | 
 |   } | 
 |   CodeItemParser code_item_parser(image_); | 
 |   if (!code_item_parser.Init(code_map_item_)) | 
 |     return false; | 
 |   code_item_offsets_.resize(code_map_item_.size); | 
 |   for (size_t i = 0; i < code_map_item_.size; ++i) { | 
 |     const offset_t code_item_offset = code_item_parser.GetNext(); | 
 |     if (code_item_offset == kInvalidOffset) | 
 |       return false; | 
 |     code_item_offsets_[i] = code_item_offset; | 
 |   } | 
 |   // DEX files are required to have parsable code items. | 
 |   return !code_item_offsets_.empty(); | 
 | } | 
 |  | 
 | }  // namespace zucchini |