aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorckitagawa <ckitagawa@chromium.org>2021-09-03 15:48:28 +0000
committerCopybara-Service <copybara-worker@google.com>2021-09-03 08:59:53 -0700
commit26518ffbdf042c4c35617be2446dac49590a89c2 (patch)
tree1fc7aa788fd12474c4df6bd18b47461cd93e6023
parentf137bf4b5542b966abc4c08762c5e60b21913f4d (diff)
downloadzucchini-26518ffbdf042c4c35617be2446dac49590a89c2.tar.gz
[Zucchini] DEX Version 38 Support
DEX Version 38 added: * CallSiteId & CallSite items * MethodHandle items * invoke-polymorphic containing meth@BBBB and proto@HHHH references * invoke-custom containing a call_site@BBBB reference This CL: * Adds CallSiteIdToCallSite * Adds MethodHandleTo{MethodId, FieldId} * Adds CodeToProtoId16 for invoke-polymorphic * Adds CodeToCallSiteId16 and WriteCallSiteId16 for invoke-custom * Updates CodeToMethodId16 for invoke-polymorphic Fuzzed about 1 million iterations locally and uploaded new samples to the clusterfuzz bucket. 97% coverage. Manually tested on hand-written dex files using smali as well as the dexdump test corpus. Bug: 1231885 Change-Id: Icd885be2cfd433d0befe689d16c4a1e99573ca6c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3060745 Reviewed-by: Samuel Huang <huangs@chromium.org> Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org> Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org> Cr-Commit-Position: refs/heads/main@{#918119} NOKEYCHECK=True GitOrigin-RevId: 9cc600ef0b60ff1ec76683a2bfb98a6bdbb05d1e
-rw-r--r--disassembler_dex.cc244
-rw-r--r--disassembler_dex.h37
-rw-r--r--testdata/all.smali628
-rw-r--r--testdata/const-method-handle-min.smali14
-rw-r--r--testdata/invoke-custom-min.smali39
-rw-r--r--testdata/invoke-polymorphic.smali70
-rw-r--r--type_dex.h83
7 files changed, 1052 insertions, 63 deletions
diff --git a/disassembler_dex.cc b/disassembler_dex.cc
index 5b25c50..74c5e69 100644
--- a/disassembler_dex.cc
+++ b/disassembler_dex.cc
@@ -69,6 +69,10 @@ size_t GetItemBaseSize(uint16_t type_item_code) {
return sizeof(dex::MethodIdItem);
case dex::kTypeClassDefItem:
return sizeof(dex::ClassDefItem);
+ case dex::kTypeCallSiteIdItem:
+ return sizeof(dex::CallSiteIdItem);
+ case dex::kTypeMethodHandleItem:
+ return sizeof(dex::MethodHandleItem);
// No need to handle dex::kTypeMapList.
case dex::kTypeTypeList:
return sizeof(uint32_t); // Variable-length.
@@ -416,17 +420,23 @@ class ItemReferenceReader : public ReferenceReader {
// |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.
+ // |rel_item_offset| is the offset to use relative to |item_offset| in cases
+ // where a value other than |rel_location| is required. For an example of this
+ // see ReadMethodHandleFieldOrMethodId.
ItemReferenceReader(offset_t lo,
offset_t hi,
const dex::MapItem& map_item,
size_t item_size,
size_t rel_location,
- Mapper&& mapper)
+ Mapper&& mapper,
+ bool mapper_wants_item = false)
: 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_input_delta_(
+ mapper_wants_item ? 0 : 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.");
@@ -457,7 +467,11 @@ class ItemReferenceReader : public ReferenceReader {
// |reference_width| is unneeded.
if (location >= hi_)
break;
- const offset_t target = mapper_.Run(location);
+
+ // |location == item_offset + mapper_input_delta_| in the majority of
+ // cases. The exception is when |mapper_| wants an item aligned location
+ // instead e.g. ReadMethodHandleFieldOrMethodId.
+ const offset_t target = mapper_.Run(item_offset + mapper_input_delta_);
// kDexSentinelOffset (0) may appear for the following:
// - ProtoIdItem: parameters_off.
@@ -467,6 +481,9 @@ class ItemReferenceReader : public ReferenceReader {
// - AnnotationSetRefItem: annotations_off.
// kDexSentinelIndexAsOffset (0xFFFFFFFF) may appear for the following:
// - ClassDefItem: superclass_idx, source_file_idx.
+ // - MethodHandleItem: |mapper_| uses ReadMethodHandleFieldOrMethodId and
+ // determines the item at |cur_idx_| is not of the required reference
+ // type.
if (target == kDexSentinelOffset || target == kDexSentinelIndexAsOffset) {
++cur_idx_;
continue;
@@ -492,6 +509,7 @@ class ItemReferenceReader : public ReferenceReader {
const uint32_t num_items_;
const uint32_t item_size_;
const uint32_t rel_location_;
+ const uint32_t mapper_input_delta_;
const Mapper mapper_;
offset_t cur_idx_ = 0;
};
@@ -688,6 +706,53 @@ static offset_t ReadTargetIndex(ConstBufferView image,
base::checked_cast<offset_t>(unsafe_idx * target_item_size);
}
+// Reads a field or method index of the MethodHandleItem located at |location|
+// in |image| and translates |method_handle_item.field_or_method_id| to the
+// offset of a fixed-size item specified by |target_map_item| and
+// |target_item_size|. The index is deemed to be of the correct target type if
+// |method_handle_item.method_handle_type| falls within the range [|min_type|,
+// |max_type|]. If the target type is correct ReadTargetIndex is called.
+// Returns the target offset if valid, or kDexSentinelIndexAsOffset if
+// |method_handle_item.method_handle_type| is of the wrong type, or
+// kInvalidOffset otherwise.
+//
+// As of DEX version 39 MethodHandleType values for FieldId and MethodId each
+// form one consecutive block of values. If this changes, then the interface to
+// this function will need to be redesigned.
+static offset_t ReadMethodHandleFieldOrMethodId(
+ ConstBufferView image,
+ const dex::MapItem& target_map_item,
+ size_t target_item_size,
+ dex::MethodHandleType min_type,
+ dex::MethodHandleType max_type,
+ offset_t location) {
+ dex::MethodHandleItem method_handle_item =
+ image.read<dex::MethodHandleItem>(location);
+
+ // Cannot use base::checked_cast as dex::MethodHandleType is an enum class so
+ // static_assert on the size instead.
+ static_assert(sizeof(decltype(dex::MethodHandleItem::method_handle_type)) <=
+ sizeof(dex::MethodHandleType),
+ "dex::MethodHandleItem::method_handle_type may not fit into "
+ "dex::MethodHandleType.");
+ dex::MethodHandleType method_handle_type =
+ static_cast<dex::MethodHandleType>(method_handle_item.method_handle_type);
+
+ if (method_handle_type >= dex::MethodHandleType::kMaxMethodHandleType) {
+ return kInvalidOffset;
+ }
+
+ // Use DexSentinelIndexAsOffset to skip the item as it isn't of the
+ // corresponding method handle type.
+ if (method_handle_type < min_type || method_handle_type > max_type) {
+ return kDexSentinelIndexAsOffset;
+ }
+
+ return ReadTargetIndex<decltype(dex::MethodHandleItem::field_or_method_id)>(
+ image, target_map_item, target_item_size,
+ location + offsetof(dex::MethodHandleItem, field_or_method_id));
+}
+
// 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
@@ -779,9 +844,9 @@ bool ReadDexHeader(ConstBufferView image, ReadDexHeaderResults* opt_results) {
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)
+ // Only support DEX versions 35, 37, and 38.
+ // TODO(ckitagawa): Handle version 39.
+ if (dex_version != 35 && dex_version != 37 && dex_version != 38)
return false;
if (header->file_size > image.size() ||
@@ -864,18 +929,27 @@ std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const {
{{2, TypeTag(kCodeToTypeId), PoolTag(kTypeId)},
&DisassemblerDex::MakeReadCodeToTypeId16,
&DisassemblerDex::MakeWriteTypeId16},
+ {{2, TypeTag(kCodeToProtoId), PoolTag(kProtoId)},
+ &DisassemblerDex::MakeReadCodeToProtoId16,
+ &DisassemblerDex::MakeWriteProtoId16},
{{2, TypeTag(kMethodIdToProtoId), PoolTag(kProtoId)},
&DisassemblerDex::MakeReadMethodIdToProtoId16,
&DisassemblerDex::MakeWriteProtoId16},
{{2, TypeTag(kCodeToFieldId), PoolTag(kFieldId)},
&DisassemblerDex::MakeReadCodeToFieldId16,
&DisassemblerDex::MakeWriteFieldId16},
+ {{2, TypeTag(kMethodHandleToFieldId), PoolTag(kFieldId)},
+ &DisassemblerDex::MakeReadMethodHandleToFieldId16,
+ &DisassemblerDex::MakeWriteFieldId16},
{{4, TypeTag(kAnnotationsDirectoryToFieldId), PoolTag(kFieldId)},
&DisassemblerDex::MakeReadAnnotationsDirectoryToFieldId32,
&DisassemblerDex::MakeWriteFieldId32},
{{2, TypeTag(kCodeToMethodId), PoolTag(kMethodId)},
&DisassemblerDex::MakeReadCodeToMethodId16,
&DisassemblerDex::MakeWriteMethodId16},
+ {{2, TypeTag(kMethodHandleToMethodId), PoolTag(kMethodId)},
+ &DisassemblerDex::MakeReadMethodHandleToMethodId16,
+ &DisassemblerDex::MakeWriteMethodId16},
{{4, TypeTag(kAnnotationsDirectoryToMethodId), PoolTag(kMethodId)},
&DisassemblerDex::MakeReadAnnotationsDirectoryToMethodId32,
&DisassemblerDex::MakeWriteMethodId32},
@@ -883,6 +957,9 @@ std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const {
PoolTag(kMethodId)},
&DisassemblerDex::MakeReadAnnotationsDirectoryToParameterMethodId32,
&DisassemblerDex::MakeWriteMethodId32},
+ {{2, TypeTag(kCodeToCallSiteId), PoolTag(kCallSiteId)},
+ &DisassemblerDex::MakeReadCodeToCallSiteId16,
+ &DisassemblerDex::MakeWriteCallSiteId16},
{{4, TypeTag(kProtoIdToParametersTypeList), PoolTag(kTypeList)},
&DisassemblerDex::MakeReadProtoIdToParametersTypeList,
&DisassemblerDex::MakeWriteAbs32},
@@ -936,6 +1013,9 @@ std::vector<ReferenceGroup> DisassemblerDex::MakeReferenceGroups() const {
PoolTag(kAnnotationsDirectory)},
&DisassemblerDex::MakeReadClassDefToAnnotationDirectory,
&DisassemblerDex::MakeWriteAbs32},
+ {{4, TypeTag(kCallSiteIdToCallSite), PoolTag(kCallSite)},
+ &DisassemblerDex::MakeReadCallSiteIdToCallSite32,
+ &DisassemblerDex::MakeWriteAbs32},
};
}
@@ -1125,6 +1205,46 @@ DisassemblerDex::MakeReadClassDefToStaticValuesEncodedArray(offset_t lo,
offsetof(dex::ClassDefItem, static_values_off), std::move(mapper));
}
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadCallSiteIdToCallSite32(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(ReadTargetOffset32, image_);
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, call_site_map_item_, sizeof(dex::CallSiteIdItem),
+ offsetof(dex::CallSiteIdItem, call_site_off), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadMethodHandleToFieldId16(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(ReadMethodHandleFieldOrMethodId, image_,
+ field_map_item_, sizeof(dex::FieldIdItem),
+ dex::MethodHandleType::kStaticPut,
+ dex::MethodHandleType::kInstanceGet);
+ // Use |mapper_wants_item == true| for ItemReferenceReader such that
+ // |location| is aligned with MethodHandleItem when passed to |mapper|. This
+ // allows ReadMethodHandleFieldOrMethodId to safely determine whether the
+ // reference in the MethodHandleItem is of the correct type to be emitted.
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, method_handle_map_item_, sizeof(dex::MethodHandleItem),
+ offsetof(dex::MethodHandleItem, field_or_method_id), std::move(mapper),
+ /*mapper_wants_item=*/true);
+}
+
+std::unique_ptr<ReferenceReader>
+DisassemblerDex::MakeReadMethodHandleToMethodId16(offset_t lo, offset_t hi) {
+ auto mapper = base::BindRepeating(ReadMethodHandleFieldOrMethodId, image_,
+ method_map_item_, sizeof(dex::MethodIdItem),
+ dex::MethodHandleType::kInvokeStatic,
+ dex::MethodHandleType::kInvokeInterface);
+ // Use |mapper_wants_item == true| for ItemReferenceReader such that
+ // |location| is aligned with MethodHandleItem when passed to |mapper|. This
+ // allows ReadMethodHandleFieldOrMethodId to safely determine whether the
+ // reference in the MethodHandleItem is of the correct type to be emitted.
+ return std::make_unique<ItemReferenceReader>(
+ lo, hi, method_handle_map_item_, sizeof(dex::MethodHandleItem),
+ offsetof(dex::MethodHandleItem, field_or_method_id), std::move(mapper),
+ /*mapper_wants_item=*/true);
+}
+
std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadTypeListToTypeId16(
offset_t lo,
offset_t hi) {
@@ -1233,6 +1353,10 @@ DisassemblerDex::MakeReadAnnotationsDirectoryToParameterAnnotationSetRef(
std::move(mapper));
}
+// MakeReadCode* readers use offset relative to the instruction beginning based
+// on the instruction format ID.
+// See https://source.android.com/devices/tech/dalvik/instruction-formats
+
std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToStringId16(
offset_t lo,
offset_t hi) {
@@ -1295,6 +1419,47 @@ std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToTypeId16(
image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper));
}
+std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToProtoId16(
+ 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 == 0xFA || // invoke-polymorphic
+ value.instr->opcode == 0xFB)) { // invoke-polymorphic/range
+ // HHHH from e.g, invoke-polymorphic {vC, vD, vE, vF, vG},
+ // meth@BBBB, proto@HHHH
+ return value.instr_offset + 6;
+ }
+ return kInvalidOffset;
+ });
+ auto mapper = base::BindRepeating(ReadTargetIndex<uint16_t>, image_,
+ proto_map_item_, sizeof(dex::ProtoIdItem));
+ return std::make_unique<InstructionReferenceReader>(
+ image_, lo, hi, code_item_offsets_, std::move(filter), std::move(mapper));
+}
+
+std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToCallSiteId16(
+ 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 == 0xFC || // invoke-custom
+ value.instr->opcode == 0xFD)) { // invoke-custom/range
+ // BBBB from e.g, invoke-custom {vC, vD, vE, vF, vG},
+ // call_site@BBBB
+ return value.instr_offset + 2;
+ }
+ return kInvalidOffset;
+ });
+ auto mapper =
+ base::BindRepeating(ReadTargetIndex<uint16_t>, image_,
+ call_site_map_item_, sizeof(dex::CallSiteIdItem));
+ 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) {
@@ -1321,7 +1486,9 @@ std::unique_ptr<ReferenceReader> DisassemblerDex::MakeReadCodeToMethodId16(
[](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
+ value.instr->opcode == 0x74 || // invoke-kind/range
+ value.instr->opcode == 0xFA || // invoke-polymorphic
+ value.instr->opcode == 0xFB)) { // invoke-polymorphic/range
// BBBB from e.g., invoke-virtual {vC, vD, vE, vF, vG}, meth@BBBB.
return value.instr_offset + 2;
}
@@ -1491,6 +1658,14 @@ std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteMethodId32(
return std::make_unique<ReferenceWriterAdaptor>(image, std::move(writer));
}
+std::unique_ptr<ReferenceWriter> DisassemblerDex::MakeWriteCallSiteId16(
+ MutableBufferView image) {
+ auto writer =
+ base::BindRepeating(WriteTargetIndex<uint16_t>, call_site_map_item_,
+ sizeof(dex::CallSiteIdItem));
+ 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) {
@@ -1587,15 +1762,7 @@ bool DisassemblerDex::ParseHeader() {
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,
- };
+ // GetItemBaseSize() should have an entry for each item.
for (offset_t i = 0; i < list_size; ++i) {
const dex::MapItem* item = &item_list[i];
// Reject unreasonably large |item->size|.
@@ -1605,29 +1772,46 @@ bool DisassemblerDex::ParseHeader() {
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::kTypeStringIdItem)) {
+ string_map_item_ = *map_item_map_[dex::kTypeStringIdItem];
+ }
+ if (map_item_map_.count(dex::kTypeTypeIdItem)) {
+ type_map_item_ = *map_item_map_[dex::kTypeTypeIdItem];
+ }
+ if (map_item_map_.count(dex::kTypeProtoIdItem)) {
+ proto_map_item_ = *map_item_map_[dex::kTypeProtoIdItem];
+ }
+ if (map_item_map_.count(dex::kTypeFieldIdItem)) {
+ field_map_item_ = *map_item_map_[dex::kTypeFieldIdItem];
+ }
+ if (map_item_map_.count(dex::kTypeMethodIdItem)) {
+ method_map_item_ = *map_item_map_[dex::kTypeMethodIdItem];
+ }
+ if (map_item_map_.count(dex::kTypeClassDefItem)) {
+ class_def_map_item_ = *map_item_map_[dex::kTypeClassDefItem];
+ }
+ if (map_item_map_.count(dex::kTypeCallSiteIdItem)) {
+ call_site_map_item_ = *map_item_map_[dex::kTypeCallSiteIdItem];
+ }
+ if (map_item_map_.count(dex::kTypeMethodHandleItem)) {
+ method_handle_map_item_ = *map_item_map_[dex::kTypeMethodHandleItem];
+ }
+ if (map_item_map_.count(dex::kTypeTypeList)) {
+ type_list_map_item_ = *map_item_map_[dex::kTypeTypeList];
+ }
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))
+ if (map_item_map_.count(dex::kTypeAnnotationSetItem)) {
annotation_set_map_item_ = *map_item_map_[dex::kTypeAnnotationSetItem];
+ }
+ if (map_item_map_.count(dex::kTypeCodeItem)) {
+ code_map_item_ = *map_item_map_[dex::kTypeCodeItem];
+ }
if (map_item_map_.count(dex::kTypeAnnotationsDirectoryItem)) {
annotations_directory_map_item_ =
*map_item_map_[dex::kTypeAnnotationsDirectoryItem];
diff --git a/disassembler_dex.h b/disassembler_dex.h
index 2038a3c..e75d13e 100644
--- a/disassembler_dex.h
+++ b/disassembler_dex.h
@@ -32,8 +32,8 @@ class DisassemblerDex : public Disassembler {
kFieldId,
kMethodId,
// kClassDef, // Unused
- // kCallSiteId, // Unused
- // kMethodHandle, // Unused
+ kCallSiteId,
+ // kMethodHandle, // Unused
kTypeList,
kAnnotationSetRefList,
kAnnotionSet,
@@ -43,7 +43,7 @@ class DisassemblerDex : public Disassembler {
kAnnotation,
kEncodedArray,
kAnnotationsDirectory,
- // kCallSite, // Unused
+ kCallSite,
kNumPools
};
@@ -69,15 +69,20 @@ class DisassemblerDex : public Disassembler {
kTypeListToTypeId,
kCodeToTypeId,
- kMethodIdToProtoId, // kProtoId
+ kCodeToProtoId, // kProtoId
+ kMethodIdToProtoId,
kCodeToFieldId, // kFieldId
+ kMethodHandleToFieldId,
kAnnotationsDirectoryToFieldId,
kCodeToMethodId, // kMethodId
+ kMethodHandleToMethodId,
kAnnotationsDirectoryToMethodId,
kAnnotationsDirectoryToParameterMethodId,
+ kCodeToCallSiteId, // kCallSiteId
+
kProtoIdToParametersTypeList, // kTypeList
kClassDefToInterfacesTypeList,
@@ -102,10 +107,7 @@ class DisassemblerDex : public Disassembler {
kClassDefToAnnotationDirectory, // kAnnotationsDirectory
- // Intentionally ignored references (never appeared in test corpus).
- // kMethodHandleToFieldId,
- // kMethodHandleToMethodId,
- // kCallSiteIdToCallSite,
+ kCallSiteIdToCallSite, // kCallSite
kNumTypes
};
@@ -172,6 +174,13 @@ class DisassemblerDex : public Disassembler {
std::unique_ptr<ReferenceReader> MakeReadClassDefToStaticValuesEncodedArray(
offset_t lo,
offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadCallSiteIdToCallSite32(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadMethodHandleToFieldId16(offset_t lo,
+ offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadMethodHandleToMethodId16(
+ offset_t lo,
+ offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadTypeListToTypeId16(offset_t lo,
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadAnnotationSetToAnnotation(
@@ -203,10 +212,14 @@ class DisassemblerDex : public Disassembler {
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToTypeId16(offset_t lo,
offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadCodeToProtoId16(offset_t lo,
+ offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToFieldId16(offset_t lo,
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToMethodId16(offset_t lo,
offset_t hi);
+ std::unique_ptr<ReferenceReader> MakeReadCodeToCallSiteId16(offset_t lo,
+ offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToRelCode8(offset_t lo,
offset_t hi);
std::unique_ptr<ReferenceReader> MakeReadCodeToRelCode16(offset_t lo,
@@ -225,6 +238,8 @@ class DisassemblerDex : public Disassembler {
std::unique_ptr<ReferenceWriter> MakeWriteFieldId32(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteMethodId16(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteMethodId32(MutableBufferView image);
+ std::unique_ptr<ReferenceWriter> MakeWriteCallSiteId16(
+ MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteRelCode8(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteRelCode16(MutableBufferView image);
std::unique_ptr<ReferenceWriter> MakeWriteRelCode32(MutableBufferView image);
@@ -248,12 +263,12 @@ class DisassemblerDex : public Disassembler {
dex::MapItem field_map_item_ = {};
dex::MapItem method_map_item_ = {};
dex::MapItem class_def_map_item_ = {};
+ dex::MapItem call_site_map_item_ = {};
+ dex::MapItem method_handle_map_item_ = {};
dex::MapItem type_list_map_item_ = {};
- dex::MapItem code_map_item_ = {};
-
- // Optionally supported (not all DEX files have these).
dex::MapItem annotation_set_ref_list_map_item_ = {};
dex::MapItem annotation_set_map_item_ = {};
+ dex::MapItem code_map_item_ = {};
dex::MapItem annotations_directory_map_item_ = {};
// Sorted list of offsets of parsed items in |image_|.
diff --git a/testdata/all.smali b/testdata/all.smali
new file mode 100644
index 0000000..7a1d272
--- /dev/null
+++ b/testdata/all.smali
@@ -0,0 +1,628 @@
+# Tests most/all DEX behaviors as of version 37.
+# Disassembled from dexdump test files.
+# Repo: https://android.googlesource.com/platform/art/
+# File: test/dexdump/all.dex
+
+# Compile using smali: https://github.com/JesusFreke/smali
+# java -jar smali.jar assemble all.smali --api 25
+
+.class public LA;
+.super Ljava/lang/Object;
+
+
+# static fields
+.field private static sB:B
+
+.field private static sC:C
+
+.field private static sI:I
+
+.field private static sJ:J
+
+.field private static sO:LA;
+
+.field private static sS:S
+
+.field private static sZ:Z
+
+
+# instance fields
+.field private mB:B
+
+.field private mC:C
+
+.field private mI:I
+
+.field private mJ:J
+
+.field private mO:LA;
+
+.field private mS:S
+
+.field private mZ:Z
+
+
+# direct methods
+.method public constructor <init>()V
+ .registers 1
+
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+ return-void
+.end method
+
+.method public static arrays()V
+ .registers 3
+
+ aget v0, v1, v2
+
+ aget-wide v0, v1, v2
+
+ aget-object v0, v1, v2
+
+ aget-boolean v0, v1, v2
+
+ aget-byte v0, v1, v2
+
+ aget-char v0, v1, v2
+
+ aget-short v0, v1, v2
+
+ aput v0, v1, v2
+
+ aput-wide v0, v1, v2
+
+ aput-object v0, v1, v2
+
+ aput-boolean v0, v1, v2
+
+ aput-byte v0, v1, v2
+
+ aput-char v0, v1, v2
+
+ aput-short v0, v1, v2
+
+ return-void
+.end method
+
+.method public static binary_ops()V
+ .registers 3
+
+ add-int v0, v1, v2
+
+ sub-int v0, v1, v2
+
+ mul-int v0, v1, v2
+
+ div-int v0, v1, v2
+
+ rem-int v0, v1, v2
+
+ and-int v0, v1, v2
+
+ or-int v0, v1, v2
+
+ xor-int v0, v1, v2
+
+ shl-int v0, v1, v2
+
+ shr-int v0, v1, v2
+
+ ushr-int v0, v1, v2
+
+ add-long v0, v1, v2
+
+ sub-long v0, v1, v2
+
+ mul-long v0, v1, v2
+
+ div-long v0, v1, v2
+
+ rem-long v0, v1, v2
+
+ and-long v0, v1, v2
+
+ or-long v0, v1, v2
+
+ xor-long v0, v1, v2
+
+ shl-long v0, v1, v2
+
+ shr-long v0, v1, v2
+
+ ushr-long v0, v1, v2
+
+ add-float v0, v1, v2
+
+ sub-float v0, v1, v2
+
+ mul-float v0, v1, v2
+
+ div-float v0, v1, v2
+
+ rem-float v0, v1, v2
+
+ add-double v0, v1, v2
+
+ sub-double v0, v1, v2
+
+ mul-double v0, v1, v2
+
+ div-double v0, v1, v2
+
+ rem-double v0, v1, v2
+
+ return-void
+.end method
+
+.method public static binary_ops_2addr()V
+ .registers 2
+
+ add-int/2addr v0, v1
+
+ sub-int/2addr v0, v1
+
+ mul-int/2addr v0, v1
+
+ div-int/2addr v0, v1
+
+ rem-int/2addr v0, v1
+
+ and-int/2addr v0, v1
+
+ or-int/2addr v0, v1
+
+ xor-int/2addr v0, v1
+
+ shl-int/2addr v0, v1
+
+ shr-int/2addr v0, v1
+
+ ushr-int/2addr v0, v1
+
+ add-long/2addr v0, v1
+
+ sub-long/2addr v0, v1
+
+ mul-long/2addr v0, v1
+
+ div-long/2addr v0, v1
+
+ rem-long/2addr v0, v1
+
+ and-long/2addr v0, v1
+
+ or-long/2addr v0, v1
+
+ xor-long/2addr v0, v1
+
+ shl-long/2addr v0, v1
+
+ shr-long/2addr v0, v1
+
+ ushr-long/2addr v0, v1
+
+ add-float/2addr v0, v1
+
+ sub-float/2addr v0, v1
+
+ mul-float/2addr v0, v1
+
+ div-float/2addr v0, v1
+
+ rem-float/2addr v0, v1
+
+ add-double/2addr v0, v1
+
+ sub-double/2addr v0, v1
+
+ mul-double/2addr v0, v1
+
+ div-double/2addr v0, v1
+
+ rem-double/2addr v0, v1
+
+ return-void
+.end method
+
+.method public static binary_ops_lit16()V
+ .registers 2
+
+ add-int/lit16 v0, v1, 0x1234
+
+ rsub-int v0, v1, 0x1234
+
+ mul-int/lit16 v0, v1, 0x1234
+
+ div-int/lit16 v0, v1, 0x1234
+
+ rem-int/lit16 v0, v1, 0x1234
+
+ and-int/lit16 v0, v1, 0x1234
+
+ or-int/lit16 v0, v1, 0x1234
+
+ xor-int/lit16 v0, v1, 0x1234
+
+ return-void
+.end method
+
+.method public static binary_ops_lit8()V
+ .registers 2
+
+ add-int/lit8 v0, v1, 0x12
+
+ rsub-int/lit8 v0, v1, 0x12
+
+ mul-int/lit8 v0, v1, 0x12
+
+ div-int/lit8 v0, v1, 0x12
+
+ rem-int/lit8 v0, v1, 0x12
+
+ and-int/lit8 v0, v1, 0x12
+
+ or-int/lit8 v0, v1, 0x12
+
+ xor-int/lit8 v0, v1, 0x12
+
+ shl-int/lit8 v0, v1, 0x12
+
+ shr-int/lit8 v0, v1, 0x12
+
+ ushr-int/lit8 v0, v1, 0x12
+
+ return-void
+.end method
+
+.method public static compares()V
+ .registers 3
+
+ cmpl-float v0, v1, v2
+
+ cmpg-float v0, v1, v2
+
+ cmpl-double v0, v1, v2
+
+ cmpg-double v0, v1, v2
+
+ cmp-long v0, v1, v2
+
+ return-void
+.end method
+
+.method public static conditionals()V
+ .registers 2
+
+ if-eq v0, v1, :cond_18
+
+ if-ne v0, v1, :cond_18
+
+ if-lt v0, v1, :cond_18
+
+ if-ge v0, v1, :cond_18
+
+ if-gt v0, v1, :cond_18
+
+ if-le v0, v1, :cond_18
+
+ if-eqz v0, :cond_18
+
+ if-nez v0, :cond_18
+
+ if-ltz v0, :cond_18
+
+ if-gez v0, :cond_18
+
+ if-gtz v0, :cond_18
+
+ if-lez v0, :cond_18
+
+ :cond_18
+ return-void
+.end method
+
+.method public static constants()V
+ .registers 1
+
+ const/4 v0, 0x1
+
+ const/16 v0, 0x1234
+
+ const v0, 0x12345678
+
+ const/high16 v0, 0x12340000
+
+ const-wide/16 v0, 0x1234
+
+ const-wide/32 v0, 0x12345678
+
+ const-wide v0, 0x1234567890abcdefL # 5.626349108908516E-221
+
+ const-wide/high16 v0, 0x1234000000000000L
+
+ const-string v0, "string"
+
+ const-string/jumbo v0, "string"
+
+ const-class v0, Ljava/lang/Object;
+
+ return-void
+.end method
+
+.method public static misc()V
+ .registers 5
+
+ nop
+
+ monitor-enter v0
+
+ monitor-exit v0
+
+ check-cast v0, Ljava/lang/Object;
+
+ instance-of v0, v1, Ljava/lang/Object;
+
+ array-length v0, v1
+
+ new-instance v0, Ljava/lang/Object;
+
+ new-array v0, v1, Ljava/lang/Object;
+
+ filled-new-array {v0, v1, v2, v3, v4}, [Ljava/lang/Object;
+
+ filled-new-array/range {v0 .. v4}, [Ljava/lang/Object;
+
+ fill-array-data v0, :array_1e
+
+ throw v0
+
+ goto :goto_1c
+
+ goto/16 :goto_1c
+
+ goto/32 :goto_1c
+
+ :goto_1c
+ return-void
+
+ nop
+
+ :array_1e
+ .array-data 4
+ 0x1
+ 0x2
+ 0x3
+ 0x4
+ 0x5
+ 0x6
+ 0x7
+ 0x8
+ 0x9
+ 0x0
+ .end array-data
+.end method
+
+.method public static moves()V
+ .registers 2
+
+ move v0, v1
+
+ move/from16 v0, v1
+
+ move/16 v0, v1
+
+ move-wide v0, v1
+
+ move-wide/from16 v0, v1
+
+ move-wide/16 v0, v1
+
+ move-object v0, v1
+
+ move-object/from16 v0, v1
+
+ move-object/16 v0, v1
+
+ move-result v0
+
+ move-result-wide v0
+
+ move-result-object v0
+
+ move-exception v0
+
+ return-void
+.end method
+
+.method public static packed_switch()V
+ .registers 1
+
+ packed-switch v0, :pswitch_data_8
+
+ :goto_3
+ return-void
+
+ goto :goto_3
+
+ :pswitch_5
+ goto :goto_3
+
+ :pswitch_6
+ goto :goto_3
+
+ nop
+
+ :pswitch_data_8
+ .packed-switch 0x7ffffffe
+ :pswitch_5
+ :pswitch_6
+ .end packed-switch
+.end method
+
+.method public static return32()I
+ .registers 1
+
+ return v0
+.end method
+
+.method public static return64()I
+ .registers 2
+
+ return-wide v0
+.end method
+
+.method public static return_object()Ljava/lang/Object;
+ .registers 1
+
+ return-object v0
+.end method
+
+.method public static sparse_switch()V
+ .registers 2
+
+ sparse-switch v0, :sswitch_data_4
+
+ :sswitch_3
+ return-void
+
+ :sswitch_data_4
+ .sparse-switch
+ 0x1111 -> :sswitch_3
+ 0x2222 -> :sswitch_3
+ 0x3333 -> :sswitch_3
+ 0x4444 -> :sswitch_3
+ .end sparse-switch
+.end method
+
+.method public static static_fields()V
+ .registers 1
+
+ sget v0, LA;->sI:I
+
+ sget-wide v0, LA;->sJ:J
+
+ sget-object v0, LA;->sO:LA;
+
+ sget-boolean v0, LA;->sZ:Z
+
+ sget-byte v0, LA;->sB:B
+
+ sget-char v0, LA;->sC:C
+
+ sget-short v0, LA;->sS:S
+
+ sput v0, LA;->sI:I
+
+ sput-wide v0, LA;->sJ:J
+
+ sput-object v0, LA;->sO:LA;
+
+ sput-boolean v0, LA;->sZ:Z
+
+ sput-byte v0, LA;->sB:B
+
+ sput-char v0, LA;->sC:C
+
+ sput-short v0, LA;->mS:S
+
+ return-void
+.end method
+
+.method public static unary_ops()V
+ .registers 2
+
+ neg-int v0, v1
+
+ not-int v0, v1
+
+ neg-long v0, v1
+
+ not-long v0, v1
+
+ neg-float v0, v1
+
+ neg-double v0, v1
+
+ int-to-long v0, v1
+
+ int-to-float v0, v1
+
+ int-to-double v0, v1
+
+ long-to-int v0, v1
+
+ long-to-float v0, v1
+
+ long-to-double v0, v1
+
+ float-to-int v0, v1
+
+ float-to-long v0, v1
+
+ float-to-double v0, v1
+
+ double-to-int v0, v1
+
+ double-to-long v0, v1
+
+ double-to-float v0, v1
+
+ int-to-byte v0, v1
+
+ int-to-char v0, v1
+
+ int-to-short v0, v1
+
+ return-void
+.end method
+
+
+# virtual methods
+.method public instance_fields()V
+ .registers 2
+
+ iget v0, p0, LA;->sI:I
+
+ iget-wide v0, p0, LA;->sJ:J
+
+ iget-object v0, p0, LA;->sO:LA;
+
+ iget-boolean v0, p0, LA;->sZ:Z
+
+ iget-byte v0, p0, LA;->sB:B
+
+ iget-char v0, p0, LA;->sC:C
+
+ iget-short v0, p0, LA;->sS:S
+
+ iput v0, p0, LA;->sI:I
+
+ iput-wide v0, p0, LA;->sJ:J
+
+ iput-object v0, p0, LA;->sO:LA;
+
+ iput-boolean v0, p0, LA;->sZ:Z
+
+ iput-byte v0, p0, LA;->sB:B
+
+ iput-char v0, p0, LA;->sC:C
+
+ iput-short v0, p0, LA;->sS:S
+
+ return-void
+.end method
+
+.method public invokes()V
+ .registers 5
+
+ invoke-virtual {v0, v1, v2, v3, p0}, LA;->invokes()V
+
+ invoke-super {v0, v1, v2, v3, p0}, LA;->invokes()V
+
+ invoke-direct {v0, v1, v2, v3, p0}, LA;->invokes()V
+
+ invoke-static {v0, v1, v2, v3, p0}, LA;->invokes()V
+
+ invoke-interface {v0, v1, v2, v3, p0}, LA;->invokes()V
+.end method
diff --git a/testdata/const-method-handle-min.smali b/testdata/const-method-handle-min.smali
new file mode 100644
index 0000000..0bf157f
--- /dev/null
+++ b/testdata/const-method-handle-min.smali
@@ -0,0 +1,14 @@
+# Tests const-method-handle added in DEX version 39.
+
+# Compile using smali: https://github.com/JesusFreke/smali
+# java -jar smali.jar assemble const-method-handle.smali --api 28
+
+.class public LConstMethodHandle;
+.super Ljava/lang/Object;
+
+.method public (I)V
+ .registers 2
+ const-method-handle v1, invoke-static@Ljava/lang/String;->copyValueOf([C)Ljava/lang/String;
+ const-method-handle v0, invoke-instance@Ljava/lang/String;->charAt(I)C
+ return-void
+.end method
diff --git a/testdata/invoke-custom-min.smali b/testdata/invoke-custom-min.smali
new file mode 100644
index 0000000..64bccbc
--- /dev/null
+++ b/testdata/invoke-custom-min.smali
@@ -0,0 +1,39 @@
+# Tests invoke-custom added in DEX version 38.
+
+# Compile using smali: https://github.com/JesusFreke/smali
+# java -jar smali.jar assemble invoke-custom-min.smali --api 28
+
+.class public LFoo;
+.super Ljava/lang/Object;
+
+.method public la1(Ljava/util/ArrayList;)V
+ .registers 5
+ .annotation system Ldalvik/annotation/Signature;
+ value = {
+ "(",
+ "Ljava/util/ArrayList",
+ "<",
+ "Ljava/lang/String;",
+ ">;)V"
+ }
+ .end annotation
+
+ .prologue
+ .line 42
+ invoke-virtual {p1}, Ljava/util/ArrayList;->stream()Ljava/util/stream/Stream;
+
+ move-result-object v0
+
+ invoke-custom {}, call_site_1("bar", ()Ljava/util/function/Predicate;, (Ljava/lang/Object;)Z, invoke-static@LFoo;->lambda$la1$1(I)Z, (I)Z)@Ljava/lang/invoke/LambdaMetafactory;->metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+
+ move-result-object v1
+
+ invoke-custom {}, call_site_2("test", ()Ljava/util/function/Predicate;, (Ljava/lang/Object;)Z, invoke-static@LFoo;->lambda$la1$1(Ljava/lang/String;)Z, (Ljava/lang/String;)Z)@Ljava/lang/invoke/LambdaMetafactory;->metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+
+ move-result-object v2
+
+ invoke-interface {v0, v1, v2}, Ljava/util/stream/Stream;->filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;
+
+ .line 50
+ return-void
+.end method
diff --git a/testdata/invoke-polymorphic.smali b/testdata/invoke-polymorphic.smali
new file mode 100644
index 0000000..1ad7246
--- /dev/null
+++ b/testdata/invoke-polymorphic.smali
@@ -0,0 +1,70 @@
+# Tests invoke-polymorphic added in DEX version 38.
+# Disassembled from dexdump test files.
+# Repo: https://android.googlesource.com/platform/art/
+# File: test/dexdump/invoke-polymorphic.dex
+
+# Compile using smali: https://github.com/JesusFreke/smali
+# java -jar smali.jar assemble invoke-polymorphic.smali --api 28
+
+.class public LMain;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# direct methods
+.method public constructor <init>()V
+ .registers 1
+
+ .prologue
+ .line 9
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+ return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+ .registers 10
+ .param p0, "args" # [Ljava/lang/String;
+ .annotation system Ldalvik/annotation/Throws;
+ value = {
+ Ljava/lang/Throwable;
+ }
+ .end annotation
+
+ .prologue
+ const-wide v2, 0x400199999999999aL # 2.2
+
+ const/4 v4, 0x1
+
+ .line 31
+ const/4 v0, 0x0
+
+ .line 32
+ .local v0, "handle":Ljava/lang/invoke/MethodHandle;
+ const/4 v5, 0x0
+
+ .line 33
+ .local v5, "o":Ljava/lang/Object;
+ const-string/jumbo v1, "a"
+
+ move v6, v4
+
+ invoke-polymorphic/range {v0 .. v6}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/String;DILjava/lang/Object;I)Ljava/lang/String;
+
+ move-result-object v7
+
+ .line 34
+ .local v7, "s":Ljava/lang/String;
+ invoke-polymorphic {v0, v2, v3, v4}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object;, (DI)I
+
+ move-result v8
+
+ .line 35
+ .local v8, "x":I
+ const-string/jumbo v1, "a"
+
+ invoke-polymorphic {v0, v1, v2, v3, v4}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/String;DI)V
+
+ .line 56
+ return-void
+.end method
diff --git a/type_dex.h b/type_dex.h
index 432a031..e0ccc28 100644
--- a/type_dex.h
+++ b/type_dex.h
@@ -12,11 +12,11 @@ namespace dex {
// Contains types that models DEX executable format data structures.
// See https://source.android.com/devices/tech/dalvik/dex-format
-// The supported versions are 035 and 037.
+// The supported versions are 035, 037, and 038.
enum class FormatId : uint8_t {
b, // 22b.
- c, // 21c, 22c, 31c, 35c, 3rc.
+ c, // 21c, 22c, 31c, 35c, 3rc, 45cc, 4rcc.
h, // 21h.
i, // 31i.
l, // 51l.
@@ -110,6 +110,10 @@ constexpr Instruction kByteCode[] = {
{0xD0, 2, FormatId::s, 8},
{0xD8, 2, FormatId::b, 11},
// {0xE3, 1, FormatId::x, 29}, unused
+ {0xFA, 4, FormatId::c},
+ {0xFB, 4, FormatId::c},
+ {0xFC, 3, FormatId::c},
+ {0xFD, 3, FormatId::c},
};
// Supported by MSVC, g++, and clang++. Ensures no gaps in packing.
@@ -185,6 +189,36 @@ struct ClassDefItem {
uint32_t static_values_off;
};
+// call_site_id_item: Call site identifiers list.
+struct CallSiteIdItem {
+ uint32_t call_site_off;
+};
+
+// method_handle_type: Determines the behavior of the MethodHandleItem.
+enum class MethodHandleType : uint16_t {
+ // FieldId
+ kStaticPut = 0x00,
+ kStaticGet = 0x01,
+ kInstancePut = 0x02,
+ kInstanceGet = 0x03,
+ // MethodId
+ kInvokeStatic = 0x04,
+ kInvokeInstance = 0x05,
+ kInvokeConstructor = 0x06,
+ kInvokeDirect = 0x07,
+ kInvokeInterface = 0x08,
+ // Sentinel. If new types are added put them before this and increment.
+ kMaxMethodHandleType = 0x09
+};
+
+// method_handle_item: Method handles referred within the Dex file.
+struct MethodHandleItem {
+ uint16_t method_handle_type;
+ uint16_t unused_1;
+ uint16_t field_or_method_id;
+ uint16_t unused_2;
+};
+
// code_item: Header of a code item.
struct CodeItem {
uint16_t registers_size;
@@ -196,7 +230,31 @@ struct CodeItem {
// Variable length data follow for complete code item.
};
-constexpr uint32_t kMaxItemListSize = 18;
+// Number of valid type codes for map_item elements in map_list.
+// See: https://source.android.com/devices/tech/dalvik/dex-format#type-codes
+constexpr uint32_t kMaxItemListSize = 21;
+
+constexpr uint16_t kTypeHeaderItem = 0x0000;
+constexpr uint16_t kTypeStringIdItem = 0x0001;
+constexpr uint16_t kTypeTypeIdItem = 0x0002;
+constexpr uint16_t kTypeProtoIdItem = 0x0003;
+constexpr uint16_t kTypeFieldIdItem = 0x0004;
+constexpr uint16_t kTypeMethodIdItem = 0x0005;
+constexpr uint16_t kTypeClassDefItem = 0x0006;
+constexpr uint16_t kTypeCallSiteIdItem = 0x0007;
+constexpr uint16_t kTypeMethodHandleItem = 0x0008;
+constexpr uint16_t kTypeMapList = 0x1000;
+constexpr uint16_t kTypeTypeList = 0x1001;
+constexpr uint16_t kTypeAnnotationSetRefList = 0x1002;
+constexpr uint16_t kTypeAnnotationSetItem = 0x1003;
+constexpr uint16_t kTypeClassDataItem = 0x2000;
+constexpr uint16_t kTypeCodeItem = 0x2001;
+constexpr uint16_t kTypeStringDataItem = 0x2002;
+constexpr uint16_t kTypeDebugInfoItem = 0x2003;
+constexpr uint16_t kTypeAnnotationItem = 0x2004;
+constexpr uint16_t kTypeEncodedArrayItem = 0x2005;
+constexpr uint16_t kTypeAnnotationsDirectoryItem = 0x2006;
+constexpr uint16_t kTypeHiddenApiClassDataItem = 0xF000;
// map_item
struct MapItem {
@@ -264,25 +322,6 @@ struct TryItem {
uint16_t handler_off;
};
-constexpr uint16_t kTypeHeaderItem = 0x0000;
-constexpr uint16_t kTypeStringIdItem = 0x0001;
-constexpr uint16_t kTypeTypeIdItem = 0x0002;
-constexpr uint16_t kTypeProtoIdItem = 0x0003;
-constexpr uint16_t kTypeFieldIdItem = 0x0004;
-constexpr uint16_t kTypeMethodIdItem = 0x0005;
-constexpr uint16_t kTypeClassDefItem = 0x0006;
-constexpr uint16_t kTypeMapList = 0x1000;
-constexpr uint16_t kTypeTypeList = 0x1001;
-constexpr uint16_t kTypeAnnotationSetRefList = 0x1002;
-constexpr uint16_t kTypeAnnotationSetItem = 0x1003;
-constexpr uint16_t kTypeClassDataItem = 0x2000;
-constexpr uint16_t kTypeCodeItem = 0x2001;
-constexpr uint16_t kTypeStringDataItem = 0x2002;
-constexpr uint16_t kTypeDebugInfoItem = 0x2003;
-constexpr uint16_t kTypeAnnotationItem = 0x2004;
-constexpr uint16_t kTypeEncodedArrayItem = 0x2005;
-constexpr uint16_t kTypeAnnotationsDirectoryItem = 0x2006;
-
#pragma pack(pop)
} // namespace dex