summaryrefslogtreecommitdiff
path: root/libdexfile/dex
diff options
context:
space:
mode:
Diffstat (limited to 'libdexfile/dex')
-rw-r--r--libdexfile/dex/base64_test_util.h99
-rw-r--r--libdexfile/dex/code_item_accessors-inl.h204
-rw-r--r--libdexfile/dex/code_item_accessors.h166
-rw-r--r--libdexfile/dex/code_item_accessors_test.cc114
-rw-r--r--libdexfile/dex/compact_dex_debug_info.cc117
-rw-r--r--libdexfile/dex/compact_dex_debug_info.h65
-rw-r--r--libdexfile/dex/compact_dex_debug_info_test.cc84
-rw-r--r--libdexfile/dex/compact_dex_file.cc108
-rw-r--r--libdexfile/dex/compact_dex_file.h291
-rw-r--r--libdexfile/dex/compact_dex_file_test.cc101
-rw-r--r--libdexfile/dex/compact_dex_level.h50
-rw-r--r--libdexfile/dex/compact_dex_utils.h37
-rw-r--r--libdexfile/dex/descriptors_names.cc426
-rw-r--r--libdexfile/dex/descriptors_names.h63
-rw-r--r--libdexfile/dex/dex_file-inl.h521
-rw-r--r--libdexfile/dex/dex_file.cc795
-rw-r--r--libdexfile/dex/dex_file.h1443
-rw-r--r--libdexfile/dex/dex_file_exception_helpers.cc104
-rw-r--r--libdexfile/dex/dex_file_exception_helpers.h68
-rw-r--r--libdexfile/dex/dex_file_loader.cc501
-rw-r--r--libdexfile/dex/dex_file_loader.h198
-rw-r--r--libdexfile/dex/dex_file_loader_test.cc485
-rw-r--r--libdexfile/dex/dex_file_reference.h52
-rw-r--r--libdexfile/dex/dex_file_tracking_registrar.cc272
-rw-r--r--libdexfile/dex/dex_file_tracking_registrar.h81
-rw-r--r--libdexfile/dex/dex_file_types.h117
-rw-r--r--libdexfile/dex/dex_file_verifier.cc3290
-rw-r--r--libdexfile/dex/dex_file_verifier.h256
-rw-r--r--libdexfile/dex/dex_file_verifier_test.cc2186
-rw-r--r--libdexfile/dex/dex_instruction-inl.h558
-rw-r--r--libdexfile/dex/dex_instruction.cc568
-rw-r--r--libdexfile/dex/dex_instruction.h757
-rw-r--r--libdexfile/dex/dex_instruction_iterator.h237
-rw-r--r--libdexfile/dex/dex_instruction_list.h308
-rw-r--r--libdexfile/dex/dex_instruction_test.cc172
-rw-r--r--libdexfile/dex/dex_instruction_utils.h219
-rw-r--r--libdexfile/dex/invoke_type.h38
-rw-r--r--libdexfile/dex/modifiers.cc58
-rw-r--r--libdexfile/dex/modifiers.h148
-rw-r--r--libdexfile/dex/standard_dex_file.cc81
-rw-r--r--libdexfile/dex/standard_dex_file.h118
-rw-r--r--libdexfile/dex/utf-inl.h99
-rw-r--r--libdexfile/dex/utf.cc321
-rw-r--r--libdexfile/dex/utf.h135
-rw-r--r--libdexfile/dex/utf_test.cc381
45 files changed, 16492 insertions, 0 deletions
diff --git a/libdexfile/dex/base64_test_util.h b/libdexfile/dex/base64_test_util.h
new file mode 100644
index 0000000000..683e429e11
--- /dev/null
+++ b/libdexfile/dex/base64_test_util.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_BASE64_TEST_UTIL_H_
+#define ART_LIBDEXFILE_DEX_BASE64_TEST_UTIL_H_
+
+#include <base/logging.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <memory>
+#include <vector>
+
+namespace art {
+
+static inline uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+ static const uint8_t kBase64Map[256] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255
+ };
+
+ CHECK(dst_size != nullptr);
+ std::vector<uint8_t> tmp;
+ uint32_t t = 0, y = 0;
+ int g = 3;
+ for (size_t i = 0; src[i] != '\0'; ++i) {
+ uint8_t c = kBase64Map[src[i] & 0xFF];
+ if (c == 255) continue;
+ // the final = symbols are read and used to trim the remaining bytes
+ if (c == 254) {
+ c = 0;
+ // prevent g < 0 which would potentially allow an overflow later
+ if (--g < 0) {
+ *dst_size = 0;
+ return nullptr;
+ }
+ } else if (g != 3) {
+ // we only allow = to be at the end
+ *dst_size = 0;
+ return nullptr;
+ }
+ t = (t << 6) | c;
+ if (++y == 4) {
+ tmp.push_back((t >> 16) & 255);
+ if (g > 1) {
+ tmp.push_back((t >> 8) & 255);
+ }
+ if (g > 2) {
+ tmp.push_back(t & 255);
+ }
+ y = t = 0;
+ }
+ }
+ if (y != 0) {
+ *dst_size = 0;
+ return nullptr;
+ }
+ std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
+ *dst_size = tmp.size();
+ std::copy(tmp.begin(), tmp.end(), dst.get());
+ return dst.release();
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_BASE64_TEST_UTIL_H_
diff --git a/libdexfile/dex/code_item_accessors-inl.h b/libdexfile/dex/code_item_accessors-inl.h
new file mode 100644
index 0000000000..c166f5f19e
--- /dev/null
+++ b/libdexfile/dex/code_item_accessors-inl.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_
+#define ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_
+
+#include "code_item_accessors.h"
+
+#include "compact_dex_file.h"
+#include "dex_file-inl.h"
+#include "standard_dex_file.h"
+
+// The no ART version is used by binaries that don't include the whole runtime.
+namespace art {
+
+inline void CodeItemInstructionAccessor::Init(uint32_t insns_size_in_code_units,
+ const uint16_t* insns) {
+ insns_size_in_code_units_ = insns_size_in_code_units;
+ insns_ = insns;
+}
+
+inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) {
+ uint32_t insns_size_in_code_units;
+ code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ true>(
+ &insns_size_in_code_units,
+ /*registers_size*/ nullptr,
+ /*ins_size*/ nullptr,
+ /*outs_size*/ nullptr,
+ /*tries_size*/ nullptr);
+ Init(insns_size_in_code_units, code_item.insns_);
+}
+
+inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ Init(code_item.insns_size_in_code_units_, code_item.insns_);
+}
+
+inline void CodeItemInstructionAccessor::Init(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ if (code_item != nullptr) {
+ DCHECK(dex_file.IsInDataSection(code_item));
+ if (dex_file.IsCompactDexFile()) {
+ Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+ } else {
+ DCHECK(dex_file.IsStandardDexFile());
+ Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+ }
+}
+
+inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(
+ const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ Init(dex_file, code_item);
+}
+
+inline DexInstructionIterator CodeItemInstructionAccessor::begin() const {
+ return DexInstructionIterator(insns_, 0u);
+}
+
+inline DexInstructionIterator CodeItemInstructionAccessor::end() const {
+ return DexInstructionIterator(insns_, insns_size_in_code_units_);
+}
+
+inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom(
+ uint32_t start_dex_pc) const {
+ DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits());
+ return {
+ DexInstructionIterator(insns_, start_dex_pc),
+ DexInstructionIterator(insns_, insns_size_in_code_units_) };
+}
+
+inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) {
+ uint32_t insns_size_in_code_units;
+ code_item.DecodeFields</*kDecodeOnlyInstructionCount*/ false>(&insns_size_in_code_units,
+ &registers_size_,
+ &ins_size_,
+ &outs_size_,
+ &tries_size_);
+ CodeItemInstructionAccessor::Init(insns_size_in_code_units, code_item.insns_);
+}
+
+inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ CodeItemInstructionAccessor::Init(code_item);
+ registers_size_ = code_item.registers_size_;
+ ins_size_ = code_item.ins_size_;
+ outs_size_ = code_item.outs_size_;
+ tries_size_ = code_item.tries_size_;
+}
+
+inline void CodeItemDataAccessor::Init(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ if (code_item != nullptr) {
+ if (dex_file.IsCompactDexFile()) {
+ CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+ } else {
+ DCHECK(dex_file.IsStandardDexFile());
+ CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+ }
+}
+
+inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ Init(dex_file, code_item);
+}
+
+inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const {
+ const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u);
+ return {
+ try_items,
+ try_items + TriesSize() };
+}
+
+inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const {
+ return DexFile::GetCatchHandlerData(end(), TriesSize(), offset);
+}
+
+inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const {
+ IterationRange<const DexFile::TryItem*> try_items(TryItems());
+ int32_t index = DexFile::FindTryItem(try_items.begin(),
+ try_items.end() - try_items.begin(),
+ try_dex_pc);
+ return index != -1 ? &try_items.begin()[index] : nullptr;
+}
+
+inline const void* CodeItemDataAccessor::CodeItemDataEnd() const {
+ const uint8_t* handler_data = GetCatchHandlerData();
+
+ if (TriesSize() == 0 || handler_data == nullptr) {
+ return &end().Inst();
+ }
+ // Get the start of the handler data.
+ const uint32_t handlers_size = DecodeUnsignedLeb128(&handler_data);
+ // Manually read each handler.
+ for (uint32_t i = 0; i < handlers_size; ++i) {
+ int32_t uleb128_count = DecodeSignedLeb128(&handler_data) * 2;
+ if (uleb128_count <= 0) {
+ uleb128_count = -uleb128_count + 1;
+ }
+ for (int32_t j = 0; j < uleb128_count; ++j) {
+ DecodeUnsignedLeb128(&handler_data);
+ }
+ }
+ return reinterpret_cast<const void*>(handler_data);
+}
+
+inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item,
+ uint32_t dex_method_index) {
+ if (code_item == nullptr) {
+ return;
+ }
+ dex_file_ = &dex_file;
+ if (dex_file.IsCompactDexFile()) {
+ Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index);
+ } else {
+ DCHECK(dex_file.IsStandardDexFile());
+ Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+ }
+}
+
+inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item,
+ uint32_t dex_method_index) {
+ debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset(
+ dex_method_index);
+ CodeItemDataAccessor::Init(code_item);
+}
+
+inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+ debug_info_offset_ = code_item.debug_info_off_;
+ CodeItemDataAccessor::Init(code_item);
+}
+
+template<typename NewLocalCallback>
+inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static,
+ uint32_t method_idx,
+ NewLocalCallback new_local,
+ void* context) const {
+ return dex_file_->DecodeDebugLocalInfo(RegistersSize(),
+ InsSize(),
+ InsnsSizeInCodeUnits(),
+ DebugInfoOffset(),
+ is_static,
+ method_idx,
+ new_local,
+ context);
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_
diff --git a/libdexfile/dex/code_item_accessors.h b/libdexfile/dex/code_item_accessors.h
new file mode 100644
index 0000000000..ba7c126ed8
--- /dev/null
+++ b/libdexfile/dex/code_item_accessors.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO: Dex helpers have ART specific APIs, we may want to refactor these for use in dexdump.
+
+#ifndef ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_H_
+#define ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_H_
+
+#include "compact_dex_file.h"
+#include "dex_file.h"
+#include "dex_instruction_iterator.h"
+#include "standard_dex_file.h"
+
+namespace art {
+
+class ArtMethod;
+
+// Abstracts accesses to the instruction fields of code items for CompactDexFile and
+// StandardDexFile.
+class CodeItemInstructionAccessor {
+ public:
+ ALWAYS_INLINE CodeItemInstructionAccessor(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item);
+
+ ALWAYS_INLINE explicit CodeItemInstructionAccessor(ArtMethod* method);
+
+ ALWAYS_INLINE DexInstructionIterator begin() const;
+
+ ALWAYS_INLINE DexInstructionIterator end() const;
+
+ IterationRange<DexInstructionIterator> InstructionsFrom(uint32_t start_dex_pc) const;
+
+ uint32_t InsnsSizeInCodeUnits() const {
+ return insns_size_in_code_units_;
+ }
+
+ const uint16_t* Insns() const {
+ return insns_;
+ }
+
+ // Return the instruction for a dex pc.
+ const Instruction& InstructionAt(uint32_t dex_pc) const {
+ DCHECK_LT(dex_pc, InsnsSizeInCodeUnits());
+ return *Instruction::At(insns_ + dex_pc);
+ }
+
+ // Return true if the accessor has a code item.
+ bool HasCodeItem() const {
+ return Insns() != nullptr;
+ }
+
+ protected:
+ CodeItemInstructionAccessor() = default;
+
+ ALWAYS_INLINE void Init(uint32_t insns_size_in_code_units, const uint16_t* insns);
+ ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item);
+
+ private:
+ // size of the insns array, in 2 byte code units. 0 if there is no code item.
+ uint32_t insns_size_in_code_units_ = 0;
+
+ // Pointer to the instructions, null if there is no code item.
+ const uint16_t* insns_ = 0;
+};
+
+// Abstracts accesses to code item fields other than debug info for CompactDexFile and
+// StandardDexFile.
+class CodeItemDataAccessor : public CodeItemInstructionAccessor {
+ public:
+ ALWAYS_INLINE CodeItemDataAccessor(const DexFile& dex_file, const DexFile::CodeItem* code_item);
+
+ uint16_t RegistersSize() const {
+ return registers_size_;
+ }
+
+ uint16_t InsSize() const {
+ return ins_size_;
+ }
+
+ uint16_t OutsSize() const {
+ return outs_size_;
+ }
+
+ uint16_t TriesSize() const {
+ return tries_size_;
+ }
+
+ IterationRange<const DexFile::TryItem*> TryItems() const;
+
+ const uint8_t* GetCatchHandlerData(size_t offset = 0) const;
+
+ const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const;
+
+ inline const void* CodeItemDataEnd() const;
+
+ protected:
+ CodeItemDataAccessor() = default;
+
+ ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
+ ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item);
+
+ private:
+ // Fields mirrored from the dex/cdex code item.
+ uint16_t registers_size_;
+ uint16_t ins_size_;
+ uint16_t outs_size_;
+ uint16_t tries_size_;
+};
+
+// Abstract accesses to code item data including debug info offset. More heavy weight than the other
+// helpers.
+class CodeItemDebugInfoAccessor : public CodeItemDataAccessor {
+ public:
+ CodeItemDebugInfoAccessor() = default;
+
+ // Initialize with an existing offset.
+ ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item,
+ uint32_t dex_method_index) {
+ Init(dex_file, code_item, dex_method_index);
+ }
+
+ ALWAYS_INLINE void Init(const DexFile& dex_file,
+ const DexFile::CodeItem* code_item,
+ uint32_t dex_method_index);
+
+ ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method);
+
+ uint32_t DebugInfoOffset() const {
+ return debug_info_offset_;
+ }
+
+ template<typename NewLocalCallback>
+ bool DecodeDebugLocalInfo(bool is_static,
+ uint32_t method_idx,
+ NewLocalCallback new_local,
+ void* context) const;
+
+ protected:
+ ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index);
+ ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
+
+ private:
+ const DexFile* dex_file_ = nullptr;
+ uint32_t debug_info_offset_ = 0u;
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_H_
diff --git a/libdexfile/dex/code_item_accessors_test.cc b/libdexfile/dex/code_item_accessors_test.cc
new file mode 100644
index 0000000000..2bb4dde649
--- /dev/null
+++ b/libdexfile/dex/code_item_accessors_test.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "code_item_accessors-inl.h"
+
+#include <sys/mman.h>
+#include <memory>
+#include <vector>
+
+#include "dex_file_loader.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+class CodeItemAccessorsTest : public testing::Test {};
+
+std::unique_ptr<const DexFile> CreateFakeDex(bool compact_dex, std::vector<uint8_t>* data) {
+ data->resize(kPageSize);
+ if (compact_dex) {
+ CompactDexFile::Header* header =
+ const_cast<CompactDexFile::Header*>(CompactDexFile::Header::At(data->data()));
+ CompactDexFile::WriteMagic(header->magic_);
+ CompactDexFile::WriteCurrentVersion(header->magic_);
+ header->data_off_ = 0;
+ header->data_size_ = data->size();
+ } else {
+ StandardDexFile::WriteMagic(data->data());
+ StandardDexFile::WriteCurrentVersion(data->data());
+ }
+ const DexFileLoader dex_file_loader;
+ std::string error_msg;
+ std::unique_ptr<const DexFile> dex(dex_file_loader.Open(data->data(),
+ data->size(),
+ "location",
+ /*location_checksum*/ 123,
+ /*oat_dex_file*/nullptr,
+ /*verify*/false,
+ /*verify_checksum*/false,
+ &error_msg));
+ CHECK(dex != nullptr) << error_msg;
+ return dex;
+}
+
+TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor) {
+ std::vector<uint8_t> standard_dex_data;
+ std::unique_ptr<const DexFile> standard_dex(CreateFakeDex(/*compact_dex*/false,
+ &standard_dex_data));
+ ASSERT_TRUE(standard_dex != nullptr);
+ std::vector<uint8_t> compact_dex_data;
+ std::unique_ptr<const DexFile> compact_dex(CreateFakeDex(/*compact_dex*/true,
+ &compact_dex_data));
+ ASSERT_TRUE(compact_dex != nullptr);
+ static constexpr uint16_t kRegisterSize = 2;
+ static constexpr uint16_t kInsSize = 1;
+ static constexpr uint16_t kOutsSize = 3;
+ static constexpr uint16_t kTriesSize = 4;
+ // debug_info_off_ is not accessible from the helpers yet.
+ static constexpr size_t kInsnsSizeInCodeUnits = 5;
+
+ auto verify_code_item = [&](const DexFile* dex,
+ const DexFile::CodeItem* item,
+ const uint16_t* insns) {
+ CodeItemInstructionAccessor insns_accessor(*dex, item);
+ EXPECT_TRUE(insns_accessor.HasCodeItem());
+ ASSERT_EQ(insns_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits);
+ EXPECT_EQ(insns_accessor.Insns(), insns);
+
+ CodeItemDataAccessor data_accessor(*dex, item);
+ EXPECT_TRUE(data_accessor.HasCodeItem());
+ EXPECT_EQ(data_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits);
+ EXPECT_EQ(data_accessor.Insns(), insns);
+ EXPECT_EQ(data_accessor.RegistersSize(), kRegisterSize);
+ EXPECT_EQ(data_accessor.InsSize(), kInsSize);
+ EXPECT_EQ(data_accessor.OutsSize(), kOutsSize);
+ EXPECT_EQ(data_accessor.TriesSize(), kTriesSize);
+ };
+
+ StandardDexFile::CodeItem* dex_code_item =
+ reinterpret_cast<StandardDexFile::CodeItem*>(const_cast<uint8_t*>(standard_dex->Begin()));
+ dex_code_item->registers_size_ = kRegisterSize;
+ dex_code_item->ins_size_ = kInsSize;
+ dex_code_item->outs_size_ = kOutsSize;
+ dex_code_item->tries_size_ = kTriesSize;
+ dex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits;
+ verify_code_item(standard_dex.get(), dex_code_item, dex_code_item->insns_);
+
+ CompactDexFile::CodeItem* cdex_code_item =
+ reinterpret_cast<CompactDexFile::CodeItem*>(const_cast<uint8_t*>(compact_dex->Begin() +
+ CompactDexFile::CodeItem::kMaxPreHeaderSize * sizeof(uint16_t)));
+ std::vector<uint16_t> preheader;
+ cdex_code_item->Create(kRegisterSize,
+ kInsSize,
+ kOutsSize,
+ kTriesSize,
+ kInsnsSizeInCodeUnits,
+ cdex_code_item->GetPreHeader());
+
+ verify_code_item(compact_dex.get(), cdex_code_item, cdex_code_item->insns_);
+}
+
+} // namespace art
diff --git a/libdexfile/dex/compact_dex_debug_info.cc b/libdexfile/dex/compact_dex_debug_info.cc
new file mode 100644
index 0000000000..19495ca92c
--- /dev/null
+++ b/libdexfile/dex/compact_dex_debug_info.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compact_dex_debug_info.h"
+
+#include "compact_dex_utils.h"
+#include "leb128.h"
+
+namespace art {
+
+constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex;
+
+CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
+ uint32_t debug_info_base,
+ uint32_t debug_info_table_offset)
+ : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)),
+ debug_info_base_(debug_info_base),
+ data_begin_(data_begin) {}
+
+uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const {
+ const uint32_t offset = table_[method_idx / kElementsPerIndex];
+ const size_t bit_index = method_idx % kElementsPerIndex;
+
+ const uint8_t* block = data_begin_ + offset;
+ uint16_t bit_mask = *block;
+ ++block;
+ bit_mask = (bit_mask << kBitsPerByte) | *block;
+ ++block;
+ if ((bit_mask & (1 << bit_index)) == 0) {
+ // Bit is not set means the offset is 0 for the debug info.
+ return 0u;
+ }
+ // Trim off the bits above the index we want and count how many bits are set. This is how many
+ // lebs we need to decode.
+ size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index));
+ DCHECK_GT(count, 0u);
+ uint32_t current_offset = debug_info_base_;
+ do {
+ current_offset += DecodeUnsignedLeb128(&block);
+ --count;
+ } while (count > 0);
+ return current_offset;
+}
+
+void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets,
+ std::vector<uint8_t>* out_data,
+ uint32_t* out_min_offset,
+ uint32_t* out_table_offset) {
+ DCHECK(out_data != nullptr);
+ DCHECK(out_data->empty());
+ // Calculate the base offset and return it.
+ *out_min_offset = std::numeric_limits<uint32_t>::max();
+ for (const uint32_t offset : debug_info_offsets) {
+ if (offset != 0u) {
+ *out_min_offset = std::min(*out_min_offset, offset);
+ }
+ }
+ // Write the leb blocks and store the important offsets (each kElementsPerIndex elements).
+ size_t block_start = 0;
+
+ std::vector<uint32_t> offset_table;
+
+ // Write data first then the table.
+ while (block_start < debug_info_offsets.size()) {
+ // Write the offset of the block for each block.
+ offset_table.push_back(out_data->size());
+
+ // Block size of up to kElementsPerIndex
+ const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex);
+
+ // Calculate bit mask since need to write that first.
+ uint16_t bit_mask = 0u;
+ for (size_t i = 0; i < block_size; ++i) {
+ if (debug_info_offsets[block_start + i] != 0u) {
+ bit_mask |= 1 << i;
+ }
+ }
+ // Write bit mask.
+ out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte));
+ out_data->push_back(static_cast<uint8_t>(bit_mask));
+
+ // Write debug info offsets relative to the current offset.
+ uint32_t current_offset = *out_min_offset;
+ for (size_t i = 0; i < block_size; ++i) {
+ const uint32_t debug_info_offset = debug_info_offsets[block_start + i];
+ if (debug_info_offset != 0u) {
+ uint32_t delta = debug_info_offset - current_offset;
+ EncodeUnsignedLeb128(out_data, delta);
+ current_offset = debug_info_offset;
+ }
+ }
+
+ block_start += block_size;
+ }
+
+ // Write the offset table.
+ AlignmentPadVector(out_data, alignof(uint32_t));
+ *out_table_offset = out_data->size();
+ out_data->insert(out_data->end(),
+ reinterpret_cast<const uint8_t*>(&offset_table[0]),
+ reinterpret_cast<const uint8_t*>(&offset_table[0] + offset_table.size()));
+}
+
+} // namespace art
diff --git a/libdexfile/dex/compact_dex_debug_info.h b/libdexfile/dex/compact_dex_debug_info.h
new file mode 100644
index 0000000000..bfd0bbe65c
--- /dev/null
+++ b/libdexfile/dex/compact_dex_debug_info.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
+#define ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
+
+#include <cstdint>
+#include <vector>
+
+namespace art {
+
+// Debug offset table for compact dex, aims to minimize size while still providing reasonable
+// speed (10-20ns average time per lookup on host).
+class CompactDexDebugInfoOffsetTable {
+ public:
+ // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the
+ // integer is modified.
+ static constexpr size_t kElementsPerIndex = 16;
+
+ // Leb block format:
+ // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk.
+ // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset
+ // changes compared to the previous index.
+
+ class Accessor {
+ public:
+ Accessor(const uint8_t* data_begin,
+ uint32_t debug_info_base,
+ uint32_t debug_info_table_offset);
+
+ // Return the debug info for a method index (or 0 if it doesn't have one).
+ uint32_t GetDebugInfoOffset(uint32_t method_idx) const;
+
+ private:
+ const uint32_t* const table_;
+ const uint32_t debug_info_base_;
+ const uint8_t* const data_begin_;
+ };
+
+ // Returned offsets are all relative to debug_info_offsets.
+ static void Build(const std::vector<uint32_t>& debug_info_offsets,
+ std::vector<uint8_t>* out_data,
+ uint32_t* out_min_offset,
+ uint32_t* out_table_offset);
+
+ // 32 bit aligned for the offset table.
+ static constexpr size_t kAlignment = sizeof(uint32_t);
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
diff --git a/libdexfile/dex/compact_dex_debug_info_test.cc b/libdexfile/dex/compact_dex_debug_info_test.cc
new file mode 100644
index 0000000000..3267612443
--- /dev/null
+++ b/libdexfile/dex/compact_dex_debug_info_test.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include "base/logging.h"
+#include "dex/compact_dex_debug_info.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(CompactDexDebugInfoTest, TestBuildAndAccess) {
+ const size_t kDebugInfoMinOffset = 1234567;
+ std::vector<uint32_t> offsets = {
+ 0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0,
+ std::numeric_limits<uint32_t>::max() - kDebugInfoMinOffset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12,
+ };
+ // Add some large offset since the debug info section will never be that close to the beginning
+ // of the file.
+ for (uint32_t& offset : offsets) {
+ if (offset != 0u) {
+ offset += kDebugInfoMinOffset;
+ }
+ }
+
+ std::vector<uint8_t> data;
+ uint32_t base_offset = 0;
+ uint32_t table_offset = 0;
+ CompactDexDebugInfoOffsetTable::Build(offsets,
+ /*out*/ &data,
+ /*out*/ &base_offset,
+ /*out*/ &table_offset);
+ EXPECT_GE(base_offset, kDebugInfoMinOffset);
+ EXPECT_LT(table_offset, data.size());
+ ASSERT_GT(data.size(), 0u);
+ const size_t before_size = offsets.size() * sizeof(offsets.front());
+ EXPECT_LT(data.size(), before_size);
+
+ // Note that the accessor requires the data to be aligned. Use memmap to accomplish this.
+ std::string error_msg;
+ // Leave some extra room since we don't copy the table at the start (for testing).
+ constexpr size_t kExtraOffset = 4 * 128;
+ std::vector<uint8_t> fake_dex(data.size() + kExtraOffset);
+ std::copy(data.begin(), data.end(), fake_dex.data() + kExtraOffset);
+
+ CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex.data() + kExtraOffset,
+ base_offset,
+ table_offset);
+ for (size_t i = 0; i < offsets.size(); ++i) {
+ EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i));
+ }
+
+ // Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller
+ // for sorted increasing order.
+ std::sort(offsets.begin(), offsets.end());
+ std::vector<uint8_t> sorted_data;
+ CompactDexDebugInfoOffsetTable::Build(offsets,
+ /*out*/ &sorted_data,
+ /*out*/ &base_offset,
+ /*out*/ &table_offset);
+ EXPECT_LT(sorted_data.size(), data.size());
+ {
+ ScopedLogSeverity sls(LogSeverity::INFO);
+ LOG(INFO) << "raw size " << before_size
+ << " table size " << data.size()
+ << " sorted table size " << sorted_data.size();
+ }
+}
+
+} // namespace art
diff --git a/libdexfile/dex/compact_dex_file.cc b/libdexfile/dex/compact_dex_file.cc
new file mode 100644
index 0000000000..ce289d4d7b
--- /dev/null
+++ b/libdexfile/dex/compact_dex_file.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compact_dex_file.h"
+
+#include "code_item_accessors-inl.h"
+#include "dex_file-inl.h"
+#include "leb128.h"
+
+namespace art {
+
+constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize];
+constexpr uint8_t CompactDexFile::kDexMagicVersion[];
+
+void CompactDexFile::WriteMagic(uint8_t* magic) {
+ std::copy_n(kDexMagic, kDexMagicSize, magic);
+}
+
+void CompactDexFile::WriteCurrentVersion(uint8_t* magic) {
+ std::copy_n(kDexMagicVersion, kDexVersionLen, magic + kDexMagicSize);
+}
+
+bool CompactDexFile::IsMagicValid(const uint8_t* magic) {
+ return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
+}
+
+bool CompactDexFile::IsVersionValid(const uint8_t* magic) {
+ const uint8_t* version = &magic[sizeof(kDexMagic)];
+ return memcmp(version, kDexMagicVersion, kDexVersionLen) == 0;
+}
+
+bool CompactDexFile::IsMagicValid() const {
+ return IsMagicValid(header_->magic_);
+}
+
+bool CompactDexFile::IsVersionValid() const {
+ return IsVersionValid(header_->magic_);
+}
+
+bool CompactDexFile::SupportsDefaultMethods() const {
+ return (GetHeader().GetFeatureFlags() &
+ static_cast<uint32_t>(FeatureFlags::kDefaultMethods)) != 0;
+}
+
+uint32_t CompactDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const {
+ DCHECK(IsInDataSection(&item));
+ return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) -
+ reinterpret_cast<uintptr_t>(&item);
+}
+
+
+uint32_t CompactDexFile::CalculateChecksum(const uint8_t* base_begin,
+ size_t base_size,
+ const uint8_t* data_begin,
+ size_t data_size) {
+ Header temp_header(*Header::At(base_begin));
+ // Zero out fields that are not included in the sum.
+ temp_header.checksum_ = 0u;
+ temp_header.data_off_ = 0u;
+ temp_header.data_size_ = 0u;
+ uint32_t checksum = ChecksumMemoryRange(reinterpret_cast<const uint8_t*>(&temp_header),
+ sizeof(temp_header));
+ // Exclude the header since we already computed it's checksum.
+ checksum = (checksum * 31) ^ ChecksumMemoryRange(base_begin + sizeof(temp_header),
+ base_size - sizeof(temp_header));
+ checksum = (checksum * 31) ^ ChecksumMemoryRange(data_begin, data_size);
+ return checksum;
+}
+
+uint32_t CompactDexFile::CalculateChecksum() const {
+ return CalculateChecksum(Begin(), Size(), DataBegin(), DataSize());
+}
+
+CompactDexFile::CompactDexFile(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_begin,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container)
+ : DexFile(base,
+ size,
+ data_begin,
+ data_size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ container,
+ /*is_compact_dex*/ true),
+ debug_info_offsets_(DataBegin() + GetHeader().debug_info_offsets_pos_,
+ GetHeader().debug_info_base_,
+ GetHeader().debug_info_offsets_table_offset_) {}
+
+} // namespace art
diff --git a/libdexfile/dex/compact_dex_file.h b/libdexfile/dex/compact_dex_file.h
new file mode 100644
index 0000000000..47b170c4a8
--- /dev/null
+++ b/libdexfile/dex/compact_dex_file.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_
+#define ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_
+
+#include "base/casts.h"
+#include "dex_file.h"
+#include "dex/compact_dex_debug_info.h"
+
+namespace art {
+
+// CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage.
+class CompactDexFile : public DexFile {
+ public:
+ static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' };
+ static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'};
+
+ enum class FeatureFlags : uint32_t {
+ kDefaultMethods = 0x1,
+ };
+
+ class Header : public DexFile::Header {
+ public:
+ static const Header* At(const void* at) {
+ return reinterpret_cast<const Header*>(at);
+ }
+
+ uint32_t GetFeatureFlags() const {
+ return feature_flags_;
+ }
+
+ uint32_t GetDataOffset() const {
+ return data_off_;
+ }
+
+ uint32_t GetDataSize() const {
+ return data_size_;
+ }
+
+ private:
+ uint32_t feature_flags_ = 0u;
+
+ // Position in the compact dex file for the debug info table data starts.
+ uint32_t debug_info_offsets_pos_ = 0u;
+
+ // Offset into the debug info table data where the lookup table is.
+ uint32_t debug_info_offsets_table_offset_ = 0u;
+
+ // Base offset of where debug info starts in the dex file.
+ uint32_t debug_info_base_ = 0u;
+
+ friend class CompactDexFile;
+ friend class CompactDexWriter;
+ };
+
+ // Like the standard code item except without a debug info offset. Each code item may have a
+ // preheader to encode large methods. In 99% of cases, the preheader is not used. This enables
+ // smaller size with a good fast path case in the accessors.
+ struct CodeItem : public DexFile::CodeItem {
+ static constexpr size_t kAlignment = sizeof(uint16_t);
+ // Max preheader size in uint16_ts.
+ static constexpr size_t kMaxPreHeaderSize = 6;
+
+ private:
+ CodeItem() = default;
+
+ static constexpr size_t kRegistersSizeShift = 12;
+ static constexpr size_t kInsSizeShift = 8;
+ static constexpr size_t kOutsSizeShift = 4;
+ static constexpr size_t kTriesSizeSizeShift = 0;
+ static constexpr uint16_t kFlagPreHeaderRegisterSize = 0x1 << 0;
+ static constexpr uint16_t kFlagPreHeaderInsSize = 0x1 << 1;
+ static constexpr uint16_t kFlagPreHeaderOutsSize = 0x1 << 2;
+ static constexpr uint16_t kFlagPreHeaderTriesSize = 0x1 << 3;
+ static constexpr uint16_t kFlagPreHeaderInsnsSize = 0x1 << 4;
+ static constexpr size_t kInsnsSizeShift = 5;
+ static constexpr size_t kInsnsSizeBits = sizeof(uint16_t) * kBitsPerByte - kInsnsSizeShift;
+
+ // Combined preheader flags for fast testing if we need to go slow path.
+ static constexpr uint16_t kFlagPreHeaderCombined =
+ kFlagPreHeaderRegisterSize |
+ kFlagPreHeaderInsSize |
+ kFlagPreHeaderOutsSize |
+ kFlagPreHeaderTriesSize |
+ kFlagPreHeaderInsnsSize;
+
+ // Create a code item and associated preheader if required based on field values.
+ // Returns the start of the preheader. The preheader buffer must be at least as large as
+ // kMaxPreHeaderSize;
+ uint16_t* Create(uint16_t registers_size,
+ uint16_t ins_size,
+ uint16_t outs_size,
+ uint16_t tries_size,
+ uint32_t insns_size_in_code_units,
+ uint16_t* out_preheader) {
+ // Dex verification ensures that registers size > ins_size, so we can subtract the registers
+ // size accordingly to reduce how often we need to use the preheader.
+ DCHECK_GE(registers_size, ins_size);
+ registers_size -= ins_size;
+ fields_ = (registers_size & 0xF) << kRegistersSizeShift;
+ fields_ |= (ins_size & 0xF) << kInsSizeShift;
+ fields_ |= (outs_size & 0xF) << kOutsSizeShift;
+ fields_ |= (tries_size & 0xF) << kTriesSizeSizeShift;
+ registers_size &= ~0xF;
+ ins_size &= ~0xF;
+ outs_size &= ~0xF;
+ tries_size &= ~0xF;
+ insns_count_and_flags_ = 0;
+ const size_t masked_count = insns_size_in_code_units & ((1 << kInsnsSizeBits) - 1);
+ insns_count_and_flags_ |= masked_count << kInsnsSizeShift;
+ insns_size_in_code_units -= masked_count;
+
+ // Since the preheader case is rare (1% of code items), use a suboptimally large but fast
+ // decoding format.
+ if (insns_size_in_code_units != 0) {
+ insns_count_and_flags_ |= kFlagPreHeaderInsnsSize;
+ --out_preheader;
+ *out_preheader = static_cast<uint16_t>(insns_size_in_code_units);
+ --out_preheader;
+ *out_preheader = static_cast<uint16_t>(insns_size_in_code_units >> 16);
+ }
+ auto preheader_encode = [&](uint16_t size, uint16_t flag) {
+ if (size != 0) {
+ insns_count_and_flags_ |= flag;
+ --out_preheader;
+ *out_preheader = size;
+ }
+ };
+ preheader_encode(registers_size, kFlagPreHeaderRegisterSize);
+ preheader_encode(ins_size, kFlagPreHeaderInsSize);
+ preheader_encode(outs_size, kFlagPreHeaderOutsSize);
+ preheader_encode(tries_size, kFlagPreHeaderTriesSize);
+ return out_preheader;
+ }
+
+ ALWAYS_INLINE bool HasPreHeader(uint16_t flag) const {
+ return (insns_count_and_flags_ & flag) != 0;
+ }
+
+ // Return true if the code item has any preheaders.
+ ALWAYS_INLINE static bool HasAnyPreHeader(uint16_t insns_count_and_flags) {
+ return (insns_count_and_flags & kFlagPreHeaderCombined) != 0;
+ }
+
+ ALWAYS_INLINE uint16_t* GetPreHeader() {
+ return reinterpret_cast<uint16_t*>(this);
+ }
+
+ ALWAYS_INLINE const uint16_t* GetPreHeader() const {
+ return reinterpret_cast<const uint16_t*>(this);
+ }
+
+ // Decode fields and read the preheader if necessary. If kDecodeOnlyInstructionCount is
+ // specified then only the instruction count is decoded.
+ template <bool kDecodeOnlyInstructionCount>
+ ALWAYS_INLINE void DecodeFields(uint32_t* insns_count,
+ uint16_t* registers_size,
+ uint16_t* ins_size,
+ uint16_t* outs_size,
+ uint16_t* tries_size) const {
+ *insns_count = insns_count_and_flags_ >> kInsnsSizeShift;
+ if (!kDecodeOnlyInstructionCount) {
+ const uint16_t fields = fields_;
+ *registers_size = (fields >> kRegistersSizeShift) & 0xF;
+ *ins_size = (fields >> kInsSizeShift) & 0xF;
+ *outs_size = (fields >> kOutsSizeShift) & 0xF;
+ *tries_size = (fields >> kTriesSizeSizeShift) & 0xF;
+ }
+ if (UNLIKELY(HasAnyPreHeader(insns_count_and_flags_))) {
+ const uint16_t* preheader = GetPreHeader();
+ if (HasPreHeader(kFlagPreHeaderInsnsSize)) {
+ --preheader;
+ *insns_count += static_cast<uint32_t>(*preheader);
+ --preheader;
+ *insns_count += static_cast<uint32_t>(*preheader) << 16;
+ }
+ if (!kDecodeOnlyInstructionCount) {
+ if (HasPreHeader(kFlagPreHeaderRegisterSize)) {
+ --preheader;
+ *registers_size += preheader[0];
+ }
+ if (HasPreHeader(kFlagPreHeaderInsSize)) {
+ --preheader;
+ *ins_size += preheader[0];
+ }
+ if (HasPreHeader(kFlagPreHeaderOutsSize)) {
+ --preheader;
+ *outs_size += preheader[0];
+ }
+ if (HasPreHeader(kFlagPreHeaderTriesSize)) {
+ --preheader;
+ *tries_size += preheader[0];
+ }
+ }
+ }
+ if (!kDecodeOnlyInstructionCount) {
+ *registers_size += *ins_size;
+ }
+ }
+
+ // Packed code item data, 4 bits each: [registers_size, ins_size, outs_size, tries_size]
+ uint16_t fields_;
+
+ // 5 bits for if either of the fields required preheader extension, 11 bits for the number of
+ // instruction code units.
+ uint16_t insns_count_and_flags_;
+
+ uint16_t insns_[1]; // actual array of bytecode.
+
+ ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
+ ART_FRIEND_TEST(CompactDexFileTest, CodeItemFields);
+ friend class CodeItemDataAccessor;
+ friend class CodeItemDebugInfoAccessor;
+ friend class CodeItemInstructionAccessor;
+ friend class CompactDexFile;
+ friend class CompactDexWriter;
+ DISALLOW_COPY_AND_ASSIGN(CodeItem);
+ };
+
+ // Write the compact dex specific magic.
+ static void WriteMagic(uint8_t* magic);
+
+ // Write the current version, note that the input is the address of the magic.
+ static void WriteCurrentVersion(uint8_t* magic);
+
+ // Returns true if the byte string points to the magic value.
+ static bool IsMagicValid(const uint8_t* magic);
+ virtual bool IsMagicValid() const OVERRIDE;
+
+ // Returns true if the byte string after the magic is the correct value.
+ static bool IsVersionValid(const uint8_t* magic);
+ virtual bool IsVersionValid() const OVERRIDE;
+
+ // TODO This is completely a guess. We really need to do better. b/72402467
+ // We ask for 64 megabytes which should be big enough for any realistic dex file.
+ virtual size_t GetDequickenedSize() const OVERRIDE {
+ return 64 * MB;
+ }
+
+ const Header& GetHeader() const {
+ return down_cast<const Header&>(DexFile::GetHeader());
+ }
+
+ virtual bool SupportsDefaultMethods() const OVERRIDE;
+
+ uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
+
+ uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const {
+ return debug_info_offsets_.GetDebugInfoOffset(dex_method_index);
+ }
+
+ static uint32_t CalculateChecksum(const uint8_t* base_begin,
+ size_t base_size,
+ const uint8_t* data_begin,
+ size_t data_size);
+ virtual uint32_t CalculateChecksum() const OVERRIDE;
+
+ private:
+ CompactDexFile(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_begin,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container);
+
+ CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_;
+
+ friend class DexFile;
+ friend class DexFileLoader;
+ DISALLOW_COPY_AND_ASSIGN(CompactDexFile);
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_FILE_H_
diff --git a/libdexfile/dex/compact_dex_file_test.cc b/libdexfile/dex/compact_dex_file_test.cc
new file mode 100644
index 0000000000..517c5873ed
--- /dev/null
+++ b/libdexfile/dex/compact_dex_file_test.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "compact_dex_file.h"
+#include "dex_file_loader.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(CompactDexFileTest, MagicAndVersion) {
+ // Test permutations of valid/invalid headers.
+ for (size_t i = 0; i < 2; ++i) {
+ for (size_t j = 0; j < 2; ++j) {
+ static const size_t len = CompactDexFile::kDexVersionLen + CompactDexFile::kDexMagicSize;
+ uint8_t header[len] = {};
+ std::fill_n(header, len, 0x99);
+ const bool valid_magic = (i & 1) == 0;
+ const bool valid_version = (j & 1) == 0;
+ if (valid_magic) {
+ CompactDexFile::WriteMagic(header);
+ }
+ if (valid_version) {
+ CompactDexFile::WriteCurrentVersion(header);
+ }
+ EXPECT_EQ(valid_magic, CompactDexFile::IsMagicValid(header));
+ EXPECT_EQ(valid_version, CompactDexFile::IsVersionValid(header));
+ EXPECT_EQ(valid_magic, DexFileLoader::IsMagicValid(header));
+ EXPECT_EQ(valid_magic && valid_version, DexFileLoader::IsVersionAndMagicValid(header));
+ }
+ }
+}
+
+TEST(CompactDexFileTest, CodeItemFields) {
+ auto test_and_write = [&] (uint16_t registers_size,
+ uint16_t ins_size,
+ uint16_t outs_size,
+ uint16_t tries_size,
+ uint32_t insns_size_in_code_units) {
+ ASSERT_GE(registers_size, ins_size);
+ uint16_t buffer[sizeof(CompactDexFile::CodeItem) +
+ CompactDexFile::CodeItem::kMaxPreHeaderSize] = {};
+ CompactDexFile::CodeItem* code_item = reinterpret_cast<CompactDexFile::CodeItem*>(
+ &buffer[CompactDexFile::CodeItem::kMaxPreHeaderSize]);
+ const uint16_t* preheader_ptr = code_item->Create(registers_size,
+ ins_size,
+ outs_size,
+ tries_size,
+ insns_size_in_code_units,
+ code_item->GetPreHeader());
+ ASSERT_GT(preheader_ptr, buffer);
+
+ uint16_t out_registers_size;
+ uint16_t out_ins_size;
+ uint16_t out_outs_size;
+ uint16_t out_tries_size;
+ uint32_t out_insns_size_in_code_units;
+ code_item->DecodeFields</*kDecodeOnlyInstructionCount*/false>(&out_insns_size_in_code_units,
+ &out_registers_size,
+ &out_ins_size,
+ &out_outs_size,
+ &out_tries_size);
+ ASSERT_EQ(registers_size, out_registers_size);
+ ASSERT_EQ(ins_size, out_ins_size);
+ ASSERT_EQ(outs_size, out_outs_size);
+ ASSERT_EQ(tries_size, out_tries_size);
+ ASSERT_EQ(insns_size_in_code_units, out_insns_size_in_code_units);
+
+ ++out_insns_size_in_code_units; // Force value to change.
+ code_item->DecodeFields</*kDecodeOnlyInstructionCount*/true>(&out_insns_size_in_code_units,
+ /*registers_size*/ nullptr,
+ /*ins_size*/ nullptr,
+ /*outs_size*/ nullptr,
+ /*tries_size*/ nullptr);
+ ASSERT_EQ(insns_size_in_code_units, out_insns_size_in_code_units);
+ };
+ static constexpr uint32_t kMax32 = std::numeric_limits<uint32_t>::max();
+ static constexpr uint16_t kMax16 = std::numeric_limits<uint16_t>::max();
+ test_and_write(0, 0, 0, 0, 0);
+ test_and_write(kMax16, kMax16, kMax16, kMax16, kMax32);
+ test_and_write(kMax16 - 1, kMax16 - 2, kMax16 - 3, kMax16 - 4, kMax32 - 5);
+ test_and_write(kMax16 - 4, kMax16 - 5, kMax16 - 3, kMax16 - 2, kMax32 - 1);
+ test_and_write(5, 4, 3, 2, 1);
+ test_and_write(5, 0, 3, 2, 1);
+ test_and_write(kMax16, 0, kMax16 / 2, 1234, kMax32 / 4);
+}
+
+} // namespace art
diff --git a/libdexfile/dex/compact_dex_level.h b/libdexfile/dex/compact_dex_level.h
new file mode 100644
index 0000000000..2325ac2cae
--- /dev/null
+++ b/libdexfile/dex/compact_dex_level.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_COMPACT_DEX_LEVEL_H_
+#define ART_LIBDEXFILE_DEX_COMPACT_DEX_LEVEL_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "dex_file.h"
+
+namespace art {
+
+// Optimization level for compact dex generation.
+enum class CompactDexLevel {
+ // Level none means not generated.
+ kCompactDexLevelNone,
+ // Level fast means optimizations that don't take many resources to perform.
+ kCompactDexLevelFast,
+};
+
+#ifndef ART_DEFAULT_COMPACT_DEX_LEVEL
+#error ART_DEFAULT_COMPACT_DEX_LEVEL not specified.
+#else
+#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_fast CompactDexLevel::kCompactDexLevelFast
+#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_none CompactDexLevel::kCompactDexLevelNone
+
+#define ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT APPEND_TOKENS_AFTER_EVAL( \
+ ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_, \
+ ART_DEFAULT_COMPACT_DEX_LEVEL)
+
+static constexpr CompactDexLevel kDefaultCompactDexLevel = ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT;
+#endif
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_LEVEL_H_
diff --git a/libdexfile/dex/compact_dex_utils.h b/libdexfile/dex/compact_dex_utils.h
new file mode 100644
index 0000000000..c88b799e1f
--- /dev/null
+++ b/libdexfile/dex/compact_dex_utils.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_COMPACT_DEX_UTILS_H_
+#define ART_LIBDEXFILE_DEX_COMPACT_DEX_UTILS_H_
+
+#include <vector>
+
+#include "base/bit_utils.h"
+
+namespace art {
+
+// Add padding to the end of the array until the size is aligned.
+template <typename T, template<typename> class Allocator>
+static inline void AlignmentPadVector(std::vector<T, Allocator<T>>* dest,
+ size_t alignment) {
+ while (!IsAlignedParam(dest->size(), alignment)) {
+ dest->push_back(T());
+ }
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_COMPACT_DEX_UTILS_H_
diff --git a/libdexfile/dex/descriptors_names.cc b/libdexfile/dex/descriptors_names.cc
new file mode 100644
index 0000000000..8124e7256f
--- /dev/null
+++ b/libdexfile/dex/descriptors_names.cc
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "descriptors_names.h"
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+
+#include "dex/utf-inl.h"
+
+namespace art {
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+void AppendPrettyDescriptor(const char* descriptor, std::string* result) {
+ // Count the number of '['s to get the dimensionality.
+ const char* c = descriptor;
+ size_t dim = 0;
+ while (*c == '[') {
+ dim++;
+ c++;
+ }
+
+ // Reference or primitive?
+ if (*c == 'L') {
+ // "[[La/b/C;" -> "a.b.C[][]".
+ c++; // Skip the 'L'.
+ } else {
+ // "[[B" -> "byte[][]".
+ // To make life easier, we make primitives look like unqualified
+ // reference types.
+ switch (*c) {
+ case 'B': c = "byte;"; break;
+ case 'C': c = "char;"; break;
+ case 'D': c = "double;"; break;
+ case 'F': c = "float;"; break;
+ case 'I': c = "int;"; break;
+ case 'J': c = "long;"; break;
+ case 'S': c = "short;"; break;
+ case 'Z': c = "boolean;"; break;
+ case 'V': c = "void;"; break; // Used when decoding return types.
+ default: result->append(descriptor); return;
+ }
+ }
+
+ // At this point, 'c' is a string of the form "fully/qualified/Type;"
+ // or "primitive;". Rewrite the type with '.' instead of '/':
+ const char* p = c;
+ while (*p != ';') {
+ char ch = *p++;
+ if (ch == '/') {
+ ch = '.';
+ }
+ result->push_back(ch);
+ }
+ // ...and replace the semicolon with 'dim' "[]" pairs:
+ for (size_t i = 0; i < dim; ++i) {
+ result->append("[]");
+ }
+}
+
+std::string PrettyDescriptor(const char* descriptor) {
+ std::string result;
+ AppendPrettyDescriptor(descriptor, &result);
+ return result;
+}
+
+std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
+ // Remove the leading 'L' and trailing ';'...
+ std::string class_name(class_descriptor);
+ CHECK_EQ(class_name[0], 'L') << class_name;
+ CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name;
+ class_name.erase(0, 1);
+ class_name.erase(class_name.size() - 1, 1);
+
+ std::string short_name;
+ short_name += "Java_";
+ short_name += MangleForJni(class_name);
+ short_name += "_";
+ short_name += MangleForJni(method);
+ return short_name;
+}
+
+// See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615 for the full rules.
+std::string MangleForJni(const std::string& s) {
+ std::string result;
+ size_t char_count = CountModifiedUtf8Chars(s.c_str());
+ const char* cp = &s[0];
+ for (size_t i = 0; i < char_count; ++i) {
+ uint32_t ch = GetUtf16FromUtf8(&cp);
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
+ result.push_back(ch);
+ } else if (ch == '.' || ch == '/') {
+ result += "_";
+ } else if (ch == '_') {
+ result += "_1";
+ } else if (ch == ';') {
+ result += "_2";
+ } else if (ch == '[') {
+ result += "_3";
+ } else {
+ const uint16_t leading = GetLeadingUtf16Char(ch);
+ const uint32_t trailing = GetTrailingUtf16Char(ch);
+
+ StringAppendF(&result, "_0%04x", leading);
+ if (trailing != 0) {
+ StringAppendF(&result, "_0%04x", trailing);
+ }
+ }
+ }
+ return result;
+}
+
+std::string DotToDescriptor(const char* class_name) {
+ std::string descriptor(class_name);
+ std::replace(descriptor.begin(), descriptor.end(), '.', '/');
+ if (descriptor.length() > 0 && descriptor[0] != '[') {
+ descriptor = "L" + descriptor + ";";
+ }
+ return descriptor;
+}
+
+std::string DescriptorToDot(const char* descriptor) {
+ size_t length = strlen(descriptor);
+ if (length > 1) {
+ if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
+ // Descriptors have the leading 'L' and trailing ';' stripped.
+ std::string result(descriptor + 1, length - 2);
+ std::replace(result.begin(), result.end(), '/', '.');
+ return result;
+ } else {
+ // For arrays the 'L' and ';' remain intact.
+ std::string result(descriptor);
+ std::replace(result.begin(), result.end(), '/', '.');
+ return result;
+ }
+ }
+ // Do nothing for non-class/array descriptors.
+ return descriptor;
+}
+
+std::string DescriptorToName(const char* descriptor) {
+ size_t length = strlen(descriptor);
+ if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
+ std::string result(descriptor + 1, length - 2);
+ return result;
+ }
+ return descriptor;
+}
+
+// Helper for IsValidPartOfMemberNameUtf8(), a bit vector indicating valid low ascii.
+static uint32_t DEX_MEMBER_VALID_LOW_ASCII[4] = {
+ 0x00000000, // 00..1f low control characters; nothing valid
+ 0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
+ 0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
+ 0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z'
+};
+
+// Helper for IsValidPartOfMemberNameUtf8(); do not call directly.
+static bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) {
+ /*
+ * It's a multibyte encoded character. Decode it and analyze. We
+ * accept anything that isn't (a) an improperly encoded low value,
+ * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high
+ * control character, or (e) a high space, layout, or special
+ * character (U+00a0, U+2000..U+200f, U+2028..U+202f,
+ * U+fff0..U+ffff). This is all specified in the dex format
+ * document.
+ */
+
+ const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr);
+ const uint16_t leading = GetLeadingUtf16Char(pair);
+
+ // We have a surrogate pair resulting from a valid 4 byte UTF sequence.
+ // No further checks are necessary because 4 byte sequences span code
+ // points [U+10000, U+1FFFFF], which are valid codepoints in a dex
+ // identifier. Furthermore, GetUtf16FromUtf8 guarantees that each of
+ // the surrogate halves are valid and well formed in this instance.
+ if (GetTrailingUtf16Char(pair) != 0) {
+ return true;
+ }
+
+
+ // We've encountered a one, two or three byte UTF-8 sequence. The
+ // three byte UTF-8 sequence could be one half of a surrogate pair.
+ switch (leading >> 8) {
+ case 0x00:
+ // It's only valid if it's above the ISO-8859-1 high space (0xa0).
+ return (leading > 0x00a0);
+ case 0xd8:
+ case 0xd9:
+ case 0xda:
+ case 0xdb:
+ {
+ // We found a three byte sequence encoding one half of a surrogate.
+ // Look for the other half.
+ const uint32_t pair2 = GetUtf16FromUtf8(pUtf8Ptr);
+ const uint16_t trailing = GetLeadingUtf16Char(pair2);
+
+ return (GetTrailingUtf16Char(pair2) == 0) && (0xdc00 <= trailing && trailing <= 0xdfff);
+ }
+ case 0xdc:
+ case 0xdd:
+ case 0xde:
+ case 0xdf:
+ // It's a trailing surrogate, which is not valid at this point.
+ return false;
+ case 0x20:
+ case 0xff:
+ // It's in the range that has spaces, controls, and specials.
+ switch (leading & 0xfff8) {
+ case 0x2000:
+ case 0x2008:
+ case 0x2028:
+ case 0xfff0:
+ case 0xfff8:
+ return false;
+ }
+ return true;
+ default:
+ return true;
+ }
+
+ UNREACHABLE();
+}
+
+/* Return whether the pointed-at modified-UTF-8 encoded character is
+ * valid as part of a member name, updating the pointer to point past
+ * the consumed character. This will consume two encoded UTF-16 code
+ * points if the character is encoded as a surrogate pair. Also, if
+ * this function returns false, then the given pointer may only have
+ * been partially advanced.
+ */
+static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) {
+ uint8_t c = (uint8_t) **pUtf8Ptr;
+ if (LIKELY(c <= 0x7f)) {
+ // It's low-ascii, so check the table.
+ uint32_t wordIdx = c >> 5;
+ uint32_t bitIdx = c & 0x1f;
+ (*pUtf8Ptr)++;
+ return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0;
+ }
+
+ // It's a multibyte encoded character. Call a non-inline function
+ // for the heavy lifting.
+ return IsValidPartOfMemberNameUtf8Slow(pUtf8Ptr);
+}
+
+bool IsValidMemberName(const char* s) {
+ bool angle_name = false;
+
+ switch (*s) {
+ case '\0':
+ // The empty string is not a valid name.
+ return false;
+ case '<':
+ angle_name = true;
+ s++;
+ break;
+ }
+
+ while (true) {
+ switch (*s) {
+ case '\0':
+ return !angle_name;
+ case '>':
+ return angle_name && s[1] == '\0';
+ }
+
+ if (!IsValidPartOfMemberNameUtf8(&s)) {
+ return false;
+ }
+ }
+}
+
+enum ClassNameType { kName, kDescriptor };
+template<ClassNameType kType, char kSeparator>
+static bool IsValidClassName(const char* s) {
+ int arrayCount = 0;
+ while (*s == '[') {
+ arrayCount++;
+ s++;
+ }
+
+ if (arrayCount > 255) {
+ // Arrays may have no more than 255 dimensions.
+ return false;
+ }
+
+ ClassNameType type = kType;
+ if (type != kDescriptor && arrayCount != 0) {
+ /*
+ * If we're looking at an array of some sort, then it doesn't
+ * matter if what is being asked for is a class name; the
+ * format looks the same as a type descriptor in that case, so
+ * treat it as such.
+ */
+ type = kDescriptor;
+ }
+
+ if (type == kDescriptor) {
+ /*
+ * We are looking for a descriptor. Either validate it as a
+ * single-character primitive type, or continue on to check the
+ * embedded class name (bracketed by "L" and ";").
+ */
+ switch (*(s++)) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ // These are all single-character descriptors for primitive types.
+ return (*s == '\0');
+ case 'V':
+ // Non-array void is valid, but you can't have an array of void.
+ return (arrayCount == 0) && (*s == '\0');
+ case 'L':
+ // Class name: Break out and continue below.
+ break;
+ default:
+ // Oddball descriptor character.
+ return false;
+ }
+ }
+
+ /*
+ * We just consumed the 'L' that introduces a class name as part
+ * of a type descriptor, or we are looking for an unadorned class
+ * name.
+ */
+
+ bool sepOrFirst = true; // first character or just encountered a separator.
+ for (;;) {
+ uint8_t c = (uint8_t) *s;
+ switch (c) {
+ case '\0':
+ /*
+ * Premature end for a type descriptor, but valid for
+ * a class name as long as we haven't encountered an
+ * empty component (including the degenerate case of
+ * the empty string "").
+ */
+ return (type == kName) && !sepOrFirst;
+ case ';':
+ /*
+ * Invalid character for a class name, but the
+ * legitimate end of a type descriptor. In the latter
+ * case, make sure that this is the end of the string
+ * and that it doesn't end with an empty component
+ * (including the degenerate case of "L;").
+ */
+ return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0');
+ case '/':
+ case '.':
+ if (c != kSeparator) {
+ // The wrong separator character.
+ return false;
+ }
+ if (sepOrFirst) {
+ // Separator at start or two separators in a row.
+ return false;
+ }
+ sepOrFirst = true;
+ s++;
+ break;
+ default:
+ if (!IsValidPartOfMemberNameUtf8(&s)) {
+ return false;
+ }
+ sepOrFirst = false;
+ break;
+ }
+ }
+}
+
+bool IsValidBinaryClassName(const char* s) {
+ return IsValidClassName<kName, '.'>(s);
+}
+
+bool IsValidJniClassName(const char* s) {
+ return IsValidClassName<kName, '/'>(s);
+}
+
+bool IsValidDescriptor(const char* s) {
+ return IsValidClassName<kDescriptor, '/'>(s);
+}
+
+void Split(const std::string& s, char separator, std::vector<std::string>* result) {
+ const char* p = s.data();
+ const char* end = p + s.size();
+ while (p != end) {
+ if (*p == separator) {
+ ++p;
+ } else {
+ const char* start = p;
+ while (++p != end && *p != separator) {
+ // Skip to the next occurrence of the separator.
+ }
+ result->push_back(std::string(start, p - start));
+ }
+ }
+}
+
+std::string PrettyDescriptor(Primitive::Type type) {
+ return PrettyDescriptor(Primitive::Descriptor(type));
+}
+
+} // namespace art
diff --git a/libdexfile/dex/descriptors_names.h b/libdexfile/dex/descriptors_names.h
new file mode 100644
index 0000000000..10738eead0
--- /dev/null
+++ b/libdexfile/dex/descriptors_names.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DESCRIPTORS_NAMES_H_
+#define ART_LIBDEXFILE_DEX_DESCRIPTORS_NAMES_H_
+
+#include <string>
+
+#include "primitive.h"
+
+namespace art {
+
+// Used to implement PrettyClass, PrettyField, PrettyMethod, and PrettyTypeOf,
+// one of which is probably more useful to you.
+// Returns a human-readable equivalent of 'descriptor'. So "I" would be "int",
+// "[[I" would be "int[][]", "[Ljava/lang/String;" would be
+// "java.lang.String[]", and so forth.
+void AppendPrettyDescriptor(const char* descriptor, std::string* result);
+std::string PrettyDescriptor(const char* descriptor);
+std::string PrettyDescriptor(Primitive::Type type);
+
+// Performs JNI name mangling as described in section 11.3 "Linking Native Methods"
+// of the JNI spec.
+std::string MangleForJni(const std::string& s);
+
+std::string GetJniShortName(const std::string& class_name, const std::string& method_name);
+
+// Turn "java.lang.String" into "Ljava/lang/String;".
+std::string DotToDescriptor(const char* class_name);
+
+// Turn "Ljava/lang/String;" into "java.lang.String" using the conventions of
+// java.lang.Class.getName().
+std::string DescriptorToDot(const char* descriptor);
+
+// Turn "Ljava/lang/String;" into "java/lang/String" using the opposite conventions of
+// java.lang.Class.getName().
+std::string DescriptorToName(const char* descriptor);
+
+// Tests for whether 's' is a valid class name in the three common forms:
+bool IsValidBinaryClassName(const char* s); // "java.lang.String"
+bool IsValidJniClassName(const char* s); // "java/lang/String"
+bool IsValidDescriptor(const char* s); // "Ljava/lang/String;"
+
+// Returns whether the given string is a valid field or method name,
+// additionally allowing names that begin with '<' and end with '>'.
+bool IsValidMemberName(const char* s);
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DESCRIPTORS_NAMES_H_
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
new file mode 100644
index 0000000000..b424b50f21
--- /dev/null
+++ b/libdexfile/dex/dex_file-inl.h
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_
+
+#include "base/bit_utils.h"
+#include "base/casts.h"
+#include "base/stringpiece.h"
+#include "compact_dex_file.h"
+#include "dex_file.h"
+#include "invoke_type.h"
+#include "leb128.h"
+#include "standard_dex_file.h"
+
+namespace art {
+
+inline int32_t DexFile::GetStringLength(const StringId& string_id) const {
+ const uint8_t* ptr = DataBegin() + string_id.string_data_off_;
+ return DecodeUnsignedLeb128(&ptr);
+}
+
+inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id,
+ uint32_t* utf16_length) const {
+ DCHECK(utf16_length != nullptr) << GetLocation();
+ const uint8_t* ptr = DataBegin() + string_id.string_data_off_;
+ *utf16_length = DecodeUnsignedLeb128(&ptr);
+ return reinterpret_cast<const char*>(ptr);
+}
+
+inline const char* DexFile::GetStringData(const StringId& string_id) const {
+ uint32_t ignored;
+ return GetStringDataAndUtf16Length(string_id, &ignored);
+}
+
+inline const char* DexFile::StringDataAndUtf16LengthByIdx(dex::StringIndex idx,
+ uint32_t* utf16_length) const {
+ if (!idx.IsValid()) {
+ *utf16_length = 0;
+ return nullptr;
+ }
+ const StringId& string_id = GetStringId(idx);
+ return GetStringDataAndUtf16Length(string_id, utf16_length);
+}
+
+inline const char* DexFile::StringDataByIdx(dex::StringIndex idx) const {
+ uint32_t unicode_length;
+ return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
+}
+
+inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const {
+ if (!idx.IsValid()) {
+ return nullptr;
+ }
+ const TypeId& type_id = GetTypeId(idx);
+ return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
+}
+
+inline const char* DexFile::StringByTypeIdx(dex::TypeIndex idx) const {
+ if (!idx.IsValid()) {
+ return nullptr;
+ }
+ const TypeId& type_id = GetTypeId(idx);
+ return StringDataByIdx(type_id.descriptor_idx_);
+}
+
+inline const char* DexFile::GetTypeDescriptor(const TypeId& type_id) const {
+ return StringDataByIdx(type_id.descriptor_idx_);
+}
+
+inline const char* DexFile::GetFieldTypeDescriptor(const FieldId& field_id) const {
+ const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_);
+ return GetTypeDescriptor(type_id);
+}
+
+inline const char* DexFile::GetFieldName(const FieldId& field_id) const {
+ return StringDataByIdx(field_id.name_idx_);
+}
+
+inline const char* DexFile::GetMethodDeclaringClassDescriptor(const MethodId& method_id) const {
+ const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_);
+ return GetTypeDescriptor(type_id);
+}
+
+inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const {
+ return Signature(this, GetProtoId(method_id.proto_idx_));
+}
+
+inline const Signature DexFile::GetProtoSignature(const ProtoId& proto_id) const {
+ return Signature(this, proto_id);
+}
+
+inline const char* DexFile::GetMethodName(const MethodId& method_id) const {
+ return StringDataByIdx(method_id.name_idx_);
+}
+
+inline const char* DexFile::GetMethodShorty(uint32_t idx) const {
+ return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_);
+}
+
+inline const char* DexFile::GetMethodShorty(const MethodId& method_id) const {
+ return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_);
+}
+
+inline const char* DexFile::GetMethodShorty(const MethodId& method_id, uint32_t* length) const {
+ // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters.
+ return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
+}
+
+inline const char* DexFile::GetClassDescriptor(const ClassDef& class_def) const {
+ return StringByTypeIdx(class_def.class_idx_);
+}
+
+inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) const {
+ return StringByTypeIdx(proto_id.return_type_idx_);
+}
+
+inline const char* DexFile::GetShorty(uint32_t proto_idx) const {
+ const ProtoId& proto_id = GetProtoId(proto_idx);
+ return StringDataByIdx(proto_id.shorty_idx_);
+}
+
+inline const DexFile::TryItem* DexFile::GetTryItems(const DexInstructionIterator& code_item_end,
+ uint32_t offset) {
+ return reinterpret_cast<const TryItem*>
+ (RoundUp(reinterpret_cast<uintptr_t>(&code_item_end.Inst()), TryItem::kAlignment)) + offset;
+}
+
+static inline bool DexFileStringEquals(const DexFile* df1, dex::StringIndex sidx1,
+ const DexFile* df2, dex::StringIndex sidx2) {
+ uint32_t s1_len; // Note: utf16 length != mutf8 length.
+ const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len);
+ uint32_t s2_len;
+ const char* s2_data = df2->StringDataAndUtf16LengthByIdx(sidx2, &s2_len);
+ return (s1_len == s2_len) && (strcmp(s1_data, s2_data) == 0);
+}
+
+inline bool Signature::operator==(const Signature& rhs) const {
+ if (dex_file_ == nullptr) {
+ return rhs.dex_file_ == nullptr;
+ }
+ if (rhs.dex_file_ == nullptr) {
+ return false;
+ }
+ if (dex_file_ == rhs.dex_file_) {
+ return proto_id_ == rhs.proto_id_;
+ }
+ uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length.
+ const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_,
+ &lhs_shorty_len);
+ StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len);
+ {
+ uint32_t rhs_shorty_len;
+ const char* rhs_shorty_data =
+ rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_,
+ &rhs_shorty_len);
+ StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len);
+ if (lhs_shorty != rhs_shorty) {
+ return false; // Shorty mismatch.
+ }
+ }
+ if (lhs_shorty[0] == 'L') {
+ const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_);
+ const DexFile::TypeId& rhs_return_type_id =
+ rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_);
+ if (!DexFileStringEquals(dex_file_, return_type_id.descriptor_idx_,
+ rhs.dex_file_, rhs_return_type_id.descriptor_idx_)) {
+ return false; // Return type mismatch.
+ }
+ }
+ if (lhs_shorty.find('L', 1) != StringPiece::npos) {
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_);
+ // We found a reference parameter in the matching shorty, so both lists must be non-empty.
+ DCHECK(params != nullptr);
+ DCHECK(rhs_params != nullptr);
+ uint32_t params_size = params->Size();
+ DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match.
+ for (uint32_t i = 0; i < params_size; ++i) {
+ const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_);
+ const DexFile::TypeId& rhs_param_id =
+ rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_);
+ if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_,
+ rhs.dex_file_, rhs_param_id.descriptor_idx_)) {
+ return false; // Parameter type mismatch.
+ }
+ }
+ }
+ return true;
+}
+
+inline
+InvokeType ClassDataItemIterator::GetMethodInvokeType(const DexFile::ClassDef& class_def) const {
+ if (HasNextDirectMethod()) {
+ if ((GetRawMemberAccessFlags() & kAccStatic) != 0) {
+ return kStatic;
+ } else {
+ return kDirect;
+ }
+ } else {
+ DCHECK_EQ(GetRawMemberAccessFlags() & kAccStatic, 0U);
+ if ((class_def.access_flags_ & kAccInterface) != 0) {
+ return kInterface;
+ } else if ((GetRawMemberAccessFlags() & kAccConstructor) != 0) {
+ return kSuper;
+ } else {
+ return kVirtual;
+ }
+ }
+}
+
+template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
+bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream,
+ const std::string& location,
+ const char* declaring_class_descriptor,
+ const std::vector<const char*>& arg_descriptors,
+ const std::string& method_name,
+ bool is_static,
+ uint16_t registers_size,
+ uint16_t ins_size,
+ uint16_t insns_size_in_code_units,
+ IndexToStringData index_to_string_data,
+ TypeIndexToStringData type_index_to_string_data,
+ NewLocalCallback new_local_callback,
+ void* context) {
+ if (stream == nullptr) {
+ return false;
+ }
+ std::vector<LocalInfo> local_in_reg(registers_size);
+
+ uint16_t arg_reg = registers_size - ins_size;
+ if (!is_static) {
+ const char* descriptor = declaring_class_descriptor;
+ local_in_reg[arg_reg].name_ = "this";
+ local_in_reg[arg_reg].descriptor_ = descriptor;
+ local_in_reg[arg_reg].signature_ = nullptr;
+ local_in_reg[arg_reg].start_address_ = 0;
+ local_in_reg[arg_reg].reg_ = arg_reg;
+ local_in_reg[arg_reg].is_live_ = true;
+ arg_reg++;
+ }
+
+ DecodeUnsignedLeb128(&stream); // Line.
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ uint32_t i;
+ if (parameters_size != arg_descriptors.size()) {
+ LOG(ERROR) << "invalid stream - problem with parameter iterator in " << location
+ << " for method " << method_name;
+ return false;
+ }
+ for (i = 0; i < parameters_size && i < arg_descriptors.size(); ++i) {
+ if (arg_reg >= registers_size) {
+ LOG(ERROR) << "invalid stream - arg reg >= reg size (" << arg_reg
+ << " >= " << registers_size << ") in " << location;
+ return false;
+ }
+ uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+ const char* descriptor = arg_descriptors[i];
+ local_in_reg[arg_reg].name_ = index_to_string_data(name_idx);
+ local_in_reg[arg_reg].descriptor_ = descriptor;
+ local_in_reg[arg_reg].signature_ = nullptr;
+ local_in_reg[arg_reg].start_address_ = 0;
+ local_in_reg[arg_reg].reg_ = arg_reg;
+ local_in_reg[arg_reg].is_live_ = true;
+ switch (*descriptor) {
+ case 'D':
+ case 'J':
+ arg_reg += 2;
+ break;
+ default:
+ arg_reg += 1;
+ break;
+ }
+ }
+
+ uint32_t address = 0;
+ for (;;) {
+ uint8_t opcode = *stream++;
+ switch (opcode) {
+ case DBG_END_SEQUENCE:
+ // Emit all variables which are still alive at the end of the method.
+ for (uint16_t reg = 0; reg < registers_size; reg++) {
+ if (local_in_reg[reg].is_live_) {
+ local_in_reg[reg].end_address_ = insns_size_in_code_units;
+ new_local_callback(context, local_in_reg[reg]);
+ }
+ }
+ return true;
+ case DBG_ADVANCE_PC:
+ address += DecodeUnsignedLeb128(&stream);
+ break;
+ case DBG_ADVANCE_LINE:
+ DecodeSignedLeb128(&stream); // Line.
+ break;
+ case DBG_START_LOCAL:
+ case DBG_START_LOCAL_EXTENDED: {
+ uint16_t reg = DecodeUnsignedLeb128(&stream);
+ if (reg >= registers_size) {
+ LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
+ << registers_size << ") in " << location;
+ return false;
+ }
+
+ uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+ uint16_t descriptor_idx = DecodeUnsignedLeb128P1(&stream);
+ uint32_t signature_idx = dex::kDexNoIndex;
+ if (opcode == DBG_START_LOCAL_EXTENDED) {
+ signature_idx = DecodeUnsignedLeb128P1(&stream);
+ }
+
+ // Emit what was previously there, if anything
+ if (local_in_reg[reg].is_live_) {
+ local_in_reg[reg].end_address_ = address;
+ new_local_callback(context, local_in_reg[reg]);
+ }
+
+ local_in_reg[reg].name_ = index_to_string_data(name_idx);
+ local_in_reg[reg].descriptor_ = type_index_to_string_data(descriptor_idx);;
+ local_in_reg[reg].signature_ = index_to_string_data(signature_idx);
+ local_in_reg[reg].start_address_ = address;
+ local_in_reg[reg].reg_ = reg;
+ local_in_reg[reg].is_live_ = true;
+ break;
+ }
+ case DBG_END_LOCAL: {
+ uint16_t reg = DecodeUnsignedLeb128(&stream);
+ if (reg >= registers_size) {
+ LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
+ << registers_size << ") in " << location;
+ return false;
+ }
+ // If the register is live, close it properly. Otherwise, closing an already
+ // closed register is sloppy, but harmless if no further action is taken.
+ if (local_in_reg[reg].is_live_) {
+ local_in_reg[reg].end_address_ = address;
+ new_local_callback(context, local_in_reg[reg]);
+ local_in_reg[reg].is_live_ = false;
+ }
+ break;
+ }
+ case DBG_RESTART_LOCAL: {
+ uint16_t reg = DecodeUnsignedLeb128(&stream);
+ if (reg >= registers_size) {
+ LOG(ERROR) << "invalid stream - reg >= reg size (" << reg << " >= "
+ << registers_size << ") in " << location;
+ return false;
+ }
+ // If the register is live, the "restart" is superfluous,
+ // and we don't want to mess with the existing start address.
+ if (!local_in_reg[reg].is_live_) {
+ local_in_reg[reg].start_address_ = address;
+ local_in_reg[reg].is_live_ = true;
+ }
+ break;
+ }
+ case DBG_SET_PROLOGUE_END:
+ case DBG_SET_EPILOGUE_BEGIN:
+ break;
+ case DBG_SET_FILE:
+ DecodeUnsignedLeb128P1(&stream); // name.
+ break;
+ default:
+ address += (opcode - DBG_FIRST_SPECIAL) / DBG_LINE_RANGE;
+ break;
+ }
+ }
+}
+
+template<typename NewLocalCallback>
+bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size,
+ uint32_t ins_size,
+ uint32_t insns_size_in_code_units,
+ uint32_t debug_info_offset,
+ bool is_static,
+ uint32_t method_idx,
+ NewLocalCallback new_local_callback,
+ void* context) const {
+ const uint8_t* const stream = GetDebugInfoStream(debug_info_offset);
+ if (stream == nullptr) {
+ return false;
+ }
+ std::vector<const char*> arg_descriptors;
+ DexFileParameterIterator it(*this, GetMethodPrototype(GetMethodId(method_idx)));
+ for (; it.HasNext(); it.Next()) {
+ arg_descriptors.push_back(it.GetDescriptor());
+ }
+ return DecodeDebugLocalInfo(stream,
+ GetLocation(),
+ GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)),
+ arg_descriptors,
+ this->PrettyMethod(method_idx),
+ is_static,
+ registers_size,
+ ins_size,
+ insns_size_in_code_units,
+ [this](uint32_t idx) {
+ return StringDataByIdx(dex::StringIndex(idx));
+ },
+ [this](uint32_t idx) {
+ return StringByTypeIdx(dex::TypeIndex(
+ dchecked_integral_cast<uint16_t>(idx)));
+ },
+ new_local_callback,
+ context);
+}
+
+template<typename DexDebugNewPosition, typename IndexToStringData>
+bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream,
+ IndexToStringData index_to_string_data,
+ DexDebugNewPosition position_functor,
+ void* context) {
+ if (stream == nullptr) {
+ return false;
+ }
+
+ PositionInfo entry = PositionInfo();
+ entry.line_ = DecodeUnsignedLeb128(&stream);
+ uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+ for (uint32_t i = 0; i < parameters_size; ++i) {
+ DecodeUnsignedLeb128P1(&stream); // Parameter name.
+ }
+
+ for (;;) {
+ uint8_t opcode = *stream++;
+ switch (opcode) {
+ case DBG_END_SEQUENCE:
+ return true; // end of stream.
+ case DBG_ADVANCE_PC:
+ entry.address_ += DecodeUnsignedLeb128(&stream);
+ break;
+ case DBG_ADVANCE_LINE:
+ entry.line_ += DecodeSignedLeb128(&stream);
+ break;
+ case DBG_START_LOCAL:
+ DecodeUnsignedLeb128(&stream); // reg.
+ DecodeUnsignedLeb128P1(&stream); // name.
+ DecodeUnsignedLeb128P1(&stream); // descriptor.
+ break;
+ case DBG_START_LOCAL_EXTENDED:
+ DecodeUnsignedLeb128(&stream); // reg.
+ DecodeUnsignedLeb128P1(&stream); // name.
+ DecodeUnsignedLeb128P1(&stream); // descriptor.
+ DecodeUnsignedLeb128P1(&stream); // signature.
+ break;
+ case DBG_END_LOCAL:
+ case DBG_RESTART_LOCAL:
+ DecodeUnsignedLeb128(&stream); // reg.
+ break;
+ case DBG_SET_PROLOGUE_END:
+ entry.prologue_end_ = true;
+ break;
+ case DBG_SET_EPILOGUE_BEGIN:
+ entry.epilogue_begin_ = true;
+ break;
+ case DBG_SET_FILE: {
+ uint32_t name_idx = DecodeUnsignedLeb128P1(&stream);
+ entry.source_file_ = index_to_string_data(name_idx);
+ break;
+ }
+ default: {
+ int adjopcode = opcode - DBG_FIRST_SPECIAL;
+ entry.address_ += adjopcode / DBG_LINE_RANGE;
+ entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+ if (position_functor(context, entry)) {
+ return true; // early exit.
+ }
+ entry.prologue_end_ = false;
+ entry.epilogue_begin_ = false;
+ break;
+ }
+ }
+ }
+}
+
+template<typename DexDebugNewPosition>
+bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset,
+ DexDebugNewPosition position_functor,
+ void* context) const {
+ return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset),
+ [this](uint32_t idx) {
+ return StringDataByIdx(dex::StringIndex(idx));
+ },
+ position_functor,
+ context);
+}
+
+inline const CompactDexFile* DexFile::AsCompactDexFile() const {
+ DCHECK(IsCompactDexFile());
+ return down_cast<const CompactDexFile*>(this);
+}
+
+inline const StandardDexFile* DexFile::AsStandardDexFile() const {
+ DCHECK(IsStandardDexFile());
+ return down_cast<const StandardDexFile*>(this);
+}
+
+// Get the base of the encoded data for the given DexCode.
+inline const uint8_t* DexFile::GetCatchHandlerData(const DexInstructionIterator& code_item_end,
+ uint32_t tries_size,
+ uint32_t offset) {
+ const uint8_t* handler_data =
+ reinterpret_cast<const uint8_t*>(GetTryItems(code_item_end, tries_size));
+ return handler_data + offset;
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
new file mode 100644
index 0000000000..18eb903551
--- /dev/null
+++ b/libdexfile/dex/dex_file.cc
@@ -0,0 +1,795 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#include <memory>
+#include <sstream>
+#include <type_traits>
+
+#include "android-base/stringprintf.h"
+
+#include "base/enums.h"
+#include "base/stl_util.h"
+#include "descriptors_names.h"
+#include "dex_file-inl.h"
+#include "leb128.h"
+#include "standard_dex_file.h"
+#include "utf-inl.h"
+
+namespace art {
+
+using android::base::StringPrintf;
+
+static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong");
+static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial");
+static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
+static_assert(std::is_trivially_copyable<dex::TypeIndex>::value, "TypeIndex not trivial");
+
+uint32_t DexFile::CalculateChecksum() const {
+ return CalculateChecksum(Begin(), Size());
+}
+
+uint32_t DexFile::CalculateChecksum(const uint8_t* begin, size_t size) {
+ const uint32_t non_sum_bytes = OFFSETOF_MEMBER(DexFile::Header, signature_);
+ return ChecksumMemoryRange(begin + non_sum_bytes, size - non_sum_bytes);
+}
+
+uint32_t DexFile::ChecksumMemoryRange(const uint8_t* begin, size_t size) {
+ return adler32(adler32(0L, Z_NULL, 0), begin, size);
+}
+
+int DexFile::GetPermissions() const {
+ CHECK(container_.get() != nullptr);
+ return container_->GetPermissions();
+}
+
+bool DexFile::IsReadOnly() const {
+ CHECK(container_.get() != nullptr);
+ return container_->IsReadOnly();
+}
+
+bool DexFile::EnableWrite() const {
+ CHECK(container_.get() != nullptr);
+ return container_->EnableWrite();
+}
+
+bool DexFile::DisableWrite() const {
+ CHECK(container_.get() != nullptr);
+ return container_->DisableWrite();
+}
+
+DexFile::DexFile(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_begin,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container,
+ bool is_compact_dex)
+ : begin_(base),
+ size_(size),
+ data_begin_(data_begin),
+ data_size_(data_size),
+ location_(location),
+ location_checksum_(location_checksum),
+ header_(reinterpret_cast<const Header*>(base)),
+ string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
+ type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
+ field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
+ method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
+ proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
+ class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
+ method_handles_(nullptr),
+ num_method_handles_(0),
+ call_site_ids_(nullptr),
+ num_call_site_ids_(0),
+ oat_dex_file_(oat_dex_file),
+ container_(container),
+ is_compact_dex_(is_compact_dex) {
+ CHECK(begin_ != nullptr) << GetLocation();
+ CHECK_GT(size_, 0U) << GetLocation();
+ // Check base (=header) alignment.
+ // Must be 4-byte aligned to avoid undefined behavior when accessing
+ // any of the sections via a pointer.
+ CHECK_ALIGNED(begin_, alignof(Header));
+
+ InitializeSectionsFromMapList();
+}
+
+DexFile::~DexFile() {
+ // We don't call DeleteGlobalRef on dex_object_ because we're only called by DestroyJavaVM, and
+ // that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
+ // re-attach, but cleaning up these global references is not obviously useful. It's not as if
+ // the global reference table is otherwise empty!
+}
+
+bool DexFile::Init(std::string* error_msg) {
+ if (!CheckMagicAndVersion(error_msg)) {
+ return false;
+ }
+ return true;
+}
+
+bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
+ if (!IsMagicValid()) {
+ std::ostringstream oss;
+ oss << "Unrecognized magic number in " << GetLocation() << ":"
+ << " " << header_->magic_[0]
+ << " " << header_->magic_[1]
+ << " " << header_->magic_[2]
+ << " " << header_->magic_[3];
+ *error_msg = oss.str();
+ return false;
+ }
+ if (!IsVersionValid()) {
+ std::ostringstream oss;
+ oss << "Unrecognized version number in " << GetLocation() << ":"
+ << " " << header_->magic_[4]
+ << " " << header_->magic_[5]
+ << " " << header_->magic_[6]
+ << " " << header_->magic_[7];
+ *error_msg = oss.str();
+ return false;
+ }
+ return true;
+}
+
+void DexFile::InitializeSectionsFromMapList() {
+ const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
+ if (header_->map_off_ == 0 || header_->map_off_ > DataSize()) {
+ // Bad offset. The dex file verifier runs after this method and will reject the file.
+ return;
+ }
+ const size_t count = map_list->size_;
+
+ size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
+ if (header_->map_off_ >= map_limit || map_limit > DataSize()) {
+ // Overflow or out out of bounds. The dex file verifier runs after
+ // this method and will reject the file as it is malformed.
+ return;
+ }
+
+ for (size_t i = 0; i < count; ++i) {
+ const MapItem& map_item = map_list->list_[i];
+ if (map_item.type_ == kDexTypeMethodHandleItem) {
+ method_handles_ = reinterpret_cast<const MethodHandleItem*>(Begin() + map_item.offset_);
+ num_method_handles_ = map_item.size_;
+ } else if (map_item.type_ == kDexTypeCallSiteIdItem) {
+ call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(Begin() + map_item.offset_);
+ num_call_site_ids_ = map_item.size_;
+ }
+ }
+}
+
+uint32_t DexFile::Header::GetVersion() const {
+ const char* version = reinterpret_cast<const char*>(&magic_[kDexMagicSize]);
+ return atoi(version);
+}
+
+const DexFile::ClassDef* DexFile::FindClassDef(dex::TypeIndex type_idx) const {
+ size_t num_class_defs = NumClassDefs();
+ // Fast path for rare no class defs case.
+ if (num_class_defs == 0) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < num_class_defs; ++i) {
+ const ClassDef& class_def = GetClassDef(i);
+ if (class_def.class_idx_ == type_idx) {
+ return &class_def;
+ }
+ }
+ return nullptr;
+}
+
+uint32_t DexFile::FindCodeItemOffset(const DexFile::ClassDef& class_def,
+ uint32_t method_idx) const {
+ const uint8_t* class_data = GetClassData(class_def);
+ CHECK(class_data != nullptr);
+ ClassDataItemIterator it(*this, class_data);
+ it.SkipAllFields();
+ while (it.HasNextDirectMethod()) {
+ if (it.GetMemberIndex() == method_idx) {
+ return it.GetMethodCodeItemOffset();
+ }
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ if (it.GetMemberIndex() == method_idx) {
+ return it.GetMethodCodeItemOffset();
+ }
+ it.Next();
+ }
+ LOG(FATAL) << "Unable to find method " << method_idx;
+ UNREACHABLE();
+}
+
+const DexFile::FieldId* DexFile::FindFieldId(const DexFile::TypeId& declaring_klass,
+ const DexFile::StringId& name,
+ const DexFile::TypeId& type) const {
+ // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
+ const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
+ const dex::StringIndex name_idx = GetIndexForStringId(name);
+ const dex::TypeIndex type_idx = GetIndexForTypeId(type);
+ int32_t lo = 0;
+ int32_t hi = NumFieldIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const DexFile::FieldId& field = GetFieldId(mid);
+ if (class_idx > field.class_idx_) {
+ lo = mid + 1;
+ } else if (class_idx < field.class_idx_) {
+ hi = mid - 1;
+ } else {
+ if (name_idx > field.name_idx_) {
+ lo = mid + 1;
+ } else if (name_idx < field.name_idx_) {
+ hi = mid - 1;
+ } else {
+ if (type_idx > field.type_idx_) {
+ lo = mid + 1;
+ } else if (type_idx < field.type_idx_) {
+ hi = mid - 1;
+ } else {
+ return &field;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+const DexFile::MethodId* DexFile::FindMethodId(const DexFile::TypeId& declaring_klass,
+ const DexFile::StringId& name,
+ const DexFile::ProtoId& signature) const {
+ // Binary search MethodIds knowing that they are sorted by class_idx, name_idx then proto_idx
+ const dex::TypeIndex class_idx = GetIndexForTypeId(declaring_klass);
+ const dex::StringIndex name_idx = GetIndexForStringId(name);
+ const uint16_t proto_idx = GetIndexForProtoId(signature);
+ int32_t lo = 0;
+ int32_t hi = NumMethodIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const DexFile::MethodId& method = GetMethodId(mid);
+ if (class_idx > method.class_idx_) {
+ lo = mid + 1;
+ } else if (class_idx < method.class_idx_) {
+ hi = mid - 1;
+ } else {
+ if (name_idx > method.name_idx_) {
+ lo = mid + 1;
+ } else if (name_idx < method.name_idx_) {
+ hi = mid - 1;
+ } else {
+ if (proto_idx > method.proto_idx_) {
+ lo = mid + 1;
+ } else if (proto_idx < method.proto_idx_) {
+ hi = mid - 1;
+ } else {
+ return &method;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+const DexFile::StringId* DexFile::FindStringId(const char* string) const {
+ int32_t lo = 0;
+ int32_t hi = NumStringIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid));
+ const char* str = GetStringData(str_id);
+ int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
+ if (compare > 0) {
+ lo = mid + 1;
+ } else if (compare < 0) {
+ hi = mid - 1;
+ } else {
+ return &str_id;
+ }
+ }
+ return nullptr;
+}
+
+const DexFile::TypeId* DexFile::FindTypeId(const char* string) const {
+ int32_t lo = 0;
+ int32_t hi = NumTypeIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const TypeId& type_id = GetTypeId(dex::TypeIndex(mid));
+ const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_);
+ const char* str = GetStringData(str_id);
+ int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
+ if (compare > 0) {
+ lo = mid + 1;
+ } else if (compare < 0) {
+ hi = mid - 1;
+ } else {
+ return &type_id;
+ }
+ }
+ return nullptr;
+}
+
+const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const {
+ int32_t lo = 0;
+ int32_t hi = NumStringIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const DexFile::StringId& str_id = GetStringId(dex::StringIndex(mid));
+ const char* str = GetStringData(str_id);
+ int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string, length);
+ if (compare > 0) {
+ lo = mid + 1;
+ } else if (compare < 0) {
+ hi = mid - 1;
+ } else {
+ return &str_id;
+ }
+ }
+ return nullptr;
+}
+
+const DexFile::TypeId* DexFile::FindTypeId(dex::StringIndex string_idx) const {
+ int32_t lo = 0;
+ int32_t hi = NumTypeIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const TypeId& type_id = GetTypeId(dex::TypeIndex(mid));
+ if (string_idx > type_id.descriptor_idx_) {
+ lo = mid + 1;
+ } else if (string_idx < type_id.descriptor_idx_) {
+ hi = mid - 1;
+ } else {
+ return &type_id;
+ }
+ }
+ return nullptr;
+}
+
+const DexFile::ProtoId* DexFile::FindProtoId(dex::TypeIndex return_type_idx,
+ const dex::TypeIndex* signature_type_idxs,
+ uint32_t signature_length) const {
+ int32_t lo = 0;
+ int32_t hi = NumProtoIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const DexFile::ProtoId& proto = GetProtoId(mid);
+ int compare = return_type_idx.index_ - proto.return_type_idx_.index_;
+ if (compare == 0) {
+ DexFileParameterIterator it(*this, proto);
+ size_t i = 0;
+ while (it.HasNext() && i < signature_length && compare == 0) {
+ compare = signature_type_idxs[i].index_ - it.GetTypeIdx().index_;
+ it.Next();
+ i++;
+ }
+ if (compare == 0) {
+ if (it.HasNext()) {
+ compare = -1;
+ } else if (i < signature_length) {
+ compare = 1;
+ }
+ }
+ }
+ if (compare > 0) {
+ lo = mid + 1;
+ } else if (compare < 0) {
+ hi = mid - 1;
+ } else {
+ return &proto;
+ }
+ }
+ return nullptr;
+}
+
+// Given a signature place the type ids into the given vector
+bool DexFile::CreateTypeList(const StringPiece& signature,
+ dex::TypeIndex* return_type_idx,
+ std::vector<dex::TypeIndex>* param_type_idxs) const {
+ if (signature[0] != '(') {
+ return false;
+ }
+ size_t offset = 1;
+ size_t end = signature.size();
+ bool process_return = false;
+ while (offset < end) {
+ size_t start_offset = offset;
+ char c = signature[offset];
+ offset++;
+ if (c == ')') {
+ process_return = true;
+ continue;
+ }
+ while (c == '[') { // process array prefix
+ if (offset >= end) { // expect some descriptor following [
+ return false;
+ }
+ c = signature[offset];
+ offset++;
+ }
+ if (c == 'L') { // process type descriptors
+ do {
+ if (offset >= end) { // unexpected early termination of descriptor
+ return false;
+ }
+ c = signature[offset];
+ offset++;
+ } while (c != ';');
+ }
+ // TODO: avoid creating a std::string just to get a 0-terminated char array
+ std::string descriptor(signature.data() + start_offset, offset - start_offset);
+ const DexFile::TypeId* type_id = FindTypeId(descriptor.c_str());
+ if (type_id == nullptr) {
+ return false;
+ }
+ dex::TypeIndex type_idx = GetIndexForTypeId(*type_id);
+ if (!process_return) {
+ param_type_idxs->push_back(type_idx);
+ } else {
+ *return_type_idx = type_idx;
+ return offset == end; // return true if the signature had reached a sensible end
+ }
+ }
+ return false; // failed to correctly parse return type
+}
+
+const Signature DexFile::CreateSignature(const StringPiece& signature) const {
+ dex::TypeIndex return_type_idx;
+ std::vector<dex::TypeIndex> param_type_indices;
+ bool success = CreateTypeList(signature, &return_type_idx, &param_type_indices);
+ if (!success) {
+ return Signature::NoSignature();
+ }
+ const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices);
+ if (proto_id == nullptr) {
+ return Signature::NoSignature();
+ }
+ return Signature(this, *proto_id);
+}
+
+int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) {
+ uint32_t min = 0;
+ uint32_t max = tries_size;
+ while (min < max) {
+ const uint32_t mid = (min + max) / 2;
+
+ const art::DexFile::TryItem& ti = try_items[mid];
+ const uint32_t start = ti.start_addr_;
+ const uint32_t end = start + ti.insn_count_;
+
+ if (address < start) {
+ max = mid;
+ } else if (address >= end) {
+ min = mid + 1;
+ } else { // We have a winner!
+ return mid;
+ }
+ }
+ // No match.
+ return -1;
+}
+
+bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) {
+ LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context);
+
+ // We know that this callback will be called in
+ // ascending address order, so keep going until we find
+ // a match or we've just gone past it.
+ if (entry.address_ > context->address_) {
+ // The line number from the previous positions callback
+ // wil be the final result.
+ return true;
+ } else {
+ context->line_num_ = entry.line_;
+ return entry.address_ == context->address_;
+ }
+}
+
+// Read a signed integer. "zwidth" is the zero-based byte count.
+int32_t DexFile::ReadSignedInt(const uint8_t* ptr, int zwidth) {
+ int32_t val = 0;
+ for (int i = zwidth; i >= 0; --i) {
+ val = ((uint32_t)val >> 8) | (((int32_t)*ptr++) << 24);
+ }
+ val >>= (3 - zwidth) * 8;
+ return val;
+}
+
+// Read an unsigned integer. "zwidth" is the zero-based byte count,
+// "fill_on_right" indicates which side we want to zero-fill from.
+uint32_t DexFile::ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right) {
+ uint32_t val = 0;
+ for (int i = zwidth; i >= 0; --i) {
+ val = (val >> 8) | (((uint32_t)*ptr++) << 24);
+ }
+ if (!fill_on_right) {
+ val >>= (3 - zwidth) * 8;
+ }
+ return val;
+}
+
+// Read a signed long. "zwidth" is the zero-based byte count.
+int64_t DexFile::ReadSignedLong(const uint8_t* ptr, int zwidth) {
+ int64_t val = 0;
+ for (int i = zwidth; i >= 0; --i) {
+ val = ((uint64_t)val >> 8) | (((int64_t)*ptr++) << 56);
+ }
+ val >>= (7 - zwidth) * 8;
+ return val;
+}
+
+// Read an unsigned long. "zwidth" is the zero-based byte count,
+// "fill_on_right" indicates which side we want to zero-fill from.
+uint64_t DexFile::ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right) {
+ uint64_t val = 0;
+ for (int i = zwidth; i >= 0; --i) {
+ val = (val >> 8) | (((uint64_t)*ptr++) << 56);
+ }
+ if (!fill_on_right) {
+ val >>= (7 - zwidth) * 8;
+ }
+ return val;
+}
+
+std::string DexFile::PrettyMethod(uint32_t method_idx, bool with_signature) const {
+ if (method_idx >= NumMethodIds()) {
+ return StringPrintf("<<invalid-method-idx-%d>>", method_idx);
+ }
+ const DexFile::MethodId& method_id = GetMethodId(method_idx);
+ std::string result;
+ const DexFile::ProtoId* proto_id = with_signature ? &GetProtoId(method_id.proto_idx_) : nullptr;
+ if (with_signature) {
+ AppendPrettyDescriptor(StringByTypeIdx(proto_id->return_type_idx_), &result);
+ result += ' ';
+ }
+ AppendPrettyDescriptor(GetMethodDeclaringClassDescriptor(method_id), &result);
+ result += '.';
+ result += GetMethodName(method_id);
+ if (with_signature) {
+ result += '(';
+ const DexFile::TypeList* params = GetProtoParameters(*proto_id);
+ if (params != nullptr) {
+ const char* separator = "";
+ for (uint32_t i = 0u, size = params->Size(); i != size; ++i) {
+ result += separator;
+ separator = ", ";
+ AppendPrettyDescriptor(StringByTypeIdx(params->GetTypeItem(i).type_idx_), &result);
+ }
+ }
+ result += ')';
+ }
+ return result;
+}
+
+std::string DexFile::PrettyField(uint32_t field_idx, bool with_type) const {
+ if (field_idx >= NumFieldIds()) {
+ return StringPrintf("<<invalid-field-idx-%d>>", field_idx);
+ }
+ const DexFile::FieldId& field_id = GetFieldId(field_idx);
+ std::string result;
+ if (with_type) {
+ result += GetFieldTypeDescriptor(field_id);
+ result += ' ';
+ }
+ AppendPrettyDescriptor(GetFieldDeclaringClassDescriptor(field_id), &result);
+ result += '.';
+ result += GetFieldName(field_id);
+ return result;
+}
+
+std::string DexFile::PrettyType(dex::TypeIndex type_idx) const {
+ if (type_idx.index_ >= NumTypeIds()) {
+ return StringPrintf("<<invalid-type-idx-%d>>", type_idx.index_);
+ }
+ const DexFile::TypeId& type_id = GetTypeId(type_idx);
+ return PrettyDescriptor(GetTypeDescriptor(type_id));
+}
+
+// Checks that visibility is as expected. Includes special behavior for M and
+// before to allow runtime and build visibility when expecting runtime.
+std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) {
+ os << StringPrintf("[DexFile: %s dex-checksum=%08x location-checksum=%08x %p-%p]",
+ dex_file.GetLocation().c_str(),
+ dex_file.GetHeader().checksum_, dex_file.GetLocationChecksum(),
+ dex_file.Begin(), dex_file.Begin() + dex_file.Size());
+ return os;
+}
+
+std::string Signature::ToString() const {
+ if (dex_file_ == nullptr) {
+ CHECK(proto_id_ == nullptr);
+ return "<no signature>";
+ }
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ std::string result;
+ if (params == nullptr) {
+ result += "()";
+ } else {
+ result += "(";
+ for (uint32_t i = 0; i < params->Size(); ++i) {
+ result += dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_);
+ }
+ result += ")";
+ }
+ result += dex_file_->StringByTypeIdx(proto_id_->return_type_idx_);
+ return result;
+}
+
+uint32_t Signature::GetNumberOfParameters() const {
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ return (params != nullptr) ? params->Size() : 0;
+}
+
+bool Signature::IsVoid() const {
+ const char* return_type = dex_file_->GetReturnTypeDescriptor(*proto_id_);
+ return strcmp(return_type, "V") == 0;
+}
+
+bool Signature::operator==(const StringPiece& rhs) const {
+ if (dex_file_ == nullptr) {
+ return false;
+ }
+ StringPiece tail(rhs);
+ if (!tail.starts_with("(")) {
+ return false; // Invalid signature
+ }
+ tail.remove_prefix(1); // "(";
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ if (params != nullptr) {
+ for (uint32_t i = 0; i < params->Size(); ++i) {
+ StringPiece param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_));
+ if (!tail.starts_with(param)) {
+ return false;
+ }
+ tail.remove_prefix(param.length());
+ }
+ }
+ if (!tail.starts_with(")")) {
+ return false;
+ }
+ tail.remove_prefix(1); // ")";
+ return tail == dex_file_->StringByTypeIdx(proto_id_->return_type_idx_);
+}
+
+std::ostream& operator<<(std::ostream& os, const Signature& sig) {
+ return os << sig.ToString();
+}
+
+// Decodes the header section from the class data bytes.
+void ClassDataItemIterator::ReadClassDataHeader() {
+ CHECK(ptr_pos_ != nullptr);
+ header_.static_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_);
+ header_.instance_fields_size_ = DecodeUnsignedLeb128(&ptr_pos_);
+ header_.direct_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_);
+ header_.virtual_methods_size_ = DecodeUnsignedLeb128(&ptr_pos_);
+}
+
+void ClassDataItemIterator::ReadClassDataField() {
+ field_.field_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_);
+ field_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
+ // The user of the iterator is responsible for checking if there
+ // are unordered or duplicate indexes.
+}
+
+void ClassDataItemIterator::ReadClassDataMethod() {
+ method_.method_idx_delta_ = DecodeUnsignedLeb128(&ptr_pos_);
+ method_.access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
+ method_.code_off_ = DecodeUnsignedLeb128(&ptr_pos_);
+ if (last_idx_ != 0 && method_.method_idx_delta_ == 0) {
+ LOG(WARNING) << "Duplicate method in " << dex_file_.GetLocation();
+ }
+}
+
+EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file,
+ const uint8_t* array_data)
+ : dex_file_(dex_file),
+ array_size_(),
+ pos_(-1),
+ ptr_(array_data),
+ type_(kByte) {
+ array_size_ = (ptr_ != nullptr) ? DecodeUnsignedLeb128(&ptr_) : 0;
+ if (array_size_ > 0) {
+ Next();
+ }
+}
+
+void EncodedArrayValueIterator::Next() {
+ pos_++;
+ if (pos_ >= array_size_) {
+ return;
+ }
+ uint8_t value_type = *ptr_++;
+ uint8_t value_arg = value_type >> kEncodedValueArgShift;
+ size_t width = value_arg + 1; // assume and correct later
+ type_ = static_cast<ValueType>(value_type & kEncodedValueTypeMask);
+ switch (type_) {
+ case kBoolean:
+ jval_.i = (value_arg != 0) ? 1 : 0;
+ width = 0;
+ break;
+ case kByte:
+ jval_.i = DexFile::ReadSignedInt(ptr_, value_arg);
+ CHECK(IsInt<8>(jval_.i));
+ break;
+ case kShort:
+ jval_.i = DexFile::ReadSignedInt(ptr_, value_arg);
+ CHECK(IsInt<16>(jval_.i));
+ break;
+ case kChar:
+ jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false);
+ CHECK(IsUint<16>(jval_.i));
+ break;
+ case kInt:
+ jval_.i = DexFile::ReadSignedInt(ptr_, value_arg);
+ break;
+ case kLong:
+ jval_.j = DexFile::ReadSignedLong(ptr_, value_arg);
+ break;
+ case kFloat:
+ jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, true);
+ break;
+ case kDouble:
+ jval_.j = DexFile::ReadUnsignedLong(ptr_, value_arg, true);
+ break;
+ case kString:
+ case kType:
+ case kMethodType:
+ case kMethodHandle:
+ jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false);
+ break;
+ case kField:
+ case kMethod:
+ case kEnum:
+ case kArray:
+ case kAnnotation:
+ UNIMPLEMENTED(FATAL) << ": type " << type_;
+ UNREACHABLE();
+ case kNull:
+ jval_.l = nullptr;
+ width = 0;
+ break;
+ default:
+ LOG(FATAL) << "Unreached";
+ UNREACHABLE();
+ }
+ ptr_ += width;
+}
+
+namespace dex {
+
+std::ostream& operator<<(std::ostream& os, const StringIndex& index) {
+ os << "StringIndex[" << index.index_ << "]";
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const TypeIndex& index) {
+ os << "TypeIndex[" << index.index_ << "]";
+ return os;
+}
+
+} // namespace dex
+
+} // namespace art
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
new file mode 100644
index 0000000000..a38e76cfd0
--- /dev/null
+++ b/libdexfile/dex/dex_file.h
@@ -0,0 +1,1443 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "base/iteration_range.h"
+#include "base/macros.h"
+#include "base/value_object.h"
+#include "dex_file_types.h"
+#include "dex_instruction_iterator.h"
+#include "globals.h"
+#include "hidden_api_access_flags.h"
+#include "jni.h"
+#include "modifiers.h"
+
+namespace art {
+
+class CompactDexFile;
+enum InvokeType : uint32_t;
+class MemMap;
+class OatDexFile;
+class Signature;
+class StandardDexFile;
+class StringPiece;
+class ZipArchive;
+
+// Some instances of DexFile own the storage referred to by DexFile. Clients who create
+// such management do so by subclassing Container.
+class DexFileContainer {
+ public:
+ DexFileContainer() { }
+ virtual ~DexFileContainer() { }
+ virtual int GetPermissions() = 0;
+ virtual bool IsReadOnly() = 0;
+ virtual bool EnableWrite() = 0;
+ virtual bool DisableWrite() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DexFileContainer);
+};
+
+// Dex file is the API that exposes native dex files (ordinary dex files) and CompactDex.
+// Originally, the dex file format used by ART was mostly the same as APKs. The only change was
+// quickened opcodes and layout optimizations.
+// Since ART needs to support both native dex files and CompactDex files, the DexFile interface
+// provides an abstraction to facilitate this.
+class DexFile {
+ public:
+ // Number of bytes in the dex file magic.
+ static constexpr size_t kDexMagicSize = 4;
+ static constexpr size_t kDexVersionLen = 4;
+
+ // First Dex format version enforcing class definition ordering rules.
+ static const uint32_t kClassDefinitionOrderEnforcedVersion = 37;
+
+ static constexpr size_t kSha1DigestSize = 20;
+ static constexpr uint32_t kDexEndianConstant = 0x12345678;
+
+ // The value of an invalid index.
+ static const uint16_t kDexNoIndex16 = 0xFFFF;
+
+ // Raw header_item.
+ struct Header {
+ uint8_t magic_[8] = {};
+ uint32_t checksum_ = 0; // See also location_checksum_
+ uint8_t signature_[kSha1DigestSize] = {};
+ uint32_t file_size_ = 0; // size of entire file
+ uint32_t header_size_ = 0; // offset to start of next section
+ uint32_t endian_tag_ = 0;
+ uint32_t link_size_ = 0; // unused
+ uint32_t link_off_ = 0; // unused
+ uint32_t map_off_ = 0; // unused
+ uint32_t string_ids_size_ = 0; // number of StringIds
+ uint32_t string_ids_off_ = 0; // file offset of StringIds array
+ uint32_t type_ids_size_ = 0; // number of TypeIds, we don't support more than 65535
+ uint32_t type_ids_off_ = 0; // file offset of TypeIds array
+ uint32_t proto_ids_size_ = 0; // number of ProtoIds, we don't support more than 65535
+ uint32_t proto_ids_off_ = 0; // file offset of ProtoIds array
+ uint32_t field_ids_size_ = 0; // number of FieldIds
+ uint32_t field_ids_off_ = 0; // file offset of FieldIds array
+ uint32_t method_ids_size_ = 0; // number of MethodIds
+ uint32_t method_ids_off_ = 0; // file offset of MethodIds array
+ uint32_t class_defs_size_ = 0; // number of ClassDefs
+ uint32_t class_defs_off_ = 0; // file offset of ClassDef array
+ uint32_t data_size_ = 0; // size of data section
+ uint32_t data_off_ = 0; // file offset of data section
+
+ // Decode the dex magic version
+ uint32_t GetVersion() const;
+ };
+
+ // Map item type codes.
+ enum MapItemType : uint16_t { // private
+ kDexTypeHeaderItem = 0x0000,
+ kDexTypeStringIdItem = 0x0001,
+ kDexTypeTypeIdItem = 0x0002,
+ kDexTypeProtoIdItem = 0x0003,
+ kDexTypeFieldIdItem = 0x0004,
+ kDexTypeMethodIdItem = 0x0005,
+ kDexTypeClassDefItem = 0x0006,
+ kDexTypeCallSiteIdItem = 0x0007,
+ kDexTypeMethodHandleItem = 0x0008,
+ kDexTypeMapList = 0x1000,
+ kDexTypeTypeList = 0x1001,
+ kDexTypeAnnotationSetRefList = 0x1002,
+ kDexTypeAnnotationSetItem = 0x1003,
+ kDexTypeClassDataItem = 0x2000,
+ kDexTypeCodeItem = 0x2001,
+ kDexTypeStringDataItem = 0x2002,
+ kDexTypeDebugInfoItem = 0x2003,
+ kDexTypeAnnotationItem = 0x2004,
+ kDexTypeEncodedArrayItem = 0x2005,
+ kDexTypeAnnotationsDirectoryItem = 0x2006,
+ };
+
+ struct MapItem {
+ uint16_t type_;
+ uint16_t unused_;
+ uint32_t size_;
+ uint32_t offset_;
+ };
+
+ struct MapList {
+ uint32_t size_;
+ MapItem list_[1];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MapList);
+ };
+
+ // Raw string_id_item.
+ struct StringId {
+ uint32_t string_data_off_; // offset in bytes from the base address
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringId);
+ };
+
+ // Raw type_id_item.
+ struct TypeId {
+ dex::StringIndex descriptor_idx_; // index into string_ids
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TypeId);
+ };
+
+ // Raw field_id_item.
+ struct FieldId {
+ dex::TypeIndex class_idx_; // index into type_ids_ array for defining class
+ dex::TypeIndex type_idx_; // index into type_ids_ array for field type
+ dex::StringIndex name_idx_; // index into string_ids_ array for field name
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldId);
+ };
+
+ // Raw proto_id_item.
+ struct ProtoId {
+ dex::StringIndex shorty_idx_; // index into string_ids array for shorty descriptor
+ dex::TypeIndex return_type_idx_; // index into type_ids array for return type
+ uint16_t pad_; // padding = 0
+ uint32_t parameters_off_; // file offset to type_list for parameter types
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProtoId);
+ };
+
+ // Raw method_id_item.
+ struct MethodId {
+ dex::TypeIndex class_idx_; // index into type_ids_ array for defining class
+ uint16_t proto_idx_; // index into proto_ids_ array for method prototype
+ dex::StringIndex name_idx_; // index into string_ids_ array for method name
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MethodId);
+ };
+
+ // Raw class_def_item.
+ struct ClassDef {
+ dex::TypeIndex class_idx_; // index into type_ids_ array for this class
+ uint16_t pad1_; // padding = 0
+ uint32_t access_flags_;
+ dex::TypeIndex superclass_idx_; // index into type_ids_ array for superclass
+ uint16_t pad2_; // padding = 0
+ uint32_t interfaces_off_; // file offset to TypeList
+ dex::StringIndex source_file_idx_; // index into string_ids_ for source file name
+ uint32_t annotations_off_; // file offset to annotations_directory_item
+ uint32_t class_data_off_; // file offset to class_data_item
+ uint32_t static_values_off_; // file offset to EncodedArray
+
+ // Returns the valid access flags, that is, Java modifier bits relevant to the ClassDef type
+ // (class or interface). These are all in the lower 16b and do not contain runtime flags.
+ uint32_t GetJavaAccessFlags() const {
+ // Make sure that none of our runtime-only flags are set.
+ static_assert((kAccValidClassFlags & kAccJavaFlagsMask) == kAccValidClassFlags,
+ "Valid class flags not a subset of Java flags");
+ static_assert((kAccValidInterfaceFlags & kAccJavaFlagsMask) == kAccValidInterfaceFlags,
+ "Valid interface flags not a subset of Java flags");
+
+ if ((access_flags_ & kAccInterface) != 0) {
+ // Interface.
+ return access_flags_ & kAccValidInterfaceFlags;
+ } else {
+ // Class.
+ return access_flags_ & kAccValidClassFlags;
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ClassDef);
+ };
+
+ // Raw type_item.
+ struct TypeItem {
+ dex::TypeIndex type_idx_; // index into type_ids section
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TypeItem);
+ };
+
+ // Raw type_list.
+ class TypeList {
+ public:
+ uint32_t Size() const {
+ return size_;
+ }
+
+ const TypeItem& GetTypeItem(uint32_t idx) const {
+ DCHECK_LT(idx, this->size_);
+ return this->list_[idx];
+ }
+
+ // Size in bytes of the part of the list that is common.
+ static constexpr size_t GetHeaderSize() {
+ return 4U;
+ }
+
+ // Size in bytes of the whole type list including all the stored elements.
+ static constexpr size_t GetListSize(size_t count) {
+ return GetHeaderSize() + sizeof(TypeItem) * count;
+ }
+
+ private:
+ uint32_t size_; // size of the list, in entries
+ TypeItem list_[1]; // elements of the list
+ DISALLOW_COPY_AND_ASSIGN(TypeList);
+ };
+
+ // MethodHandle Types
+ enum class MethodHandleType : uint16_t { // private
+ kStaticPut = 0x0000, // a setter for a given static field.
+ kStaticGet = 0x0001, // a getter for a given static field.
+ kInstancePut = 0x0002, // a setter for a given instance field.
+ kInstanceGet = 0x0003, // a getter for a given instance field.
+ kInvokeStatic = 0x0004, // an invoker for a given static method.
+ kInvokeInstance = 0x0005, // invoke_instance : an invoker for a given instance method. This
+ // can be any non-static method on any class (or interface) except
+ // for “<init>”.
+ kInvokeConstructor = 0x0006, // an invoker for a given constructor.
+ kInvokeDirect = 0x0007, // an invoker for a direct (special) method.
+ kInvokeInterface = 0x0008, // an invoker for an interface method.
+ kLast = kInvokeInterface
+ };
+
+ // raw method_handle_item
+ struct MethodHandleItem {
+ uint16_t method_handle_type_;
+ uint16_t reserved1_; // Reserved for future use.
+ uint16_t field_or_method_idx_; // Field index for accessors, method index otherwise.
+ uint16_t reserved2_; // Reserved for future use.
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MethodHandleItem);
+ };
+
+ // raw call_site_id_item
+ struct CallSiteIdItem {
+ uint32_t data_off_; // Offset into data section pointing to encoded array items.
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem);
+ };
+
+ // Base code_item, compact dex and standard dex have different code item layouts.
+ struct CodeItem {
+ protected:
+ CodeItem() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CodeItem);
+ };
+
+ // Raw try_item.
+ struct TryItem {
+ static constexpr size_t kAlignment = sizeof(uint32_t);
+
+ uint32_t start_addr_;
+ uint16_t insn_count_;
+ uint16_t handler_off_;
+
+ private:
+ TryItem() = default;
+ friend class DexWriter;
+ DISALLOW_COPY_AND_ASSIGN(TryItem);
+ };
+
+ // Annotation constants.
+ enum {
+ kDexVisibilityBuild = 0x00, /* annotation visibility */
+ kDexVisibilityRuntime = 0x01,
+ kDexVisibilitySystem = 0x02,
+
+ kDexAnnotationByte = 0x00,
+ kDexAnnotationShort = 0x02,
+ kDexAnnotationChar = 0x03,
+ kDexAnnotationInt = 0x04,
+ kDexAnnotationLong = 0x06,
+ kDexAnnotationFloat = 0x10,
+ kDexAnnotationDouble = 0x11,
+ kDexAnnotationMethodType = 0x15,
+ kDexAnnotationMethodHandle = 0x16,
+ kDexAnnotationString = 0x17,
+ kDexAnnotationType = 0x18,
+ kDexAnnotationField = 0x19,
+ kDexAnnotationMethod = 0x1a,
+ kDexAnnotationEnum = 0x1b,
+ kDexAnnotationArray = 0x1c,
+ kDexAnnotationAnnotation = 0x1d,
+ kDexAnnotationNull = 0x1e,
+ kDexAnnotationBoolean = 0x1f,
+
+ kDexAnnotationValueTypeMask = 0x1f, /* low 5 bits */
+ kDexAnnotationValueArgShift = 5,
+ };
+
+ struct AnnotationsDirectoryItem {
+ uint32_t class_annotations_off_;
+ uint32_t fields_size_;
+ uint32_t methods_size_;
+ uint32_t parameters_size_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AnnotationsDirectoryItem);
+ };
+
+ struct FieldAnnotationsItem {
+ uint32_t field_idx_;
+ uint32_t annotations_off_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FieldAnnotationsItem);
+ };
+
+ struct MethodAnnotationsItem {
+ uint32_t method_idx_;
+ uint32_t annotations_off_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MethodAnnotationsItem);
+ };
+
+ struct ParameterAnnotationsItem {
+ uint32_t method_idx_;
+ uint32_t annotations_off_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ParameterAnnotationsItem);
+ };
+
+ struct AnnotationSetRefItem {
+ uint32_t annotations_off_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefItem);
+ };
+
+ struct AnnotationSetRefList {
+ uint32_t size_;
+ AnnotationSetRefItem list_[1];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefList);
+ };
+
+ struct AnnotationSetItem {
+ uint32_t size_;
+ uint32_t entries_[1];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AnnotationSetItem);
+ };
+
+ struct AnnotationItem {
+ uint8_t visibility_;
+ uint8_t annotation_[1];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
+ };
+
+ enum AnnotationResultStyle { // private
+ kAllObjects,
+ kPrimitivesOrObjects,
+ kAllRaw
+ };
+
+ struct AnnotationValue;
+
+ // Closes a .dex file.
+ virtual ~DexFile();
+
+ const std::string& GetLocation() const {
+ return location_;
+ }
+
+ // For DexFiles directly from .dex files, this is the checksum from the DexFile::Header.
+ // For DexFiles opened from a zip files, this will be the ZipEntry CRC32 of classes.dex.
+ uint32_t GetLocationChecksum() const {
+ return location_checksum_;
+ }
+
+ const Header& GetHeader() const {
+ DCHECK(header_ != nullptr) << GetLocation();
+ return *header_;
+ }
+
+ // Decode the dex magic version
+ uint32_t GetDexVersion() const {
+ return GetHeader().GetVersion();
+ }
+
+ // Returns true if the byte string points to the magic value.
+ virtual bool IsMagicValid() const = 0;
+
+ // Returns true if the byte string after the magic is the correct value.
+ virtual bool IsVersionValid() const = 0;
+
+ // Returns true if the dex file supports default methods.
+ virtual bool SupportsDefaultMethods() const = 0;
+
+ // Returns the maximum size in bytes needed to store an equivalent dex file strictly conforming to
+ // the dex file specification. That is the size if we wanted to get rid of all the
+ // quickening/compact-dexing/etc.
+ //
+ // TODO This should really be an exact size! b/72402467
+ virtual size_t GetDequickenedSize() const = 0;
+
+ // Returns the number of string identifiers in the .dex file.
+ size_t NumStringIds() const {
+ DCHECK(header_ != nullptr) << GetLocation();
+ return header_->string_ids_size_;
+ }
+
+ // Returns the StringId at the specified index.
+ const StringId& GetStringId(dex::StringIndex idx) const {
+ DCHECK_LT(idx.index_, NumStringIds()) << GetLocation();
+ return string_ids_[idx.index_];
+ }
+
+ dex::StringIndex GetIndexForStringId(const StringId& string_id) const {
+ CHECK_GE(&string_id, string_ids_) << GetLocation();
+ CHECK_LT(&string_id, string_ids_ + header_->string_ids_size_) << GetLocation();
+ return dex::StringIndex(&string_id - string_ids_);
+ }
+
+ int32_t GetStringLength(const StringId& string_id) const;
+
+ // Returns a pointer to the UTF-8 string data referred to by the given string_id as well as the
+ // length of the string when decoded as a UTF-16 string. Note the UTF-16 length is not the same
+ // as the string length of the string data.
+ const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const;
+
+ const char* GetStringData(const StringId& string_id) const;
+
+ // Index version of GetStringDataAndUtf16Length.
+ const char* StringDataAndUtf16LengthByIdx(dex::StringIndex idx, uint32_t* utf16_length) const;
+
+ const char* StringDataByIdx(dex::StringIndex idx) const;
+
+ // Looks up a string id for a given modified utf8 string.
+ const StringId* FindStringId(const char* string) const;
+
+ const TypeId* FindTypeId(const char* string) const;
+
+ // Looks up a string id for a given utf16 string.
+ const StringId* FindStringId(const uint16_t* string, size_t length) const;
+
+ // Returns the number of type identifiers in the .dex file.
+ uint32_t NumTypeIds() const {
+ DCHECK(header_ != nullptr) << GetLocation();
+ return header_->type_ids_size_;
+ }
+
+ bool IsTypeIndexValid(dex::TypeIndex idx) const {
+ return idx.IsValid() && idx.index_ < NumTypeIds();
+ }
+
+ // Returns the TypeId at the specified index.
+ const TypeId& GetTypeId(dex::TypeIndex idx) const {
+ DCHECK_LT(idx.index_, NumTypeIds()) << GetLocation();
+ return type_ids_[idx.index_];
+ }
+
+ dex::TypeIndex GetIndexForTypeId(const TypeId& type_id) const {
+ CHECK_GE(&type_id, type_ids_) << GetLocation();
+ CHECK_LT(&type_id, type_ids_ + header_->type_ids_size_) << GetLocation();
+ size_t result = &type_id - type_ids_;
+ DCHECK_LT(result, 65536U) << GetLocation();
+ return dex::TypeIndex(static_cast<uint16_t>(result));
+ }
+
+ // Get the descriptor string associated with a given type index.
+ const char* StringByTypeIdx(dex::TypeIndex idx, uint32_t* unicode_length) const;
+
+ const char* StringByTypeIdx(dex::TypeIndex idx) const;
+
+ // Returns the type descriptor string of a type id.
+ const char* GetTypeDescriptor(const TypeId& type_id) const;
+
+ // Looks up a type for the given string index
+ const TypeId* FindTypeId(dex::StringIndex string_idx) const;
+
+ // Returns the number of field identifiers in the .dex file.
+ size_t NumFieldIds() const {
+ DCHECK(header_ != nullptr) << GetLocation();
+ return header_->field_ids_size_;
+ }
+
+ // Returns the FieldId at the specified index.
+ const FieldId& GetFieldId(uint32_t idx) const {
+ DCHECK_LT(idx, NumFieldIds()) << GetLocation();
+ return field_ids_[idx];
+ }
+
+ uint32_t GetIndexForFieldId(const FieldId& field_id) const {
+ CHECK_GE(&field_id, field_ids_) << GetLocation();
+ CHECK_LT(&field_id, field_ids_ + header_->field_ids_size_) << GetLocation();
+ return &field_id - field_ids_;
+ }
+
+ // Looks up a field by its declaring class, name and type
+ const FieldId* FindFieldId(const DexFile::TypeId& declaring_klass,
+ const DexFile::StringId& name,
+ const DexFile::TypeId& type) const;
+
+ uint32_t FindCodeItemOffset(const DexFile::ClassDef& class_def,
+ uint32_t dex_method_idx) const;
+
+ virtual uint32_t GetCodeItemSize(const DexFile::CodeItem& disk_code_item) const = 0;
+
+ // Returns the declaring class descriptor string of a field id.
+ const char* GetFieldDeclaringClassDescriptor(const FieldId& field_id) const {
+ const DexFile::TypeId& type_id = GetTypeId(field_id.class_idx_);
+ return GetTypeDescriptor(type_id);
+ }
+
+ // Returns the class descriptor string of a field id.
+ const char* GetFieldTypeDescriptor(const FieldId& field_id) const;
+
+ // Returns the name of a field id.
+ const char* GetFieldName(const FieldId& field_id) const;
+
+ // Returns the number of method identifiers in the .dex file.
+ size_t NumMethodIds() const {
+ DCHECK(header_ != nullptr) << GetLocation();
+ return header_->method_ids_size_;
+ }
+
+ // Returns the MethodId at the specified index.
+ const MethodId& GetMethodId(uint32_t idx) const {
+ DCHECK_LT(idx, NumMethodIds()) << GetLocation();
+ return method_ids_[idx];
+ }
+
+ uint32_t GetIndexForMethodId(const MethodId& method_id) const {
+ CHECK_GE(&method_id, method_ids_) << GetLocation();
+ CHECK_LT(&method_id, method_ids_ + header_->method_ids_size_) << GetLocation();
+ return &method_id - method_ids_;
+ }
+
+ // Looks up a method by its declaring class, name and proto_id
+ const MethodId* FindMethodId(const DexFile::TypeId& declaring_klass,
+ const DexFile::StringId& name,
+ const DexFile::ProtoId& signature) const;
+
+ // Returns the declaring class descriptor string of a method id.
+ const char* GetMethodDeclaringClassDescriptor(const MethodId& method_id) const;
+
+ // Returns the prototype of a method id.
+ const ProtoId& GetMethodPrototype(const MethodId& method_id) const {
+ return GetProtoId(method_id.proto_idx_);
+ }
+
+ // Returns a representation of the signature of a method id.
+ const Signature GetMethodSignature(const MethodId& method_id) const;
+
+ // Returns a representation of the signature of a proto id.
+ const Signature GetProtoSignature(const ProtoId& proto_id) const;
+
+ // Returns the name of a method id.
+ const char* GetMethodName(const MethodId& method_id) const;
+
+ // Returns the shorty of a method by its index.
+ const char* GetMethodShorty(uint32_t idx) const;
+
+ // Returns the shorty of a method id.
+ const char* GetMethodShorty(const MethodId& method_id) const;
+ const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const;
+
+ // Returns the number of class definitions in the .dex file.
+ uint32_t NumClassDefs() const {
+ DCHECK(header_ != nullptr) << GetLocation();
+ return header_->class_defs_size_;
+ }
+
+ // Returns the ClassDef at the specified index.
+ const ClassDef& GetClassDef(uint16_t idx) const {
+ DCHECK_LT(idx, NumClassDefs()) << GetLocation();
+ return class_defs_[idx];
+ }
+
+ uint16_t GetIndexForClassDef(const ClassDef& class_def) const {
+ CHECK_GE(&class_def, class_defs_) << GetLocation();
+ CHECK_LT(&class_def, class_defs_ + header_->class_defs_size_) << GetLocation();
+ return &class_def - class_defs_;
+ }
+
+ // Returns the class descriptor string of a class definition.
+ const char* GetClassDescriptor(const ClassDef& class_def) const;
+
+ // Looks up a class definition by its type index.
+ const ClassDef* FindClassDef(dex::TypeIndex type_idx) const;
+
+ const TypeList* GetInterfacesList(const ClassDef& class_def) const {
+ return DataPointer<TypeList>(class_def.interfaces_off_);
+ }
+
+ uint32_t NumMethodHandles() const {
+ return num_method_handles_;
+ }
+
+ const MethodHandleItem& GetMethodHandle(uint32_t idx) const {
+ CHECK_LT(idx, NumMethodHandles());
+ return method_handles_[idx];
+ }
+
+ uint32_t NumCallSiteIds() const {
+ return num_call_site_ids_;
+ }
+
+ const CallSiteIdItem& GetCallSiteId(uint32_t idx) const {
+ CHECK_LT(idx, NumCallSiteIds());
+ return call_site_ids_[idx];
+ }
+
+ // Returns a pointer to the raw memory mapped class_data_item
+ const uint8_t* GetClassData(const ClassDef& class_def) const {
+ return DataPointer<uint8_t>(class_def.class_data_off_);
+ }
+
+ // Return the code item for a provided offset.
+ const CodeItem* GetCodeItem(const uint32_t code_off) const {
+ // May be null for native or abstract methods.
+ return DataPointer<CodeItem>(code_off);
+ }
+
+ const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const;
+
+ // Returns the number of prototype identifiers in the .dex file.
+ size_t NumProtoIds() const {
+ DCHECK(header_ != nullptr) << GetLocation();
+ return header_->proto_ids_size_;
+ }
+
+ // Returns the ProtoId at the specified index.
+ const ProtoId& GetProtoId(uint16_t idx) const {
+ DCHECK_LT(idx, NumProtoIds()) << GetLocation();
+ return proto_ids_[idx];
+ }
+
+ uint16_t GetIndexForProtoId(const ProtoId& proto_id) const {
+ CHECK_GE(&proto_id, proto_ids_) << GetLocation();
+ CHECK_LT(&proto_id, proto_ids_ + header_->proto_ids_size_) << GetLocation();
+ return &proto_id - proto_ids_;
+ }
+
+ // Looks up a proto id for a given return type and signature type list
+ const ProtoId* FindProtoId(dex::TypeIndex return_type_idx,
+ const dex::TypeIndex* signature_type_idxs,
+ uint32_t signature_length) const;
+ const ProtoId* FindProtoId(dex::TypeIndex return_type_idx,
+ const std::vector<dex::TypeIndex>& signature_type_idxs) const {
+ return FindProtoId(return_type_idx, &signature_type_idxs[0], signature_type_idxs.size());
+ }
+
+ // Given a signature place the type ids into the given vector, returns true on success
+ bool CreateTypeList(const StringPiece& signature,
+ dex::TypeIndex* return_type_idx,
+ std::vector<dex::TypeIndex>* param_type_idxs) const;
+
+ // Create a Signature from the given string signature or return Signature::NoSignature if not
+ // possible.
+ const Signature CreateSignature(const StringPiece& signature) const;
+
+ // Returns the short form method descriptor for the given prototype.
+ const char* GetShorty(uint32_t proto_idx) const;
+
+ const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
+ return DataPointer<TypeList>(proto_id.parameters_off_);
+ }
+
+ const uint8_t* GetEncodedStaticFieldValuesArray(const ClassDef& class_def) const {
+ return DataPointer<uint8_t>(class_def.static_values_off_);
+ }
+
+ const uint8_t* GetCallSiteEncodedValuesArray(const CallSiteIdItem& call_site_id) const {
+ return DataBegin() + call_site_id.data_off_;
+ }
+
+ static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset);
+
+ // Get the base of the encoded data for the given DexCode.
+ static const uint8_t* GetCatchHandlerData(const DexInstructionIterator& code_item_end,
+ uint32_t tries_size,
+ uint32_t offset);
+
+ // Find which try region is associated with the given address (ie dex pc). Returns -1 if none.
+ static int32_t FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address);
+
+ // Get the pointer to the start of the debugging data
+ const uint8_t* GetDebugInfoStream(uint32_t debug_info_off) const {
+ // Check that the offset is in bounds.
+ // Note that although the specification says that 0 should be used if there
+ // is no debug information, some applications incorrectly use 0xFFFFFFFF.
+ return (debug_info_off == 0 || debug_info_off >= data_size_)
+ ? nullptr
+ : DataBegin() + debug_info_off;
+ }
+
+ struct PositionInfo {
+ PositionInfo() = default;
+
+ uint32_t address_ = 0; // In 16-bit code units.
+ uint32_t line_ = 0; // Source code line number starting at 1.
+ const char* source_file_ = nullptr; // nullptr if the file from ClassDef still applies.
+ bool prologue_end_ = false;
+ bool epilogue_begin_ = false;
+ };
+
+ struct LocalInfo {
+ LocalInfo() = default;
+
+ const char* name_ = nullptr; // E.g., list. It can be nullptr if unknown.
+ const char* descriptor_ = nullptr; // E.g., Ljava/util/LinkedList;
+ const char* signature_ = nullptr; // E.g., java.util.LinkedList<java.lang.Integer>
+ uint32_t start_address_ = 0; // PC location where the local is first defined.
+ uint32_t end_address_ = 0; // PC location where the local is no longer defined.
+ uint16_t reg_ = 0; // Dex register which stores the values.
+ bool is_live_ = false; // Is the local defined and live.
+ };
+
+ // Callback for "new locals table entry".
+ typedef void (*DexDebugNewLocalCb)(void* context, const LocalInfo& entry);
+
+ static bool LineNumForPcCb(void* context, const PositionInfo& entry);
+
+ const AnnotationsDirectoryItem* GetAnnotationsDirectory(const ClassDef& class_def) const {
+ return DataPointer<AnnotationsDirectoryItem>(class_def.annotations_off_);
+ }
+
+ const AnnotationSetItem* GetClassAnnotationSet(const AnnotationsDirectoryItem* anno_dir) const {
+ return DataPointer<AnnotationSetItem>(anno_dir->class_annotations_off_);
+ }
+
+ const FieldAnnotationsItem* GetFieldAnnotations(const AnnotationsDirectoryItem* anno_dir) const {
+ return (anno_dir->fields_size_ == 0)
+ ? nullptr
+ : reinterpret_cast<const FieldAnnotationsItem*>(&anno_dir[1]);
+ }
+
+ const MethodAnnotationsItem* GetMethodAnnotations(const AnnotationsDirectoryItem* anno_dir)
+ const {
+ if (anno_dir->methods_size_ == 0) {
+ return nullptr;
+ }
+ // Skip past the header and field annotations.
+ const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
+ addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
+ return reinterpret_cast<const MethodAnnotationsItem*>(addr);
+ }
+
+ const ParameterAnnotationsItem* GetParameterAnnotations(const AnnotationsDirectoryItem* anno_dir)
+ const {
+ if (anno_dir->parameters_size_ == 0) {
+ return nullptr;
+ }
+ // Skip past the header, field annotations, and method annotations.
+ const uint8_t* addr = reinterpret_cast<const uint8_t*>(&anno_dir[1]);
+ addr += anno_dir->fields_size_ * sizeof(FieldAnnotationsItem);
+ addr += anno_dir->methods_size_ * sizeof(MethodAnnotationsItem);
+ return reinterpret_cast<const ParameterAnnotationsItem*>(addr);
+ }
+
+ const AnnotationSetItem* GetFieldAnnotationSetItem(const FieldAnnotationsItem& anno_item) const {
+ return DataPointer<AnnotationSetItem>(anno_item.annotations_off_);
+ }
+
+ const AnnotationSetItem* GetMethodAnnotationSetItem(const MethodAnnotationsItem& anno_item)
+ const {
+ return DataPointer<AnnotationSetItem>(anno_item.annotations_off_);
+ }
+
+ const AnnotationSetRefList* GetParameterAnnotationSetRefList(
+ const ParameterAnnotationsItem* anno_item) const {
+ return DataPointer<AnnotationSetRefList>(anno_item->annotations_off_);
+ }
+
+ ALWAYS_INLINE const AnnotationItem* GetAnnotationItemAtOffset(uint32_t offset) const {
+ return DataPointer<AnnotationItem>(offset);
+ }
+
+ const AnnotationItem* GetAnnotationItem(const AnnotationSetItem* set_item, uint32_t index) const {
+ DCHECK_LE(index, set_item->size_);
+ return GetAnnotationItemAtOffset(set_item->entries_[index]);
+ }
+
+ const AnnotationSetItem* GetSetRefItemItem(const AnnotationSetRefItem* anno_item) const {
+ return DataPointer<AnnotationSetItem>(anno_item->annotations_off_);
+ }
+
+ // Debug info opcodes and constants
+ enum {
+ DBG_END_SEQUENCE = 0x00,
+ DBG_ADVANCE_PC = 0x01,
+ DBG_ADVANCE_LINE = 0x02,
+ DBG_START_LOCAL = 0x03,
+ DBG_START_LOCAL_EXTENDED = 0x04,
+ DBG_END_LOCAL = 0x05,
+ DBG_RESTART_LOCAL = 0x06,
+ DBG_SET_PROLOGUE_END = 0x07,
+ DBG_SET_EPILOGUE_BEGIN = 0x08,
+ DBG_SET_FILE = 0x09,
+ DBG_FIRST_SPECIAL = 0x0a,
+ DBG_LINE_BASE = -4,
+ DBG_LINE_RANGE = 15,
+ };
+
+ struct LineNumFromPcContext {
+ LineNumFromPcContext(uint32_t address, uint32_t line_num)
+ : address_(address), line_num_(line_num) {}
+ uint32_t address_;
+ uint32_t line_num_;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LineNumFromPcContext);
+ };
+
+ // Returns false if there is no debugging information or if it cannot be decoded.
+ template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
+ static bool DecodeDebugLocalInfo(const uint8_t* stream,
+ const std::string& location,
+ const char* declaring_class_descriptor,
+ const std::vector<const char*>& arg_descriptors,
+ const std::string& method_name,
+ bool is_static,
+ uint16_t registers_size,
+ uint16_t ins_size,
+ uint16_t insns_size_in_code_units,
+ IndexToStringData index_to_string_data,
+ TypeIndexToStringData type_index_to_string_data,
+ NewLocalCallback new_local,
+ void* context);
+ template<typename NewLocalCallback>
+ bool DecodeDebugLocalInfo(uint32_t registers_size,
+ uint32_t ins_size,
+ uint32_t insns_size_in_code_units,
+ uint32_t debug_info_offset,
+ bool is_static,
+ uint32_t method_idx,
+ NewLocalCallback new_local,
+ void* context) const;
+
+ // Returns false if there is no debugging information or if it cannot be decoded.
+ template<typename DexDebugNewPosition, typename IndexToStringData>
+ static bool DecodeDebugPositionInfo(const uint8_t* stream,
+ IndexToStringData index_to_string_data,
+ DexDebugNewPosition position_functor,
+ void* context);
+ template<typename DexDebugNewPosition>
+ bool DecodeDebugPositionInfo(uint32_t debug_info_offset,
+ DexDebugNewPosition position_functor,
+ void* context) const;
+
+ const char* GetSourceFile(const ClassDef& class_def) const {
+ if (!class_def.source_file_idx_.IsValid()) {
+ return nullptr;
+ } else {
+ return StringDataByIdx(class_def.source_file_idx_);
+ }
+ }
+
+ int GetPermissions() const;
+
+ bool IsReadOnly() const;
+
+ bool EnableWrite() const;
+
+ bool DisableWrite() const;
+
+ const uint8_t* Begin() const {
+ return begin_;
+ }
+
+ size_t Size() const {
+ return size_;
+ }
+
+ const uint8_t* DataBegin() const {
+ return data_begin_;
+ }
+
+ size_t DataSize() const {
+ return data_size_;
+ }
+
+ template <typename T>
+ const T* DataPointer(size_t offset) const {
+ DCHECK_LT(offset, DataSize()) << "Offset past end of data section";
+ return (offset != 0u) ? reinterpret_cast<const T*>(DataBegin() + offset) : nullptr;
+ }
+
+ const OatDexFile* GetOatDexFile() const {
+ return oat_dex_file_;
+ }
+
+ // Used by oat writer.
+ void SetOatDexFile(OatDexFile* oat_dex_file) const {
+ oat_dex_file_ = oat_dex_file;
+ }
+
+ // Read MapItems and validate/set remaining offsets.
+ const DexFile::MapList* GetMapList() const {
+ return reinterpret_cast<const DexFile::MapList*>(DataBegin() + header_->map_off_);
+ }
+
+ // Utility methods for reading integral values from a buffer.
+ static int32_t ReadSignedInt(const uint8_t* ptr, int zwidth);
+ static uint32_t ReadUnsignedInt(const uint8_t* ptr, int zwidth, bool fill_on_right);
+ static int64_t ReadSignedLong(const uint8_t* ptr, int zwidth);
+ static uint64_t ReadUnsignedLong(const uint8_t* ptr, int zwidth, bool fill_on_right);
+
+ // Recalculates the checksum of the dex file. Does not use the current value in the header.
+ virtual uint32_t CalculateChecksum() const;
+ static uint32_t CalculateChecksum(const uint8_t* begin, size_t size);
+ static uint32_t ChecksumMemoryRange(const uint8_t* begin, size_t size);
+
+ // Returns a human-readable form of the method at an index.
+ std::string PrettyMethod(uint32_t method_idx, bool with_signature = true) const;
+ // Returns a human-readable form of the field at an index.
+ std::string PrettyField(uint32_t field_idx, bool with_type = true) const;
+ // Returns a human-readable form of the type at an index.
+ std::string PrettyType(dex::TypeIndex type_idx) const;
+
+ // Not virtual for performance reasons.
+ ALWAYS_INLINE bool IsCompactDexFile() const {
+ return is_compact_dex_;
+ }
+ ALWAYS_INLINE bool IsStandardDexFile() const {
+ return !is_compact_dex_;
+ }
+ ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const;
+ ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const;
+
+ bool IsInMainSection(const void* addr) const {
+ return Begin() <= addr && addr < Begin() + Size();
+ }
+
+ bool IsInDataSection(const void* addr) const {
+ return DataBegin() <= addr && addr < DataBegin() + DataSize();
+ }
+
+ DexFileContainer* GetContainer() const {
+ return container_.get();
+ }
+
+ protected:
+ // First Dex format version supporting default methods.
+ static const uint32_t kDefaultMethodsVersion = 37;
+
+ DexFile(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_begin,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container,
+ bool is_compact_dex);
+
+ // Top-level initializer that calls other Init methods.
+ bool Init(std::string* error_msg);
+
+ // Returns true if the header magic and version numbers are of the expected values.
+ bool CheckMagicAndVersion(std::string* error_msg) const;
+
+ // Initialize section info for sections only found in map. Returns true on success.
+ void InitializeSectionsFromMapList();
+
+ // The base address of the memory mapping.
+ const uint8_t* const begin_;
+
+ // The size of the underlying memory allocation in bytes.
+ const size_t size_;
+
+ // The base address of the data section (same as Begin() for standard dex).
+ const uint8_t* const data_begin_;
+
+ // The size of the data section.
+ const size_t data_size_;
+
+ // Typically the dex file name when available, alternatively some identifying string.
+ //
+ // The ClassLinker will use this to match DexFiles the boot class
+ // path to DexCache::GetLocation when loading from an image.
+ const std::string location_;
+
+ const uint32_t location_checksum_;
+
+ // Points to the header section.
+ const Header* const header_;
+
+ // Points to the base of the string identifier list.
+ const StringId* const string_ids_;
+
+ // Points to the base of the type identifier list.
+ const TypeId* const type_ids_;
+
+ // Points to the base of the field identifier list.
+ const FieldId* const field_ids_;
+
+ // Points to the base of the method identifier list.
+ const MethodId* const method_ids_;
+
+ // Points to the base of the prototype identifier list.
+ const ProtoId* const proto_ids_;
+
+ // Points to the base of the class definition list.
+ const ClassDef* const class_defs_;
+
+ // Points to the base of the method handles list.
+ const MethodHandleItem* method_handles_;
+
+ // Number of elements in the method handles list.
+ size_t num_method_handles_;
+
+ // Points to the base of the call sites id list.
+ const CallSiteIdItem* call_site_ids_;
+
+ // Number of elements in the call sites list.
+ size_t num_call_site_ids_;
+
+ // If this dex file was loaded from an oat file, oat_dex_file_ contains a
+ // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
+ // null.
+ mutable const OatDexFile* oat_dex_file_;
+
+ // Manages the underlying memory allocation.
+ std::unique_ptr<DexFileContainer> container_;
+
+ // If the dex file is a compact dex file. If false then the dex file is a standard dex file.
+ const bool is_compact_dex_;
+
+ friend class DexFileLoader;
+ friend class DexFileVerifierTest;
+ friend class OatWriter;
+};
+
+std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
+
+// Iterate over a dex file's ProtoId's paramters
+class DexFileParameterIterator {
+ public:
+ DexFileParameterIterator(const DexFile& dex_file, const DexFile::ProtoId& proto_id)
+ : dex_file_(dex_file) {
+ type_list_ = dex_file_.GetProtoParameters(proto_id);
+ if (type_list_ != nullptr) {
+ size_ = type_list_->Size();
+ }
+ }
+ bool HasNext() const { return pos_ < size_; }
+ size_t Size() const { return size_; }
+ void Next() { ++pos_; }
+ dex::TypeIndex GetTypeIdx() {
+ return type_list_->GetTypeItem(pos_).type_idx_;
+ }
+ const char* GetDescriptor() {
+ return dex_file_.StringByTypeIdx(dex::TypeIndex(GetTypeIdx()));
+ }
+ private:
+ const DexFile& dex_file_;
+ const DexFile::TypeList* type_list_ = nullptr;
+ uint32_t size_ = 0;
+ uint32_t pos_ = 0;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DexFileParameterIterator);
+};
+
+// Abstract the signature of a method.
+class Signature : public ValueObject {
+ public:
+ std::string ToString() const;
+
+ static Signature NoSignature() {
+ return Signature();
+ }
+
+ bool IsVoid() const;
+ uint32_t GetNumberOfParameters() const;
+
+ bool operator==(const Signature& rhs) const;
+ bool operator!=(const Signature& rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator==(const StringPiece& rhs) const;
+
+ private:
+ Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) {
+ }
+
+ Signature() = default;
+
+ friend class DexFile;
+
+ const DexFile* const dex_file_ = nullptr;
+ const DexFile::ProtoId* const proto_id_ = nullptr;
+};
+std::ostream& operator<<(std::ostream& os, const Signature& sig);
+
+// Iterate and decode class_data_item
+class ClassDataItemIterator {
+ public:
+ ClassDataItemIterator(const DexFile& dex_file, const uint8_t* raw_class_data_item)
+ : dex_file_(dex_file), pos_(0), ptr_pos_(raw_class_data_item), last_idx_(0) {
+ ReadClassDataHeader();
+ if (EndOfInstanceFieldsPos() > 0) {
+ ReadClassDataField();
+ } else if (EndOfVirtualMethodsPos() > 0) {
+ ReadClassDataMethod();
+ }
+ }
+ uint32_t NumStaticFields() const {
+ return header_.static_fields_size_;
+ }
+ uint32_t NumInstanceFields() const {
+ return header_.instance_fields_size_;
+ }
+ uint32_t NumDirectMethods() const {
+ return header_.direct_methods_size_;
+ }
+ uint32_t NumVirtualMethods() const {
+ return header_.virtual_methods_size_;
+ }
+ bool IsAtMethod() const {
+ return pos_ >= EndOfInstanceFieldsPos();
+ }
+ bool HasNextStaticField() const {
+ return pos_ < EndOfStaticFieldsPos();
+ }
+ bool HasNextInstanceField() const {
+ return pos_ >= EndOfStaticFieldsPos() && pos_ < EndOfInstanceFieldsPos();
+ }
+ bool HasNextDirectMethod() const {
+ return pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfDirectMethodsPos();
+ }
+ bool HasNextVirtualMethod() const {
+ return pos_ >= EndOfDirectMethodsPos() && pos_ < EndOfVirtualMethodsPos();
+ }
+ bool HasNextMethod() const {
+ const bool result = pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfVirtualMethodsPos();
+ DCHECK_EQ(result, HasNextDirectMethod() || HasNextVirtualMethod());
+ return result;
+ }
+ void SkipStaticFields() {
+ while (HasNextStaticField()) {
+ Next();
+ }
+ }
+ void SkipInstanceFields() {
+ while (HasNextInstanceField()) {
+ Next();
+ }
+ }
+ void SkipAllFields() {
+ SkipStaticFields();
+ SkipInstanceFields();
+ }
+ void SkipDirectMethods() {
+ while (HasNextDirectMethod()) {
+ Next();
+ }
+ }
+ void SkipVirtualMethods() {
+ while (HasNextVirtualMethod()) {
+ Next();
+ }
+ }
+ bool HasNext() const {
+ return pos_ < EndOfVirtualMethodsPos();
+ }
+ inline void Next() {
+ pos_++;
+ if (pos_ < EndOfStaticFieldsPos()) {
+ last_idx_ = GetMemberIndex();
+ ReadClassDataField();
+ } else if (pos_ == EndOfStaticFieldsPos() && NumInstanceFields() > 0) {
+ last_idx_ = 0; // transition to next array, reset last index
+ ReadClassDataField();
+ } else if (pos_ < EndOfInstanceFieldsPos()) {
+ last_idx_ = GetMemberIndex();
+ ReadClassDataField();
+ } else if (pos_ == EndOfInstanceFieldsPos() && NumDirectMethods() > 0) {
+ last_idx_ = 0; // transition to next array, reset last index
+ ReadClassDataMethod();
+ } else if (pos_ < EndOfDirectMethodsPos()) {
+ last_idx_ = GetMemberIndex();
+ ReadClassDataMethod();
+ } else if (pos_ == EndOfDirectMethodsPos() && NumVirtualMethods() > 0) {
+ last_idx_ = 0; // transition to next array, reset last index
+ ReadClassDataMethod();
+ } else if (pos_ < EndOfVirtualMethodsPos()) {
+ last_idx_ = GetMemberIndex();
+ ReadClassDataMethod();
+ } else {
+ DCHECK(!HasNext());
+ }
+ }
+ uint32_t GetMemberIndex() const {
+ if (pos_ < EndOfInstanceFieldsPos()) {
+ return last_idx_ + field_.field_idx_delta_;
+ } else {
+ DCHECK_LT(pos_, EndOfVirtualMethodsPos());
+ return last_idx_ + method_.method_idx_delta_;
+ }
+ }
+ uint32_t GetRawMemberAccessFlags() const {
+ if (pos_ < EndOfInstanceFieldsPos()) {
+ return field_.access_flags_;
+ } else {
+ DCHECK_LT(pos_, EndOfVirtualMethodsPos());
+ return method_.access_flags_;
+ }
+ }
+ uint32_t GetFieldAccessFlags() const {
+ return GetMemberAccessFlags() & kAccValidFieldFlags;
+ }
+ uint32_t GetMethodAccessFlags() const {
+ return GetMemberAccessFlags() & kAccValidMethodFlags;
+ }
+ uint32_t GetMemberAccessFlags() const {
+ return HiddenApiAccessFlags::RemoveFromDex(GetRawMemberAccessFlags());
+ }
+ HiddenApiAccessFlags::ApiList DecodeHiddenAccessFlags() const {
+ return HiddenApiAccessFlags::DecodeFromDex(GetRawMemberAccessFlags());
+ }
+ bool MemberIsNative() const {
+ return GetRawMemberAccessFlags() & kAccNative;
+ }
+ bool MemberIsFinal() const {
+ return GetRawMemberAccessFlags() & kAccFinal;
+ }
+ ALWAYS_INLINE InvokeType GetMethodInvokeType(const DexFile::ClassDef& class_def) const;
+ const DexFile::CodeItem* GetMethodCodeItem() const {
+ return dex_file_.GetCodeItem(method_.code_off_);
+ }
+ uint32_t GetMethodCodeItemOffset() const {
+ return method_.code_off_;
+ }
+ const uint8_t* DataPointer() const {
+ return ptr_pos_;
+ }
+ const uint8_t* EndDataPointer() const {
+ CHECK(!HasNext());
+ return ptr_pos_;
+ }
+
+ private:
+ // A dex file's class_data_item is leb128 encoded, this structure holds a decoded form of the
+ // header for a class_data_item
+ struct ClassDataHeader {
+ uint32_t static_fields_size_; // the number of static fields
+ uint32_t instance_fields_size_; // the number of instance fields
+ uint32_t direct_methods_size_; // the number of direct methods
+ uint32_t virtual_methods_size_; // the number of virtual methods
+ } header_;
+
+ // Read and decode header from a class_data_item stream into header
+ void ReadClassDataHeader();
+
+ uint32_t EndOfStaticFieldsPos() const {
+ return header_.static_fields_size_;
+ }
+ uint32_t EndOfInstanceFieldsPos() const {
+ return EndOfStaticFieldsPos() + header_.instance_fields_size_;
+ }
+ uint32_t EndOfDirectMethodsPos() const {
+ return EndOfInstanceFieldsPos() + header_.direct_methods_size_;
+ }
+ uint32_t EndOfVirtualMethodsPos() const {
+ return EndOfDirectMethodsPos() + header_.virtual_methods_size_;
+ }
+
+ // A decoded version of the field of a class_data_item
+ struct ClassDataField {
+ uint32_t field_idx_delta_; // delta of index into the field_ids array for FieldId
+ uint32_t access_flags_; // access flags for the field
+ ClassDataField() : field_idx_delta_(0), access_flags_(0) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ClassDataField);
+ };
+ ClassDataField field_;
+
+ // Read and decode a field from a class_data_item stream into field
+ void ReadClassDataField();
+
+ // A decoded version of the method of a class_data_item
+ struct ClassDataMethod {
+ uint32_t method_idx_delta_; // delta of index into the method_ids array for MethodId
+ uint32_t access_flags_;
+ uint32_t code_off_;
+ ClassDataMethod() : method_idx_delta_(0), access_flags_(0), code_off_(0) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ClassDataMethod);
+ };
+ ClassDataMethod method_;
+
+ // Read and decode a method from a class_data_item stream into method
+ void ReadClassDataMethod();
+
+ const DexFile& dex_file_;
+ size_t pos_; // integral number of items passed
+ const uint8_t* ptr_pos_; // pointer into stream of class_data_item
+ uint32_t last_idx_; // last read field or method index to apply delta to
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator);
+};
+
+class EncodedArrayValueIterator {
+ public:
+ EncodedArrayValueIterator(const DexFile& dex_file, const uint8_t* array_data);
+
+ bool HasNext() const { return pos_ < array_size_; }
+
+ void Next();
+
+ enum ValueType {
+ kByte = 0x00,
+ kShort = 0x02,
+ kChar = 0x03,
+ kInt = 0x04,
+ kLong = 0x06,
+ kFloat = 0x10,
+ kDouble = 0x11,
+ kMethodType = 0x15,
+ kMethodHandle = 0x16,
+ kString = 0x17,
+ kType = 0x18,
+ kField = 0x19,
+ kMethod = 0x1a,
+ kEnum = 0x1b,
+ kArray = 0x1c,
+ kAnnotation = 0x1d,
+ kNull = 0x1e,
+ kBoolean = 0x1f,
+ };
+
+ ValueType GetValueType() const { return type_; }
+ const jvalue& GetJavaValue() const { return jval_; }
+
+ protected:
+ static constexpr uint8_t kEncodedValueTypeMask = 0x1f; // 0b11111
+ static constexpr uint8_t kEncodedValueArgShift = 5;
+
+ const DexFile& dex_file_;
+ size_t array_size_; // Size of array.
+ size_t pos_; // Current position.
+ const uint8_t* ptr_; // Pointer into encoded data array.
+ ValueType type_; // Type of current encoded value.
+ jvalue jval_; // Value of current encoded value.
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedArrayValueIterator);
+};
+std::ostream& operator<<(std::ostream& os, const EncodedArrayValueIterator::ValueType& code);
+
+class EncodedStaticFieldValueIterator : public EncodedArrayValueIterator {
+ public:
+ EncodedStaticFieldValueIterator(const DexFile& dex_file,
+ const DexFile::ClassDef& class_def)
+ : EncodedArrayValueIterator(dex_file,
+ dex_file.GetEncodedStaticFieldValuesArray(class_def))
+ {}
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedStaticFieldValueIterator);
+};
+std::ostream& operator<<(std::ostream& os, const EncodedStaticFieldValueIterator::ValueType& code);
+
+class CallSiteArrayValueIterator : public EncodedArrayValueIterator {
+ public:
+ CallSiteArrayValueIterator(const DexFile& dex_file,
+ const DexFile::CallSiteIdItem& call_site_id)
+ : EncodedArrayValueIterator(dex_file,
+ dex_file.GetCallSiteEncodedValuesArray(call_site_id))
+ {}
+
+ uint32_t Size() const { return array_size_; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CallSiteArrayValueIterator);
+};
+std::ostream& operator<<(std::ostream& os, const CallSiteArrayValueIterator::ValueType& code);
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_H_
diff --git a/libdexfile/dex/dex_file_exception_helpers.cc b/libdexfile/dex/dex_file_exception_helpers.cc
new file mode 100644
index 0000000000..8e597fd3dd
--- /dev/null
+++ b/libdexfile/dex/dex_file_exception_helpers.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file_exception_helpers.h"
+
+#include "code_item_accessors-inl.h"
+
+namespace art {
+
+CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address) {
+ handler_.address_ = -1;
+ int32_t offset = -1;
+
+ // Short-circuit the overwhelmingly common cases.
+ switch (accessor.TriesSize()) {
+ case 0:
+ break;
+ case 1: {
+ const DexFile::TryItem* tries = accessor.TryItems().begin();
+ uint32_t start = tries->start_addr_;
+ if (address >= start) {
+ uint32_t end = start + tries->insn_count_;
+ if (address < end) {
+ offset = tries->handler_off_;
+ }
+ }
+ break;
+ }
+ default: {
+ const DexFile::TryItem* try_item = accessor.FindTryItem(address);
+ offset = try_item != nullptr ? try_item->handler_off_ : -1;
+ break;
+ }
+ }
+ Init(accessor, offset);
+}
+
+CatchHandlerIterator::CatchHandlerIterator(const CodeItemDataAccessor& accessor,
+ const DexFile::TryItem& try_item) {
+ handler_.address_ = -1;
+ Init(accessor, try_item.handler_off_);
+}
+
+void CatchHandlerIterator::Init(const CodeItemDataAccessor& accessor, int32_t offset) {
+ if (offset >= 0) {
+ Init(accessor.GetCatchHandlerData(offset));
+ } else {
+ // Not found, initialize as empty
+ current_data_ = nullptr;
+ remaining_count_ = -1;
+ catch_all_ = false;
+ DCHECK(!HasNext());
+ }
+}
+
+void CatchHandlerIterator::Init(const uint8_t* handler_data) {
+ current_data_ = handler_data;
+ remaining_count_ = DecodeSignedLeb128(&current_data_);
+
+ // If remaining_count_ is non-positive, then it is the negative of
+ // the number of catch types, and the catches are followed by a
+ // catch-all handler.
+ if (remaining_count_ <= 0) {
+ catch_all_ = true;
+ remaining_count_ = -remaining_count_;
+ } else {
+ catch_all_ = false;
+ }
+ Next();
+}
+
+void CatchHandlerIterator::Next() {
+ if (remaining_count_ > 0) {
+ handler_.type_idx_ = dex::TypeIndex(DecodeUnsignedLeb128(&current_data_));
+ handler_.address_ = DecodeUnsignedLeb128(&current_data_);
+ remaining_count_--;
+ return;
+ }
+
+ if (catch_all_) {
+ handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16);
+ handler_.address_ = DecodeUnsignedLeb128(&current_data_);
+ catch_all_ = false;
+ return;
+ }
+
+ // no more handler
+ remaining_count_ = -1;
+}
+
+} // namespace art
diff --git a/libdexfile/dex/dex_file_exception_helpers.h b/libdexfile/dex/dex_file_exception_helpers.h
new file mode 100644
index 0000000000..a05fd68e86
--- /dev/null
+++ b/libdexfile/dex/dex_file_exception_helpers.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_EXCEPTION_HELPERS_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_EXCEPTION_HELPERS_H_
+
+#include "dex_file.h"
+
+namespace art {
+
+class CodeItemDataAccessor;
+
+class CatchHandlerIterator {
+ public:
+ CatchHandlerIterator(const CodeItemDataAccessor& accessor, uint32_t address);
+
+ CatchHandlerIterator(const CodeItemDataAccessor& accessor, const DexFile::TryItem& try_item);
+
+ explicit CatchHandlerIterator(const uint8_t* handler_data) {
+ Init(handler_data);
+ }
+
+ dex::TypeIndex GetHandlerTypeIndex() const {
+ return handler_.type_idx_;
+ }
+ uint32_t GetHandlerAddress() const {
+ return handler_.address_;
+ }
+ void Next();
+ bool HasNext() const {
+ return remaining_count_ != -1 || catch_all_;
+ }
+ // End of this set of catch blocks, convenience method to locate next set of catch blocks
+ const uint8_t* EndDataPointer() const {
+ CHECK(!HasNext());
+ return current_data_;
+ }
+
+ private:
+ void Init(const CodeItemDataAccessor& accessor, int32_t offset);
+ void Init(const uint8_t* handler_data);
+
+ struct CatchHandlerItem {
+ dex::TypeIndex type_idx_; // type index of the caught exception type
+ uint32_t address_; // handler address
+ } handler_;
+ const uint8_t* current_data_; // the current handler in dex file.
+ int32_t remaining_count_; // number of handlers not read.
+ bool catch_all_; // is there a handler that will catch all exceptions in case
+ // that all typed handler does not match.
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_EXCEPTION_HELPERS_H_
diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc
new file mode 100644
index 0000000000..2c75c5b5d9
--- /dev/null
+++ b/libdexfile/dex/dex_file_loader.cc
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file_loader.h"
+
+#include "android-base/stringprintf.h"
+
+#include "base/stl_util.h"
+#include "compact_dex_file.h"
+#include "dex_file.h"
+#include "dex_file_verifier.h"
+#include "standard_dex_file.h"
+#include "ziparchive/zip_archive.h"
+
+// system/core/zip_archive definitions.
+struct ZipEntry;
+typedef void* ZipArchiveHandle;
+
+namespace art {
+
+namespace {
+
+class VectorContainer : public DexFileContainer {
+ public:
+ explicit VectorContainer(std::vector<uint8_t>&& vector) : vector_(std::move(vector)) { }
+ virtual ~VectorContainer() OVERRIDE { }
+
+ int GetPermissions() OVERRIDE {
+ return 0;
+ }
+
+ bool IsReadOnly() OVERRIDE {
+ return true;
+ }
+
+ bool EnableWrite() OVERRIDE {
+ return false;
+ }
+
+ bool DisableWrite() OVERRIDE {
+ return false;
+ }
+
+ private:
+ std::vector<uint8_t> vector_;
+ DISALLOW_COPY_AND_ASSIGN(VectorContainer);
+};
+
+} // namespace
+
+using android::base::StringPrintf;
+
+class DexZipArchive;
+
+class DexZipEntry {
+ public:
+ // Extract this entry to memory.
+ // Returns null on failure and sets error_msg.
+ const std::vector<uint8_t> Extract(std::string* error_msg) {
+ std::vector<uint8_t> map(GetUncompressedLength());
+ if (map.size() == 0) {
+ DCHECK(!error_msg->empty());
+ return map;
+ }
+ const int32_t error = ExtractToMemory(handle_, zip_entry_, map.data(), map.size());
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
+ }
+ return map;
+ }
+
+ virtual ~DexZipEntry() {
+ delete zip_entry_;
+ }
+
+ uint32_t GetUncompressedLength() {
+ return zip_entry_->uncompressed_length;
+ }
+
+ uint32_t GetCrc32() {
+ return zip_entry_->crc32;
+ }
+
+ private:
+ DexZipEntry(ZipArchiveHandle handle,
+ ::ZipEntry* zip_entry,
+ const std::string& entry_name)
+ : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {}
+
+ ZipArchiveHandle handle_;
+ ::ZipEntry* const zip_entry_;
+ std::string const entry_name_;
+
+ friend class DexZipArchive;
+ DISALLOW_COPY_AND_ASSIGN(DexZipEntry);
+};
+
+class DexZipArchive {
+ public:
+ // return new DexZipArchive instance on success, null on error.
+ static DexZipArchive* Open(const uint8_t* base, size_t size, std::string* error_msg) {
+ ZipArchiveHandle handle;
+ uint8_t* nonconst_base = const_cast<uint8_t*>(base);
+ const int32_t error = OpenArchiveFromMemory(nonconst_base, size, "ZipArchiveMemory", &handle);
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
+ CloseArchive(handle);
+ return nullptr;
+ }
+ return new DexZipArchive(handle);
+ }
+
+ DexZipEntry* Find(const char* name, std::string* error_msg) const {
+ DCHECK(name != nullptr);
+ // Resist the urge to delete the space. <: is a bigraph sequence.
+ std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
+ const int32_t error = FindEntry(handle_, ZipString(name), zip_entry.get());
+ if (error) {
+ *error_msg = std::string(ErrorCodeString(error));
+ return nullptr;
+ }
+ return new DexZipEntry(handle_, zip_entry.release(), name);
+ }
+
+ ~DexZipArchive() {
+ CloseArchive(handle_);
+ }
+
+
+ private:
+ explicit DexZipArchive(ZipArchiveHandle handle) : handle_(handle) {}
+ ZipArchiveHandle handle_;
+
+ friend class DexZipEntry;
+ DISALLOW_COPY_AND_ASSIGN(DexZipArchive);
+};
+
+static bool IsZipMagic(uint32_t magic) {
+ return (('P' == ((magic >> 0) & 0xff)) &&
+ ('K' == ((magic >> 8) & 0xff)));
+}
+
+bool DexFileLoader::IsMagicValid(uint32_t magic) {
+ return IsMagicValid(reinterpret_cast<uint8_t*>(&magic));
+}
+
+bool DexFileLoader::IsMagicValid(const uint8_t* magic) {
+ return StandardDexFile::IsMagicValid(magic) ||
+ CompactDexFile::IsMagicValid(magic);
+}
+
+bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) {
+ if (StandardDexFile::IsMagicValid(magic)) {
+ return StandardDexFile::IsVersionValid(magic);
+ }
+ if (CompactDexFile::IsMagicValid(magic)) {
+ return CompactDexFile::IsVersionValid(magic);
+ }
+ return false;
+}
+
+bool DexFileLoader::IsMultiDexLocation(const char* location) {
+ return strrchr(location, kMultiDexSeparator) != nullptr;
+}
+
+std::string DexFileLoader::GetMultiDexClassesDexName(size_t index) {
+ return (index == 0) ? "classes.dex" : StringPrintf("classes%zu.dex", index + 1);
+}
+
+std::string DexFileLoader::GetMultiDexLocation(size_t index, const char* dex_location) {
+ return (index == 0)
+ ? dex_location
+ : StringPrintf("%s%cclasses%zu.dex", dex_location, kMultiDexSeparator, index + 1);
+}
+
+std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) {
+ CHECK_NE(dex_location, static_cast<const char*>(nullptr));
+ std::string base_location = GetBaseLocation(dex_location);
+ const char* suffix = dex_location + base_location.size();
+ DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
+ UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
+ if (path != nullptr && path.get() != base_location) {
+ return std::string(path.get()) + suffix;
+ } else if (suffix[0] == 0) {
+ return base_location;
+ } else {
+ return dex_location;
+ }
+}
+
+// All of the implementations here should be independent of the runtime.
+// TODO: implement all the virtual methods.
+
+bool DexFileLoader::GetMultiDexChecksums(const char* filename ATTRIBUTE_UNUSED,
+ std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED,
+ std::string* error_msg,
+ int zip_fd ATTRIBUTE_UNUSED) const {
+ *error_msg = "UNIMPLEMENTED";
+ return false;
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) const {
+ return OpenCommon(base,
+ size,
+ /*data_base*/ nullptr,
+ /*data_size*/ 0,
+ location,
+ location_checksum,
+ oat_dex_file,
+ verify,
+ verify_checksum,
+ error_msg,
+ /*container*/ nullptr,
+ /*verify_result*/ nullptr);
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::OpenWithDataSection(
+ const uint8_t* base,
+ size_t size,
+ const uint8_t* data_base,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) const {
+ return OpenCommon(base,
+ size,
+ data_base,
+ data_size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ verify,
+ verify_checksum,
+ error_msg,
+ /*container*/ nullptr,
+ /*verify_result*/ nullptr);
+}
+
+bool DexFileLoader::OpenAll(
+ const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
+ DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
+ uint32_t magic = *reinterpret_cast<const uint32_t*>(base);
+ if (IsZipMagic(magic)) {
+ std::unique_ptr<DexZipArchive> zip_archive(DexZipArchive::Open(base, size, error_msg));
+ if (zip_archive.get() == nullptr) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ return OpenAllDexFilesFromZip(*zip_archive.get(),
+ location,
+ verify,
+ verify_checksum,
+ error_msg,
+ dex_files);
+ }
+ if (IsMagicValid(magic)) {
+ const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(base);
+ std::unique_ptr<const DexFile> dex_file(Open(base,
+ size,
+ location,
+ dex_header->checksum_,
+ /*oat_dex_file*/ nullptr,
+ verify,
+ verify_checksum,
+ error_msg));
+ if (dex_file.get() != nullptr) {
+ dex_files->push_back(std::move(dex_file));
+ return true;
+ } else {
+ return false;
+ }
+ }
+ *error_msg = StringPrintf("Expected valid zip or dex file");
+ return false;
+}
+
+std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_base,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ DexFileContainer* container,
+ VerifyResult* verify_result) {
+ if (verify_result != nullptr) {
+ *verify_result = VerifyResult::kVerifyNotAttempted;
+ }
+ std::unique_ptr<DexFile> dex_file;
+ if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
+ if (data_size != 0) {
+ CHECK_EQ(base, data_base) << "Unsupported for standard dex";
+ }
+ dex_file.reset(new StandardDexFile(base,
+ size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ container));
+ } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
+ if (data_base == nullptr) {
+ // TODO: Is there a clean way to support both an explicit data section and reading the one
+ // from the header.
+ CHECK_EQ(data_size, 0u);
+ const CompactDexFile::Header* const header = CompactDexFile::Header::At(base);
+ data_base = base + header->data_off_;
+ data_size = header->data_size_;
+ }
+ dex_file.reset(new CompactDexFile(base,
+ size,
+ data_base,
+ data_size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ container));
+ } else {
+ *error_msg = "Invalid or truncated dex file";
+ }
+ if (dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
+ error_msg->c_str());
+ return nullptr;
+ }
+ if (!dex_file->Init(error_msg)) {
+ dex_file.reset();
+ return nullptr;
+ }
+ if (verify && !DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location.c_str(),
+ verify_checksum,
+ error_msg)) {
+ if (verify_result != nullptr) {
+ *verify_result = VerifyResult::kVerifyFailed;
+ }
+ return nullptr;
+ }
+ if (verify_result != nullptr) {
+ *verify_result = VerifyResult::kVerifySucceeded;
+ }
+ return dex_file;
+}
+
+std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip(
+ const DexZipArchive& zip_archive,
+ const char* entry_name,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ ZipOpenErrorCode* error_code) const {
+ CHECK(!location.empty());
+ std::unique_ptr<DexZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg));
+ if (zip_entry == nullptr) {
+ *error_code = ZipOpenErrorCode::kEntryNotFound;
+ return nullptr;
+ }
+ if (zip_entry->GetUncompressedLength() == 0) {
+ *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
+ *error_code = ZipOpenErrorCode::kDexFileError;
+ return nullptr;
+ }
+
+ std::vector<uint8_t> map(zip_entry->Extract(error_msg));
+ if (map.size() == 0) {
+ *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
+ error_msg->c_str());
+ *error_code = ZipOpenErrorCode::kExtractToMemoryError;
+ return nullptr;
+ }
+ VerifyResult verify_result;
+ std::unique_ptr<const DexFile> dex_file = OpenCommon(map.data(),
+ map.size(),
+ /*data_base*/ nullptr,
+ /*data_size*/ 0u,
+ location,
+ zip_entry->GetCrc32(),
+ /*oat_dex_file*/ nullptr,
+ verify,
+ verify_checksum,
+ error_msg,
+ new VectorContainer(std::move(map)),
+ &verify_result);
+ if (dex_file == nullptr) {
+ if (verify_result == VerifyResult::kVerifyNotAttempted) {
+ *error_code = ZipOpenErrorCode::kDexFileError;
+ } else {
+ *error_code = ZipOpenErrorCode::kVerifyError;
+ }
+ return nullptr;
+ }
+ if (verify_result != VerifyResult::kVerifySucceeded) {
+ *error_code = ZipOpenErrorCode::kVerifyError;
+ return nullptr;
+ }
+ *error_code = ZipOpenErrorCode::kNoError;
+ return dex_file;
+}
+
+// Technically we do not have a limitation with respect to the number of dex files that can be in a
+// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols
+// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what
+// seems an excessive number.
+static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
+
+bool DexFileLoader::OpenAllDexFilesFromZip(
+ const DexZipArchive& zip_archive,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
+ DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
+ ZipOpenErrorCode error_code;
+ std::unique_ptr<const DexFile> dex_file(OpenOneDexFileFromZip(zip_archive,
+ kClassesDex,
+ location,
+ verify,
+ verify_checksum,
+ error_msg,
+ &error_code));
+ if (dex_file.get() == nullptr) {
+ return false;
+ } else {
+ // Had at least classes.dex.
+ dex_files->push_back(std::move(dex_file));
+
+ // Now try some more.
+
+ // We could try to avoid std::string allocations by working on a char array directly. As we
+ // do not expect a lot of iterations, this seems too involved and brittle.
+
+ for (size_t i = 1; ; ++i) {
+ std::string name = GetMultiDexClassesDexName(i);
+ std::string fake_location = GetMultiDexLocation(i, location.c_str());
+ std::unique_ptr<const DexFile> next_dex_file(OpenOneDexFileFromZip(zip_archive,
+ name.c_str(),
+ fake_location,
+ verify,
+ verify_checksum,
+ error_msg,
+ &error_code));
+ if (next_dex_file.get() == nullptr) {
+ if (error_code != ZipOpenErrorCode::kEntryNotFound) {
+ LOG(WARNING) << "Zip open failed: " << *error_msg;
+ }
+ break;
+ } else {
+ dex_files->push_back(std::move(next_dex_file));
+ }
+
+ if (i == kWarnOnManyDexFilesThreshold) {
+ LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold
+ << " dex files. Please consider coalescing and shrinking the number to "
+ " avoid runtime overhead.";
+ }
+
+ if (i == std::numeric_limits<size_t>::max()) {
+ LOG(ERROR) << "Overflow in number of dex files!";
+ break;
+ }
+ }
+
+ return true;
+ }
+}
+} // namespace art
diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h
new file mode 100644
index 0000000000..41d9b1691b
--- /dev/null
+++ b/libdexfile/dex/dex_file_loader.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace art {
+
+class DexFile;
+class DexFileContainer;
+class MemMap;
+class OatDexFile;
+
+class DexZipArchive;
+
+// Class that is used to open dex files and deal with corresponding multidex and location logic.
+class DexFileLoader {
+ public:
+ // name of the DexFile entry within a zip archive
+ static constexpr const char* kClassesDex = "classes.dex";
+
+ // The separator character in MultiDex locations.
+ static constexpr char kMultiDexSeparator = '!';
+
+ // Return true if the magic is valid for dex or cdex.
+ static bool IsMagicValid(uint32_t magic);
+ static bool IsMagicValid(const uint8_t* magic);
+
+ // Return true if the corresponding version and magic is valid.
+ static bool IsVersionAndMagicValid(const uint8_t* magic);
+
+ // Check whether a location denotes a multidex dex file. This is a very simple check: returns
+ // whether the string contains the separator character.
+ static bool IsMultiDexLocation(const char* location);
+
+ // Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for
+ // index == 0, and classes{index + 1}.dex else.
+ static std::string GetMultiDexClassesDexName(size_t index);
+
+ // Return the (possibly synthetic) dex location for a multidex entry. This is dex_location for
+ // index == 0, and dex_location + multi-dex-separator + GetMultiDexClassesDexName(index) else.
+ static std::string GetMultiDexLocation(size_t index, const char* dex_location);
+
+ // Returns the canonical form of the given dex location.
+ //
+ // There are different flavors of "dex locations" as follows:
+ // the file name of a dex file:
+ // The actual file path that the dex file has on disk.
+ // dex_location:
+ // This acts as a key for the class linker to know which dex file to load.
+ // It may correspond to either an old odex file or a particular dex file
+ // inside an oat file. In the first case it will also match the file name
+ // of the dex file. In the second case (oat) it will include the file name
+ // and possibly some multidex annotation to uniquely identify it.
+ // canonical_dex_location:
+ // the dex_location where it's file name part has been made canonical.
+ static std::string GetDexCanonicalLocation(const char* dex_location);
+
+ // For normal dex files, location and base location coincide. If a dex file is part of a multidex
+ // archive, the base location is the name of the originating jar/apk, stripped of any internal
+ // classes*.dex path.
+ static std::string GetBaseLocation(const char* location) {
+ const char* pos = strrchr(location, kMultiDexSeparator);
+ return (pos == nullptr) ? location : std::string(location, pos - location);
+ }
+
+ static std::string GetBaseLocation(const std::string& location) {
+ return GetBaseLocation(location.c_str());
+ }
+
+ // Returns the '!classes*.dex' part of the dex location. Returns an empty
+ // string if there is no multidex suffix for the given location.
+ // The kMultiDexSeparator is included in the returned suffix.
+ static std::string GetMultiDexSuffix(const std::string& location) {
+ size_t pos = location.rfind(kMultiDexSeparator);
+ return (pos == std::string::npos) ? std::string() : location.substr(pos);
+ }
+
+ virtual ~DexFileLoader() { }
+
+ // Returns the checksums of a file for comparison with GetLocationChecksum().
+ // For .dex files, this is the single header checksum.
+ // For zip files, this is the zip entry CRC32 checksum for classes.dex and
+ // each additional multidex entry classes2.dex, classes3.dex, etc.
+ // If a valid zip_fd is provided the file content will be read directly from
+ // the descriptor and `filename` will be used as alias for error logging. If
+ // zip_fd is -1, the method will try to open the `filename` and read the
+ // content from it.
+ // Return true if the checksums could be found, false otherwise.
+ virtual bool GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg,
+ int zip_fd = -1) const;
+
+ // Opens .dex file, backed by existing memory
+ virtual std::unique_ptr<const DexFile> Open(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) const;
+
+ // Open a dex file with a separate data section.
+ virtual std::unique_ptr<const DexFile> OpenWithDataSection(
+ const uint8_t* base,
+ size_t size,
+ const uint8_t* data_base,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg) const;
+
+
+ // Opens all .dex files found in the memory map, guessing the container format based on file
+ // extension.
+ virtual bool OpenAll(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
+
+ protected:
+ enum class ZipOpenErrorCode {
+ kNoError,
+ kEntryNotFound,
+ kExtractToMemoryError,
+ kDexFileError,
+ kMakeReadOnlyError,
+ kVerifyError
+ };
+
+ enum class VerifyResult { // private
+ kVerifyNotAttempted,
+ kVerifySucceeded,
+ kVerifyFailed
+ };
+
+ static std::unique_ptr<DexFile> OpenCommon(const uint8_t* base,
+ size_t size,
+ const uint8_t* data_base,
+ size_t data_size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ DexFileContainer* container,
+ VerifyResult* verify_result);
+
+ private:
+ // Open all classesXXX.dex files from a zip archive.
+ bool OpenAllDexFilesFromZip(const DexZipArchive& zip_archive,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files) const;
+
+ // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
+ // return.
+ std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const DexZipArchive& zip_archive,
+ const char* entry_name,
+ const std::string& location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg,
+ ZipOpenErrorCode* error_code) const;
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_LOADER_H_
diff --git a/libdexfile/dex/dex_file_loader_test.cc b/libdexfile/dex/dex_file_loader_test.cc
new file mode 100644
index 0000000000..ab5c3f9a26
--- /dev/null
+++ b/libdexfile/dex/dex_file_loader_test.cc
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file.h"
+
+#include <memory>
+
+#include "base64_test_util.h"
+#include "code_item_accessors-inl.h"
+#include "descriptors_names.h"
+#include "dex_file-inl.h"
+#include "dex_file_loader.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+class DexFileLoaderTest : public testing::Test {};
+
+static constexpr char kLocationString[] = "/a/dex/file/location";
+
+static inline std::vector<uint8_t> DecodeBase64Vec(const char* src) {
+ std::vector<uint8_t> res;
+ size_t size;
+ std::unique_ptr<uint8_t[]> data(DecodeBase64(src, &size));
+ res.resize(size);
+ memcpy(res.data(), data.get(), size);
+ return res;
+}
+
+// Although this is the same content logically as the Nested test dex,
+// the DexFileHeader test is sensitive to subtle changes in the
+// contents due to the checksum etc, so we embed the exact input here.
+//
+// class Nested {
+// class Inner {
+// }
+// }
+static const char kRawDex[] =
+ "ZGV4CjAzNQAQedgAe7gM1B/WHsWJ6L7lGAISGC7yjD2IAwAAcAAAAHhWNBIAAAAAAAAAAMQCAAAP"
+ "AAAAcAAAAAcAAACsAAAAAgAAAMgAAAABAAAA4AAAAAMAAADoAAAAAgAAAAABAABIAgAAQAEAAK4B"
+ "AAC2AQAAvQEAAM0BAADXAQAA+wEAABsCAAA+AgAAUgIAAF8CAABiAgAAZgIAAHMCAAB5AgAAgQIA"
+ "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABgAAAAAAAAAKAAAABgAAAKgBAAAAAAEA"
+ "DQAAAAAAAQAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAiAEAAKsCAAAA"
+ "AAAAAQAAAAAAAAAFAAAAAAAAAAgAAACYAQAAuAIAAAAAAAACAAAAlAIAAJoCAAABAAAAowIAAAIA"
+ "AgABAAAAiAIAAAYAAABbAQAAcBACAAAADgABAAEAAQAAAI4CAAAEAAAAcBACAAAADgBAAQAAAAAA"
+ "AAAAAAAAAAAATAEAAAAAAAAAAAAAAAAAAAEAAAABAAY8aW5pdD4ABUlubmVyAA5MTmVzdGVkJElu"
+ "bmVyOwAITE5lc3RlZDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2"
+ "aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNz"
+ "ZXM7ABJMamF2YS9sYW5nL09iamVjdDsAC05lc3RlZC5qYXZhAAFWAAJWTAALYWNjZXNzRmxhZ3MA"
+ "BG5hbWUABnRoaXMkMAAFdmFsdWUAAgEABw4AAQAHDjwAAgIBDhgBAgMCCwQADBcBAgQBDhwBGAAA"
+ "AQEAAJAgAICABNQCAAABAAGAgATwAgAAEAAAAAAAAAABAAAAAAAAAAEAAAAPAAAAcAAAAAIAAAAH"
+ "AAAArAAAAAMAAAACAAAAyAAAAAQAAAABAAAA4AAAAAUAAAADAAAA6AAAAAYAAAACAAAAAAEAAAMQ"
+ "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
+ "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
+
+// kRawDex{38,39,40,41} are dex'ed versions of the following Java source :
+//
+// public class Main {
+// public static void main(String[] foo) {
+// }
+// }
+//
+// The dex file was manually edited to change its dex version code to 38
+// or 39, respectively.
+static const char kRawDex38[] =
+ "ZGV4CjAzOAC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
+ "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
+ "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
+ "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
+ "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
+ "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
+ "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
+ "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
+ "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
+
+static const char kRawDex39[] =
+ "ZGV4CjAzOQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
+ "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
+ "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
+ "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
+ "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
+ "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
+ "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
+ "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
+ "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
+
+static const char kRawDex40[] =
+ "ZGV4CjA0MAC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
+ "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
+ "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
+ "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
+ "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
+ "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
+ "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
+ "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
+ "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
+
+static const char kRawDex41[] =
+ "ZGV4CjA0MQC4OovJlJ1089ikzK6asMf/f8qp3Kve5VsgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
+ "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
+ "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAADAAAA"
+ "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
+ "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
+ "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgYAE8AEBCYgCDAAA"
+ "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
+ "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
+ "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
+
+static const char kRawDexZeroLength[] =
+ "UEsDBAoAAAAAAOhxAkkAAAAAAAAAAAAAAAALABwAY2xhc3Nlcy5kZXhVVAkAA2QNoVdnDaFXdXgL"
+ "AAEE5AMBAASIEwAAUEsBAh4DCgAAAAAA6HECSQAAAAAAAAAAAAAAAAsAGAAAAAAAAAAAAKCBAAAA"
+ "AGNsYXNzZXMuZGV4VVQFAANkDaFXdXgLAAEE5AMBAASIEwAAUEsFBgAAAAABAAEAUQAAAEUAAAAA"
+ "AA==";
+
+static const char kRawZipClassesDexPresent[] =
+ "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VXdXgL"
+ "AAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMDQ0WY"
+ "iRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEIEEcA"
+ "cS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu4IOa"
+ "wczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhHIykL"
+ "LinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkVIGpA"
+ "Yc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEBAAAg"
+ "AgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEABIgTAABQ"
+ "SwUGAAAAAAEAAQBRAAAAdgEAAAAA";
+
+static const char kRawZipClassesDexAbsent[] =
+ "UEsDBBQAAAAIANVRN0ms99lIMQEAACACAAAOABwAbm90Y2xhc3Nlcy5kZXhVVAkAAwFj5VcUY+VX"
+ "dXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQCEwNDAQMD"
+ "Q0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGxGxAHAnEI"
+ "EEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8UFGgP6Fu"
+ "4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYxMIX5MAhH"
+ "IykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHCmg0pvBkV"
+ "IGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACADVUTdJrPfZSDEB"
+ "AAAgAgAADgAYAAAAAAAAAAAAoIEAAAAAbm90Y2xhc3Nlcy5kZXhVVAUAAwFj5Vd1eAsAAQTkAwEA"
+ "BIgTAABQSwUGAAAAAAEAAQBUAAAAeQEAAAAA";
+
+static const char kRawZipThreeDexFiles[] =
+ "UEsDBBQAAAAIAP1WN0ms99lIMQEAACACAAAMABwAY2xhc3NlczIuZGV4VVQJAAOtbOVXrWzlV3V4"
+ "CwABBOQDAQAEiBMAAEtJreAyMLZg2GHVfXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NF"
+ "mIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBAAGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBH"
+ "AHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiDHWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCD"
+ "msHMwGaTmZdZYsfA5uObmJlnzSDkk5VYlqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMp"
+ "Cy4pysxLt2ZgyQUqAzmYj4EZTIL909jA8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBq"
+ "QGHOKAARB4UbkwLETFA8MEPVgMKCQQGiBhxOUPWgeAYAUEsDBBQAAAAIAABXN0ms99lIMQEAACAC"
+ "AAAMABwAY2xhc3NlczMuZGV4VVQJAAOvbOVXr2zlV3V4CwABBOQDAQAEiBMAAEtJreAyMLZg2GHV"
+ "fXLK3JLPN5acWTdrw/H/9adW3ll972m0AhMDQwEDA0NFmIkQAxT0MDIwcDBAxFmAeAIQA5UxLGBA"
+ "AGYg3gHEQKUMF4DYAMj4AKSVgLQWEBsBsRsQBwJxCBBHAHEuI0Q9E9RcNijNBDWTFcqWYUSyCKiD"
+ "HWoPQgTBZ4bSdYwwOUawXDEjxOwCAZCJfFBRoD+hbuCDmsHMwGaTmZdZYsfA5uObmJlnzSDkk5VY"
+ "lqifk5iXru+flJWaXGLNwAmS0gOJMzCGMTCF+TAIRyMpCy4pysxLt2ZgyQUqAzmYj4EZTIL909jA"
+ "8oGRkbODiQfJ/TAaFs6wMJkA9RMsvFmRwpoNKbwZFSBqQGHOKAARB4UbkwLETFA8MEPVgMKCQQGi"
+ "BhxOUPWgeAYAUEsDBBQAAAAIANVRN0ms99lIMQEAACACAAALABwAY2xhc3Nlcy5kZXhVVAkAAwFj"
+ "5VetbOVXdXgLAAEE5AMBAASIEwAAS0mt4DIwtmDYYdV9csrcks83lpxZN2vD8f/1p1beWX3vabQC"
+ "EwNDAQMDQ0WYiRADFPQwMjBwMEDEWYB4AhADlTEsYEAAZiDeAcRApQwXgNgAyPgApJWAtBYQGwGx"
+ "GxAHAnEIEEcAcS4jRD0T1Fw2KM0ENZMVypZhRLIIqIMdag9CBMFnhtJ1jDA5RrBcMSPE7AIBkIl8"
+ "UFGgP6Fu4IOawczAZpOZl1lix8Dm45uYmWfNIOSTlViWqJ+TmJeu75+UlZpcYs3ACZLSA4kzMIYx"
+ "MIX5MAhHIykLLinKzEu3ZmDJBSoDOZiPgRlMgv3T2MDygZGRs4OJB8n9MBoWzrAwmQD1Eyy8WZHC"
+ "mg0pvBkVIGpAYc4oABEHhRuTAsRMUDwwQ9WAwoJBAaIGHE5Q9aB4BgBQSwECHgMUAAAACAD9VjdJ"
+ "rPfZSDEBAAAgAgAADAAYAAAAAAAAAAAAoIEAAAAAY2xhc3NlczIuZGV4VVQFAAOtbOVXdXgLAAEE"
+ "5AMBAASIEwAAUEsBAh4DFAAAAAgAAFc3Saz32UgxAQAAIAIAAAwAGAAAAAAAAAAAAKCBdwEAAGNs"
+ "YXNzZXMzLmRleFVUBQADr2zlV3V4CwABBOQDAQAEiBMAAFBLAQIeAxQAAAAIANVRN0ms99lIMQEA"
+ "ACACAAALABgAAAAAAAAAAACgge4CAABjbGFzc2VzLmRleFVUBQADAWPlV3V4CwABBOQDAQAEiBMA"
+ "AFBLBQYAAAAAAwADAPUAAABkBAAAAAA=";
+
+static const char kRawDexBadMapOffset[] =
+ "ZGV4CjAzNQAZKGSz85r+tXJ1I24FYi+FpQtWbXtelAmoAQAAcAAAAHhWNBIAAAAAAAAAAEAwIBAF"
+ "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADcAAAAzAAAAOQA"
+ "AADsAAAA9AAAAPkAAAANAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAA"
+ "AAAAAAABAAAAAQAAAAAAAAABAAAAAAAAABUBAAAAAAAAAQABAAEAAAAQAQAABAAAAHAQAQAAAA4A"
+ "Bjxpbml0PgAGQS5qYXZhAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgABAAcOAAAAAQAAgYAE"
+ "zAEACwAAAAAAAAABAAAAAAAAAAEAAAAFAAAAcAAAAAIAAAADAAAAhAAAAAMAAAABAAAAkAAAAAUA"
+ "AAACAAAAnAAAAAYAAAABAAAArAAAAAEgAAABAAAAzAAAAAIgAAAFAAAA5AAAAAMgAAABAAAAEAEA"
+ "AAAgAAABAAAAFQEAAAAQAAABAAAAIAEAAA==";
+
+static const char kRawDexDebugInfoLocalNullType[] =
+ "ZGV4CjAzNQA+Kwj2g6OZMH88OvK9Ey6ycdIsFCt18ED8AQAAcAAAAHhWNBIAAAAAAAAAAHQBAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAMAQAA8AAAABwB"
+ "AAAkAQAALAEAAC8BAAA0AQAASAEAAEsBAABOAQAAAgAAAAMAAAAEAAAABQAAAAIAAAAAAAAAAAAA"
+ "AAUAAAADAAAAAAAAAAEAAQAAAAAAAQAAAAYAAAACAAEAAAAAAAEAAAABAAAAAgAAAAAAAAABAAAA"
+ "AAAAAGMBAAAAAAAAAQABAAEAAABUAQAABAAAAHAQAgAAAA4AAgABAAAAAABZAQAAAgAAABIQDwAG"
+ "PGluaXQ+AAZBLmphdmEAAUkAA0xBOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAFhAAR0aGlzAAEA"
+ "Bw4AAwAHDh4DAAcAAAAAAQEAgYAE8AEBAIgCAAAACwAAAAAAAAABAAAAAAAAAAEAAAAIAAAAcAAA"
+ "AAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAAuAAAAAYAAAABAAAA0AAAAAEgAAACAAAA"
+ "8AAAAAIgAAAIAAAAHAEAAAMgAAACAAAAVAEAAAAgAAABAAAAYwEAAAAQAAABAAAAdAEAAA==";
+
+static void DecodeDexFile(const char* base64, std::vector<uint8_t>* dex_bytes) {
+ // decode base64
+ CHECK(base64 != nullptr);
+ *dex_bytes = DecodeBase64Vec(base64);
+ CHECK_NE(dex_bytes->size(), 0u);
+}
+
+static bool OpenDexFilesBase64(const char* base64,
+ const char* location,
+ std::vector<uint8_t>* dex_bytes,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::string* error_msg) {
+ DecodeDexFile(base64, dex_bytes);
+
+ // read dex file(s)
+ static constexpr bool kVerifyChecksum = true;
+ std::vector<std::unique_ptr<const DexFile>> tmp;
+ const DexFileLoader dex_file_loader;
+ bool success = dex_file_loader.OpenAll(dex_bytes->data(),
+ dex_bytes->size(),
+ location,
+ /* verify */ true,
+ kVerifyChecksum,
+ error_msg,
+ dex_files);
+ return success;
+}
+
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+ const char* location,
+ std::vector<uint8_t>* dex_bytes) {
+ // read dex files.
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ bool success = OpenDexFilesBase64(base64, location, dex_bytes, &dex_files, &error_msg);
+ CHECK(success) << error_msg;
+ EXPECT_EQ(1U, dex_files.size());
+ return std::move(dex_files[0]);
+}
+
+static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base64,
+ const char* location,
+ uint32_t location_checksum,
+ bool expect_success,
+ std::vector<uint8_t>* dex_bytes) {
+ DecodeDexFile(base64, dex_bytes);
+
+ std::string error_message;
+ const DexFileLoader dex_file_loader;
+ std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(dex_bytes->data(),
+ dex_bytes->size(),
+ location,
+ location_checksum,
+ /* oat_dex_file */ nullptr,
+ /* verify */ true,
+ /* verify_checksum */ true,
+ &error_message));
+ if (expect_success) {
+ CHECK(dex_file != nullptr) << error_message;
+ } else {
+ CHECK(dex_file == nullptr) << "Expected dex file open to fail.";
+ }
+ return dex_file;
+}
+
+static void ValidateDexFileHeader(std::unique_ptr<const DexFile> dex_file) {
+ static const uint8_t kExpectedDexFileMagic[8] = {
+ /* d */ 0x64, /* e */ 0x64, /* x */ 0x78, /* \n */ 0x0d,
+ /* 0 */ 0x30, /* 3 */ 0x33, /* 5 */ 0x35, /* \0 */ 0x00
+ };
+ static const uint8_t kExpectedSha1[DexFile::kSha1DigestSize] = {
+ 0x7b, 0xb8, 0x0c, 0xd4, 0x1f, 0xd6, 0x1e, 0xc5,
+ 0x89, 0xe8, 0xbe, 0xe5, 0x18, 0x02, 0x12, 0x18,
+ 0x2e, 0xf2, 0x8c, 0x3d,
+ };
+
+ const DexFile::Header& header = dex_file->GetHeader();
+ EXPECT_EQ(*kExpectedDexFileMagic, *header.magic_);
+ EXPECT_EQ(0x00d87910U, header.checksum_);
+ EXPECT_EQ(*kExpectedSha1, *header.signature_);
+ EXPECT_EQ(904U, header.file_size_);
+ EXPECT_EQ(112U, header.header_size_);
+ EXPECT_EQ(0U, header.link_size_);
+ EXPECT_EQ(0U, header.link_off_);
+ EXPECT_EQ(15U, header.string_ids_size_);
+ EXPECT_EQ(112U, header.string_ids_off_);
+ EXPECT_EQ(7U, header.type_ids_size_);
+ EXPECT_EQ(172U, header.type_ids_off_);
+ EXPECT_EQ(2U, header.proto_ids_size_);
+ EXPECT_EQ(200U, header.proto_ids_off_);
+ EXPECT_EQ(1U, header.field_ids_size_);
+ EXPECT_EQ(224U, header.field_ids_off_);
+ EXPECT_EQ(3U, header.method_ids_size_);
+ EXPECT_EQ(232U, header.method_ids_off_);
+ EXPECT_EQ(2U, header.class_defs_size_);
+ EXPECT_EQ(256U, header.class_defs_off_);
+ EXPECT_EQ(584U, header.data_size_);
+ EXPECT_EQ(320U, header.data_off_);
+
+ EXPECT_EQ(header.checksum_, dex_file->GetLocationChecksum());
+}
+
+TEST_F(DexFileLoaderTest, Header) {
+ std::vector<uint8_t> dex_bytes;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, kLocationString, &dex_bytes));
+ ValidateDexFileHeader(std::move(raw));
+}
+
+TEST_F(DexFileLoaderTest, HeaderInMemory) {
+ std::vector<uint8_t> dex_bytes;
+ std::unique_ptr<const DexFile> raw =
+ OpenDexFileInMemoryBase64(kRawDex, kLocationString, 0x00d87910U, true, &dex_bytes);
+ ValidateDexFileHeader(std::move(raw));
+}
+
+TEST_F(DexFileLoaderTest, Version38Accepted) {
+ std::vector<uint8_t> dex_bytes;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex38, kLocationString, &dex_bytes));
+ ASSERT_TRUE(raw.get() != nullptr);
+
+ const DexFile::Header& header = raw->GetHeader();
+ EXPECT_EQ(38u, header.GetVersion());
+}
+
+TEST_F(DexFileLoaderTest, Version39Accepted) {
+ std::vector<uint8_t> dex_bytes;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex39, kLocationString, &dex_bytes));
+ ASSERT_TRUE(raw.get() != nullptr);
+
+ const DexFile::Header& header = raw->GetHeader();
+ EXPECT_EQ(39u, header.GetVersion());
+}
+
+TEST_F(DexFileLoaderTest, Version40Rejected) {
+ std::vector<uint8_t> dex_bytes;
+ DecodeDexFile(kRawDex40, &dex_bytes);
+
+ static constexpr bool kVerifyChecksum = true;
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ const DexFileLoader dex_file_loader;
+ ASSERT_FALSE(dex_file_loader.OpenAll(dex_bytes.data(),
+ dex_bytes.size(),
+ kLocationString,
+ /* verify */ true,
+ kVerifyChecksum,
+ &error_msg,
+ &dex_files));
+}
+
+TEST_F(DexFileLoaderTest, Version41Rejected) {
+ std::vector<uint8_t> dex_bytes;
+ DecodeDexFile(kRawDex41, &dex_bytes);
+
+ static constexpr bool kVerifyChecksum = true;
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ const DexFileLoader dex_file_loader;
+ ASSERT_FALSE(dex_file_loader.OpenAll(dex_bytes.data(),
+ dex_bytes.size(),
+ kLocationString,
+ /* verify */ true,
+ kVerifyChecksum,
+ &error_msg,
+ &dex_files));
+}
+
+TEST_F(DexFileLoaderTest, ZeroLengthDexRejected) {
+ std::vector<uint8_t> dex_bytes;
+ DecodeDexFile(kRawDexZeroLength, &dex_bytes);
+
+ static constexpr bool kVerifyChecksum = true;
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ const DexFileLoader dex_file_loader;
+ ASSERT_FALSE(dex_file_loader.OpenAll(dex_bytes.data(),
+ dex_bytes.size(),
+ kLocationString,
+ /* verify */ true,
+ kVerifyChecksum,
+ &error_msg,
+ &dex_files));
+}
+
+TEST_F(DexFileLoaderTest, GetMultiDexClassesDexName) {
+ ASSERT_EQ("classes.dex", DexFileLoader::GetMultiDexClassesDexName(0));
+ ASSERT_EQ("classes2.dex", DexFileLoader::GetMultiDexClassesDexName(1));
+ ASSERT_EQ("classes3.dex", DexFileLoader::GetMultiDexClassesDexName(2));
+ ASSERT_EQ("classes100.dex", DexFileLoader::GetMultiDexClassesDexName(99));
+}
+
+TEST_F(DexFileLoaderTest, GetMultiDexLocation) {
+ std::string dex_location_str = "/system/app/framework.jar";
+ const char* dex_location = dex_location_str.c_str();
+ ASSERT_EQ("/system/app/framework.jar", DexFileLoader::GetMultiDexLocation(0, dex_location));
+ ASSERT_EQ("/system/app/framework.jar!classes2.dex",
+ DexFileLoader::GetMultiDexLocation(1, dex_location));
+ ASSERT_EQ("/system/app/framework.jar!classes101.dex",
+ DexFileLoader::GetMultiDexLocation(100, dex_location));
+}
+
+TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) {
+ EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes2.dex"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFileLoader::GetBaseLocation("/foo/bar/baz.jar!classes8.dex"));
+ EXPECT_EQ("", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar"));
+ EXPECT_EQ("!classes2.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes2.dex"));
+ EXPECT_EQ("!classes8.dex", DexFileLoader::GetMultiDexSuffix("/foo/bar/baz.jar!classes8.dex"));
+}
+
+TEST_F(DexFileLoaderTest, ZipOpenClassesPresent) {
+ std::vector<uint8_t> dex_bytes;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::string error_msg;
+ ASSERT_TRUE(OpenDexFilesBase64(kRawZipClassesDexPresent,
+ kLocationString,
+ &dex_bytes,
+ &dex_files,
+ &error_msg));
+ EXPECT_EQ(dex_files.size(), 1u);
+}
+
+TEST_F(DexFileLoaderTest, ZipOpenClassesAbsent) {
+ std::vector<uint8_t> dex_bytes;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::string error_msg;
+ ASSERT_FALSE(OpenDexFilesBase64(kRawZipClassesDexAbsent,
+ kLocationString,
+ &dex_bytes,
+ &dex_files,
+ &error_msg));
+ EXPECT_EQ(dex_files.size(), 0u);
+}
+
+TEST_F(DexFileLoaderTest, ZipOpenThreeDexFiles) {
+ std::vector<uint8_t> dex_bytes;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::string error_msg;
+ ASSERT_TRUE(OpenDexFilesBase64(kRawZipThreeDexFiles,
+ kLocationString,
+ &dex_bytes,
+ &dex_files,
+ &error_msg));
+ EXPECT_EQ(dex_files.size(), 3u);
+}
+
+TEST_F(DexFileLoaderTest, OpenDexBadMapOffset) {
+ std::vector<uint8_t> dex_bytes;
+ std::unique_ptr<const DexFile> raw =
+ OpenDexFileInMemoryBase64(kRawDexBadMapOffset,
+ kLocationString,
+ 0xb3642819U,
+ false,
+ &dex_bytes);
+ EXPECT_EQ(raw, nullptr);
+}
+
+TEST_F(DexFileLoaderTest, GetStringWithNoIndex) {
+ std::vector<uint8_t> dex_bytes;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kRawDex, kLocationString, &dex_bytes));
+ dex::TypeIndex idx;
+ EXPECT_EQ(raw->StringByTypeIdx(idx), nullptr);
+}
+
+static void Callback(void* context ATTRIBUTE_UNUSED,
+ const DexFile::LocalInfo& entry ATTRIBUTE_UNUSED) {
+}
+
+TEST_F(DexFileLoaderTest, OpenDexDebugInfoLocalNullType) {
+ std::vector<uint8_t> dex_bytes;
+ std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64(kRawDexDebugInfoLocalNullType,
+ kLocationString,
+ 0xf25f2b38U,
+ true,
+ &dex_bytes);
+ const DexFile::ClassDef& class_def = raw->GetClassDef(0);
+ constexpr uint32_t kMethodIdx = 1;
+ const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def,
+ kMethodIdx));
+ CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx);
+ ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr));
+}
+
+} // namespace art
diff --git a/libdexfile/dex/dex_file_reference.h b/libdexfile/dex/dex_file_reference.h
new file mode 100644
index 0000000000..3ac778121a
--- /dev/null
+++ b/libdexfile/dex/dex_file_reference.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_
+
+#include <cstdint>
+
+namespace art {
+
+class DexFile;
+
+class DexFileReference {
+ public:
+ DexFileReference(const DexFile* file, uint32_t idx) : dex_file(file), index(idx) {}
+ const DexFile* dex_file;
+ uint32_t index;
+
+ struct Comparator {
+ bool operator()(const DexFileReference& a, const DexFileReference& b) const {
+ if (a.dex_file != b.dex_file) {
+ return a.dex_file < b.dex_file;
+ }
+ return a.index < b.index;
+ }
+ };
+};
+
+// Default comparators, compares the indicies, not the backing data.
+inline bool operator<(const DexFileReference& a, const DexFileReference& b) {
+ return DexFileReference::Comparator()(a, b);
+}
+inline bool operator==(const DexFileReference& a, const DexFileReference& b) {
+ return a.dex_file == b.dex_file && a.index == b.index;
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_REFERENCE_H_
diff --git a/libdexfile/dex/dex_file_tracking_registrar.cc b/libdexfile/dex/dex_file_tracking_registrar.cc
new file mode 100644
index 0000000000..78ea9c16cb
--- /dev/null
+++ b/libdexfile/dex/dex_file_tracking_registrar.cc
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file_tracking_registrar.h"
+
+#include <deque>
+#include <tuple>
+
+#include <android-base/logging.h>
+
+// For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for
+// the ifdefs and early include.
+#ifdef ART_DEX_FILE_ACCESS_TRACKING
+#ifndef ART_ENABLE_ADDRESS_SANITIZER
+#define ART_ENABLE_ADDRESS_SANITIZER
+#endif
+#endif
+#include "base/memory_tool.h"
+
+#include "code_item_accessors-inl.h"
+#include "dex_file-inl.h"
+
+namespace art {
+namespace dex {
+namespace tracking {
+
+// If true, poison dex files to track accesses.
+static constexpr bool kDexFileAccessTracking =
+#ifdef ART_DEX_FILE_ACCESS_TRACKING
+ true;
+#else
+ false;
+#endif
+
+// The following are configurations of poisoning certain sections of a Dex File.
+// More will be added
+enum DexTrackingType {
+ // Poisons all of a Dex File when set.
+ kWholeDexTracking,
+ // Poisons all Code Items of a Dex File when set.
+ kCodeItemTracking,
+ // Poisons all subsections of a Code Item, except the Insns bytecode array
+ // section, when set for all Code Items in a Dex File.
+ kCodeItemNonInsnsTracking,
+ // Poisons all subsections of a Code Item, except the Insns bytecode array
+ // section, when set for all Code Items in a Dex File.
+ // Additionally unpoisons the entire Code Item when method is a class
+ // initializer.
+ kCodeItemNonInsnsNoClinitTracking,
+ // Poisons the size and offset information along with the first instruction.
+ // This is so that accessing multiple instructions while accessing a code item
+ // once will not trigger unnecessary accesses.
+ kCodeItemStartTracking,
+ // Poisons all String Data Items of a Dex Files when set.
+ kStringDataItemTracking,
+ // Poisons the first byte of the utf16_size value and the first byte of the
+ // data section for all String Data Items of a Dex File.
+ kStringDataItemStartTracking,
+ // Poisons based on a custom tracking system which can be specified in
+ // SetDexSections
+ kCustomTracking,
+};
+
+// Intended for local changes only.
+// Represents the current configuration being run.
+static constexpr DexTrackingType kCurrentTrackingSystem = kWholeDexTracking;
+
+// Intended for local changes only.
+void DexFileTrackingRegistrar::SetDexSections() {
+ if (kDexFileAccessTracking && dex_file_ != nullptr) {
+ // Logs the Dex File's location and starting address if tracking is enabled
+ LOG(ERROR) << "RegisterDexFile: " << dex_file_->GetLocation() + " @ " << std::hex
+ << reinterpret_cast<uintptr_t>(dex_file_->Begin());
+ switch (kCurrentTrackingSystem) {
+ case kWholeDexTracking:
+ SetDexFileRegistration(true);
+ break;
+ case kCodeItemTracking:
+ SetAllCodeItemRegistration(true);
+ break;
+ case kCodeItemNonInsnsTracking:
+ SetAllCodeItemRegistration(true);
+ SetAllInsnsRegistration(false);
+ break;
+ case kCodeItemNonInsnsNoClinitTracking:
+ SetAllCodeItemRegistration(true);
+ SetAllInsnsRegistration(false);
+ SetCodeItemRegistration("<clinit>", false);
+ break;
+ case kCodeItemStartTracking:
+ SetAllCodeItemStartRegistration(true);
+ break;
+ case kStringDataItemTracking:
+ SetAllStringDataRegistration(true);
+ break;
+ case kStringDataItemStartTracking:
+ SetAllStringDataStartRegistration(true);
+ break;
+ case kCustomTracking:
+ // TODO: Add/remove additional calls here to (un)poison sections of
+ // dex_file_
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void RegisterDexFile(const DexFile* dex_file) {
+ DexFileTrackingRegistrar dex_tracking_registrar(dex_file);
+ dex_tracking_registrar.SetDexSections();
+ dex_tracking_registrar.SetCurrentRanges();
+}
+
+inline void SetRegistrationRange(const void* begin, size_t size, bool should_poison) {
+ if (should_poison) {
+ MEMORY_TOOL_MAKE_NOACCESS(begin, size);
+ } else {
+ // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address
+ // Sanitizer. The difference has not been tested with Valgrind
+ MEMORY_TOOL_MAKE_DEFINED(begin, size);
+ }
+}
+
+void DexFileTrackingRegistrar::SetCurrentRanges() {
+ // This also empties range_values_ to avoid redundant (un)poisoning upon
+ // subsequent calls.
+ while (!range_values_.empty()) {
+ const std::tuple<const void*, size_t, bool>& current_range = range_values_.front();
+ SetRegistrationRange(std::get<0>(current_range),
+ std::get<1>(current_range),
+ std::get<2>(current_range));
+ range_values_.pop_front();
+ }
+}
+
+void DexFileTrackingRegistrar::SetDexFileRegistration(bool should_poison) {
+ const void* dex_file_begin = reinterpret_cast<const void*>(dex_file_->Begin());
+ size_t dex_file_size = dex_file_->Size();
+ range_values_.push_back(std::make_tuple(dex_file_begin, dex_file_size, should_poison));
+}
+
+void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) {
+ for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
+ const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
+ const uint8_t* class_data = dex_file_->GetClassData(cd);
+ if (class_data != nullptr) {
+ ClassDataItemIterator cdit(*dex_file_, class_data);
+ cdit.SkipAllFields();
+ while (cdit.HasNextMethod()) {
+ const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ const void* code_item_begin = reinterpret_cast<const void*>(code_item);
+ size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
+ range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
+ }
+ cdit.Next();
+ }
+ }
+ }
+}
+
+void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poison) {
+ for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
+ const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
+ const uint8_t* class_data = dex_file_->GetClassData(cd);
+ if (class_data != nullptr) {
+ ClassDataItemIterator cdit(*dex_file_, class_data);
+ cdit.SkipAllFields();
+ while (cdit.HasNextMethod()) {
+ const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ const void* code_item_begin = reinterpret_cast<const void*>(code_item);
+ size_t code_item_start = reinterpret_cast<size_t>(code_item);
+ CodeItemInstructionAccessor accessor(*dex_file_, code_item);
+ size_t code_item_start_end = reinterpret_cast<size_t>(accessor.Insns());
+ size_t code_item_start_size = code_item_start_end - code_item_start;
+ range_values_.push_back(std::make_tuple(code_item_begin,
+ code_item_start_size,
+ should_poison));
+ }
+ cdit.Next();
+ }
+ }
+ }
+}
+
+void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) {
+ for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
+ const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
+ const uint8_t* class_data = dex_file_->GetClassData(cd);
+ if (class_data != nullptr) {
+ ClassDataItemIterator cdit(*dex_file_, class_data);
+ cdit.SkipAllFields();
+ while (cdit.HasNextMethod()) {
+ const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ CodeItemInstructionAccessor accessor(*dex_file_, code_item);
+ const void* insns_begin = reinterpret_cast<const void*>(accessor.Insns());
+ // Member insns_size_in_code_units_ is in 2-byte units
+ size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2;
+ range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison));
+ }
+ cdit.Next();
+ }
+ }
+ }
+}
+
+void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, bool should_poison) {
+ for (size_t classdef_ctr = 0; classdef_ctr < dex_file_->NumClassDefs(); ++classdef_ctr) {
+ const DexFile::ClassDef& cd = dex_file_->GetClassDef(classdef_ctr);
+ const uint8_t* class_data = dex_file_->GetClassData(cd);
+ if (class_data != nullptr) {
+ ClassDataItemIterator cdit(*dex_file_, class_data);
+ cdit.SkipAllFields();
+ while (cdit.HasNextMethod()) {
+ const DexFile::MethodId& methodid_item = dex_file_->GetMethodId(cdit.GetMemberIndex());
+ const char * methodid_name = dex_file_->GetMethodName(methodid_item);
+ const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
+ if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) {
+ const void* code_item_begin = reinterpret_cast<const void*>(code_item);
+ size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
+ range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
+ }
+ cdit.Next();
+ }
+ }
+ }
+}
+
+void DexFileTrackingRegistrar::SetAllStringDataStartRegistration(bool should_poison) {
+ for (size_t stringid_ctr = 0; stringid_ctr < dex_file_->NumStringIds(); ++stringid_ctr) {
+ const DexFile::StringId & string_id = dex_file_->GetStringId(StringIndex(stringid_ctr));
+ const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + string_id.string_data_off_);
+ // Data Section of String Data Item
+ const void* string_data_data_begin = reinterpret_cast<const void*>(dex_file_->GetStringData(string_id));
+ range_values_.push_back(std::make_tuple(string_data_begin, 1, should_poison));
+ range_values_.push_back(std::make_tuple(string_data_data_begin, 1, should_poison));
+ }
+}
+
+void DexFileTrackingRegistrar::SetAllStringDataRegistration(bool should_poison) {
+ size_t map_offset = dex_file_->GetHeader().map_off_;
+ auto map_list = reinterpret_cast<const DexFile::MapList*>(dex_file_->Begin() + map_offset);
+ for (size_t map_ctr = 0; map_ctr < map_list->size_; ++map_ctr) {
+ const DexFile::MapItem& map_item = map_list->list_[map_ctr];
+ if (map_item.type_ == DexFile::kDexTypeStringDataItem) {
+ const DexFile::MapItem& next_map_item = map_list->list_[map_ctr + 1];
+ const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + map_item.offset_);
+ size_t string_data_size = next_map_item.offset_ - map_item.offset_;
+ range_values_.push_back(std::make_tuple(string_data_begin, string_data_size, should_poison));
+ }
+ }
+}
+
+} // namespace tracking
+} // namespace dex
+} // namespace art
diff --git a/libdexfile/dex/dex_file_tracking_registrar.h b/libdexfile/dex/dex_file_tracking_registrar.h
new file mode 100644
index 0000000000..8b7716e729
--- /dev/null
+++ b/libdexfile/dex/dex_file_tracking_registrar.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_TRACKING_REGISTRAR_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_TRACKING_REGISTRAR_H_
+
+#include <deque>
+#include <tuple>
+
+#include "dex_file.h"
+
+namespace art {
+namespace dex {
+namespace tracking {
+
+// Class for (un)poisoning various sections of Dex Files
+//
+// This class provides the means to log accesses only of sections whose
+// accesses are needed. All accesses are displayed as stack traces in
+// logcat.
+class DexFileTrackingRegistrar {
+ public:
+ explicit DexFileTrackingRegistrar(const DexFile* const dex_file)
+ : dex_file_(dex_file) {
+ }
+
+ // This function is where the functions below it are called to actually
+ // poison sections.
+ void SetDexSections();
+
+ // Uses data contained inside range_values_ to poison memory through the
+ // memory tool.
+ void SetCurrentRanges();
+
+ private:
+ void SetDexFileRegistration(bool should_poison);
+
+ // Set of functions concerning Code Items of dex_file_
+ void SetAllCodeItemRegistration(bool should_poison);
+ // Sets the insns_ section of all code items.
+ void SetAllInsnsRegistration(bool should_poison);
+ // This function finds the code item of a class based on class name.
+ void SetCodeItemRegistration(const char* class_name, bool should_poison);
+ // Sets the size and offset information along with first instruction in insns_
+ // section of all code items.
+ void SetAllCodeItemStartRegistration(bool should_poison);
+
+ // Set of functions concerning String Data Items of dex_file_
+ void SetAllStringDataRegistration(bool should_poison);
+ // Sets the first byte of size value and data section of all string data
+ // items.
+ void SetAllStringDataStartRegistration(bool should_poison);
+
+ // Contains tuples of all ranges of memory that need to be explicitly
+ // (un)poisoned by the memory tool.
+ std::deque<std::tuple<const void *, size_t, bool>> range_values_;
+
+ const DexFile* const dex_file_;
+};
+
+// This function is meant to called externally to use DexfileTrackingRegistrar
+void RegisterDexFile(const DexFile* dex_file);
+
+} // namespace tracking
+} // namespace dex
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_TRACKING_REGISTRAR_H_
diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h
new file mode 100644
index 0000000000..2bb70ff261
--- /dev/null
+++ b/libdexfile/dex/dex_file_types.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
+
+#include <limits>
+#include <ostream>
+
+namespace art {
+namespace dex {
+
+constexpr uint32_t kDexNoIndex = 0xFFFFFFFF;
+
+class StringIndex {
+ public:
+ uint32_t index_;
+
+ constexpr StringIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
+ explicit constexpr StringIndex(uint32_t idx) : index_(idx) {}
+
+ bool IsValid() const {
+ return index_ != std::numeric_limits<decltype(index_)>::max();
+ }
+ static StringIndex Invalid() {
+ return StringIndex(std::numeric_limits<decltype(index_)>::max());
+ }
+
+ bool operator==(const StringIndex& other) const {
+ return index_ == other.index_;
+ }
+ bool operator!=(const StringIndex& other) const {
+ return index_ != other.index_;
+ }
+ bool operator<(const StringIndex& other) const {
+ return index_ < other.index_;
+ }
+ bool operator<=(const StringIndex& other) const {
+ return index_ <= other.index_;
+ }
+ bool operator>(const StringIndex& other) const {
+ return index_ > other.index_;
+ }
+ bool operator>=(const StringIndex& other) const {
+ return index_ >= other.index_;
+ }
+};
+std::ostream& operator<<(std::ostream& os, const StringIndex& index);
+
+class TypeIndex {
+ public:
+ uint16_t index_;
+
+ constexpr TypeIndex() : index_(std::numeric_limits<decltype(index_)>::max()) {}
+ explicit constexpr TypeIndex(uint16_t idx) : index_(idx) {}
+
+ bool IsValid() const {
+ return index_ != std::numeric_limits<decltype(index_)>::max();
+ }
+ static TypeIndex Invalid() {
+ return TypeIndex(std::numeric_limits<decltype(index_)>::max());
+ }
+
+ bool operator==(const TypeIndex& other) const {
+ return index_ == other.index_;
+ }
+ bool operator!=(const TypeIndex& other) const {
+ return index_ != other.index_;
+ }
+ bool operator<(const TypeIndex& other) const {
+ return index_ < other.index_;
+ }
+ bool operator<=(const TypeIndex& other) const {
+ return index_ <= other.index_;
+ }
+ bool operator>(const TypeIndex& other) const {
+ return index_ > other.index_;
+ }
+ bool operator>=(const TypeIndex& other) const {
+ return index_ >= other.index_;
+ }
+};
+std::ostream& operator<<(std::ostream& os, const TypeIndex& index);
+
+} // namespace dex
+} // namespace art
+
+namespace std {
+
+template<> struct hash<art::dex::StringIndex> {
+ size_t operator()(const art::dex::StringIndex& index) const {
+ return hash<uint32_t>()(index.index_);
+ }
+};
+
+template<> struct hash<art::dex::TypeIndex> {
+ size_t operator()(const art::dex::TypeIndex& index) const {
+ return hash<uint16_t>()(index.index_);
+ }
+};
+
+} // namespace std
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
new file mode 100644
index 0000000000..62667052ad
--- /dev/null
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -0,0 +1,3290 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file_verifier.h"
+
+#include <inttypes.h>
+
+#include <limits>
+#include <memory>
+
+#include "android-base/stringprintf.h"
+
+#include "code_item_accessors-inl.h"
+#include "descriptors_names.h"
+#include "dex_file-inl.h"
+#include "leb128.h"
+#include "modifiers.h"
+#include "utf-inl.h"
+
+namespace art {
+
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
+static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max();
+
+static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) {
+ return (high == 0) || ((high == 0xffffU) && (low == 0xffffU));
+}
+
+static bool IsValidTypeId(uint16_t low ATTRIBUTE_UNUSED, uint16_t high) {
+ return (high == 0);
+}
+
+static uint32_t MapTypeToBitMask(DexFile::MapItemType map_item_type) {
+ switch (map_item_type) {
+ case DexFile::kDexTypeHeaderItem: return 1 << 0;
+ case DexFile::kDexTypeStringIdItem: return 1 << 1;
+ case DexFile::kDexTypeTypeIdItem: return 1 << 2;
+ case DexFile::kDexTypeProtoIdItem: return 1 << 3;
+ case DexFile::kDexTypeFieldIdItem: return 1 << 4;
+ case DexFile::kDexTypeMethodIdItem: return 1 << 5;
+ case DexFile::kDexTypeClassDefItem: return 1 << 6;
+ case DexFile::kDexTypeCallSiteIdItem: return 1 << 7;
+ case DexFile::kDexTypeMethodHandleItem: return 1 << 8;
+ case DexFile::kDexTypeMapList: return 1 << 9;
+ case DexFile::kDexTypeTypeList: return 1 << 10;
+ case DexFile::kDexTypeAnnotationSetRefList: return 1 << 11;
+ case DexFile::kDexTypeAnnotationSetItem: return 1 << 12;
+ case DexFile::kDexTypeClassDataItem: return 1 << 13;
+ case DexFile::kDexTypeCodeItem: return 1 << 14;
+ case DexFile::kDexTypeStringDataItem: return 1 << 15;
+ case DexFile::kDexTypeDebugInfoItem: return 1 << 16;
+ case DexFile::kDexTypeAnnotationItem: return 1 << 17;
+ case DexFile::kDexTypeEncodedArrayItem: return 1 << 18;
+ case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 19;
+ }
+ return 0;
+}
+
+static bool IsDataSectionType(DexFile::MapItemType map_item_type) {
+ switch (map_item_type) {
+ case DexFile::kDexTypeHeaderItem:
+ case DexFile::kDexTypeStringIdItem:
+ case DexFile::kDexTypeTypeIdItem:
+ case DexFile::kDexTypeProtoIdItem:
+ case DexFile::kDexTypeFieldIdItem:
+ case DexFile::kDexTypeMethodIdItem:
+ case DexFile::kDexTypeClassDefItem:
+ return false;
+ case DexFile::kDexTypeCallSiteIdItem:
+ case DexFile::kDexTypeMethodHandleItem:
+ case DexFile::kDexTypeMapList:
+ case DexFile::kDexTypeTypeList:
+ case DexFile::kDexTypeAnnotationSetRefList:
+ case DexFile::kDexTypeAnnotationSetItem:
+ case DexFile::kDexTypeClassDataItem:
+ case DexFile::kDexTypeCodeItem:
+ case DexFile::kDexTypeStringDataItem:
+ case DexFile::kDexTypeDebugInfoItem:
+ case DexFile::kDexTypeAnnotationItem:
+ case DexFile::kDexTypeEncodedArrayItem:
+ case DexFile::kDexTypeAnnotationsDirectoryItem:
+ return true;
+ }
+ return true;
+}
+
+const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const char* error_string) {
+ if (UNLIKELY(!CheckIndex(idx.index_, dex_file_->NumStringIds(), error_string))) {
+ return nullptr;
+ }
+ return dex_file_->StringDataByIdx(idx);
+}
+
+// Try to find the name of the method with the given index. We do not want to rely on DexFile
+// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and
+// header_ of the DexFileVerifier. str will contain the pointer to the method name on success
+// (flagged by the return value), otherwise error_msg will contain an error string.
+static bool FindMethodName(uint32_t method_index,
+ const uint8_t* begin,
+ const DexFile::Header* header,
+ const char** str,
+ std::string* error_msg) {
+ if (method_index >= header->method_ids_size_) {
+ *error_msg = "Method index not available for method flags verification";
+ return false;
+ }
+ uint32_t string_idx =
+ (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) +
+ method_index)->name_idx_.index_;
+ if (string_idx >= header->string_ids_size_) {
+ *error_msg = "String index not available for method flags verification";
+ return false;
+ }
+ uint32_t string_off =
+ (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)->
+ string_data_off_;
+ if (string_off >= header->file_size_) {
+ *error_msg = "String offset out of bounds for method flags verification";
+ return false;
+ }
+ const uint8_t* str_data_ptr = begin + string_off;
+ uint32_t dummy;
+ if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) {
+ *error_msg = "String size out of bounds for method flags verification";
+ return false;
+ }
+ *str = reinterpret_cast<const char*>(str_data_ptr);
+ return true;
+}
+
+// Gets constructor flags based on the |method_name|. Returns true if
+// method_name is either <clinit> or <init> and sets
+// |constructor_flags_by_name| appropriately. Otherwise set
+// |constructor_flags_by_name| to zero and returns whether
+// |method_name| is valid.
+bool GetConstructorFlagsForMethodName(const char* method_name,
+ uint32_t* constructor_flags_by_name) {
+ if (method_name[0] != '<') {
+ *constructor_flags_by_name = 0;
+ return true;
+ }
+ if (strcmp(method_name + 1, "clinit>") == 0) {
+ *constructor_flags_by_name = kAccStatic | kAccConstructor;
+ return true;
+ }
+ if (strcmp(method_name + 1, "init>") == 0) {
+ *constructor_flags_by_name = kAccConstructor;
+ return true;
+ }
+ *constructor_flags_by_name = 0;
+ return false;
+}
+
+const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx,
+ const char* error_string) {
+ if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) {
+ return nullptr;
+ }
+ return CheckLoadStringByIdx(dex_file_->GetTypeId(type_idx).descriptor_idx_, error_string);
+}
+
+const DexFile::FieldId* DexFileVerifier::CheckLoadFieldId(uint32_t idx, const char* error_string) {
+ if (UNLIKELY(!CheckIndex(idx, dex_file_->NumFieldIds(), error_string))) {
+ return nullptr;
+ }
+ return &dex_file_->GetFieldId(idx);
+}
+
+const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const char* err_string) {
+ if (UNLIKELY(!CheckIndex(idx, dex_file_->NumMethodIds(), err_string))) {
+ return nullptr;
+ }
+ return &dex_file_->GetMethodId(idx);
+}
+
+const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) {
+ if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) {
+ return nullptr;
+ }
+ return &dex_file_->GetProtoId(idx);
+}
+
+// Helper macro to load string and return false on error.
+#define LOAD_STRING(var, idx, error) \
+ const char* (var) = CheckLoadStringByIdx(idx, error); \
+ if (UNLIKELY((var) == nullptr)) { \
+ return false; \
+ }
+
+// Helper macro to load string by type idx and return false on error.
+#define LOAD_STRING_BY_TYPE(var, type_idx, error) \
+ const char* (var) = CheckLoadStringByTypeIdx(type_idx, error); \
+ if (UNLIKELY((var) == nullptr)) { \
+ return false; \
+ }
+
+// Helper macro to load method id. Return last parameter on error.
+#define LOAD_METHOD(var, idx, error_string, error_stmt) \
+ const DexFile::MethodId* (var) = CheckLoadMethodId(idx, error_string); \
+ if (UNLIKELY((var) == nullptr)) { \
+ error_stmt; \
+ }
+
+// Helper macro to load method id. Return last parameter on error.
+#define LOAD_FIELD(var, idx, fmt, error_stmt) \
+ const DexFile::FieldId* (var) = CheckLoadFieldId(idx, fmt); \
+ if (UNLIKELY((var) == nullptr)) { \
+ error_stmt; \
+ }
+
+bool DexFileVerifier::Verify(const DexFile* dex_file,
+ const uint8_t* begin,
+ size_t size,
+ const char* location,
+ bool verify_checksum,
+ std::string* error_msg) {
+ std::unique_ptr<DexFileVerifier> verifier(
+ new DexFileVerifier(dex_file, begin, size, location, verify_checksum));
+ if (!verifier->Verify()) {
+ *error_msg = verifier->FailureReason();
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckShortyDescriptorMatch(char shorty_char, const char* descriptor,
+ bool is_return_type) {
+ switch (shorty_char) {
+ case 'V':
+ if (UNLIKELY(!is_return_type)) {
+ ErrorStringPrintf("Invalid use of void");
+ return false;
+ }
+ FALLTHROUGH_INTENDED;
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ if (UNLIKELY((descriptor[0] != shorty_char) || (descriptor[1] != '\0'))) {
+ ErrorStringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'",
+ shorty_char, descriptor);
+ return false;
+ }
+ break;
+ case 'L':
+ if (UNLIKELY((descriptor[0] != 'L') && (descriptor[0] != '['))) {
+ ErrorStringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor);
+ return false;
+ }
+ break;
+ default:
+ ErrorStringPrintf("Bad shorty character: '%c'", shorty_char);
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckListSize(const void* start, size_t count, size_t elem_size,
+ const char* label) {
+ // Check that size is not 0.
+ CHECK_NE(elem_size, 0U);
+
+ const uint8_t* range_start = reinterpret_cast<const uint8_t*>(start);
+ const uint8_t* file_start = reinterpret_cast<const uint8_t*>(begin_);
+
+ // Check for overflow.
+ uintptr_t max = 0 - 1;
+ size_t available_bytes_till_end_of_mem = max - reinterpret_cast<uintptr_t>(start);
+ size_t max_count = available_bytes_till_end_of_mem / elem_size;
+ if (max_count < count) {
+ ErrorStringPrintf("Overflow in range for %s: %zx for %zu@%zu", label,
+ static_cast<size_t>(range_start - file_start),
+ count, elem_size);
+ return false;
+ }
+
+ const uint8_t* range_end = range_start + count * elem_size;
+ const uint8_t* file_end = file_start + size_;
+ if (UNLIKELY((range_start < file_start) || (range_end > file_end))) {
+ // Note: these two tests are enough as we make sure above that there's no overflow.
+ ErrorStringPrintf("Bad range for %s: %zx to %zx", label,
+ static_cast<size_t>(range_start - file_start),
+ static_cast<size_t>(range_end - file_start));
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckList(size_t element_size, const char* label, const uint8_t* *ptr) {
+ // Check that the list is available. The first 4B are the count.
+ if (!CheckListSize(*ptr, 1, 4U, label)) {
+ return false;
+ }
+
+ uint32_t count = *reinterpret_cast<const uint32_t*>(*ptr);
+ if (count > 0) {
+ if (!CheckListSize(*ptr + 4, count, element_size, label)) {
+ return false;
+ }
+ }
+
+ *ptr += 4 + count * element_size;
+ return true;
+}
+
+bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) {
+ if (UNLIKELY(field >= limit)) {
+ ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit);
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset,
+ uint32_t size,
+ size_t alignment,
+ const char* label) {
+ if (size == 0) {
+ if (offset != 0) {
+ ErrorStringPrintf("Offset(%d) should be zero when size is zero for %s.", offset, label);
+ return false;
+ }
+ }
+ if (size_ <= offset) {
+ ErrorStringPrintf("Offset(%d) should be within file size(%zu) for %s.", offset, size_, label);
+ return false;
+ }
+ if (alignment != 0 && !IsAlignedParam(offset, alignment)) {
+ ErrorStringPrintf("Offset(%d) should be aligned by %zu for %s.", offset, alignment, label);
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckSizeLimit(uint32_t size, uint32_t limit, const char* label) {
+ if (size > limit) {
+ ErrorStringPrintf("Size(%u) should not exceed limit(%u) for %s.", size, limit, label);
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckHeader() {
+ // Check file size from the header.
+ uint32_t expected_size = header_->file_size_;
+ if (size_ != expected_size) {
+ ErrorStringPrintf("Bad file size (%zd, expected %u)", size_, expected_size);
+ return false;
+ }
+
+ uint32_t adler_checksum = dex_file_->CalculateChecksum();
+ // Compute and verify the checksum in the header.
+ if (adler_checksum != header_->checksum_) {
+ if (verify_checksum_) {
+ ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
+ return false;
+ } else {
+ LOG(WARNING) << StringPrintf(
+ "Ignoring bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
+ }
+ }
+
+ // Check the contents of the header.
+ if (header_->endian_tag_ != DexFile::kDexEndianConstant) {
+ ErrorStringPrintf("Unexpected endian_tag: %x", header_->endian_tag_);
+ return false;
+ }
+
+ const uint32_t expected_header_size = dex_file_->IsCompactDexFile()
+ ? sizeof(CompactDexFile::Header)
+ : sizeof(StandardDexFile::Header);
+
+ if (header_->header_size_ != expected_header_size) {
+ ErrorStringPrintf("Bad header size: %ud expected %ud",
+ header_->header_size_,
+ expected_header_size);
+ return false;
+ }
+
+ // Check that all offsets are inside the file.
+ bool result =
+ CheckValidOffsetAndSize(header_->link_off_,
+ header_->link_size_,
+ 0 /* unaligned */,
+ "link") &&
+ CheckValidOffsetAndSize(header_->map_off_,
+ header_->map_off_,
+ 4,
+ "map") &&
+ CheckValidOffsetAndSize(header_->string_ids_off_,
+ header_->string_ids_size_,
+ 4,
+ "string-ids") &&
+ CheckValidOffsetAndSize(header_->type_ids_off_,
+ header_->type_ids_size_,
+ 4,
+ "type-ids") &&
+ CheckSizeLimit(header_->type_ids_size_, DexFile::kDexNoIndex16, "type-ids") &&
+ CheckValidOffsetAndSize(header_->proto_ids_off_,
+ header_->proto_ids_size_,
+ 4,
+ "proto-ids") &&
+ CheckSizeLimit(header_->proto_ids_size_, DexFile::kDexNoIndex16, "proto-ids") &&
+ CheckValidOffsetAndSize(header_->field_ids_off_,
+ header_->field_ids_size_,
+ 4,
+ "field-ids") &&
+ CheckValidOffsetAndSize(header_->method_ids_off_,
+ header_->method_ids_size_,
+ 4,
+ "method-ids") &&
+ CheckValidOffsetAndSize(header_->class_defs_off_,
+ header_->class_defs_size_,
+ 4,
+ "class-defs") &&
+ CheckValidOffsetAndSize(header_->data_off_,
+ header_->data_size_,
+ 0, // Unaligned, spec doesn't talk about it, even though size
+ // is supposed to be a multiple of 4.
+ "data");
+ return result;
+}
+
+bool DexFileVerifier::CheckMap() {
+ const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ +
+ header_->map_off_);
+ // Check that map list content is available.
+ if (!CheckListSize(map, 1, sizeof(DexFile::MapList), "maplist content")) {
+ return false;
+ }
+
+ const DexFile::MapItem* item = map->list_;
+
+ uint32_t count = map->size_;
+ uint32_t last_offset = 0;
+ uint32_t last_type = 0;
+ uint32_t data_item_count = 0;
+ uint32_t data_items_left = header_->data_size_;
+ uint32_t used_bits = 0;
+
+ // Sanity check the size of the map list.
+ if (!CheckListSize(item, count, sizeof(DexFile::MapItem), "map size")) {
+ return false;
+ }
+
+ // Check the items listed in the map.
+ for (uint32_t i = 0; i < count; i++) {
+ if (UNLIKELY(last_offset >= item->offset_ && i != 0)) {
+ ErrorStringPrintf("Out of order map item: %x then %x for type %x last type was %x",
+ last_offset,
+ item->offset_,
+ static_cast<uint32_t>(item->type_),
+ last_type);
+ return false;
+ }
+ if (UNLIKELY(item->offset_ >= header_->file_size_)) {
+ ErrorStringPrintf("Map item after end of file: %x, size %x",
+ item->offset_, header_->file_size_);
+ return false;
+ }
+
+ DexFile::MapItemType item_type = static_cast<DexFile::MapItemType>(item->type_);
+ if (IsDataSectionType(item_type)) {
+ uint32_t icount = item->size_;
+ if (UNLIKELY(icount > data_items_left)) {
+ ErrorStringPrintf("Too many items in data section: %ud item_type %zx",
+ data_item_count + icount,
+ static_cast<size_t>(item_type));
+ return false;
+ }
+ data_items_left -= icount;
+ data_item_count += icount;
+ }
+
+ uint32_t bit = MapTypeToBitMask(item_type);
+
+ if (UNLIKELY(bit == 0)) {
+ ErrorStringPrintf("Unknown map section type %x", item->type_);
+ return false;
+ }
+
+ if (UNLIKELY((used_bits & bit) != 0)) {
+ ErrorStringPrintf("Duplicate map section of type %x", item->type_);
+ return false;
+ }
+
+ used_bits |= bit;
+ last_offset = item->offset_;
+ last_type = item->type_;
+ item++;
+ }
+
+ // Check for missing sections in the map.
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0)) {
+ ErrorStringPrintf("Map is missing header entry");
+ return false;
+ }
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0)) {
+ ErrorStringPrintf("Map is missing map_list entry");
+ return false;
+ }
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 &&
+ ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing string_ids entry");
+ return false;
+ }
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 &&
+ ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing type_ids entry");
+ return false;
+ }
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 &&
+ ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing proto_ids entry");
+ return false;
+ }
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 &&
+ ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing field_ids entry");
+ return false;
+ }
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 &&
+ ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing method_ids entry");
+ return false;
+ }
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 &&
+ ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing class_defs entry");
+ return false;
+ }
+ return true;
+}
+
+uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) {
+ uint32_t result = 0;
+ if (LIKELY(CheckListSize(ptr_, size, sizeof(uint8_t), "encoded_value"))) {
+ for (uint32_t i = 0; i < size; i++) {
+ result |= ((uint32_t) *(ptr_++)) << (i * 8);
+ }
+ }
+ return result;
+}
+
+
+#define DECODE_UNSIGNED_CHECKED_FROM_WITH_ERROR_VALUE(ptr, var, error_value) \
+ uint32_t var; \
+ if (!DecodeUnsignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \
+ return error_value; \
+ }
+
+#define DECODE_UNSIGNED_CHECKED_FROM(ptr, var) \
+ uint32_t var; \
+ if (!DecodeUnsignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \
+ ErrorStringPrintf("Read out of bounds"); \
+ return false; \
+ }
+
+#define DECODE_SIGNED_CHECKED_FROM(ptr, var) \
+ int32_t var; \
+ if (!DecodeSignedLeb128Checked(&(ptr), begin_ + size_, &(var))) { \
+ ErrorStringPrintf("Read out of bounds"); \
+ return false; \
+ }
+
+bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
+ uint32_t* handler_offsets, uint32_t handlers_size) {
+ CodeItemDataAccessor accessor(*dex_file_, code_item);
+ const uint8_t* handlers_base = accessor.GetCatchHandlerData();
+
+ for (uint32_t i = 0; i < handlers_size; i++) {
+ bool catch_all;
+ size_t offset = ptr_ - handlers_base;
+ DECODE_SIGNED_CHECKED_FROM(ptr_, size);
+
+ if (UNLIKELY((size < -65536) || (size > 65536))) {
+ ErrorStringPrintf("Invalid exception handler size: %d", size);
+ return false;
+ }
+
+ if (size <= 0) {
+ catch_all = true;
+ size = -size;
+ } else {
+ catch_all = false;
+ }
+
+ handler_offsets[i] = static_cast<uint32_t>(offset);
+
+ while (size-- > 0) {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
+ if (!CheckIndex(type_idx, header_->type_ids_size_, "handler type_idx")) {
+ return false;
+ }
+
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr);
+ if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) {
+ ErrorStringPrintf("Invalid handler addr: %x", addr);
+ return false;
+ }
+ }
+
+ if (catch_all) {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, addr);
+ if (UNLIKELY(addr >= accessor.InsnsSizeInCodeUnits())) {
+ ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckClassDataItemField(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ dex::TypeIndex class_type_index,
+ bool expect_static) {
+ // Check for overflow.
+ if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) {
+ return false;
+ }
+
+ // Check that it's the right class.
+ dex::TypeIndex my_class_index =
+ (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + idx)->
+ class_idx_;
+ if (class_type_index != my_class_index) {
+ ErrorStringPrintf("Field's class index unexpected, %" PRIu16 "vs %" PRIu16,
+ my_class_index.index_,
+ class_type_index.index_);
+ return false;
+ }
+
+ // Check that it falls into the right class-data list.
+ bool is_static = (access_flags & kAccStatic) != 0;
+ if (UNLIKELY(is_static != expect_static)) {
+ ErrorStringPrintf("Static/instance field not in expected list");
+ return false;
+ }
+
+ // Check field access flags.
+ std::string error_msg;
+ if (!CheckFieldAccessFlags(idx, access_flags, class_access_flags, &error_msg)) {
+ ErrorStringPrintf("%s", error_msg.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ dex::TypeIndex class_type_index,
+ uint32_t code_offset,
+ std::unordered_set<uint32_t>* direct_method_indexes,
+ bool expect_direct) {
+ DCHECK(direct_method_indexes != nullptr);
+ // Check for overflow.
+ if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
+ return false;
+ }
+
+ // Check that it's the right class.
+ dex::TypeIndex my_class_index =
+ (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + idx)->
+ class_idx_;
+ if (class_type_index != my_class_index) {
+ ErrorStringPrintf("Method's class index unexpected, %" PRIu16 " vs %" PRIu16,
+ my_class_index.index_,
+ class_type_index.index_);
+ return false;
+ }
+
+ // Check that it's not defined as both direct and virtual.
+ if (expect_direct) {
+ direct_method_indexes->insert(idx);
+ } else if (direct_method_indexes->find(idx) != direct_method_indexes->end()) {
+ ErrorStringPrintf("Found virtual method with same index as direct method: %d", idx);
+ return false;
+ }
+
+ std::string error_msg;
+ const char* method_name;
+ if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) {
+ ErrorStringPrintf("%s", error_msg.c_str());
+ return false;
+ }
+
+ uint32_t constructor_flags_by_name = 0;
+ if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) {
+ ErrorStringPrintf("Bad method name: %s", method_name);
+ return false;
+ }
+
+ bool has_code = (code_offset != 0);
+ if (!CheckMethodAccessFlags(idx,
+ access_flags,
+ class_access_flags,
+ constructor_flags_by_name,
+ has_code,
+ expect_direct,
+ &error_msg)) {
+ ErrorStringPrintf("%s", error_msg.c_str());
+ return false;
+ }
+
+ if (constructor_flags_by_name != 0) {
+ if (!CheckConstructorProperties(idx, constructor_flags_by_name)) {
+ DCHECK(FailureReasonIsSet());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckPadding(size_t offset,
+ uint32_t aligned_offset,
+ DexFile::MapItemType type) {
+ if (offset < aligned_offset) {
+ if (!CheckListSize(begin_ + offset, aligned_offset - offset, sizeof(uint8_t), "section")) {
+ return false;
+ }
+ while (offset < aligned_offset) {
+ if (UNLIKELY(*ptr_ != '\0')) {
+ ErrorStringPrintf("Non-zero padding %x before section of type %zu at offset 0x%zx",
+ *ptr_,
+ static_cast<size_t>(type),
+ offset);
+ return false;
+ }
+ ptr_++;
+ offset++;
+ }
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckEncodedValue() {
+ if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "encoded_value header")) {
+ return false;
+ }
+
+ uint8_t header_byte = *(ptr_++);
+ uint32_t value_type = header_byte & DexFile::kDexAnnotationValueTypeMask;
+ uint32_t value_arg = header_byte >> DexFile::kDexAnnotationValueArgShift;
+
+ switch (value_type) {
+ case DexFile::kDexAnnotationByte:
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value byte size %x", value_arg);
+ return false;
+ }
+ ptr_++;
+ break;
+ case DexFile::kDexAnnotationShort:
+ case DexFile::kDexAnnotationChar:
+ if (UNLIKELY(value_arg > 1)) {
+ ErrorStringPrintf("Bad encoded_value char/short size %x", value_arg);
+ return false;
+ }
+ ptr_ += value_arg + 1;
+ break;
+ case DexFile::kDexAnnotationInt:
+ case DexFile::kDexAnnotationFloat:
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value int/float size %x", value_arg);
+ return false;
+ }
+ ptr_ += value_arg + 1;
+ break;
+ case DexFile::kDexAnnotationLong:
+ case DexFile::kDexAnnotationDouble:
+ ptr_ += value_arg + 1;
+ break;
+ case DexFile::kDexAnnotationString: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value string size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, header_->string_ids_size_, "encoded_value string")) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationType: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value type size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, header_->type_ids_size_, "encoded_value type")) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationField:
+ case DexFile::kDexAnnotationEnum: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value field/enum size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, header_->field_ids_size_, "encoded_value field")) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationMethod: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value method size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, header_->method_ids_size_, "encoded_value method")) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationArray:
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value array value_arg %x", value_arg);
+ return false;
+ }
+ if (!CheckEncodedArray()) {
+ return false;
+ }
+ break;
+ case DexFile::kDexAnnotationAnnotation:
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value annotation value_arg %x", value_arg);
+ return false;
+ }
+ if (!CheckEncodedAnnotation()) {
+ return false;
+ }
+ break;
+ case DexFile::kDexAnnotationNull:
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value null value_arg %x", value_arg);
+ return false;
+ }
+ break;
+ case DexFile::kDexAnnotationBoolean:
+ if (UNLIKELY(value_arg > 1)) {
+ ErrorStringPrintf("Bad encoded_value boolean size %x", value_arg);
+ return false;
+ }
+ break;
+ case DexFile::kDexAnnotationMethodType: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value method type size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, header_->proto_ids_size_, "method_type value")) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationMethodHandle: {
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value method handle size %x", value_arg);
+ return false;
+ }
+ uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
+ if (!CheckIndex(idx, dex_file_->NumMethodHandles(), "method_handle value")) {
+ return false;
+ }
+ break;
+ }
+ default:
+ ErrorStringPrintf("Bogus encoded_value value_type %x", value_type);
+ return false;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckEncodedArray() {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
+
+ while (size--) {
+ if (!CheckEncodedValue()) {
+ failure_reason_ = StringPrintf("Bad encoded_array value: %s", failure_reason_.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckEncodedAnnotation() {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, anno_idx);
+ if (!CheckIndex(anno_idx, header_->type_ids_size_, "encoded_annotation type_idx")) {
+ return false;
+ }
+
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
+ uint32_t last_idx = 0;
+
+ for (uint32_t i = 0; i < size; i++) {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, idx);
+ if (!CheckIndex(idx, header_->string_ids_size_, "annotation_element name_idx")) {
+ return false;
+ }
+
+ if (UNLIKELY(last_idx >= idx && i != 0)) {
+ ErrorStringPrintf("Out-of-order annotation_element name_idx: %x then %x",
+ last_idx, idx);
+ return false;
+ }
+
+ if (!CheckEncodedValue()) {
+ return false;
+ }
+
+ last_idx = idx;
+ }
+ return true;
+}
+
+bool DexFileVerifier::FindClassIndexAndDef(uint32_t index,
+ bool is_field,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** output_class_def) {
+ DCHECK(class_type_index != nullptr);
+ DCHECK(output_class_def != nullptr);
+
+ // First check if the index is valid.
+ if (index >= (is_field ? header_->field_ids_size_ : header_->method_ids_size_)) {
+ return false;
+ }
+
+ // Next get the type index.
+ if (is_field) {
+ *class_type_index =
+ (reinterpret_cast<const DexFile::FieldId*>(begin_ + header_->field_ids_off_) + index)->
+ class_idx_;
+ } else {
+ *class_type_index =
+ (reinterpret_cast<const DexFile::MethodId*>(begin_ + header_->method_ids_off_) + index)->
+ class_idx_;
+ }
+
+ // Check if that is valid.
+ if (class_type_index->index_ >= header_->type_ids_size_) {
+ return false;
+ }
+
+ // Now search for the class def. This is basically a specialized version of the DexFile code, as
+ // we should not trust that this is a valid DexFile just yet.
+ const DexFile::ClassDef* class_def_begin =
+ reinterpret_cast<const DexFile::ClassDef*>(begin_ + header_->class_defs_off_);
+ for (size_t i = 0; i < header_->class_defs_size_; ++i) {
+ const DexFile::ClassDef* class_def = class_def_begin + i;
+ if (class_def->class_idx_ == *class_type_index) {
+ *output_class_def = class_def;
+ return true;
+ }
+ }
+
+ // Didn't find the class-def, not defined here...
+ return false;
+}
+
+bool DexFileVerifier::CheckOrderAndGetClassDef(bool is_field,
+ const char* type_descr,
+ uint32_t curr_index,
+ uint32_t prev_index,
+ bool* have_class,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** class_def) {
+ if (curr_index < prev_index) {
+ ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32,
+ type_descr,
+ prev_index,
+ curr_index);
+ return false;
+ }
+
+ if (!*have_class) {
+ *have_class = FindClassIndexAndDef(curr_index, is_field, class_type_index, class_def);
+ if (!*have_class) {
+ // Should have really found one.
+ ErrorStringPrintf("could not find declaring class for %s index %" PRIu32,
+ type_descr,
+ curr_index);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckStaticFieldTypes(const DexFile::ClassDef* class_def) {
+ if (class_def == nullptr) {
+ return true;
+ }
+
+ ClassDataItemIterator field_it(*dex_file_, ptr_);
+ EncodedStaticFieldValueIterator array_it(*dex_file_, *class_def);
+
+ for (; field_it.HasNextStaticField() && array_it.HasNext(); field_it.Next(), array_it.Next()) {
+ uint32_t index = field_it.GetMemberIndex();
+ const DexFile::TypeId& type_id = dex_file_->GetTypeId(dex_file_->GetFieldId(index).type_idx_);
+ const char* field_type_name =
+ dex_file_->GetStringData(dex_file_->GetStringId(type_id.descriptor_idx_));
+ Primitive::Type field_type = Primitive::GetType(field_type_name[0]);
+ EncodedArrayValueIterator::ValueType array_type = array_it.GetValueType();
+ // Ensure this matches RuntimeEncodedStaticFieldValueIterator.
+ switch (array_type) {
+ case EncodedArrayValueIterator::ValueType::kBoolean:
+ if (field_type != Primitive::kPrimBoolean) {
+ ErrorStringPrintf("unexpected static field initial value type: 'Z' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kByte:
+ if (field_type != Primitive::kPrimByte) {
+ ErrorStringPrintf("unexpected static field initial value type: 'B' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kShort:
+ if (field_type != Primitive::kPrimShort) {
+ ErrorStringPrintf("unexpected static field initial value type: 'S' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kChar:
+ if (field_type != Primitive::kPrimChar) {
+ ErrorStringPrintf("unexpected static field initial value type: 'C' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kInt:
+ if (field_type != Primitive::kPrimInt) {
+ ErrorStringPrintf("unexpected static field initial value type: 'I' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kLong:
+ if (field_type != Primitive::kPrimLong) {
+ ErrorStringPrintf("unexpected static field initial value type: 'J' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kFloat:
+ if (field_type != Primitive::kPrimFloat) {
+ ErrorStringPrintf("unexpected static field initial value type: 'F' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kDouble:
+ if (field_type != Primitive::kPrimDouble) {
+ ErrorStringPrintf("unexpected static field initial value type: 'D' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ case EncodedArrayValueIterator::ValueType::kNull:
+ case EncodedArrayValueIterator::ValueType::kString:
+ case EncodedArrayValueIterator::ValueType::kType:
+ if (field_type != Primitive::kPrimNot) {
+ ErrorStringPrintf("unexpected static field initial value type: 'L' vs '%c'",
+ field_type_name[0]);
+ return false;
+ }
+ break;
+ default:
+ ErrorStringPrintf("unexpected static field initial value type: %x", array_type);
+ return false;
+ }
+ }
+
+ if (array_it.HasNext()) {
+ ErrorStringPrintf("too many static field initial values");
+ return false;
+ }
+ return true;
+}
+
+template <bool kStatic>
+bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it,
+ bool* have_class,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** class_def) {
+ DCHECK(it != nullptr);
+ // These calls use the raw access flags to check whether the whole dex field is valid.
+ uint32_t prev_index = 0;
+ for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) {
+ uint32_t curr_index = it->GetMemberIndex();
+ if (!CheckOrderAndGetClassDef(true,
+ kStatic ? "static field" : "instance field",
+ curr_index,
+ prev_index,
+ have_class,
+ class_type_index,
+ class_def)) {
+ return false;
+ }
+ DCHECK(class_def != nullptr);
+ if (!CheckClassDataItemField(curr_index,
+ it->GetRawMemberAccessFlags(),
+ (*class_def)->access_flags_,
+ *class_type_index,
+ kStatic)) {
+ return false;
+ }
+
+ prev_index = curr_index;
+ }
+
+ return true;
+}
+
+template <bool kDirect>
+bool DexFileVerifier::CheckIntraClassDataItemMethods(
+ ClassDataItemIterator* it,
+ std::unordered_set<uint32_t>* direct_method_indexes,
+ bool* have_class,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** class_def) {
+ uint32_t prev_index = 0;
+ for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) {
+ uint32_t curr_index = it->GetMemberIndex();
+ if (!CheckOrderAndGetClassDef(false,
+ kDirect ? "direct method" : "virtual method",
+ curr_index,
+ prev_index,
+ have_class,
+ class_type_index,
+ class_def)) {
+ return false;
+ }
+ DCHECK(class_def != nullptr);
+ if (!CheckClassDataItemMethod(curr_index,
+ it->GetRawMemberAccessFlags(),
+ (*class_def)->access_flags_,
+ *class_type_index,
+ it->GetMethodCodeItemOffset(),
+ direct_method_indexes,
+ kDirect)) {
+ return false;
+ }
+
+ prev_index = curr_index;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraClassDataItem() {
+ ClassDataItemIterator it(*dex_file_, ptr_);
+ std::unordered_set<uint32_t> direct_method_indexes;
+
+ // This code is complicated by the fact that we don't directly know which class this belongs to.
+ // So we need to explicitly search with the first item we find (either field or method), and then,
+ // as the lookup is expensive, cache the result.
+ bool have_class = false;
+ dex::TypeIndex class_type_index;
+ const DexFile::ClassDef* class_def = nullptr;
+
+ // Check fields.
+ if (!CheckIntraClassDataItemFields<true>(&it,
+ &have_class,
+ &class_type_index,
+ &class_def)) {
+ return false;
+ }
+ if (!CheckIntraClassDataItemFields<false>(&it,
+ &have_class,
+ &class_type_index,
+ &class_def)) {
+ return false;
+ }
+
+ // Check methods.
+ if (!CheckIntraClassDataItemMethods<true>(&it,
+ &direct_method_indexes,
+ &have_class,
+ &class_type_index,
+ &class_def)) {
+ return false;
+ }
+ if (!CheckIntraClassDataItemMethods<false>(&it,
+ &direct_method_indexes,
+ &have_class,
+ &class_type_index,
+ &class_def)) {
+ return false;
+ }
+
+ const uint8_t* end_ptr = it.EndDataPointer();
+
+ // Check static field types against initial static values in encoded array.
+ if (!CheckStaticFieldTypes(class_def)) {
+ return false;
+ }
+
+ ptr_ = end_ptr;
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraCodeItem() {
+ const DexFile::CodeItem* code_item = reinterpret_cast<const DexFile::CodeItem*>(ptr_);
+ if (!CheckListSize(code_item, 1, sizeof(DexFile::CodeItem), "code")) {
+ return false;
+ }
+
+ CodeItemDataAccessor accessor(*dex_file_, code_item);
+ if (UNLIKELY(accessor.InsSize() > accessor.RegistersSize())) {
+ ErrorStringPrintf("ins_size (%ud) > registers_size (%ud)",
+ accessor.InsSize(), accessor.RegistersSize());
+ return false;
+ }
+
+ if (UNLIKELY(accessor.OutsSize() > 5 && accessor.OutsSize() > accessor.RegistersSize())) {
+ /*
+ * outs_size can be up to 5, even if registers_size is smaller, since the
+ * short forms of method invocation allow repetitions of a register multiple
+ * times within a single parameter list. However, longer parameter lists
+ * need to be represented in-order in the register file.
+ */
+ ErrorStringPrintf("outs_size (%ud) > registers_size (%ud)",
+ accessor.OutsSize(), accessor.RegistersSize());
+ return false;
+ }
+
+ const uint16_t* insns = accessor.Insns();
+ uint32_t insns_size = accessor.InsnsSizeInCodeUnits();
+ if (!CheckListSize(insns, insns_size, sizeof(uint16_t), "insns size")) {
+ return false;
+ }
+
+ // Grab the end of the insns if there are no try_items.
+ uint32_t try_items_size = accessor.TriesSize();
+ if (try_items_size == 0) {
+ ptr_ = reinterpret_cast<const uint8_t*>(&insns[insns_size]);
+ return true;
+ }
+
+ // try_items are 4-byte aligned. Verify the spacer is 0.
+ if (((reinterpret_cast<uintptr_t>(&insns[insns_size]) & 3) != 0) && (insns[insns_size] != 0)) {
+ ErrorStringPrintf("Non-zero padding: %x", insns[insns_size]);
+ return false;
+ }
+
+ const DexFile::TryItem* try_items = accessor.TryItems().begin();
+ if (!CheckListSize(try_items, try_items_size, sizeof(DexFile::TryItem), "try_items size")) {
+ return false;
+ }
+
+ ptr_ = accessor.GetCatchHandlerData();
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, handlers_size);
+
+ if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) {
+ ErrorStringPrintf("Invalid handlers_size: %ud", handlers_size);
+ return false;
+ }
+
+ std::unique_ptr<uint32_t[]> handler_offsets(new uint32_t[handlers_size]);
+ if (!CheckAndGetHandlerOffsets(code_item, &handler_offsets[0], handlers_size)) {
+ return false;
+ }
+
+ uint32_t last_addr = 0;
+ while (try_items_size--) {
+ if (UNLIKELY(try_items->start_addr_ < last_addr)) {
+ ErrorStringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_);
+ return false;
+ }
+
+ if (UNLIKELY(try_items->start_addr_ >= insns_size)) {
+ ErrorStringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_);
+ return false;
+ }
+
+ uint32_t i;
+ for (i = 0; i < handlers_size; i++) {
+ if (try_items->handler_off_ == handler_offsets[i]) {
+ break;
+ }
+ }
+
+ if (UNLIKELY(i == handlers_size)) {
+ ErrorStringPrintf("Bogus handler offset: %x", try_items->handler_off_);
+ return false;
+ }
+
+ last_addr = try_items->start_addr_ + try_items->insn_count_;
+ if (UNLIKELY(last_addr > insns_size)) {
+ ErrorStringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_);
+ return false;
+ }
+
+ try_items++;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraStringDataItem() {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, size);
+ const uint8_t* file_end = begin_ + size_;
+
+ for (uint32_t i = 0; i < size; i++) {
+ CHECK_LT(i, size); // b/15014252 Prevents hitting the impossible case below
+ if (UNLIKELY(ptr_ >= file_end)) {
+ ErrorStringPrintf("String data would go beyond end-of-file");
+ return false;
+ }
+
+ uint8_t byte = *(ptr_++);
+
+ // Switch on the high 4 bits.
+ switch (byte >> 4) {
+ case 0x00:
+ // Special case of bit pattern 0xxx.
+ if (UNLIKELY(byte == 0)) {
+ CHECK_LT(i, size); // b/15014252 Actually hit this impossible case with clang
+ ErrorStringPrintf("String data shorter than indicated utf16_size %x", size);
+ return false;
+ }
+ break;
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ // No extra checks necessary for bit pattern 0xxx.
+ break;
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f:
+ // Illegal bit patterns 10xx or 1111.
+ // Note: 1111 is valid for normal UTF-8, but not here.
+ ErrorStringPrintf("Illegal start byte %x in string data", byte);
+ return false;
+ case 0x0c:
+ case 0x0d: {
+ // Bit pattern 110x has an additional byte.
+ uint8_t byte2 = *(ptr_++);
+ if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
+ return false;
+ }
+ uint16_t value = ((byte & 0x1f) << 6) | (byte2 & 0x3f);
+ if (UNLIKELY((value != 0) && (value < 0x80))) {
+ ErrorStringPrintf("Illegal representation for value %x in string data", value);
+ return false;
+ }
+ break;
+ }
+ case 0x0e: {
+ // Bit pattern 1110 has 2 additional bytes.
+ uint8_t byte2 = *(ptr_++);
+ if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
+ return false;
+ }
+ uint8_t byte3 = *(ptr_++);
+ if (UNLIKELY((byte3 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte3);
+ return false;
+ }
+ uint16_t value = ((byte & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f);
+ if (UNLIKELY(value < 0x800)) {
+ ErrorStringPrintf("Illegal representation for value %x in string data", value);
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ if (UNLIKELY(*(ptr_++) != '\0')) {
+ ErrorStringPrintf("String longer than indicated size %x", size);
+ return false;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraDebugInfoItem() {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, dummy);
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameters_size);
+ if (UNLIKELY(parameters_size > 65536)) {
+ ErrorStringPrintf("Invalid parameters_size: %x", parameters_size);
+ return false;
+ }
+
+ for (uint32_t j = 0; j < parameters_size; j++) {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, parameter_name);
+ if (parameter_name != 0) {
+ parameter_name--;
+ if (!CheckIndex(parameter_name, header_->string_ids_size_, "debug_info_item parameter_name")) {
+ return false;
+ }
+ }
+ }
+
+ while (true) {
+ uint8_t opcode = *(ptr_++);
+ switch (opcode) {
+ case DexFile::DBG_END_SEQUENCE: {
+ return true;
+ }
+ case DexFile::DBG_ADVANCE_PC: {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, advance_pc_dummy);
+ break;
+ }
+ case DexFile::DBG_ADVANCE_LINE: {
+ DECODE_SIGNED_CHECKED_FROM(ptr_, advance_line_dummy);
+ break;
+ }
+ case DexFile::DBG_START_LOCAL: {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
+ return false;
+ }
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
+ if (name_idx != 0) {
+ name_idx--;
+ if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL name_idx")) {
+ return false;
+ }
+ }
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
+ if (type_idx != 0) {
+ type_idx--;
+ if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL type_idx")) {
+ return false;
+ }
+ }
+ break;
+ }
+ case DexFile::DBG_END_LOCAL:
+ case DexFile::DBG_RESTART_LOCAL: {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
+ return false;
+ }
+ break;
+ }
+ case DexFile::DBG_START_LOCAL_EXTENDED: {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, reg_num);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
+ return false;
+ }
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
+ if (name_idx != 0) {
+ name_idx--;
+ if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED name_idx")) {
+ return false;
+ }
+ }
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, type_idx);
+ if (type_idx != 0) {
+ type_idx--;
+ if (!CheckIndex(type_idx, header_->type_ids_size_, "DBG_START_LOCAL_EXTENDED type_idx")) {
+ return false;
+ }
+ }
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, sig_idx);
+ if (sig_idx != 0) {
+ sig_idx--;
+ if (!CheckIndex(sig_idx, header_->string_ids_size_, "DBG_START_LOCAL_EXTENDED sig_idx")) {
+ return false;
+ }
+ }
+ break;
+ }
+ case DexFile::DBG_SET_FILE: {
+ DECODE_UNSIGNED_CHECKED_FROM(ptr_, name_idx);
+ if (name_idx != 0) {
+ name_idx--;
+ if (!CheckIndex(name_idx, header_->string_ids_size_, "DBG_SET_FILE name_idx")) {
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+bool DexFileVerifier::CheckIntraAnnotationItem() {
+ if (!CheckListSize(ptr_, 1, sizeof(uint8_t), "annotation visibility")) {
+ return false;
+ }
+
+ // Check visibility
+ switch (*(ptr_++)) {
+ case DexFile::kDexVisibilityBuild:
+ case DexFile::kDexVisibilityRuntime:
+ case DexFile::kDexVisibilitySystem:
+ break;
+ default:
+ ErrorStringPrintf("Bad annotation visibility: %x", *ptr_);
+ return false;
+ }
+
+ if (!CheckEncodedAnnotation()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() {
+ const DexFile::AnnotationsDirectoryItem* item =
+ reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_);
+ if (!CheckListSize(item, 1, sizeof(DexFile::AnnotationsDirectoryItem), "annotations_directory")) {
+ return false;
+ }
+
+ // Field annotations follow immediately after the annotations directory.
+ const DexFile::FieldAnnotationsItem* field_item =
+ reinterpret_cast<const DexFile::FieldAnnotationsItem*>(item + 1);
+ uint32_t field_count = item->fields_size_;
+ if (!CheckListSize(field_item, field_count, sizeof(DexFile::FieldAnnotationsItem), "field_annotations list")) {
+ return false;
+ }
+
+ uint32_t last_idx = 0;
+ for (uint32_t i = 0; i < field_count; i++) {
+ if (UNLIKELY(last_idx >= field_item->field_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_);
+ return false;
+ }
+ last_idx = field_item->field_idx_;
+ field_item++;
+ }
+
+ // Method annotations follow immediately after field annotations.
+ const DexFile::MethodAnnotationsItem* method_item =
+ reinterpret_cast<const DexFile::MethodAnnotationsItem*>(field_item);
+ uint32_t method_count = item->methods_size_;
+ if (!CheckListSize(method_item, method_count, sizeof(DexFile::MethodAnnotationsItem), "method_annotations list")) {
+ return false;
+ }
+
+ last_idx = 0;
+ for (uint32_t i = 0; i < method_count; i++) {
+ if (UNLIKELY(last_idx >= method_item->method_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
+ last_idx, method_item->method_idx_);
+ return false;
+ }
+ last_idx = method_item->method_idx_;
+ method_item++;
+ }
+
+ // Parameter annotations follow immediately after method annotations.
+ const DexFile::ParameterAnnotationsItem* parameter_item =
+ reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item);
+ uint32_t parameter_count = item->parameters_size_;
+ if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem),
+ "parameter_annotations list")) {
+ return false;
+ }
+
+ last_idx = 0;
+ for (uint32_t i = 0; i < parameter_count; i++) {
+ if (UNLIKELY(last_idx >= parameter_item->method_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
+ last_idx, parameter_item->method_idx_);
+ return false;
+ }
+ last_idx = parameter_item->method_idx_;
+ parameter_item++;
+ }
+
+ // Return a pointer to the end of the annotations.
+ ptr_ = reinterpret_cast<const uint8_t*>(parameter_item);
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count,
+ DexFile::MapItemType type) {
+ // Get the right alignment mask for the type of section.
+ size_t alignment_mask;
+ switch (type) {
+ case DexFile::kDexTypeClassDataItem:
+ case DexFile::kDexTypeStringDataItem:
+ case DexFile::kDexTypeDebugInfoItem:
+ case DexFile::kDexTypeAnnotationItem:
+ case DexFile::kDexTypeEncodedArrayItem:
+ alignment_mask = sizeof(uint8_t) - 1;
+ break;
+ default:
+ alignment_mask = sizeof(uint32_t) - 1;
+ break;
+ }
+
+ // Iterate through the items in the section.
+ for (uint32_t i = 0; i < section_count; i++) {
+ size_t aligned_offset = (offset + alignment_mask) & ~alignment_mask;
+
+ // Check the padding between items.
+ if (!CheckPadding(offset, aligned_offset, type)) {
+ return false;
+ }
+
+ // Check depending on the section type.
+ const uint8_t* start_ptr = ptr_;
+ switch (type) {
+ case DexFile::kDexTypeStringIdItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::StringId);
+ break;
+ }
+ case DexFile::kDexTypeTypeIdItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::TypeId), "type_ids")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::TypeId);
+ break;
+ }
+ case DexFile::kDexTypeProtoIdItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::ProtoId), "proto_ids")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::ProtoId);
+ break;
+ }
+ case DexFile::kDexTypeFieldIdItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::FieldId), "field_ids")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::FieldId);
+ break;
+ }
+ case DexFile::kDexTypeMethodIdItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodId), "method_ids")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::MethodId);
+ break;
+ }
+ case DexFile::kDexTypeClassDefItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::ClassDef), "class_defs")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::ClassDef);
+ break;
+ }
+ case DexFile::kDexTypeCallSiteIdItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::CallSiteIdItem), "call_site_ids")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::CallSiteIdItem);
+ break;
+ }
+ case DexFile::kDexTypeMethodHandleItem: {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodHandleItem), "method_handles")) {
+ return false;
+ }
+ ptr_ += sizeof(DexFile::MethodHandleItem);
+ break;
+ }
+ case DexFile::kDexTypeTypeList: {
+ if (!CheckList(sizeof(DexFile::TypeItem), "type_list", &ptr_)) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeAnnotationSetRefList: {
+ if (!CheckList(sizeof(DexFile::AnnotationSetRefItem), "annotation_set_ref_list", &ptr_)) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeAnnotationSetItem: {
+ if (!CheckList(sizeof(uint32_t), "annotation_set_item", &ptr_)) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeClassDataItem: {
+ if (!CheckIntraClassDataItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeCodeItem: {
+ if (!CheckIntraCodeItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeStringDataItem: {
+ if (!CheckIntraStringDataItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeDebugInfoItem: {
+ if (!CheckIntraDebugInfoItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeAnnotationItem: {
+ if (!CheckIntraAnnotationItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeEncodedArrayItem: {
+ if (!CheckEncodedArray()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeAnnotationsDirectoryItem: {
+ if (!CheckIntraAnnotationsDirectoryItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeHeaderItem:
+ case DexFile::kDexTypeMapList:
+ break;
+ }
+
+ if (start_ptr == ptr_) {
+ ErrorStringPrintf("Unknown map item type %x", type);
+ return false;
+ }
+
+ if (IsDataSectionType(type)) {
+ if (aligned_offset == 0u) {
+ ErrorStringPrintf("Item %d offset is 0", i);
+ return false;
+ }
+ DCHECK(offset_to_type_map_.Find(aligned_offset) == offset_to_type_map_.end());
+ offset_to_type_map_.Insert(std::pair<uint32_t, uint16_t>(aligned_offset, type));
+ }
+
+ aligned_offset = ptr_ - begin_;
+ if (UNLIKELY(aligned_offset > size_)) {
+ ErrorStringPrintf("Item %d at ends out of bounds", i);
+ return false;
+ }
+
+ offset = aligned_offset;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraIdSection(size_t offset,
+ uint32_t count,
+ DexFile::MapItemType type) {
+ uint32_t expected_offset;
+ uint32_t expected_size;
+
+ // Get the expected offset and size from the header.
+ switch (type) {
+ case DexFile::kDexTypeStringIdItem:
+ expected_offset = header_->string_ids_off_;
+ expected_size = header_->string_ids_size_;
+ break;
+ case DexFile::kDexTypeTypeIdItem:
+ expected_offset = header_->type_ids_off_;
+ expected_size = header_->type_ids_size_;
+ break;
+ case DexFile::kDexTypeProtoIdItem:
+ expected_offset = header_->proto_ids_off_;
+ expected_size = header_->proto_ids_size_;
+ break;
+ case DexFile::kDexTypeFieldIdItem:
+ expected_offset = header_->field_ids_off_;
+ expected_size = header_->field_ids_size_;
+ break;
+ case DexFile::kDexTypeMethodIdItem:
+ expected_offset = header_->method_ids_off_;
+ expected_size = header_->method_ids_size_;
+ break;
+ case DexFile::kDexTypeClassDefItem:
+ expected_offset = header_->class_defs_off_;
+ expected_size = header_->class_defs_size_;
+ break;
+ default:
+ ErrorStringPrintf("Bad type for id section: %x", type);
+ return false;
+ }
+
+ // Check that the offset and size are what were expected from the header.
+ if (UNLIKELY(offset != expected_offset)) {
+ ErrorStringPrintf("Bad offset for section: got %zx, expected %x", offset, expected_offset);
+ return false;
+ }
+ if (UNLIKELY(count != expected_size)) {
+ ErrorStringPrintf("Bad size for section: got %x, expected %x", count, expected_size);
+ return false;
+ }
+
+ return CheckIntraSectionIterate(offset, count, type);
+}
+
+bool DexFileVerifier::CheckIntraDataSection(size_t offset,
+ uint32_t count,
+ DexFile::MapItemType type) {
+ size_t data_start = header_->data_off_;
+ size_t data_end = data_start + header_->data_size_;
+
+ // Sanity check the offset of the section.
+ if (UNLIKELY((offset < data_start) || (offset > data_end))) {
+ ErrorStringPrintf("Bad offset for data subsection: %zx", offset);
+ return false;
+ }
+
+ if (!CheckIntraSectionIterate(offset, count, type)) {
+ return false;
+ }
+
+ size_t next_offset = ptr_ - begin_;
+ if (next_offset > data_end) {
+ ErrorStringPrintf("Out-of-bounds end of data subsection: %zu data_off=%u data_size=%u",
+ next_offset,
+ header_->data_off_,
+ header_->data_size_);
+ return false;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckIntraSection() {
+ const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
+ const DexFile::MapItem* item = map->list_;
+ size_t offset = 0;
+ uint32_t count = map->size_;
+ ptr_ = begin_;
+
+ // Check the items listed in the map.
+ while (count--) {
+ const size_t current_offset = offset;
+ uint32_t section_offset = item->offset_;
+ uint32_t section_count = item->size_;
+ DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
+
+ // Check for padding and overlap between items.
+ if (!CheckPadding(offset, section_offset, type)) {
+ return false;
+ } else if (UNLIKELY(offset > section_offset)) {
+ ErrorStringPrintf("Section overlap or out-of-order map: %zx, %x", offset, section_offset);
+ return false;
+ }
+
+ // Check each item based on its type.
+ switch (type) {
+ case DexFile::kDexTypeHeaderItem:
+ if (UNLIKELY(section_count != 1)) {
+ ErrorStringPrintf("Multiple header items");
+ return false;
+ }
+ if (UNLIKELY(section_offset != 0)) {
+ ErrorStringPrintf("Header at %x, not at start of file", section_offset);
+ return false;
+ }
+ ptr_ = begin_ + header_->header_size_;
+ offset = header_->header_size_;
+ break;
+ case DexFile::kDexTypeStringIdItem:
+ case DexFile::kDexTypeTypeIdItem:
+ case DexFile::kDexTypeProtoIdItem:
+ case DexFile::kDexTypeFieldIdItem:
+ case DexFile::kDexTypeMethodIdItem:
+ case DexFile::kDexTypeClassDefItem:
+ if (!CheckIntraIdSection(section_offset, section_count, type)) {
+ return false;
+ }
+ offset = ptr_ - begin_;
+ break;
+ case DexFile::kDexTypeMapList:
+ if (UNLIKELY(section_count != 1)) {
+ ErrorStringPrintf("Multiple map list items");
+ return false;
+ }
+ if (UNLIKELY(section_offset != header_->map_off_)) {
+ ErrorStringPrintf("Map not at header-defined offset: %x, expected %x",
+ section_offset, header_->map_off_);
+ return false;
+ }
+ ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
+ offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
+ break;
+ case DexFile::kDexTypeMethodHandleItem:
+ case DexFile::kDexTypeCallSiteIdItem:
+ CheckIntraSectionIterate(section_offset, section_count, type);
+ offset = ptr_ - begin_;
+ break;
+ case DexFile::kDexTypeTypeList:
+ case DexFile::kDexTypeAnnotationSetRefList:
+ case DexFile::kDexTypeAnnotationSetItem:
+ case DexFile::kDexTypeClassDataItem:
+ case DexFile::kDexTypeCodeItem:
+ case DexFile::kDexTypeStringDataItem:
+ case DexFile::kDexTypeDebugInfoItem:
+ case DexFile::kDexTypeAnnotationItem:
+ case DexFile::kDexTypeEncodedArrayItem:
+ case DexFile::kDexTypeAnnotationsDirectoryItem:
+ if (!CheckIntraDataSection(section_offset, section_count, type)) {
+ return false;
+ }
+ offset = ptr_ - begin_;
+ break;
+ }
+
+ if (offset == current_offset) {
+ ErrorStringPrintf("Unknown map item type %x", type);
+ return false;
+ }
+
+ item++;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckOffsetToTypeMap(size_t offset, uint16_t type) {
+ DCHECK_NE(offset, 0u);
+ auto it = offset_to_type_map_.Find(offset);
+ if (UNLIKELY(it == offset_to_type_map_.end())) {
+ ErrorStringPrintf("No data map entry found @ %zx; expected %x", offset, type);
+ return false;
+ }
+ if (UNLIKELY(it->second != type)) {
+ ErrorStringPrintf("Unexpected data map entry @ %zx; expected %x, found %x",
+ offset, type, it->second);
+ return false;
+ }
+ return true;
+}
+
+dex::TypeIndex DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bool* success) {
+ ClassDataItemIterator it(*dex_file_, ptr);
+ *success = true;
+
+ if (it.HasNextStaticField() || it.HasNextInstanceField()) {
+ LOAD_FIELD(field, it.GetMemberIndex(), "first_class_data_definer field_id",
+ *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
+ return field->class_idx_;
+ }
+
+ if (it.HasNextMethod()) {
+ LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id",
+ *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
+ return method->class_idx_;
+ }
+
+ return dex::TypeIndex(DexFile::kDexNoIndex16);
+}
+
+dex::TypeIndex DexFileVerifier::FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr,
+ bool* success) {
+ const DexFile::AnnotationsDirectoryItem* item =
+ reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr);
+ *success = true;
+
+ if (item->fields_size_ != 0) {
+ DexFile::FieldAnnotationsItem* field_items = (DexFile::FieldAnnotationsItem*) (item + 1);
+ LOAD_FIELD(field, field_items[0].field_idx_, "first_annotations_dir_definer field_id",
+ *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
+ return field->class_idx_;
+ }
+
+ if (item->methods_size_ != 0) {
+ DexFile::MethodAnnotationsItem* method_items = (DexFile::MethodAnnotationsItem*) (item + 1);
+ LOAD_METHOD(method, method_items[0].method_idx_, "first_annotations_dir_definer method id",
+ *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
+ return method->class_idx_;
+ }
+
+ if (item->parameters_size_ != 0) {
+ DexFile::ParameterAnnotationsItem* parameter_items = (DexFile::ParameterAnnotationsItem*) (item + 1);
+ LOAD_METHOD(method, parameter_items[0].method_idx_, "first_annotations_dir_definer method id",
+ *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16))
+ return method->class_idx_;
+ }
+
+ return dex::TypeIndex(DexFile::kDexNoIndex16);
+}
+
+bool DexFileVerifier::CheckInterStringIdItem() {
+ const DexFile::StringId* item = reinterpret_cast<const DexFile::StringId*>(ptr_);
+
+ // Check the map to make sure it has the right offset->type.
+ if (!CheckOffsetToTypeMap(item->string_data_off_, DexFile::kDexTypeStringDataItem)) {
+ return false;
+ }
+
+ // Check ordering between items.
+ if (previous_item_ != nullptr) {
+ const DexFile::StringId* prev_item = reinterpret_cast<const DexFile::StringId*>(previous_item_);
+ const char* prev_str = dex_file_->GetStringData(*prev_item);
+ const char* str = dex_file_->GetStringData(*item);
+ if (UNLIKELY(CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0)) {
+ ErrorStringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str);
+ return false;
+ }
+ }
+
+ ptr_ += sizeof(DexFile::StringId);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterTypeIdItem() {
+ const DexFile::TypeId* item = reinterpret_cast<const DexFile::TypeId*>(ptr_);
+
+ LOAD_STRING(descriptor, item->descriptor_idx_, "inter_type_id_item descriptor_idx")
+
+ // Check that the descriptor is a valid type.
+ if (UNLIKELY(!IsValidDescriptor(descriptor))) {
+ ErrorStringPrintf("Invalid type descriptor: '%s'", descriptor);
+ return false;
+ }
+
+ // Check ordering between items.
+ if (previous_item_ != nullptr) {
+ const DexFile::TypeId* prev_item = reinterpret_cast<const DexFile::TypeId*>(previous_item_);
+ if (UNLIKELY(prev_item->descriptor_idx_ >= item->descriptor_idx_)) {
+ ErrorStringPrintf("Out-of-order type_ids: %x then %x",
+ prev_item->descriptor_idx_.index_,
+ item->descriptor_idx_.index_);
+ return false;
+ }
+ }
+
+ ptr_ += sizeof(DexFile::TypeId);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterProtoIdItem() {
+ const DexFile::ProtoId* item = reinterpret_cast<const DexFile::ProtoId*>(ptr_);
+
+ LOAD_STRING(shorty, item->shorty_idx_, "inter_proto_id_item shorty_idx")
+
+ if (item->parameters_off_ != 0 &&
+ !CheckOffsetToTypeMap(item->parameters_off_, DexFile::kDexTypeTypeList)) {
+ return false;
+ }
+
+ // Check that return type is representable as a uint16_t;
+ if (UNLIKELY(!IsValidOrNoTypeId(item->return_type_idx_.index_, item->pad_))) {
+ ErrorStringPrintf("proto with return type idx outside uint16_t range '%x:%x'",
+ item->pad_, item->return_type_idx_.index_);
+ return false;
+ }
+ // Check the return type and advance the shorty.
+ LOAD_STRING_BY_TYPE(return_type, item->return_type_idx_, "inter_proto_id_item return_type_idx")
+ if (!CheckShortyDescriptorMatch(*shorty, return_type, true)) {
+ return false;
+ }
+ shorty++;
+
+ DexFileParameterIterator it(*dex_file_, *item);
+ while (it.HasNext() && *shorty != '\0') {
+ if (!CheckIndex(it.GetTypeIdx().index_,
+ dex_file_->NumTypeIds(),
+ "inter_proto_id_item shorty type_idx")) {
+ return false;
+ }
+ const char* descriptor = it.GetDescriptor();
+ if (!CheckShortyDescriptorMatch(*shorty, descriptor, false)) {
+ return false;
+ }
+ it.Next();
+ shorty++;
+ }
+ if (UNLIKELY(it.HasNext() || *shorty != '\0')) {
+ ErrorStringPrintf("Mismatched length for parameters and shorty");
+ return false;
+ }
+
+ // Check ordering between items. This relies on type_ids being in order.
+ if (previous_item_ != nullptr) {
+ const DexFile::ProtoId* prev = reinterpret_cast<const DexFile::ProtoId*>(previous_item_);
+ if (UNLIKELY(prev->return_type_idx_ > item->return_type_idx_)) {
+ ErrorStringPrintf("Out-of-order proto_id return types");
+ return false;
+ } else if (prev->return_type_idx_ == item->return_type_idx_) {
+ DexFileParameterIterator curr_it(*dex_file_, *item);
+ DexFileParameterIterator prev_it(*dex_file_, *prev);
+
+ while (curr_it.HasNext() && prev_it.HasNext()) {
+ dex::TypeIndex prev_idx = prev_it.GetTypeIdx();
+ dex::TypeIndex curr_idx = curr_it.GetTypeIdx();
+ DCHECK_NE(prev_idx, dex::TypeIndex(DexFile::kDexNoIndex16));
+ DCHECK_NE(curr_idx, dex::TypeIndex(DexFile::kDexNoIndex16));
+
+ if (prev_idx < curr_idx) {
+ break;
+ } else if (UNLIKELY(prev_idx > curr_idx)) {
+ ErrorStringPrintf("Out-of-order proto_id arguments");
+ return false;
+ }
+
+ prev_it.Next();
+ curr_it.Next();
+ }
+ if (!curr_it.HasNext()) {
+ // Either a duplicate ProtoId or a ProtoId with a shorter argument list follows
+ // a ProtoId with a longer one. Both cases are forbidden by the specification.
+ ErrorStringPrintf("Out-of-order proto_id arguments");
+ return false;
+ }
+ }
+ }
+
+ ptr_ += sizeof(DexFile::ProtoId);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterFieldIdItem() {
+ const DexFile::FieldId* item = reinterpret_cast<const DexFile::FieldId*>(ptr_);
+
+ // Check that the class descriptor is valid.
+ LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_field_id_item class_idx")
+ if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor);
+ return false;
+ }
+
+ // Check that the type descriptor is a valid field name.
+ LOAD_STRING_BY_TYPE(type_descriptor, item->type_idx_, "inter_field_id_item type_idx")
+ if (UNLIKELY(!IsValidDescriptor(type_descriptor) || type_descriptor[0] == 'V')) {
+ ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", type_descriptor);
+ return false;
+ }
+
+ // Check that the name is valid.
+ LOAD_STRING(descriptor, item->name_idx_, "inter_field_id_item name_idx")
+ if (UNLIKELY(!IsValidMemberName(descriptor))) {
+ ErrorStringPrintf("Invalid field name: '%s'", descriptor);
+ return false;
+ }
+
+ // Check ordering between items. This relies on the other sections being in order.
+ if (previous_item_ != nullptr) {
+ const DexFile::FieldId* prev_item = reinterpret_cast<const DexFile::FieldId*>(previous_item_);
+ if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
+ return false;
+ } else if (prev_item->class_idx_ == item->class_idx_) {
+ if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
+ return false;
+ } else if (prev_item->name_idx_ == item->name_idx_) {
+ if (UNLIKELY(prev_item->type_idx_ >= item->type_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
+ return false;
+ }
+ }
+ }
+ }
+
+ ptr_ += sizeof(DexFile::FieldId);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterMethodIdItem() {
+ const DexFile::MethodId* item = reinterpret_cast<const DexFile::MethodId*>(ptr_);
+
+ // Check that the class descriptor is a valid reference name.
+ LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_method_id_item class_idx")
+ if (UNLIKELY(!IsValidDescriptor(class_descriptor) || (class_descriptor[0] != 'L' &&
+ class_descriptor[0] != '['))) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", class_descriptor);
+ return false;
+ }
+
+ // Check that the name is valid.
+ LOAD_STRING(descriptor, item->name_idx_, "inter_method_id_item name_idx")
+ if (UNLIKELY(!IsValidMemberName(descriptor))) {
+ ErrorStringPrintf("Invalid method name: '%s'", descriptor);
+ return false;
+ }
+
+ // Check that the proto id is valid.
+ if (UNLIKELY(!CheckIndex(item->proto_idx_, dex_file_->NumProtoIds(),
+ "inter_method_id_item proto_idx"))) {
+ return false;
+ }
+
+ // Check ordering between items. This relies on the other sections being in order.
+ if (previous_item_ != nullptr) {
+ const DexFile::MethodId* prev_item = reinterpret_cast<const DexFile::MethodId*>(previous_item_);
+ if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
+ return false;
+ } else if (prev_item->class_idx_ == item->class_idx_) {
+ if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
+ return false;
+ } else if (prev_item->name_idx_ == item->name_idx_) {
+ if (UNLIKELY(prev_item->proto_idx_ >= item->proto_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
+ return false;
+ }
+ }
+ }
+ }
+
+ ptr_ += sizeof(DexFile::MethodId);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterClassDefItem() {
+ const DexFile::ClassDef* item = reinterpret_cast<const DexFile::ClassDef*>(ptr_);
+
+ // Check that class_idx_ is representable as a uint16_t;
+ if (UNLIKELY(!IsValidTypeId(item->class_idx_.index_, item->pad1_))) {
+ ErrorStringPrintf("class with type idx outside uint16_t range '%x:%x'", item->pad1_,
+ item->class_idx_.index_);
+ return false;
+ }
+ // Check that superclass_idx_ is representable as a uint16_t;
+ if (UNLIKELY(!IsValidOrNoTypeId(item->superclass_idx_.index_, item->pad2_))) {
+ ErrorStringPrintf("class with superclass type idx outside uint16_t range '%x:%x'", item->pad2_,
+ item->superclass_idx_.index_);
+ return false;
+ }
+ // Check for duplicate class def.
+ if (defined_classes_.find(item->class_idx_) != defined_classes_.end()) {
+ ErrorStringPrintf("Redefinition of class with type idx: '%d'", item->class_idx_.index_);
+ return false;
+ }
+ defined_classes_.insert(item->class_idx_);
+
+ LOAD_STRING_BY_TYPE(class_descriptor, item->class_idx_, "inter_class_def_item class_idx")
+ if (UNLIKELY(!IsValidDescriptor(class_descriptor) || class_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid class descriptor: '%s'", class_descriptor);
+ return false;
+ }
+
+ // Only allow non-runtime modifiers.
+ if ((item->access_flags_ & ~kAccJavaFlagsMask) != 0) {
+ ErrorStringPrintf("Invalid class flags: '%d'", item->access_flags_);
+ return false;
+ }
+
+ if (item->interfaces_off_ != 0 &&
+ !CheckOffsetToTypeMap(item->interfaces_off_, DexFile::kDexTypeTypeList)) {
+ return false;
+ }
+ if (item->annotations_off_ != 0 &&
+ !CheckOffsetToTypeMap(item->annotations_off_, DexFile::kDexTypeAnnotationsDirectoryItem)) {
+ return false;
+ }
+ if (item->class_data_off_ != 0 &&
+ !CheckOffsetToTypeMap(item->class_data_off_, DexFile::kDexTypeClassDataItem)) {
+ return false;
+ }
+ if (item->static_values_off_ != 0 &&
+ !CheckOffsetToTypeMap(item->static_values_off_, DexFile::kDexTypeEncodedArrayItem)) {
+ return false;
+ }
+
+ if (item->superclass_idx_.IsValid()) {
+ if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) {
+ // Check that a class does not inherit from itself directly (by having
+ // the same type idx as its super class).
+ if (UNLIKELY(item->superclass_idx_ == item->class_idx_)) {
+ ErrorStringPrintf("Class with same type idx as its superclass: '%d'",
+ item->class_idx_.index_);
+ return false;
+ }
+
+ // Check that a class is defined after its super class (if the
+ // latter is defined in the same Dex file).
+ const DexFile::ClassDef* superclass_def = dex_file_->FindClassDef(item->superclass_idx_);
+ if (superclass_def != nullptr) {
+ // The superclass is defined in this Dex file.
+ if (superclass_def > item) {
+ // ClassDef item for super class appearing after the class' ClassDef item.
+ ErrorStringPrintf("Invalid class definition ordering:"
+ " class with type idx: '%d' defined before"
+ " superclass with type idx: '%d'",
+ item->class_idx_.index_,
+ item->superclass_idx_.index_);
+ return false;
+ }
+ }
+ }
+
+ LOAD_STRING_BY_TYPE(superclass_descriptor, item->superclass_idx_,
+ "inter_class_def_item superclass_idx")
+ if (UNLIKELY(!IsValidDescriptor(superclass_descriptor) || superclass_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid superclass: '%s'", superclass_descriptor);
+ return false;
+ }
+ }
+
+ // Check interfaces.
+ const DexFile::TypeList* interfaces = dex_file_->GetInterfacesList(*item);
+ if (interfaces != nullptr) {
+ uint32_t size = interfaces->Size();
+ for (uint32_t i = 0; i < size; i++) {
+ if (header_->GetVersion() >= DexFile::kClassDefinitionOrderEnforcedVersion) {
+ // Check that a class does not implement itself directly (by having the
+ // same type idx as one of its immediate implemented interfaces).
+ if (UNLIKELY(interfaces->GetTypeItem(i).type_idx_ == item->class_idx_)) {
+ ErrorStringPrintf("Class with same type idx as implemented interface: '%d'",
+ item->class_idx_.index_);
+ return false;
+ }
+
+ // Check that a class is defined after the interfaces it implements
+ // (if they are defined in the same Dex file).
+ const DexFile::ClassDef* interface_def =
+ dex_file_->FindClassDef(interfaces->GetTypeItem(i).type_idx_);
+ if (interface_def != nullptr) {
+ // The interface is defined in this Dex file.
+ if (interface_def > item) {
+ // ClassDef item for interface appearing after the class' ClassDef item.
+ ErrorStringPrintf("Invalid class definition ordering:"
+ " class with type idx: '%d' defined before"
+ " implemented interface with type idx: '%d'",
+ item->class_idx_.index_,
+ interfaces->GetTypeItem(i).type_idx_.index_);
+ return false;
+ }
+ }
+ }
+
+ // Ensure that the interface refers to a class (not an array nor a primitive type).
+ LOAD_STRING_BY_TYPE(inf_descriptor, interfaces->GetTypeItem(i).type_idx_,
+ "inter_class_def_item interface type_idx")
+ if (UNLIKELY(!IsValidDescriptor(inf_descriptor) || inf_descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid interface: '%s'", inf_descriptor);
+ return false;
+ }
+ }
+
+ /*
+ * Ensure that there are no duplicates. This is an O(N^2) test, but in
+ * practice the number of interfaces implemented by any given class is low.
+ */
+ for (uint32_t i = 1; i < size; i++) {
+ dex::TypeIndex idx1 = interfaces->GetTypeItem(i).type_idx_;
+ for (uint32_t j =0; j < i; j++) {
+ dex::TypeIndex idx2 = interfaces->GetTypeItem(j).type_idx_;
+ if (UNLIKELY(idx1 == idx2)) {
+ ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1));
+ return false;
+ }
+ }
+ }
+ }
+
+ // Check that references in class_data_item are to the right class.
+ if (item->class_data_off_ != 0) {
+ const uint8_t* data = begin_ + item->class_data_off_;
+ bool success;
+ dex::TypeIndex data_definer = FindFirstClassDataDefiner(data, &success);
+ if (!success) {
+ return false;
+ }
+ if (UNLIKELY((data_definer != item->class_idx_) &&
+ (data_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) {
+ ErrorStringPrintf("Invalid class_data_item");
+ return false;
+ }
+ }
+
+ // Check that references in annotations_directory_item are to right class.
+ if (item->annotations_off_ != 0) {
+ // annotations_off_ is supposed to be aligned by 4.
+ if (!IsAlignedParam(item->annotations_off_, 4)) {
+ ErrorStringPrintf("Invalid annotations_off_, not aligned by 4");
+ return false;
+ }
+ const uint8_t* data = begin_ + item->annotations_off_;
+ bool success;
+ dex::TypeIndex annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success);
+ if (!success) {
+ return false;
+ }
+ if (UNLIKELY((annotations_definer != item->class_idx_) &&
+ (annotations_definer != dex::TypeIndex(DexFile::kDexNoIndex16)))) {
+ ErrorStringPrintf("Invalid annotations_directory_item");
+ return false;
+ }
+ }
+
+ ptr_ += sizeof(DexFile::ClassDef);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterCallSiteIdItem() {
+ const DexFile::CallSiteIdItem* item = reinterpret_cast<const DexFile::CallSiteIdItem*>(ptr_);
+
+ // Check call site referenced by item is in encoded array section.
+ if (!CheckOffsetToTypeMap(item->data_off_, DexFile::kDexTypeEncodedArrayItem)) {
+ ErrorStringPrintf("Invalid offset in CallSideIdItem");
+ return false;
+ }
+
+ CallSiteArrayValueIterator it(*dex_file_, *item);
+
+ // Check Method Handle
+ if (!it.HasNext() || it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) {
+ ErrorStringPrintf("CallSiteArray missing method handle");
+ return false;
+ }
+
+ uint32_t handle_index = static_cast<uint32_t>(it.GetJavaValue().i);
+ if (handle_index >= dex_file_->NumMethodHandles()) {
+ ErrorStringPrintf("CallSite has bad method handle id: %x", handle_index);
+ return false;
+ }
+
+ // Check target method name.
+ it.Next();
+ if (!it.HasNext() ||
+ it.GetValueType() != EncodedArrayValueIterator::ValueType::kString) {
+ ErrorStringPrintf("CallSiteArray missing target method name");
+ return false;
+ }
+
+ uint32_t name_index = static_cast<uint32_t>(it.GetJavaValue().i);
+ if (name_index >= dex_file_->NumStringIds()) {
+ ErrorStringPrintf("CallSite has bad method name id: %x", name_index);
+ return false;
+ }
+
+ // Check method type.
+ it.Next();
+ if (!it.HasNext() ||
+ it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodType) {
+ ErrorStringPrintf("CallSiteArray missing method type");
+ return false;
+ }
+
+ uint32_t proto_index = static_cast<uint32_t>(it.GetJavaValue().i);
+ if (proto_index >= dex_file_->NumProtoIds()) {
+ ErrorStringPrintf("CallSite has bad method type: %x", proto_index);
+ return false;
+ }
+
+ ptr_ += sizeof(DexFile::CallSiteIdItem);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterMethodHandleItem() {
+ const DexFile::MethodHandleItem* item = reinterpret_cast<const DexFile::MethodHandleItem*>(ptr_);
+
+ DexFile::MethodHandleType method_handle_type =
+ static_cast<DexFile::MethodHandleType>(item->method_handle_type_);
+ if (method_handle_type > DexFile::MethodHandleType::kLast) {
+ ErrorStringPrintf("Bad method handle type %x", item->method_handle_type_);
+ return false;
+ }
+
+ uint32_t index = item->field_or_method_idx_;
+ switch (method_handle_type) {
+ case DexFile::MethodHandleType::kStaticPut:
+ case DexFile::MethodHandleType::kStaticGet:
+ case DexFile::MethodHandleType::kInstancePut:
+ case DexFile::MethodHandleType::kInstanceGet: {
+ LOAD_FIELD(field, index, "method_handle_item field_idx", return false);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic:
+ case DexFile::MethodHandleType::kInvokeInstance:
+ case DexFile::MethodHandleType::kInvokeConstructor:
+ case DexFile::MethodHandleType::kInvokeDirect:
+ case DexFile::MethodHandleType::kInvokeInterface: {
+ LOAD_METHOD(method, index, "method_handle_item method_idx", return false);
+ break;
+ }
+ }
+
+ ptr_ += sizeof(DexFile::MethodHandleItem);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterAnnotationSetRefList() {
+ const DexFile::AnnotationSetRefList* list =
+ reinterpret_cast<const DexFile::AnnotationSetRefList*>(ptr_);
+ const DexFile::AnnotationSetRefItem* item = list->list_;
+ uint32_t count = list->size_;
+
+ while (count--) {
+ if (item->annotations_off_ != 0 &&
+ !CheckOffsetToTypeMap(item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
+ return false;
+ }
+ item++;
+ }
+
+ ptr_ = reinterpret_cast<const uint8_t*>(item);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterAnnotationSetItem() {
+ const DexFile::AnnotationSetItem* set = reinterpret_cast<const DexFile::AnnotationSetItem*>(ptr_);
+ const uint32_t* offsets = set->entries_;
+ uint32_t count = set->size_;
+ uint32_t last_idx = 0;
+
+ for (uint32_t i = 0; i < count; i++) {
+ if (*offsets != 0 && !CheckOffsetToTypeMap(*offsets, DexFile::kDexTypeAnnotationItem)) {
+ return false;
+ }
+
+ // Get the annotation from the offset and the type index for the annotation.
+ const DexFile::AnnotationItem* annotation =
+ reinterpret_cast<const DexFile::AnnotationItem*>(begin_ + *offsets);
+ const uint8_t* data = annotation->annotation_;
+ DECODE_UNSIGNED_CHECKED_FROM(data, idx);
+
+ if (UNLIKELY(last_idx >= idx && i != 0)) {
+ ErrorStringPrintf("Out-of-order entry types: %x then %x", last_idx, idx);
+ return false;
+ }
+
+ last_idx = idx;
+ offsets++;
+ }
+
+ ptr_ = reinterpret_cast<const uint8_t*>(offsets);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterClassDataItem() {
+ ClassDataItemIterator it(*dex_file_, ptr_);
+ bool success;
+ dex::TypeIndex defining_class = FindFirstClassDataDefiner(ptr_, &success);
+ if (!success) {
+ return false;
+ }
+
+ for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) {
+ LOAD_FIELD(field, it.GetMemberIndex(), "inter_class_data_item field_id", return false)
+ if (UNLIKELY(field->class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for class_data_item field");
+ return false;
+ }
+ }
+ for (; it.HasNextMethod(); it.Next()) {
+ uint32_t code_off = it.GetMethodCodeItemOffset();
+ if (code_off != 0 && !CheckOffsetToTypeMap(code_off, DexFile::kDexTypeCodeItem)) {
+ return false;
+ }
+ LOAD_METHOD(method, it.GetMemberIndex(), "inter_class_data_item method_id", return false)
+ if (UNLIKELY(method->class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for class_data_item method");
+ return false;
+ }
+ }
+
+ ptr_ = it.EndDataPointer();
+ return true;
+}
+
+bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() {
+ const DexFile::AnnotationsDirectoryItem* item =
+ reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_);
+ bool success;
+ dex::TypeIndex defining_class = FindFirstAnnotationsDirectoryDefiner(ptr_, &success);
+ if (!success) {
+ return false;
+ }
+
+ if (item->class_annotations_off_ != 0 &&
+ !CheckOffsetToTypeMap(item->class_annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
+ return false;
+ }
+
+ // Field annotations follow immediately after the annotations directory.
+ const DexFile::FieldAnnotationsItem* field_item =
+ reinterpret_cast<const DexFile::FieldAnnotationsItem*>(item + 1);
+ uint32_t field_count = item->fields_size_;
+ for (uint32_t i = 0; i < field_count; i++) {
+ LOAD_FIELD(field, field_item->field_idx_, "inter_annotations_directory_item field_id",
+ return false)
+ if (UNLIKELY(field->class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for field_annotation");
+ return false;
+ }
+ if (!CheckOffsetToTypeMap(field_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
+ return false;
+ }
+ field_item++;
+ }
+
+ // Method annotations follow immediately after field annotations.
+ const DexFile::MethodAnnotationsItem* method_item =
+ reinterpret_cast<const DexFile::MethodAnnotationsItem*>(field_item);
+ uint32_t method_count = item->methods_size_;
+ for (uint32_t i = 0; i < method_count; i++) {
+ LOAD_METHOD(method, method_item->method_idx_, "inter_annotations_directory_item method_id",
+ return false)
+ if (UNLIKELY(method->class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for method_annotation");
+ return false;
+ }
+ if (!CheckOffsetToTypeMap(method_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
+ return false;
+ }
+ method_item++;
+ }
+
+ // Parameter annotations follow immediately after method annotations.
+ const DexFile::ParameterAnnotationsItem* parameter_item =
+ reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item);
+ uint32_t parameter_count = item->parameters_size_;
+ for (uint32_t i = 0; i < parameter_count; i++) {
+ LOAD_METHOD(parameter_method, parameter_item->method_idx_,
+ "inter_annotations_directory_item parameter method_id", return false)
+ if (UNLIKELY(parameter_method->class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for parameter_annotation");
+ return false;
+ }
+ if (!CheckOffsetToTypeMap(parameter_item->annotations_off_,
+ DexFile::kDexTypeAnnotationSetRefList)) {
+ return false;
+ }
+ parameter_item++;
+ }
+
+ ptr_ = reinterpret_cast<const uint8_t*>(parameter_item);
+ return true;
+}
+
+bool DexFileVerifier::CheckInterSectionIterate(size_t offset,
+ uint32_t count,
+ DexFile::MapItemType type) {
+ // Get the right alignment mask for the type of section.
+ size_t alignment_mask;
+ switch (type) {
+ case DexFile::kDexTypeClassDataItem:
+ alignment_mask = sizeof(uint8_t) - 1;
+ break;
+ default:
+ alignment_mask = sizeof(uint32_t) - 1;
+ break;
+ }
+
+ // Iterate through the items in the section.
+ previous_item_ = nullptr;
+ for (uint32_t i = 0; i < count; i++) {
+ uint32_t new_offset = (offset + alignment_mask) & ~alignment_mask;
+ ptr_ = begin_ + new_offset;
+ const uint8_t* prev_ptr = ptr_;
+
+ if (MapTypeToBitMask(type) == 0) {
+ ErrorStringPrintf("Unknown map item type %x", type);
+ return false;
+ }
+
+ // Check depending on the section type.
+ switch (type) {
+ case DexFile::kDexTypeHeaderItem:
+ case DexFile::kDexTypeMapList:
+ case DexFile::kDexTypeTypeList:
+ case DexFile::kDexTypeCodeItem:
+ case DexFile::kDexTypeStringDataItem:
+ case DexFile::kDexTypeDebugInfoItem:
+ case DexFile::kDexTypeAnnotationItem:
+ case DexFile::kDexTypeEncodedArrayItem:
+ break;
+ case DexFile::kDexTypeStringIdItem: {
+ if (!CheckInterStringIdItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeTypeIdItem: {
+ if (!CheckInterTypeIdItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeProtoIdItem: {
+ if (!CheckInterProtoIdItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeFieldIdItem: {
+ if (!CheckInterFieldIdItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeMethodIdItem: {
+ if (!CheckInterMethodIdItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeClassDefItem: {
+ // There shouldn't be more class definitions than type ids allow.
+ // This check should be redundant, since there are checks that the
+ // class_idx_ is within range and that there is only one definition
+ // for a given type id.
+ if (i > kTypeIdLimit) {
+ ErrorStringPrintf("Too many class definition items");
+ return false;
+ }
+ if (!CheckInterClassDefItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeCallSiteIdItem: {
+ if (!CheckInterCallSiteIdItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeMethodHandleItem: {
+ if (!CheckInterMethodHandleItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeAnnotationSetRefList: {
+ if (!CheckInterAnnotationSetRefList()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeAnnotationSetItem: {
+ if (!CheckInterAnnotationSetItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeClassDataItem: {
+ // There shouldn't be more class data than type ids allow.
+ // This check should be redundant, since there are checks that the
+ // class_idx_ is within range and that there is only one definition
+ // for a given type id.
+ if (i > kTypeIdLimit) {
+ ErrorStringPrintf("Too many class data items");
+ return false;
+ }
+ if (!CheckInterClassDataItem()) {
+ return false;
+ }
+ break;
+ }
+ case DexFile::kDexTypeAnnotationsDirectoryItem: {
+ if (!CheckInterAnnotationsDirectoryItem()) {
+ return false;
+ }
+ break;
+ }
+ }
+
+ previous_item_ = prev_ptr;
+ offset = ptr_ - begin_;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckInterSection() {
+ const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
+ const DexFile::MapItem* item = map->list_;
+ uint32_t count = map->size_;
+
+ // Cross check the items listed in the map.
+ while (count--) {
+ uint32_t section_offset = item->offset_;
+ uint32_t section_count = item->size_;
+ DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_);
+ bool found = false;
+
+ switch (type) {
+ case DexFile::kDexTypeHeaderItem:
+ case DexFile::kDexTypeMapList:
+ case DexFile::kDexTypeTypeList:
+ case DexFile::kDexTypeCodeItem:
+ case DexFile::kDexTypeStringDataItem:
+ case DexFile::kDexTypeDebugInfoItem:
+ case DexFile::kDexTypeAnnotationItem:
+ case DexFile::kDexTypeEncodedArrayItem:
+ found = true;
+ break;
+ case DexFile::kDexTypeStringIdItem:
+ case DexFile::kDexTypeTypeIdItem:
+ case DexFile::kDexTypeProtoIdItem:
+ case DexFile::kDexTypeFieldIdItem:
+ case DexFile::kDexTypeMethodIdItem:
+ case DexFile::kDexTypeClassDefItem:
+ case DexFile::kDexTypeCallSiteIdItem:
+ case DexFile::kDexTypeMethodHandleItem:
+ case DexFile::kDexTypeAnnotationSetRefList:
+ case DexFile::kDexTypeAnnotationSetItem:
+ case DexFile::kDexTypeClassDataItem:
+ case DexFile::kDexTypeAnnotationsDirectoryItem: {
+ if (!CheckInterSectionIterate(section_offset, section_count, type)) {
+ return false;
+ }
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ErrorStringPrintf("Unknown map item type %x", item->type_);
+ return false;
+ }
+
+ item++;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::Verify() {
+ // Check the header.
+ if (!CheckHeader()) {
+ return false;
+ }
+
+ // Check the map section.
+ if (!CheckMap()) {
+ return false;
+ }
+
+ // Check structure within remaining sections.
+ if (!CheckIntraSection()) {
+ return false;
+ }
+
+ // Check references from one section to another.
+ if (!CheckInterSection()) {
+ return false;
+ }
+
+ return true;
+}
+
+void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ DCHECK(failure_reason_.empty()) << failure_reason_;
+ failure_reason_ = StringPrintf("Failure to verify dex file '%s': ", location_);
+ StringAppendV(&failure_reason_, fmt, ap);
+ va_end(ap);
+}
+
+// Fields and methods may have only one of public/protected/private.
+static bool CheckAtMostOneOfPublicProtectedPrivate(uint32_t flags) {
+ size_t count = (((flags & kAccPublic) == 0) ? 0 : 1) +
+ (((flags & kAccProtected) == 0) ? 0 : 1) +
+ (((flags & kAccPrivate) == 0) ? 0 : 1);
+ return count <= 1;
+}
+
+// Helper functions to retrieve names from the dex file. We do not want to rely on DexFile
+// functionality, as we're still verifying the dex file. begin and header correspond to the
+// underscored variants in the DexFileVerifier.
+
+static std::string GetStringOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ dex::StringIndex string_idx) {
+ // The `string_idx` is not guaranteed to be valid yet.
+ if (header->string_ids_size_ <= string_idx.index_) {
+ return "(error)";
+ }
+
+ const DexFile::StringId* string_id =
+ reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_)
+ + string_idx.index_;
+
+ // Assume that the data is OK at this point. String data has been checked at this point.
+
+ const uint8_t* ptr = begin + string_id->string_data_off_;
+ uint32_t dummy;
+ if (!DecodeUnsignedLeb128Checked(&ptr, begin + header->file_size_, &dummy)) {
+ return "(error)";
+ }
+ return reinterpret_cast<const char*>(ptr);
+}
+
+static std::string GetClassOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ dex::TypeIndex class_idx) {
+ // The `class_idx` is either `FieldId::class_idx_` or `MethodId::class_idx_` and
+ // it has already been checked in `DexFileVerifier::CheckClassDataItemField()`
+ // or `DexFileVerifier::CheckClassDataItemMethod()`, respectively, to match
+ // a valid defining class.
+ CHECK_LT(class_idx.index_, header->type_ids_size_);
+
+ const DexFile::TypeId* type_id =
+ reinterpret_cast<const DexFile::TypeId*>(begin + header->type_ids_off_) + class_idx.index_;
+
+ // Assume that the data is OK at this point. Type id offsets have been checked at this point.
+
+ return GetStringOrError(begin, header, type_id->descriptor_idx_);
+}
+
+static std::string GetFieldDescriptionOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ uint32_t idx) {
+ // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemField()`.
+ CHECK_LT(idx, header->field_ids_size_);
+
+ const DexFile::FieldId* field_id =
+ reinterpret_cast<const DexFile::FieldId*>(begin + header->field_ids_off_) + idx;
+
+ // Assume that the data is OK at this point. Field id offsets have been checked at this point.
+
+ std::string class_name = GetClassOrError(begin, header, field_id->class_idx_);
+ std::string field_name = GetStringOrError(begin, header, field_id->name_idx_);
+
+ return class_name + "." + field_name;
+}
+
+static std::string GetMethodDescriptionOrError(const uint8_t* const begin,
+ const DexFile::Header* const header,
+ uint32_t idx) {
+ // The `idx` has already been checked in `DexFileVerifier::CheckClassDataItemMethod()`.
+ CHECK_LT(idx, header->method_ids_size_);
+
+ const DexFile::MethodId* method_id =
+ reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) + idx;
+
+ // Assume that the data is OK at this point. Method id offsets have been checked at this point.
+
+ std::string class_name = GetClassOrError(begin, header, method_id->class_idx_);
+ std::string method_name = GetStringOrError(begin, header, method_id->name_idx_);
+
+ return class_name + "." + method_name;
+}
+
+bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx,
+ uint32_t field_access_flags,
+ uint32_t class_access_flags,
+ std::string* error_msg) {
+ // Generally sort out >16-bit flags.
+ if ((field_access_flags & ~kAccJavaFlagsMask) != 0) {
+ *error_msg = StringPrintf("Bad field access_flags for %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
+ return false;
+ }
+
+ // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+ constexpr uint32_t kFieldAccessFlags = kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccVolatile |
+ kAccTransient |
+ kAccSynthetic |
+ kAccEnum;
+
+ // Fields may have only one of public/protected/final.
+ if (!CheckAtMostOneOfPublicProtectedPrivate(field_access_flags)) {
+ *error_msg = StringPrintf("Field may have only one of public/protected/private, %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
+ return false;
+ }
+
+ // Interfaces have a pretty restricted list.
+ if ((class_access_flags & kAccInterface) != 0) {
+ // Interface fields must be public final static.
+ constexpr uint32_t kPublicFinalStatic = kAccPublic | kAccFinal | kAccStatic;
+ if ((field_access_flags & kPublicFinalStatic) != kPublicFinalStatic) {
+ *error_msg = StringPrintf("Interface field is not public final static, %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
+ if (dex_file_->SupportsDefaultMethods()) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
+ }
+ // Interface fields may be synthetic, but may not have other flags.
+ constexpr uint32_t kDisallowed = ~(kPublicFinalStatic | kAccSynthetic);
+ if ((field_access_flags & kFieldAccessFlags & kDisallowed) != 0) {
+ *error_msg = StringPrintf("Interface field has disallowed flag, %s: %x(%s)",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
+ field_access_flags,
+ PrettyJavaAccessFlags(field_access_flags).c_str());
+ if (dex_file_->SupportsDefaultMethods()) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
+ }
+ return true;
+ }
+
+ // Volatile fields may not be final.
+ constexpr uint32_t kVolatileFinal = kAccVolatile | kAccFinal;
+ if ((field_access_flags & kVolatileFinal) == kVolatileFinal) {
+ *error_msg = StringPrintf("Fields may not be volatile and final: %s",
+ GetFieldDescriptionOrError(begin_, header_, idx).c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
+ uint32_t method_access_flags,
+ uint32_t class_access_flags,
+ uint32_t constructor_flags_by_name,
+ bool has_code,
+ bool expect_direct,
+ std::string* error_msg) {
+ // Generally sort out >16-bit flags, except dex knows Constructor and DeclaredSynchronized.
+ constexpr uint32_t kAllMethodFlags =
+ kAccJavaFlagsMask | kAccConstructor | kAccDeclaredSynchronized;
+ if ((method_access_flags & ~kAllMethodFlags) != 0) {
+ *error_msg = StringPrintf("Bad method access_flags for %s: %x",
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
+ method_access_flags);
+ return false;
+ }
+
+ // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+ constexpr uint32_t kMethodAccessFlags = kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccSynthetic |
+ kAccSynchronized |
+ kAccBridge |
+ kAccVarargs |
+ kAccNative |
+ kAccAbstract |
+ kAccStrict;
+
+ // Methods may have only one of public/protected/final.
+ if (!CheckAtMostOneOfPublicProtectedPrivate(method_access_flags)) {
+ *error_msg = StringPrintf("Method may have only one of public/protected/private, %s: %x",
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
+ method_access_flags);
+ return false;
+ }
+
+ constexpr uint32_t kConstructorFlags = kAccStatic | kAccConstructor;
+ const bool is_constructor_by_name = (constructor_flags_by_name & kConstructorFlags) != 0;
+ const bool is_clinit_by_name = constructor_flags_by_name == kConstructorFlags;
+
+ // Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce
+ // the reverse for backwards compatibility reasons.
+ if (((method_access_flags & kAccConstructor) != 0) && !is_constructor_by_name) {
+ *error_msg =
+ StringPrintf("Method %" PRIu32 "(%s) is marked constructor, but doesn't match name",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ return false;
+ }
+
+ if (is_constructor_by_name) {
+ // Check that the static constructor (= static initializer) is named "<clinit>" and that the
+ // instance constructor is called "<init>".
+ bool is_static = (method_access_flags & kAccStatic) != 0;
+ if (is_static ^ is_clinit_by_name) {
+ *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ if (dex_file_->SupportsDefaultMethods()) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
+ }
+ }
+
+ // Check that static and private methods, as well as constructors, are in the direct methods list,
+ // and other methods in the virtual methods list.
+ bool is_direct = ((method_access_flags & (kAccStatic | kAccPrivate)) != 0) ||
+ is_constructor_by_name;
+ if (is_direct != expect_direct) {
+ *error_msg = StringPrintf("Direct/virtual method %" PRIu32 "(%s) not in expected list %d",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
+ expect_direct);
+ return false;
+ }
+
+ // From here on out it is easier to mask out the bits we're supposed to ignore.
+ method_access_flags &= kMethodAccessFlags;
+
+ // Interfaces are special.
+ if ((class_access_flags & kAccInterface) != 0) {
+ // Non-static interface methods must be public or private.
+ uint32_t desired_flags = (kAccPublic | kAccStatic);
+ if (dex_file_->SupportsDefaultMethods()) {
+ desired_flags |= kAccPrivate;
+ }
+ if ((method_access_flags & desired_flags) == 0) {
+ *error_msg = StringPrintf("Interface virtual method %" PRIu32 "(%s) is not public",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ if (dex_file_->SupportsDefaultMethods()) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
+ }
+ }
+
+ // If there aren't any instructions, make sure that's expected.
+ if (!has_code) {
+ // Only native or abstract methods may not have code.
+ if ((method_access_flags & (kAccNative | kAccAbstract)) == 0) {
+ *error_msg = StringPrintf("Method %" PRIu32 "(%s) has no code, but is not marked native or "
+ "abstract",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ return false;
+ }
+ // Constructors must always have code.
+ if (is_constructor_by_name) {
+ *error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ if (dex_file_->SupportsDefaultMethods()) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
+ }
+ if ((method_access_flags & kAccAbstract) != 0) {
+ // Abstract methods are not allowed to have the following flags.
+ constexpr uint32_t kForbidden =
+ kAccPrivate | kAccStatic | kAccFinal | kAccNative | kAccStrict | kAccSynchronized;
+ if ((method_access_flags & kForbidden) != 0) {
+ *error_msg = StringPrintf("Abstract method %" PRIu32 "(%s) has disallowed access flags %x",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
+ method_access_flags);
+ return false;
+ }
+ // Abstract methods should be in an abstract class or interface.
+ if ((class_access_flags & (kAccInterface | kAccAbstract)) == 0) {
+ LOG(WARNING) << "Method " << GetMethodDescriptionOrError(begin_, header_, method_index)
+ << " is abstract, but the declaring class is neither abstract nor an "
+ << "interface in dex file "
+ << dex_file_->GetLocation();
+ }
+ }
+ // Interfaces are special.
+ if ((class_access_flags & kAccInterface) != 0) {
+ // Interface methods without code must be abstract.
+ if ((method_access_flags & (kAccPublic | kAccAbstract)) != (kAccPublic | kAccAbstract)) {
+ *error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ if (dex_file_->SupportsDefaultMethods()) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
+ }
+ // At this point, we know the method is public and abstract. This means that all the checks
+ // for invalid combinations above applies. In addition, interface methods must not be
+ // protected. This is caught by the check for only-one-of-public-protected-private.
+ }
+ return true;
+ }
+
+ // When there's code, the method must not be native or abstract.
+ if ((method_access_flags & (kAccNative | kAccAbstract)) != 0) {
+ *error_msg = StringPrintf("Method %" PRIu32 "(%s) has code, but is marked native or abstract",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ return false;
+ }
+
+ // Instance constructors must not be synchronized and a few other flags.
+ if (constructor_flags_by_name == kAccConstructor) {
+ static constexpr uint32_t kInitAllowed =
+ kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic;
+ if ((method_access_flags & ~kInitAllowed) != 0) {
+ *error_msg = StringPrintf("Constructor %" PRIu32 "(%s) flagged inappropriately %x",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str(),
+ method_access_flags);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool DexFileVerifier::CheckConstructorProperties(
+ uint32_t method_index,
+ uint32_t constructor_flags) {
+ DCHECK(constructor_flags == kAccConstructor ||
+ constructor_flags == (kAccConstructor | kAccStatic));
+
+ // Check signature matches expectations.
+ const DexFile::MethodId* const method_id = CheckLoadMethodId(method_index,
+ "Bad <init>/<clinit> method id");
+ if (method_id == nullptr) {
+ return false;
+ }
+
+ // Check the ProtoId for the corresponding method.
+ //
+ // TODO(oth): the error message here is to satisfy the MethodId test
+ // in the DexFileVerifierTest. The test is checking that the error
+ // contains this string if the index is out of range.
+ const DexFile::ProtoId* const proto_id = CheckLoadProtoId(method_id->proto_idx_,
+ "inter_method_id_item proto_idx");
+ if (proto_id == nullptr) {
+ return false;
+ }
+
+ Signature signature = dex_file_->GetMethodSignature(*method_id);
+ if (constructor_flags == (kAccStatic | kAccConstructor)) {
+ if (!signature.IsVoid() || signature.GetNumberOfParameters() != 0) {
+ ErrorStringPrintf("<clinit> must have descriptor ()V");
+ return false;
+ }
+ } else if (!signature.IsVoid()) {
+ ErrorStringPrintf("Constructor %u(%s) must be void",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace art
diff --git a/libdexfile/dex/dex_file_verifier.h b/libdexfile/dex/dex_file_verifier.h
new file mode 100644
index 0000000000..c4982c24c9
--- /dev/null
+++ b/libdexfile/dex/dex_file_verifier.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_
+#define ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_
+
+#include <unordered_set>
+
+#include "base/hash_map.h"
+#include "dex_file.h"
+#include "dex_file_types.h"
+#include "safe_map.h"
+
+namespace art {
+
+class DexFileVerifier {
+ public:
+ static bool Verify(const DexFile* dex_file,
+ const uint8_t* begin,
+ size_t size,
+ const char* location,
+ bool verify_checksum,
+ std::string* error_msg);
+
+ const std::string& FailureReason() const {
+ return failure_reason_;
+ }
+
+ private:
+ DexFileVerifier(const DexFile* dex_file,
+ const uint8_t* begin,
+ size_t size,
+ const char* location,
+ bool verify_checksum)
+ : dex_file_(dex_file),
+ begin_(begin),
+ size_(size),
+ location_(location),
+ verify_checksum_(verify_checksum),
+ header_(&dex_file->GetHeader()),
+ ptr_(nullptr),
+ previous_item_(nullptr) {
+ }
+
+ bool Verify();
+
+ bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type);
+ bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label);
+ // Check a list. The head is assumed to be at *ptr, and elements to be of size element_size. If
+ // successful, the ptr will be moved forward the amount covered by the list.
+ bool CheckList(size_t element_size, const char* label, const uint8_t* *ptr);
+ // Checks whether the offset is zero (when size is zero) or that the offset falls within the area
+ // claimed by the file.
+ bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, size_t alignment, const char* label);
+ // Checks whether the size is less than the limit.
+ bool CheckSizeLimit(uint32_t size, uint32_t limit, const char* label);
+ bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
+
+ bool CheckHeader();
+ bool CheckMap();
+
+ uint32_t ReadUnsignedLittleEndian(uint32_t size);
+ bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
+ uint32_t* handler_offsets, uint32_t handlers_size);
+ bool CheckClassDataItemField(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ dex::TypeIndex class_type_index,
+ bool expect_static);
+ bool CheckClassDataItemMethod(uint32_t idx,
+ uint32_t access_flags,
+ uint32_t class_access_flags,
+ dex::TypeIndex class_type_index,
+ uint32_t code_offset,
+ std::unordered_set<uint32_t>* direct_method_indexes,
+ bool expect_direct);
+ bool CheckOrderAndGetClassDef(bool is_field,
+ const char* type_descr,
+ uint32_t curr_index,
+ uint32_t prev_index,
+ bool* have_class,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** class_def);
+ bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def);
+
+ bool CheckPadding(size_t offset, uint32_t aligned_offset, DexFile::MapItemType type);
+ bool CheckEncodedValue();
+ bool CheckEncodedArray();
+ bool CheckEncodedAnnotation();
+
+ bool CheckIntraClassDataItem();
+ // Check all fields of the given type from the given iterator. Load the class data from the first
+ // field, if necessary (and return it), or use the given values.
+ template <bool kStatic>
+ bool CheckIntraClassDataItemFields(ClassDataItemIterator* it,
+ bool* have_class,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** class_def);
+ // Check all methods of the given type from the given iterator. Load the class data from the first
+ // method, if necessary (and return it), or use the given values.
+ template <bool kDirect>
+ bool CheckIntraClassDataItemMethods(ClassDataItemIterator* it,
+ std::unordered_set<uint32_t>* direct_method_indexes,
+ bool* have_class,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** class_def);
+
+ bool CheckIntraCodeItem();
+ bool CheckIntraStringDataItem();
+ bool CheckIntraDebugInfoItem();
+ bool CheckIntraAnnotationItem();
+ bool CheckIntraAnnotationsDirectoryItem();
+
+ bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type);
+ bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type);
+ bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type);
+ bool CheckIntraSection();
+
+ bool CheckOffsetToTypeMap(size_t offset, uint16_t type);
+
+ // Note: as sometimes kDexNoIndex16, being 0xFFFF, is a valid return value, we need an
+ // additional out parameter to signal any errors loading an index.
+ dex::TypeIndex FindFirstClassDataDefiner(const uint8_t* ptr, bool* success);
+ dex::TypeIndex FindFirstAnnotationsDirectoryDefiner(const uint8_t* ptr, bool* success);
+
+ bool CheckInterStringIdItem();
+ bool CheckInterTypeIdItem();
+ bool CheckInterProtoIdItem();
+ bool CheckInterFieldIdItem();
+ bool CheckInterMethodIdItem();
+ bool CheckInterClassDefItem();
+ bool CheckInterCallSiteIdItem();
+ bool CheckInterMethodHandleItem();
+ bool CheckInterAnnotationSetRefList();
+ bool CheckInterAnnotationSetItem();
+ bool CheckInterClassDataItem();
+ bool CheckInterAnnotationsDirectoryItem();
+
+ bool CheckInterSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type);
+ bool CheckInterSection();
+
+ // Load a string by (type) index. Checks whether the index is in bounds, printing the error if
+ // not. If there is an error, null is returned.
+ const char* CheckLoadStringByIdx(dex::StringIndex idx, const char* error_fmt);
+ const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt);
+
+ // Load a field/method/proto Id by index. Checks whether the index is in bounds, printing the
+ // error if not. If there is an error, null is returned.
+ const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt);
+ const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt);
+ const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt);
+
+ void ErrorStringPrintf(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
+ bool FailureReasonIsSet() const { return failure_reason_.size() != 0; }
+
+ // Retrieve class index and class def from the given member. index is the member index, which is
+ // taken as either a field or a method index (as designated by is_field). The result, if the
+ // member and declaring class could be found, is stored in class_type_index and class_def.
+ // This is an expensive lookup, as we have to find the class def by type index, which is a
+ // linear search. The output values should thus be cached by the caller.
+ bool FindClassIndexAndDef(uint32_t index,
+ bool is_field,
+ dex::TypeIndex* class_type_index,
+ const DexFile::ClassDef** output_class_def);
+
+ // Check validity of the given access flags, interpreted for a field in the context of a class
+ // with the given second access flags.
+ bool CheckFieldAccessFlags(uint32_t idx,
+ uint32_t field_access_flags,
+ uint32_t class_access_flags,
+ std::string* error_message);
+
+ // Check validity of the given method and access flags, in the context of a class with the given
+ // second access flags.
+ bool CheckMethodAccessFlags(uint32_t method_index,
+ uint32_t method_access_flags,
+ uint32_t class_access_flags,
+ uint32_t constructor_flags_by_name,
+ bool has_code,
+ bool expect_direct,
+ std::string* error_message);
+
+ // Check validity of given method if it's a constructor or class initializer.
+ bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags);
+
+ const DexFile* const dex_file_;
+ const uint8_t* const begin_;
+ const size_t size_;
+ const char* const location_;
+ const bool verify_checksum_;
+ const DexFile::Header* const header_;
+
+ struct OffsetTypeMapEmptyFn {
+ // Make a hash map slot empty by making the offset 0. Offset 0 is a valid dex file offset that
+ // is in the offset of the dex file header. However, we only store data section items in the
+ // map, and these are after the header.
+ void MakeEmpty(std::pair<uint32_t, uint16_t>& pair) const {
+ pair.first = 0u;
+ }
+ // Check if a hash map slot is empty.
+ bool IsEmpty(const std::pair<uint32_t, uint16_t>& pair) const {
+ return pair.first == 0;
+ }
+ };
+ struct OffsetTypeMapHashCompareFn {
+ // Hash function for offset.
+ size_t operator()(const uint32_t key) const {
+ return key;
+ }
+ // std::equal function for offset.
+ bool operator()(const uint32_t a, const uint32_t b) const {
+ return a == b;
+ }
+ };
+ // Map from offset to dex file type, HashMap for performance reasons.
+ template<class Key,
+ class T,
+ class EmptyFn,
+ AllocatorTag kTag,
+ class Hash = std::hash<Key>,
+ class Pred = std::equal_to<Key>>
+ using AllocationTrackingHashMap = HashMap<
+ Key, T, EmptyFn, Hash, Pred, TrackingAllocator<std::pair<Key, T>, kTag>>;
+
+ AllocationTrackingHashMap<uint32_t,
+ uint16_t,
+ OffsetTypeMapEmptyFn,
+ kAllocatorTagDexFileVerifier,
+ OffsetTypeMapHashCompareFn,
+ OffsetTypeMapHashCompareFn> offset_to_type_map_;
+ const uint8_t* ptr_;
+ const void* previous_item_;
+
+ std::string failure_reason_;
+
+ // Set of type ids for which there are ClassDef elements in the dex file.
+ std::unordered_set<decltype(DexFile::ClassDef::class_idx_)> defined_classes_;
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_FILE_VERIFIER_H_
diff --git a/libdexfile/dex/dex_file_verifier_test.cc b/libdexfile/dex/dex_file_verifier_test.cc
new file mode 100644
index 0000000000..1cd4b2c07b
--- /dev/null
+++ b/libdexfile/dex/dex_file_verifier_test.cc
@@ -0,0 +1,2186 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file_verifier.h"
+
+#include <zlib.h>
+
+#include <functional>
+#include <memory>
+
+#include "base/bit_utils.h"
+#include "base/macros.h"
+#include "base64_test_util.h"
+#include "descriptors_names.h"
+#include "dex_file-inl.h"
+#include "dex_file_loader.h"
+#include "dex_file_types.h"
+#include "gtest/gtest.h"
+#include "leb128.h"
+#include "standard_dex_file.h"
+
+namespace art {
+
+static constexpr char kLocationString[] = "dex_file_location";
+
+// Make the Dex file version 37.
+static void MakeDexVersion37(DexFile* dex_file) {
+ size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
+ CHECK_EQ(*(dex_file->Begin() + offset), '5');
+ *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
+}
+
+static void FixUpChecksum(uint8_t* dex_file) {
+ DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file);
+ uint32_t expected_size = header->file_size_;
+ uint32_t adler_checksum = adler32(0L, Z_NULL, 0);
+ const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_);
+ const uint8_t* non_sum_ptr = dex_file + non_sum;
+ adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
+ header->checksum_ = adler_checksum;
+}
+
+class DexFileVerifierTest : public testing::Test {
+ protected:
+ DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) {
+ return new StandardDexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
+ }
+
+ void VerifyModification(const char* dex_file_base64_content,
+ const char* location,
+ const std::function<void(DexFile*)>& f,
+ const char* expected_error) {
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(dex_file_base64_content, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ f(dex_file.get());
+ FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin()));
+
+ static constexpr bool kVerifyChecksum = true;
+ std::string error_msg;
+ bool success = DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location,
+ kVerifyChecksum,
+ &error_msg);
+ if (expected_error == nullptr) {
+ EXPECT_TRUE(success) << error_msg;
+ } else {
+ EXPECT_FALSE(success) << "Expected " << expected_error;
+ if (!success) {
+ EXPECT_NE(error_msg.find(expected_error), std::string::npos) << error_msg;
+ }
+ }
+ }
+};
+
+static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64,
+ const char* location,
+ std::string* error_msg) {
+ // decode base64
+ CHECK(base64 != nullptr);
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length));
+ CHECK(dex_bytes.get() != nullptr);
+
+ // read dex
+ std::vector<std::unique_ptr<const DexFile>> tmp;
+ const DexFileLoader dex_file_loader;
+ bool success = dex_file_loader.OpenAll(dex_bytes.get(),
+ length,
+ location,
+ /* verify */ true,
+ /* verify_checksum */ true,
+ error_msg,
+ &tmp);
+ CHECK(success) << *error_msg;
+ EXPECT_EQ(1U, tmp.size());
+ std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
+ return dex_file;
+}
+
+// To generate a base64 encoded Dex file (such as kGoodTestDex, below)
+// from Smali files, use:
+//
+// smali assemble -o classes.dex class1.smali [class2.smali ...]
+// base64 classes.dex >classes.dex.base64
+
+// For reference.
+static const char kGoodTestDex[] =
+ "ZGV4CjAzNQDrVbyVkxX1HljTznNf95AglkUAhQuFtmKkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAN"
+ "AAAAcAAAAAYAAACkAAAAAgAAALwAAAABAAAA1AAAAAQAAADcAAAAAQAAAPwAAACIAQAAHAEAAFoB"
+ "AABiAQAAagEAAIEBAACVAQAAqQEAAL0BAADDAQAAzgEAANEBAADVAQAA2gEAAN8BAAABAAAAAgAA"
+ "AAMAAAAEAAAABQAAAAgAAAAIAAAABQAAAAAAAAAJAAAABQAAAFQBAAAEAAEACwAAAAAAAAAAAAAA"
+ "AAAAAAoAAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAcAAAAAAAAA8wEAAAAAAAAB"
+ "AAEAAQAAAOgBAAAEAAAAcBADAAAADgACAAAAAgAAAO0BAAAIAAAAYgAAABoBBgBuIAIAEAAOAAEA"
+ "AAADAAY8aW5pdD4ABkxUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09i"
+ "amVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AARUZXN0AAlUZXN0"
+ "LmphdmEAAVYAAlZMAANmb28AA291dAAHcHJpbnRsbgABAAcOAAMABw54AAAAAgAAgYAEnAIBCbQC"
+ "AAAADQAAAAAAAAABAAAAAAAAAAEAAAANAAAAcAAAAAIAAAAGAAAApAAAAAMAAAACAAAAvAAAAAQA"
+ "AAABAAAA1AAAAAUAAAAEAAAA3AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA"
+ "AAIgAAANAAAAWgEAAAMgAAACAAAA6AEAAAAgAAABAAAA8wEAAAAQAAABAAAABAIAAA==";
+
+TEST_F(DexFileVerifierTest, GoodDex) {
+ std::string error_msg;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex,
+ kLocationString,
+ &error_msg));
+ ASSERT_TRUE(raw.get() != nullptr) << error_msg;
+}
+
+TEST_F(DexFileVerifierTest, MethodId) {
+ // Class idx error.
+ VerifyModification(
+ kGoodTestDex,
+ "method_id_class_idx",
+ [](DexFile* dex_file) {
+ DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+ method_id->class_idx_ = dex::TypeIndex(0xFF);
+ },
+ "could not find declaring class for direct method index 0");
+
+ // Proto idx error.
+ VerifyModification(
+ kGoodTestDex,
+ "method_id_proto_idx",
+ [](DexFile* dex_file) {
+ DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+ method_id->proto_idx_ = 0xFF;
+ },
+ "inter_method_id_item proto_idx");
+
+ // Name idx error.
+ VerifyModification(
+ kGoodTestDex,
+ "method_id_name_idx",
+ [](DexFile* dex_file) {
+ DexFile::MethodId* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(0));
+ method_id->name_idx_ = dex::StringIndex(0xFF);
+ },
+ "String index not available for method flags verification");
+}
+
+// Method flags test class generated from the following smali code. The declared-synchronized
+// flags are there to enforce a 3-byte uLEB128 encoding so we don't have to relayout
+// the code, but we need to remove them before doing tests.
+//
+// .class public LMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public constructor <init>()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method private declared-synchronized foo()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public declared-synchronized bar()V
+// .registers 1
+// return-void
+// .end method
+
+static const char kMethodFlagsTestDex[] =
+ "ZGV4CjAzNQCyOQrJaDBwiIWv5MIuYKXhxlLLsQcx5SwgAgAAcAAAAHhWNBIAAAAAAAAAAJgBAAAH"
+ "AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAA8AQAA5AAAAOQA"
+ "AADuAAAA9gAAAAUBAAAZAQAAHAEAACEBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA"
+ "AAAAAAABAAAAAAAAAAUAAAAAAAAABgAAAAAAAAABAAAAAQAAAAAAAAD/////AAAAAHoBAAAAAAAA"
+ "CDxjbGluaXQ+AAY8aW5pdD4ADUxNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAD"
+ "YmFyAANmb28AAAAAAAAAAQAAAAAAAAAAAAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAEA"
+ "AQAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAADAQCJgASsAgGBgATAAgKCgAjU"
+ "AgKBgAjoAgAACwAAAAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAADAAAAjAAAAAMAAAABAAAA"
+ "mAAAAAUAAAAEAAAApAAAAAYAAAABAAAAxAAAAAIgAAAHAAAA5AAAAAMQAAABAAAAKAEAAAEgAAAE"
+ "AAAALAEAAAAgAAABAAAAegEAAAAQAAABAAAAmAEAAA==";
+
+// Find the method data for the first method with the given name (from class 0). Note: the pointer
+// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
+// delta.
+static const uint8_t* FindMethodData(const DexFile* dex_file,
+ const char* name,
+ /*out*/ uint32_t* method_idx = nullptr) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+
+ ClassDataItemIterator it(*dex_file, class_data);
+
+ const uint8_t* trailing = class_data;
+ // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
+ // element has already been loaded into the iterator.
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+
+ // Skip all fields.
+ while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+ trailing = it.DataPointer();
+ it.Next();
+ }
+
+ while (it.HasNextMethod()) {
+ uint32_t method_index = it.GetMemberIndex();
+ dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_;
+ const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
+ const char* str = dex_file->GetStringData(string_id);
+ if (strcmp(name, str) == 0) {
+ if (method_idx != nullptr) {
+ *method_idx = method_index;
+ }
+ DecodeUnsignedLeb128(&trailing);
+ return trailing;
+ }
+
+ trailing = it.DataPointer();
+ it.Next();
+ }
+
+ return nullptr;
+}
+
+// Set the method flags to the given value.
+static void SetMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+ uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
+ CHECK(method_flags_ptr != nullptr) << method;
+
+ // Unroll this, as we only have three bytes, anyways.
+ uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
+ *(method_flags_ptr++) = (base1 | 0x80);
+ mask >>= 7;
+
+ uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
+ *(method_flags_ptr++) = (base2 | 0x80);
+ mask >>= 7;
+
+ uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
+ *method_flags_ptr = base3;
+}
+
+static uint32_t GetMethodFlags(DexFile* dex_file, const char* method) {
+ const uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
+ CHECK(method_flags_ptr != nullptr) << method;
+ return DecodeUnsignedLeb128(&method_flags_ptr);
+}
+
+// Apply the given mask to method flags.
+static void ApplyMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+ uint32_t value = GetMethodFlags(dex_file, method);
+ value &= mask;
+ SetMethodFlags(dex_file, method, value);
+}
+
+// Apply the given mask to method flags.
+static void OrMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) {
+ uint32_t value = GetMethodFlags(dex_file, method);
+ value |= mask;
+ SetMethodFlags(dex_file, method, value);
+}
+
+// Set code_off to 0 for the method.
+static void RemoveCode(DexFile* dex_file, const char* method) {
+ const uint8_t* ptr = FindMethodData(dex_file, method);
+ // Next is flags, pass.
+ DecodeUnsignedLeb128(&ptr);
+
+ // Figure out how many bytes the code_off is.
+ const uint8_t* tmp = ptr;
+ DecodeUnsignedLeb128(&tmp);
+ size_t bytes = tmp - ptr;
+
+ uint8_t* mod = const_cast<uint8_t*>(ptr);
+ for (size_t i = 1; i < bytes; ++i) {
+ *(mod++) = 0x80;
+ }
+ *mod = 0x00;
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsBase) {
+ // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) {
+ // Make sure we still accept constructors without their flags.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_missing_constructor_tag_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccConstructor);
+ ApplyMaskToMethodFlags(dex_file, "<clinit>", ~kAccConstructor);
+ },
+ nullptr);
+
+ constexpr const char* kConstructors[] = { "<clinit>", "<init>"};
+ for (size_t i = 0; i < 2; ++i) {
+ // Constructor with code marked native.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_native",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
+ },
+ "has code, but is marked native or abstract");
+ // Constructor with code marked abstract.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_abstract",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
+ },
+ "has code, but is marked native or abstract");
+ // Constructor as-is without code.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_nocode",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ RemoveCode(dex_file, kConstructors[i]);
+ },
+ "has no code, but is not marked native or abstract");
+ // Constructor without code marked native.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_native_nocode",
+ [&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
+ RemoveCode(dex_file, kConstructors[i]);
+ },
+ "must not be abstract or native");
+ // Constructor without code marked abstract.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_constructor_abstract_nocode",
+ [&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
+ RemoveCode(dex_file, kConstructors[i]);
+ },
+ "must not be abstract or native");
+ }
+ // <init> may only have (modulo ignored):
+ // kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic
+ static constexpr uint32_t kInitAllowed[] = {
+ 0,
+ kAccPrivate,
+ kAccProtected,
+ kAccPublic,
+ kAccStrict,
+ kAccVarargs,
+ kAccSynthetic
+ };
+ for (size_t i = 0; i < arraysize(kInitAllowed); ++i) {
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_allowed_flags",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", kInitAllowed[i]);
+ },
+ nullptr);
+ }
+ // Only one of public-private-protected.
+ for (size_t i = 1; i < 8; ++i) {
+ if (POPCOUNT(i) < 2) {
+ continue;
+ }
+ // Technically the flags match, but just be defensive here.
+ uint32_t mask = ((i & 1) != 0 ? kAccPrivate : 0) |
+ ((i & 2) != 0 ? kAccProtected : 0) |
+ ((i & 4) != 0 ? kAccPublic : 0);
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_one_of_ppp",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", mask);
+ },
+ "Method may have only one of public/protected/private");
+ }
+ // <init> doesn't allow
+ // kAccStatic | kAccFinal | kAccSynchronized | kAccBridge
+ // Need to handle static separately as it has its own error message.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_not_allowed_flags",
+ [&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", kAccStatic);
+ },
+ "Constructor 1(LMethodFlags;.<init>) is not flagged correctly wrt/ static");
+ static constexpr uint32_t kInitNotAllowed[] = {
+ kAccFinal,
+ kAccSynchronized,
+ kAccBridge
+ };
+ for (size_t i = 0; i < arraysize(kInitNotAllowed); ++i) {
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "init_not_allowed_flags",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "<init>", kInitNotAllowed[i]);
+ },
+ "Constructor 1(LMethodFlags;.<init>) flagged inappropriately");
+ }
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsMethods) {
+ constexpr const char* kMethods[] = { "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kMethods); ++i) {
+ // Make sure we reject non-constructors marked as constructors.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_non_constructor",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccConstructor);
+ },
+ "is marked constructor, but doesn't match name");
+
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_native_with_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccNative);
+ },
+ "has code, but is marked native or abstract");
+
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_abstract_with_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract);
+ },
+ "has code, but is marked native or abstract");
+
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_non_abstract_native_no_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ RemoveCode(dex_file, kMethods[i]);
+ },
+ "has no code, but is not marked native or abstract");
+
+ // Abstract methods may not have the following flags.
+ constexpr uint32_t kAbstractDisallowed[] = {
+ kAccPrivate,
+ kAccStatic,
+ kAccFinal,
+ kAccNative,
+ kAccStrict,
+ kAccSynchronized,
+ };
+ for (size_t j = 0; j < arraysize(kAbstractDisallowed); ++j) {
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_abstract_and_disallowed_no_code",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ RemoveCode(dex_file, kMethods[i]);
+
+ // Can't check private and static with foo, as it's in the virtual list and gives a
+ // different error.
+ if (((GetMethodFlags(dex_file, kMethods[i]) & kAccPublic) != 0) &&
+ ((kAbstractDisallowed[j] & (kAccPrivate | kAccStatic)) != 0)) {
+ // Use another breaking flag.
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAccFinal);
+ } else {
+ OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAbstractDisallowed[j]);
+ }
+ },
+ "has disallowed access flags");
+ }
+
+ // Only one of public-private-protected.
+ for (size_t j = 1; j < 8; ++j) {
+ if (POPCOUNT(j) < 2) {
+ continue;
+ }
+ // Technically the flags match, but just be defensive here.
+ uint32_t mask = ((j & 1) != 0 ? kAccPrivate : 0) |
+ ((j & 2) != 0 ? kAccProtected : 0) |
+ ((j & 4) != 0 ? kAccPublic : 0);
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_one_of_ppp",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, kMethods[i], ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, kMethods[i], mask);
+ },
+ "Method may have only one of public/protected/private");
+ }
+ }
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) {
+ constexpr const char* kMethods[] = { "<clinit>", "<init>", "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kMethods); ++i) {
+ // All interesting method flags, other flags are to be ignored.
+ constexpr uint32_t kAllMethodFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccSynchronized |
+ kAccBridge |
+ kAccVarargs |
+ kAccNative |
+ kAccAbstract |
+ kAccStrict |
+ kAccSynthetic;
+ constexpr uint32_t kIgnoredMask = ~kAllMethodFlags & 0xFFFF;
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "method_flags_ignored",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, kMethods[i], kIgnoredMask);
+ },
+ nullptr);
+ }
+}
+
+TEST_F(DexFileVerifierTest, B28552165) {
+ // Regression test for bad error string retrieval in different situations.
+ // Using invalid access flags to trigger the error.
+ VerifyModification(
+ kMethodFlagsTestDex,
+ "b28552165",
+ [](DexFile* dex_file) {
+ OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected);
+ },
+ "Method may have only one of public/protected/private, LMethodFlags;.foo");
+}
+
+// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's
+// just easier to break up bad cases.
+
+// Standard interface. Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceMethodFlags;
+// .super Ljava/lang/Object;
+//
+// .method public static constructor <clinit>()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public abstract declared-synchronized foo()V
+// .end method
+static const char kMethodFlagsInterface[] =
+ "ZGV4CjAzNQCOM0odZ5bws1d9GSmumXaK5iE/7XxFpOm8AQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
+ "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADwAAAAzAAAAMwA"
+ "AADWAAAA7gAAAAIBAAAFAQAAAQAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABAAA"
+ "AAAAAAABAgAAAQAAAAAAAAD/////AAAAACIBAAAAAAAACDxjbGluaXQ+ABZMSW50ZXJmYWNlTWV0"
+ "aG9kRmxhZ3M7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAA2ZvbwAAAAAAAAABAAAAAAAAAAAAAAAB"
+ "AAAADgAAAAEBAImABJACAYGICAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAMA"
+ "AACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAAAAEAAACsAAAAAiAAAAUAAADMAAAAAxAA"
+ "AAEAAAAMAQAAASAAAAEAAAAQAQAAACAAAAEAAAAiAQAAABAAAAEAAAA0AQAA";
+
+// To simplify generation of interesting "sub-states" of src_value, allow a "simple" mask to apply
+// to a src_value, such that mask bit 0 applies to the lowest set bit in src_value, and so on.
+static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) {
+ uint32_t result = 0;
+ uint32_t mask_index = 0;
+ while (src_value != 0) {
+ uint32_t index = CTZ(src_value);
+ if (((src_value & (1 << index)) != 0) &&
+ ((mask & (1 << mask_index)) != 0)) {
+ result |= (1 << index);
+ }
+ src_value &= ~(1 << index);
+ mask_index++;
+ }
+ return result;
+}
+
+TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) {
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_ok37",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
+ "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public");
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_abstract",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccAbstract);
+ },
+ "Method 1(LInterfaceMethodFlags;.foo) has no code, but is not marked native or abstract");
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_static",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ OrMaskToMethodFlags(dex_file, "foo", kAccStatic);
+ },
+ "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_private",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "foo", kAccPrivate);
+ },
+ "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
+ "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public");
+
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_protected",
+ [](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_protected",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
+ },
+ "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public");
+
+ constexpr uint32_t kAllMethodFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccSynchronized |
+ kAccBridge |
+ kAccVarargs |
+ kAccNative |
+ kAccAbstract |
+ kAccStrict |
+ kAccSynthetic;
+ constexpr uint32_t kInterfaceMethodFlags =
+ kAccPublic | kAccAbstract | kAccVarargs | kAccBridge | kAccSynthetic;
+ constexpr uint32_t kInterfaceDisallowed = kAllMethodFlags &
+ ~kInterfaceMethodFlags &
+ // Already tested, needed to be separate.
+ ~kAccStatic &
+ ~kAccPrivate &
+ ~kAccProtected;
+ static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
+
+ uint32_t bits = POPCOUNT(kInterfaceDisallowed);
+ for (uint32_t i = 1; i < (1u << bits); ++i) {
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_abstract",
+ [&](DexFile* dex_file) {
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+ if ((mask & kAccProtected) != 0) {
+ mask &= ~kAccProtected;
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ }
+ OrMaskToMethodFlags(dex_file, "foo", mask);
+ },
+ "Abstract method 1(LInterfaceMethodFlags;.foo) has disallowed access flags");
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+
+// Field flags.
+
+// Find the method data for the first method with the given name (from class 0). Note: the pointer
+// is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index
+// delta.
+static const uint8_t* FindFieldData(const DexFile* dex_file, const char* name) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(0);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+
+ ClassDataItemIterator it(*dex_file, class_data);
+
+ const uint8_t* trailing = class_data;
+ // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first
+ // element has already been loaded into the iterator.
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+ DecodeUnsignedLeb128(&trailing);
+
+ while (it.HasNextStaticField() || it.HasNextInstanceField()) {
+ uint32_t field_index = it.GetMemberIndex();
+ dex::StringIndex name_index = dex_file->GetFieldId(field_index).name_idx_;
+ const DexFile::StringId& string_id = dex_file->GetStringId(name_index);
+ const char* str = dex_file->GetStringData(string_id);
+ if (strcmp(name, str) == 0) {
+ DecodeUnsignedLeb128(&trailing);
+ return trailing;
+ }
+
+ trailing = it.DataPointer();
+ it.Next();
+ }
+
+ return nullptr;
+}
+
+// Set the method flags to the given value.
+static void SetFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+ uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
+ CHECK(field_flags_ptr != nullptr) << field;
+
+ // Unroll this, as we only have three bytes, anyways.
+ uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
+ *(field_flags_ptr++) = (base1 | 0x80);
+ mask >>= 7;
+
+ uint8_t base2 = static_cast<uint8_t>(mask & 0x7F);
+ *(field_flags_ptr++) = (base2 | 0x80);
+ mask >>= 7;
+
+ uint8_t base3 = static_cast<uint8_t>(mask & 0x7F);
+ *field_flags_ptr = base3;
+}
+
+static uint32_t GetFieldFlags(DexFile* dex_file, const char* field) {
+ const uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
+ CHECK(field_flags_ptr != nullptr) << field;
+ return DecodeUnsignedLeb128(&field_flags_ptr);
+}
+
+// Apply the given mask to method flags.
+static void ApplyMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+ uint32_t value = GetFieldFlags(dex_file, field);
+ value &= mask;
+ SetFieldFlags(dex_file, field, value);
+}
+
+// Apply the given mask to method flags.
+static void OrMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) {
+ uint32_t value = GetFieldFlags(dex_file, field);
+ value |= mask;
+ SetFieldFlags(dex_file, field, value);
+}
+
+// Standard class. Use declared-synchronized again for 3B encoding.
+//
+// .class public LFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public foo:I
+//
+// .field declared-synchronized public static bar:I
+
+static const char kFieldFlagsTestDex[] =
+ "ZGV4CjAzNQBtLw7hydbfv4TdXidZyzAB70W7w3vnYJRwAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAF"
+ "AAAAcAAAAAMAAACEAAAAAAAAAAAAAAACAAAAkAAAAAAAAAAAAAAAAQAAAKAAAACwAAAAwAAAAMAA"
+ "AADDAAAA0QAAAOUAAADqAAAAAAAAAAEAAAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAABAAAAAgAA"
+ "AAAAAAD/////AAAAAPQAAAAAAAAAAUkADExGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7"
+ "AANiYXIAA2ZvbwAAAAAAAAEBAAAAiYAIAYGACAkAAAAAAAAAAQAAAAAAAAABAAAABQAAAHAAAAAC"
+ "AAAAAwAAAIQAAAAEAAAAAgAAAJAAAAAGAAAAAQAAAKAAAAACIAAABQAAAMAAAAADEAAAAQAAAPAA"
+ "AAAAIAAAAQAAAPQAAAAAEAAAAQAAAAABAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsBase) {
+ // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_ok",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsWrongList) {
+ // Mark the field so that it should appear in the opposite list (instance vs static).
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_wrong_list",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, "foo", kAccStatic);
+ },
+ "Static/instance field not in expected list");
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_wrong_list",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccStatic);
+ },
+ "Static/instance field not in expected list");
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsPPP) {
+ static const char* kFields[] = { "foo", "bar" };
+ for (size_t i = 0; i < arraysize(kFields); ++i) {
+ // Should be OK to remove public.
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_non_public",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
+ },
+ nullptr);
+ constexpr uint32_t kAccFlags = kAccPublic | kAccPrivate | kAccProtected;
+ uint32_t bits = POPCOUNT(kAccFlags);
+ for (uint32_t j = 1; j < (1u << bits); ++j) {
+ if (POPCOUNT(j) < 2) {
+ continue;
+ }
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_ppp",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
+ uint32_t mask = ApplyMaskShifted(kAccFlags, j);
+ OrMaskToFieldFlags(dex_file, kFields[i], mask);
+ },
+ "Field may have only one of public/protected/private");
+ }
+ }
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsIgnoredOK) {
+ constexpr const char* kFields[] = { "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kFields); ++i) {
+ // All interesting method flags, other flags are to be ignored.
+ constexpr uint32_t kAllFieldFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccVolatile |
+ kAccTransient |
+ kAccSynthetic |
+ kAccEnum;
+ constexpr uint32_t kIgnoredMask = ~kAllFieldFlags & 0xFFFF;
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_ignored",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, kFields[i], kIgnoredMask);
+ },
+ nullptr);
+ }
+}
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsVolatileFinal) {
+ constexpr const char* kFields[] = { "foo", "bar"};
+ for (size_t i = 0; i < arraysize(kFields); ++i) {
+ VerifyModification(
+ kFieldFlagsTestDex,
+ "field_flags_final_and_volatile",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, kFields[i], kAccVolatile | kAccFinal);
+ },
+ "Fields may not be volatile and final");
+ }
+}
+
+// Standard interface. Needs to be separate from class as interfaces do not allow instance fields.
+// Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public static final foo:I
+
+static const char kFieldFlagsInterfaceTestDex[] =
+ "ZGV4CjAzNQCVMHfEimR1zZPk6hl6O9GPAYqkl3u0umFkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
+ "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
+ "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
+ "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
+ "b28AAAAAAAABAAAAAJmACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
+ "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
+ "AAAAEAAAAQAAAPQAAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsInterface) {
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
+
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ },
+ "Interface field is not public final static");
+
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_final",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_final",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
+ },
+ "Interface field is not public final static");
+
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_protected",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_protected",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
+ },
+ "Interface field is not public final static");
+
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_private",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_private",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
+ },
+ "Interface field is not public final static");
+
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_synthetic",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ OrMaskToFieldFlags(dex_file, "foo", kAccSynthetic);
+ },
+ nullptr);
+
+ constexpr uint32_t kAllFieldFlags =
+ kAccPublic |
+ kAccPrivate |
+ kAccProtected |
+ kAccStatic |
+ kAccFinal |
+ kAccVolatile |
+ kAccTransient |
+ kAccSynthetic |
+ kAccEnum;
+ constexpr uint32_t kInterfaceFieldFlags = kAccPublic | kAccStatic | kAccFinal | kAccSynthetic;
+ constexpr uint32_t kInterfaceDisallowed = kAllFieldFlags &
+ ~kInterfaceFieldFlags &
+ ~kAccProtected &
+ ~kAccPrivate;
+ static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags.");
+
+ uint32_t bits = POPCOUNT(kInterfaceDisallowed);
+ for (uint32_t i = 1; i < (1u << bits); ++i) {
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_disallowed",
+ [&](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+ if ((mask & kAccProtected) != 0) {
+ mask &= ~kAccProtected;
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ }
+ OrMaskToFieldFlags(dex_file, "foo", mask);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_disallowed",
+ [&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+ if ((mask & kAccProtected) != 0) {
+ mask &= ~kAccProtected;
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ }
+ OrMaskToFieldFlags(dex_file, "foo", mask);
+ },
+ "Interface field has disallowed flag");
+ }
+}
+
+// Standard bad interface. Needs to be separate from class as interfaces do not allow instance
+// fields. Use declared-synchronized again for 3B encoding.
+//
+// .class public interface LInterfaceFieldFlags;
+// .super Ljava/lang/Object;
+//
+// .field declared-synchronized public final foo:I
+
+static const char kFieldFlagsInterfaceBadTestDex[] =
+ "ZGV4CjAzNQByMUnqYKHBkUpvvNp+9CnZ2VyDkKnRN6VkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE"
+ "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA"
+ "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA"
+ "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm"
+ "b28AAAAAAAAAAQAAAJGACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE"
+ "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA"
+ "AAAAEAAAAQAAAPQAAAA=";
+
+TEST_F(DexFileVerifierTest, FieldAccessFlagsInterfaceNonStatic) {
+ VerifyModification(
+ kFieldFlagsInterfaceBadTestDex,
+ "field_flags_interface_non_static",
+ [](DexFile* dex_file) {
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceBadTestDex,
+ "field_flags_interface_non_static",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ "Interface field is not public final static");
+}
+
+// Generated from:
+//
+// .class public LTest;
+// .super Ljava/lang/Object;
+// .source "Test.java"
+//
+// .method public constructor <init>()V
+// .registers 1
+//
+// .prologue
+// .line 1
+// invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+//
+// return-void
+// .end method
+//
+// .method public static main()V
+// .registers 2
+//
+// const-string v0, "a"
+// const-string v0, "b"
+// const-string v0, "c"
+// const-string v0, "d"
+// const-string v0, "e"
+// const-string v0, "f"
+// const-string v0, "g"
+// const-string v0, "h"
+// const-string v0, "i"
+// const-string v0, "j"
+// const-string v0, "k"
+//
+// .local v1, "local_var":Ljava/lang/String;
+// const-string v1, "test"
+// .end method
+
+static const char kDebugInfoTestDex[] =
+ "ZGV4CjAzNQCHRkHix2eIMQgvLD/0VGrlllZLo0Rb6VyUAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAU"
+ "AAAAcAAAAAQAAADAAAAAAQAAANAAAAAAAAAAAAAAAAMAAADcAAAAAQAAAPQAAACAAQAAFAEAABQB"
+ "AAAcAQAAJAEAADgBAABMAQAAVwEAAFoBAABdAQAAYAEAAGMBAABmAQAAaQEAAGwBAABvAQAAcgEA"
+ "AHUBAAB4AQAAewEAAIYBAACMAQAAAQAAAAIAAAADAAAABQAAAAUAAAADAAAAAAAAAAAAAAAAAAAA"
+ "AAAAABIAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAEAAAAAAAAAPwBAAAAAAAABjxpbml0PgAG"
+ "TFRlc3Q7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAJVGVzdC5qYXZh"
+ "AAFWAAFhAAFiAAFjAAFkAAFlAAFmAAFnAAFoAAFpAAFqAAFrAAlsb2NhbF92YXIABG1haW4ABHRl"
+ "c3QAAAABAAcOAAAAARYDARIDAAAAAQABAAEAAACUAQAABAAAAHAQAgAAAA4AAgAAAAAAAACZAQAA"
+ "GAAAABoABgAaAAcAGgAIABoACQAaAAoAGgALABoADAAaAA0AGgAOABoADwAaABAAGgETAAAAAgAA"
+ "gYAEpAMBCbwDAAALAAAAAAAAAAEAAAAAAAAAAQAAABQAAABwAAAAAgAAAAQAAADAAAAAAwAAAAEA"
+ "AADQAAAABQAAAAMAAADcAAAABgAAAAEAAAD0AAAAAiAAABQAAAAUAQAAAyAAAAIAAACUAQAAASAA"
+ "AAIAAACkAQAAACAAAAEAAAD8AQAAABAAAAEAAAAMAgAA";
+
+TEST_F(DexFileVerifierTest, DebugInfoTypeIdxTest) {
+ {
+ // The input dex file should be good before modification.
+ std::string error_msg;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kDebugInfoTestDex,
+ kLocationString,
+ &error_msg));
+ ASSERT_TRUE(raw.get() != nullptr) << error_msg;
+ }
+
+ // Modify the debug information entry.
+ VerifyModification(
+ kDebugInfoTestDex,
+ "debug_start_type_idx",
+ [](DexFile* dex_file) {
+ *(const_cast<uint8_t*>(dex_file->Begin()) + 416) = 0x14U;
+ },
+ "DBG_START_LOCAL type_idx");
+}
+
+TEST_F(DexFileVerifierTest, SectionAlignment) {
+ {
+ // The input dex file should be good before modification. Any file is fine, as long as it
+ // uses all sections.
+ std::string error_msg;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex,
+ kLocationString,
+ &error_msg));
+ ASSERT_TRUE(raw.get() != nullptr) << error_msg;
+ }
+
+ // Modify all section offsets to be unaligned.
+ constexpr size_t kSections = 7;
+ for (size_t i = 0; i < kSections; ++i) {
+ VerifyModification(
+ kGoodTestDex,
+ "section_align",
+ [&](DexFile* dex_file) {
+ DexFile::Header* header = const_cast<DexFile::Header*>(
+ reinterpret_cast<const DexFile::Header*>(dex_file->Begin()));
+ uint32_t* off_ptr;
+ switch (i) {
+ case 0:
+ off_ptr = &header->map_off_;
+ break;
+ case 1:
+ off_ptr = &header->string_ids_off_;
+ break;
+ case 2:
+ off_ptr = &header->type_ids_off_;
+ break;
+ case 3:
+ off_ptr = &header->proto_ids_off_;
+ break;
+ case 4:
+ off_ptr = &header->field_ids_off_;
+ break;
+ case 5:
+ off_ptr = &header->method_ids_off_;
+ break;
+ case 6:
+ off_ptr = &header->class_defs_off_;
+ break;
+
+ static_assert(kSections == 7, "kSections is wrong");
+ default:
+ LOG(FATAL) << "Unexpected section";
+ UNREACHABLE();
+ }
+ ASSERT_TRUE(off_ptr != nullptr);
+ ASSERT_NE(*off_ptr, 0U) << i; // Should already contain a value (in use).
+ (*off_ptr)++; // Add one, which should misalign it (all the sections
+ // above are aligned by 4).
+ },
+ "should be aligned by 4 for");
+ }
+}
+
+// Generated from
+//
+// .class LOverloading;
+//
+// .super Ljava/lang/Object;
+//
+// .method public static foo()V
+// .registers 1
+// return-void
+// .end method
+//
+// .method public static foo(I)V
+// .registers 1
+// return-void
+// .end method
+static const char kProtoOrderingTestDex[] =
+ "ZGV4CjAzNQA1L+ABE6voQ9Lr4Ci//efB53oGnDr5PinsAQAAcAAAAHhWNBIAAAAAAAAAAFgBAAAG"
+ "AAAAcAAAAAQAAACIAAAAAgAAAJgAAAAAAAAAAAAAAAIAAACwAAAAAQAAAMAAAAAMAQAA4AAAAOAA"
+ "AADjAAAA8gAAAAYBAAAJAQAADQEAAAAAAAABAAAAAgAAAAMAAAADAAAAAwAAAAAAAAAEAAAAAwAA"
+ "ABQBAAABAAAABQAAAAEAAQAFAAAAAQAAAAAAAAACAAAAAAAAAP////8AAAAASgEAAAAAAAABSQAN"
+ "TE92ZXJsb2FkaW5nOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAJWSQADZm9vAAAAAQAAAAAAAAAA"
+ "AAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAACAAAJpAIBCbgC"
+ "AAAMAAAAAAAAAAEAAAAAAAAAAQAAAAYAAABwAAAAAgAAAAQAAACIAAAAAwAAAAIAAACYAAAABQAA"
+ "AAIAAACwAAAABgAAAAEAAADAAAAAAiAAAAYAAADgAAAAARAAAAEAAAAUAQAAAxAAAAIAAAAcAQAA"
+ "ASAAAAIAAAAkAQAAACAAAAEAAABKAQAAABAAAAEAAABYAQAA";
+
+TEST_F(DexFileVerifierTest, ProtoOrdering) {
+ {
+ // The input dex file should be good before modification.
+ std::string error_msg;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kProtoOrderingTestDex,
+ kLocationString,
+ &error_msg));
+ ASSERT_TRUE(raw.get() != nullptr) << error_msg;
+ }
+
+ // Modify the order of the ProtoIds for two overloads of "foo" with the
+ // same return type and one having longer parameter list than the other.
+ for (size_t i = 0; i != 2; ++i) {
+ VerifyModification(
+ kProtoOrderingTestDex,
+ "proto_ordering",
+ [i](DexFile* dex_file) {
+ uint32_t method_idx;
+ const uint8_t* data = FindMethodData(dex_file, "foo", &method_idx);
+ CHECK(data != nullptr);
+ // There should be 2 methods called "foo".
+ CHECK_LT(method_idx + 1u, dex_file->NumMethodIds());
+ CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_,
+ dex_file->GetMethodId(method_idx + 1).name_idx_);
+ CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_ + 1u,
+ dex_file->GetMethodId(method_idx + 1).proto_idx_);
+ // Their return types should be the same.
+ uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_;
+ const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx);
+ const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u);
+ CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_);
+ // And the first should not have any parameters while the second should have some.
+ CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext());
+ CHECK(DexFileParameterIterator(*dex_file, proto2).HasNext());
+ if (i == 0) {
+ // Swap the proto parameters and shorties to break the ordering.
+ std::swap(const_cast<uint32_t&>(proto1.parameters_off_),
+ const_cast<uint32_t&>(proto2.parameters_off_));
+ std::swap(const_cast<dex::StringIndex&>(proto1.shorty_idx_),
+ const_cast<dex::StringIndex&>(proto2.shorty_idx_));
+ } else {
+ // Copy the proto parameters and shorty to create duplicate proto id.
+ const_cast<uint32_t&>(proto1.parameters_off_) = proto2.parameters_off_;
+ const_cast<dex::StringIndex&>(proto1.shorty_idx_) = proto2.shorty_idx_;
+ }
+ },
+ "Out-of-order proto_id arguments");
+ }
+}
+
+// To generate a base64 encoded Dex file version 037 from Smali files, use:
+//
+// smali assemble --api 24 -o classes.dex class1.smali [class2.smali ...]
+// base64 classes.dex >classes.dex.base64
+
+// Dex file version 037 generated from:
+//
+// .class public LB28685551;
+// .super LB28685551;
+
+static const char kClassExtendsItselfTestDex[] =
+ "ZGV4CjAzNwDeGbgRg1kb6swszpcTWrrOAALB++F4OPT0AAAAcAAAAHhWNBIAAAAAAAAAAKgAAAAB"
+ "AAAAcAAAAAEAAAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAHgAAABcAAAAmAAAAJgA"
+ "AAAAAAAAAAAAAAEAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAALTEIyODY4NTU1MTsAAAAABgAA"
+ "AAAAAAABAAAAAAAAAAEAAAABAAAAcAAAAAIAAAABAAAAdAAAAAYAAAABAAAAeAAAAAIgAAABAAAA"
+ "mAAAAAAQAAABAAAAqAAAAA==";
+
+TEST_F(DexFileVerifierTest, ClassExtendsItself) {
+ VerifyModification(
+ kClassExtendsItselfTestDex,
+ "class_extends_itself",
+ [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
+ "Class with same type idx as its superclass: '0'");
+}
+
+// Dex file version 037 generated from:
+//
+// .class public LFoo;
+// .super LBar;
+//
+// and:
+//
+// .class public LBar;
+// .super LFoo;
+
+static const char kClassesExtendOneAnotherTestDex[] =
+ "ZGV4CjAzNwBXHSrwpDMwRBkg+L+JeQCuFNRLhQ86duEcAQAAcAAAAHhWNBIAAAAAAAAAANAAAAAC"
+ "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIAAAABcAAAAwAAAAMAA"
+ "AADHAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAABAAAAAQAA"
+ "AAAAAAD/////AAAAAAAAAAAAAAAABUxCYXI7AAVMRm9vOwAAAAYAAAAAAAAAAQAAAAAAAAABAAAA"
+ "AgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAgAAAIAAAAACIAAAAgAAAMAAAAAAEAAAAQAAANAAAAA=";
+
+TEST_F(DexFileVerifierTest, ClassesExtendOneAnother) {
+ VerifyModification(
+ kClassesExtendOneAnotherTestDex,
+ "classes_extend_one_another",
+ [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
+ "Invalid class definition ordering: class with type idx: '1' defined before"
+ " superclass with type idx: '0'");
+}
+
+// Dex file version 037 generated from:
+//
+// .class public LAll;
+// .super LYour;
+//
+// and:
+//
+// .class public LYour;
+// .super LBase;
+//
+// and:
+//
+// .class public LBase;
+// .super LAll;
+
+static const char kCircularClassInheritanceTestDex[] =
+ "ZGV4CjAzNwBMJxgP0SJz6oLXnKfl+J7lSEORLRwF5LNMAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAD"
+ "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAIgAAABkAAAA6AAAAOgA"
+ "AADvAAAA9wAAAAAAAAABAAAAAgAAAAEAAAABAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAgAA"
+ "AAEAAAABAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAAAAAA/////wAAAAAAAAAA"
+ "AAAAAAVMQWxsOwAGTEJhc2U7AAZMWW91cjsAAAYAAAAAAAAAAQAAAAAAAAABAAAAAwAAAHAAAAAC"
+ "AAAAAwAAAHwAAAAGAAAAAwAAAIgAAAACIAAAAwAAAOgAAAAAEAAAAQAAAAABAAA=";
+
+TEST_F(DexFileVerifierTest, CircularClassInheritance) {
+ VerifyModification(
+ kCircularClassInheritanceTestDex,
+ "circular_class_inheritance",
+ [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
+ "Invalid class definition ordering: class with type idx: '1' defined before"
+ " superclass with type idx: '0'");
+}
+
+// Dex file version 037 generated from:
+//
+// .class public abstract interface LInterfaceImplementsItself;
+// .super Ljava/lang/Object;
+// .implements LInterfaceImplementsItself;
+
+static const char kInterfaceImplementsItselfTestDex[] =
+ "ZGV4CjAzNwCKKrjatp8XbXl5S/bEVJnqaBhjZkQY4440AQAAcAAAAHhWNBIAAAAAAAAAANwAAAAC"
+ "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAIAAAACUAAAAoAAAAKAA"
+ "AAC9AAAAAAAAAAEAAAAAAAAAAQYAAAEAAADUAAAA/////wAAAAAAAAAAAAAAABtMSW50ZXJmYWNl"
+ "SW1wbGVtZW50c0l0c2VsZjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAAABAAAAAAAAAAcAAAAAAAAA"
+ "AQAAAAAAAAABAAAAAgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAQAAAIAAAAACIAAAAgAAAKAAAAAB"
+ "EAAAAQAAANQAAAAAEAAAAQAAANwAAAA=";
+
+TEST_F(DexFileVerifierTest, InterfaceImplementsItself) {
+ VerifyModification(
+ kInterfaceImplementsItselfTestDex,
+ "interface_implements_itself",
+ [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
+ "Class with same type idx as implemented interface: '0'");
+}
+
+// Dex file version 037 generated from:
+//
+// .class public abstract interface LPing;
+// .super Ljava/lang/Object;
+// .implements LPong;
+//
+// and:
+//
+// .class public abstract interface LPong;
+// .super Ljava/lang/Object;
+// .implements LPing;
+
+static const char kInterfacesImplementOneAnotherTestDex[] =
+ "ZGV4CjAzNwD0Kk9sxlYdg3Dy1Cff0gQCuJAQfEP6ohZUAQAAcAAAAHhWNBIAAAAAAAAAAPwAAAAD"
+ "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIgAAACMAAAAyAAAAMgA"
+ "AADQAAAA2AAAAAAAAAABAAAAAgAAAAEAAAABBgAAAgAAAOwAAAD/////AAAAAAAAAAAAAAAAAAAA"
+ "AAEGAAACAAAA9AAAAP////8AAAAAAAAAAAAAAAAGTFBpbmc7AAZMUG9uZzsAEkxqYXZhL2xhbmcv"
+ "T2JqZWN0OwABAAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAADAAAAcAAAAAIAAAAD"
+ "AAAAfAAAAAYAAAACAAAAiAAAAAIgAAADAAAAyAAAAAEQAAACAAAA7AAAAAAQAAABAAAA/AAAAA==";
+
+TEST_F(DexFileVerifierTest, InterfacesImplementOneAnother) {
+ VerifyModification(
+ kInterfacesImplementOneAnotherTestDex,
+ "interfaces_implement_one_another",
+ [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
+ "Invalid class definition ordering: class with type idx: '1' defined before"
+ " implemented interface with type idx: '0'");
+}
+
+// Dex file version 037 generated from:
+//
+// .class public abstract interface LA;
+// .super Ljava/lang/Object;
+// .implements LB;
+//
+// and:
+//
+// .class public abstract interface LB;
+// .super Ljava/lang/Object;
+// .implements LC;
+//
+// and:
+//
+// .class public abstract interface LC;
+// .super Ljava/lang/Object;
+// .implements LA;
+
+static const char kCircularInterfaceImplementationTestDex[] =
+ "ZGV4CjAzNwCzKmD5Fol6XAU6ichYHcUTIP7Z7MdTcEmEAQAAcAAAAHhWNBIAAAAAAAAAACwBAAAE"
+ "AAAAcAAAAAQAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAJAAAACUAAAA8AAAAPAA"
+ "AAD1AAAA+gAAAP8AAAAAAAAAAQAAAAIAAAADAAAAAgAAAAEGAAADAAAAHAEAAP////8AAAAAAAAA"
+ "AAAAAAABAAAAAQYAAAMAAAAUAQAA/////wAAAAAAAAAAAAAAAAAAAAABBgAAAwAAACQBAAD/////"
+ "AAAAAAAAAAAAAAAAA0xBOwADTEI7AANMQzsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAQAAAAIAAAAB"
+ "AAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAAEAAAAcAAAAAIAAAAEAAAAgAAAAAYA"
+ "AAADAAAAkAAAAAIgAAAEAAAA8AAAAAEQAAADAAAAFAEAAAAQAAABAAAALAEAAA==";
+
+TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) {
+ VerifyModification(
+ kCircularInterfaceImplementationTestDex,
+ "circular_interface_implementation",
+ [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
+ "Invalid class definition ordering: class with type idx: '2' defined before"
+ " implemented interface with type idx: '0'");
+}
+
+TEST_F(DexFileVerifierTest, Checksum) {
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kGoodTestDex, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+
+ // Good checksum: all pass.
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "good checksum, no verify",
+ /*verify_checksum*/ false,
+ &error_msg));
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "good checksum, verify",
+ /*verify_checksum*/ true,
+ &error_msg));
+
+ // Bad checksum: !verify_checksum passes verify_checksum fails.
+ DexFile::Header* header = reinterpret_cast<DexFile::Header*>(
+ const_cast<uint8_t*>(dex_file->Begin()));
+ header->checksum_ = 0;
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad checksum, no verify",
+ /*verify_checksum*/ false,
+ &error_msg));
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad checksum, verify",
+ /*verify_checksum*/ true,
+ &error_msg));
+ EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg;
+}
+
+TEST_F(DexFileVerifierTest, BadStaticMethodName) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LBadName;
+ // .super Ljava/lang/Object;
+ //
+ // .method public static <bad_name> (II)V
+ // .registers 2
+ // .prologue
+ // return-void
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // .prologue
+ // .line 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwC2NYlwyxEc/h6hv+hMeUVQPtiX6MQBcfgwAgAAcAAAAHhWNBIAAAAAAAAAAJABAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABAAQAA8AAAAPAA"
+ "AAD8AAAABAEAABIBAAAVAQAAIAEAADQBAAA3AQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA"
+ "AAcAAAADAAAAPAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA"
+ "AAAAAIABAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgAMQmFkTmFtZS5qYXZhAAFJAAlMQmFkTmFt"
+ "ZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgADVklJAAIAAAAAAAAAAAAAAAACAAAHAAEABw4AAAIA"
+ "AgAAAAAASAEAAAEAAAAOAAAAAQABAAEAAABOAQAABAAAAHAQAgAAAA4AAAACAAAJ1AIBgYAE6AIA"
+ "AA0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAAAgAAAKAAAAAFAAAA"
+ "AwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAADwBAAADEAAAAQAAAEQBAAAD"
+ "IAAAAgAAAEgBAAABIAAAAgAAAFQBAAAAIAAAAQAAAIABAAAAEAAAAQAAAJABAAA=";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad static method name",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadVirtualMethodName) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LBadVirtualName;
+ // .super Ljava/lang/Object;
+ //
+ // .method public <bad_name> (II)V
+ // .registers 2
+ // return-void
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwDcPC8B2E7kYTZmeHX2u2IqrpWV9EXBHpE8AgAAcAAAAHhWNBIAAAAAAAAAAJwBAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABMAQAA8AAAAPAA"
+ "AAD8AAAABAEAABkBAAAcAQAALgEAAEIBAABFAQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA"
+ "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA"
+ "AAAAAI4BAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgATQmFkVmlydHVhbE5hbWUuamF2YQABSQAQ"
+ "TEJhZFZpcnR1YWxOYW1lOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAANWSUkAAAACAAAAAAAAAAAA"
+ "AAABAAcOAAACAAAHAAABAAEAAQAAAFgBAAAEAAAAcBACAAAADgADAAMAAAAAAF0BAAABAAAADgAA"
+ "AAEBAYGABOQCAAH8Ag0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAA"
+ "AgAAAKAAAAAFAAAAAwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAAEwBAAAD"
+ "EAAAAQAAAFQBAAADIAAAAgAAAFgBAAABIAAAAgAAAGQBAAAAIAAAAQAAAI4BAAAAEAAAAQAAAJwB"
+ "AAA=";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad virtual method name",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadClinitSignature) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LOneClinitBadSig;
+ // .super Ljava/lang/Object;
+ //
+ // .method public static constructor <clinit>(II)V
+ // .registers 2
+ // return-void
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwBNOwTbfJmWq5eMOlxUY4EICGiEGJMVg8RAAgAAcAAAAHhWNBIAAAAAAAAAAKABAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABQAQAA8AAAAPAA"
+ "AAD6AAAAAgEAAAUBAAAYAQAALAEAAEIBAABFAQAAAgAAAAMAAAAEAAAABgAAAAYAAAADAAAAAAAA"
+ "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAAFAAAA"
+ "AAAAAJABAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AAUkAEUxPbmVDbGluaXRCYWRTaWc7ABJMamF2"
+ "YS9sYW5nL09iamVjdDsAFE9uZUNsaW5pdEJhZFNpZy5qYXZhAAFWAANWSUkAAAACAAAAAAAAAAAA"
+ "AAAAAgAABwABAAcOAAACAAIAAAAAAFgBAAABAAAADgAAAAEAAQABAAAAXgEAAAQAAABwEAIAAAAO"
+ "AAAAAgAAiYAE5AIBgYAE+AINAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAA"
+ "AwAAAAIAAACgAAAABQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABM"
+ "AQAAAxAAAAEAAABUAQAAAyAAAAIAAABYAQAAASAAAAIAAABkAQAAACAAAAEAAACQAQAAABAAAAEA"
+ "AACgAQAA";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad clinit signature",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadClinitSignatureAgain) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LOneClinitBadSigAgain;
+ // .super Ljava/lang/Object;
+ //
+ // .method public static constructor <clinit>()I
+ // .registers 1
+ // const/4 v0, 1
+ // return v0
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwBfPcPu5NVwKUqZIu/YR8xqVlVD5UzTk0gEAgAAcAAAAHhWNBIAAAAAAAAAAIgBAAAH"
+ "AAAAcAAAAAQAAACMAAAAAgAAAJwAAAAAAAAAAAAAAAMAAAC0AAAAAQAAAMwAAAAYAQAA7AAAAOwA"
+ "AAD2AAAA/gAAAAEBAAAZAQAALQEAAEgBAAACAAAAAwAAAAQAAAAGAAAAAgAAAAAAAAAAAAAABgAA"
+ "AAMAAAAAAAAAAQAAAAAAAAABAAEAAQAAAAIAAQABAAAAAQAAAAEAAAACAAAAAAAAAAUAAAAAAAAA"
+ "eAEAAAAAAAAIPGNsaW5pdD4ABjxpbml0PgABSQAWTE9uZUNsaW5pdEJhZFNpZ0FnYWluOwASTGph"
+ "dmEvbGFuZy9PYmplY3Q7ABlPbmVDbGluaXRCYWRTaWdBZ2Fpbi5qYXZhAAFWAAABAAAAAAAAAAAA"
+ "AAACAAAAEhAPAAEAAQABAAAAAAAAAAQAAABwEAIAAAAOAAAAAgAAiYAEzAIBgYAE4AIKAAAAAAAA"
+ "AAEAAAAAAAAAAQAAAAcAAABwAAAAAgAAAAQAAACMAAAAAwAAAAIAAACcAAAABQAAAAMAAAC0AAAA"
+ "BgAAAAEAAADMAAAAAiAAAAcAAADsAAAAASAAAAIAAABMAQAAACAAAAEAAAB4AQAAABAAAAEAAACI"
+ "AQAA";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad clinit signature",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadInitSignature) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LBadInitSig;
+ // .super Ljava/lang/Object;
+ //
+ // .method public constructor <init>()I
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // const v0, 1
+ // return v0
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwCdMdeh1KoHWamF2Prq32LF39YZ78fV7q+wAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
+ "AAAAcAAAAAQAAACEAAAAAgAAAJQAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAADUAAAA3AAAANwA"
+ "AADkAAAA5wAAAPUAAAAJAQAAAQAAAAIAAAADAAAABAAAAAEAAAAAAAAAAAAAAAQAAAADAAAAAAAA"
+ "AAEAAAAAAAAAAgABAAAAAAABAAAAAQAAAAIAAAAAAAAA/////wAAAAAqAQAAAAAAAAY8aW5pdD4A"
+ "AUkADExCYWRJbml0U2lnOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEAAQABAAAAAAAAAAcAAABw"
+ "EAEAAAAUAAEAAAAPAAAAAQAAgYAEjAIKAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAQA"
+ "AACEAAAAAwAAAAIAAACUAAAABQAAAAIAAACsAAAABgAAAAEAAAC8AAAAAiAAAAUAAADcAAAAASAA"
+ "AAEAAAAMAQAAACAAAAEAAAAqAQAAABAAAAEAAAA0AQAA";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad init signature",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+static const char* kInvokeCustomDexFiles[] = {
+ // TODO(oth): Revisit this test when we have smali / dx support.
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test001/Tests.java
+ "ZGV4CjAzOAAEj12s/acmmdGuDL92SWSBh6iLBjxgomWkCAAAcAAAAHhWNBIAAAAAAAAAALwHAAAx"
+ "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAAAMBgAAmAIAAMID"
+ "AADKAwAAzQMAANIDAADhAwAA5AMAAOoDAAAfBAAAUgQAAIMEAAC4BAAA1AQAAOsEAAD+BAAAEgUA"
+ "ACYFAAA6BQAAUQUAAG4FAACTBQAAtAUAAN0FAAD/BQAAHgYAADgGAABKBgAAVgYAAFkGAABdBgAA"
+ "YgYAAGYGAAB7BgAAgAYAAI8GAACdBgAAtAYAAMMGAADSBgAA3gYAAPIGAAD4BgAABgcAAA4HAAAU"
+ "BwAAGgcAAB8HAAAoBwAANAcAADoHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A"
+ "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA"
+ "AAAAAACMAwAABQAAAAwAAACUAwAABQAAAA4AAACgAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA"
+ "GwAAABQAAACsAwAAHAAAABQAAACMAwAAHQAAABQAAAC0AwAAHQAAABQAAAC8AwAAAwADAAMAAAAE"
+ "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA"
+ "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAJYHAACWBwAABAAA"
+ "AAEAAAAIAAAAAAAAABkAAABkAwAAnQcAAAAAAAAEAAAAAgAAAAEAAABjBwAAAQAAAIsHAAACAAAA"
+ "iwcAAJMHAAABAAEAAQAAAEEHAAAEAAAAcBAGAAAADgADAAIAAAAAAEYHAAADAAAAkAABAg8AAAAF"
+ "AAMABAAAAE0HAAAQAAAAcQAJAAAADAAcAQQAbkAIABBDDAAiAQ0AcCAHAAEAEQEEAAEAAgAAAFYH"
+ "AAAMAAAAYgACABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAABdBwAACwAAABIgEjH8IAEAEAAK"
+ "ABJRcSAKAAEADgAAAAAAAAAAAAAAAwAAAAAAAAABAAAAmAIAAAIAAACgAgAABAAAAKgCAAACAAAA"
+ "AAAAAAMAAAAPAAkAEQAAAAMAAAAHAAkAEQAAAAEAAAAAAAAAAQAAAA4AAAABAAAAFQAGPGluaXQ+"
+ "AAFJAANJSUkADUlOVk9LRV9TVEFUSUMAAUwABExMTEwAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90"
+ "YXRpb25zL0NhbGxlZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlv"
+ "bnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0"
+ "aG9kSGFuZGxlS2luZDsAM0xjb20vYW5kcm9pZC9qYWNrL2phdmE3L2ludm9rZWN1c3RvbS90ZXN0"
+ "MDAxL1Rlc3RzOwAaTGRhbHZpay9hbm5vdGF0aW9uL1Rocm93czsAFUxqYXZhL2lvL1ByaW50U3Ry"
+ "ZWFtOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9T"
+ "dHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEv"
+ "bGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRl"
+ "OwAfTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0"
+ "aG9kSGFuZGxlcyRMb29rdXA7ACBMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGph"
+ "dmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTsAGExqdW5pdC9mcmFtZXdvcmsvQXNzZXJ0OwAQTG9y"
+ "Zy9qdW5pdC9UZXN0OwAKVGVzdHMuamF2YQABVgACVkkAA1ZJSQACVkwAE1tMamF2YS9sYW5nL1N0"
+ "cmluZzsAA2FkZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQu"
+ "MC1lbmcADWVuY2xvc2luZ1R5cGUADWZpZWxkQ2FsbFNpdGUACmZpbmRTdGF0aWMAEmludm9rZU1l"
+ "dGhvZEhhbmRsZQAEa2luZAAMbGlua2VyTWV0aG9kAAZsb29rdXAABG1haW4ABG5hbWUAA291dAAH"
+ "cHJpbnRsbgAKcmV0dXJuVHlwZQAEdGVzdAAFdmFsdWUAIgAHDgAvAgAABw4ANQMAAAAHDqUAPwEA"
+ "Bw60ADsABw6lAAABBCAcAhgAGAAmHAEdAgQgHAMYDxgJGBEjGAQnGwArFygrFx8uGAACBQEwHAEY"
+ "CwETAAMWABcfFQABAAQBAQkAgYAEtAUBCswFAQrkBQEJlAYEAbwGAAAAEwAAAAAAAAABAAAAAAAA"
+ "AAEAAAAxAAAAcAAAAAIAAAAWAAAANAEAAAMAAAAJAAAAjAEAAAQAAAADAAAA+AEAAAUAAAALAAAA"
+ "EAIAAAcAAAACAAAAaAIAAAYAAAABAAAAcAIAAAgAAAABAAAAkAIAAAMQAAADAAAAmAIAAAEgAAAF"
+ "AAAAtAIAAAYgAAABAAAAZAMAAAEQAAAGAAAAjAMAAAIgAAAxAAAAwgMAAAMgAAAFAAAAQQcAAAQg"
+ "AAADAAAAYwcAAAUgAAABAAAAlgcAAAAgAAABAAAAnQcAAAAQAAABAAAAvAcAAA==",
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test002/Tests.java
+ "ZGV4CjAzOAAzq3aGAwKhT4QQj4lqNfZJAO8Tm24uTyNICQAAcAAAAHhWNBIAAAAAAAAAAGAIAAA2"
+ "AAAAcAAAABgAAABIAQAACQAAAKgBAAAEAAAAFAIAAA0AAAA0AgAAAQAAAKQCAAB8BgAAzAIAACYE"
+ "AAAwBAAAOAQAAEQEAABHBAAATAQAAE8EAABVBAAAigQAALwEAADtBAAAIgUAAD4FAABVBQAAaAUA"
+ "AH0FAACRBQAApQUAALkFAADQBQAA7QUAABIGAAAzBgAAXAYAAH4GAACdBgAAtwYAAMkGAADPBgAA"
+ "2wYAAN4GAADiBgAA5wYAAOsGAAD/BgAAFAcAABkHAAAoBwAANgcAAE0HAABcBwAAawcAAH4HAACK"
+ "BwAAkAcAAJgHAACeBwAAqgcAALAHAAC1BwAAxgcAAM8HAADbBwAA4QcAAAMAAAAHAAAACAAAAAkA"
+ "AAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAA"
+ "ABgAAAAZAAAAGgAAAB0AAAAhAAAAIgAAAAQAAAAAAAAA8AMAAAYAAAAPAAAA+AMAAAUAAAAQAAAA"
+ "AAAAAAYAAAASAAAABAQAAB0AAAAVAAAAAAAAAB4AAAAVAAAAEAQAAB8AAAAVAAAA8AMAACAAAAAV"
+ "AAAAGAQAACAAAAAVAAAAIAQAAAMAAwACAAAABAANACgAAAAIAAcAGwAAAAsABgAwAAAABAAEAAAA"
+ "AAAEAAQAAQAAAAQAAAAjAAAABAAIAC0AAAAEAAQANAAAAAYABQAyAAAACQAEAAEAAAAMAAQAMQAA"
+ "AA4ABwABAAAAEAABACoAAAARAAIALAAAABIAAwAuAAAAEwAGACUAAAA4CAAAOAgAAAQAAAABAAAA"
+ "CQAAAAAAAAAcAAAA0AMAAD8IAAAAAAAAAQAAAAEAAAABAAAADggAAAIAAAAtCAAANQgAAAgAAAAE"
+ "AAEA6AcAACoAAABxAAoAAAAMABwBBAAbAiMAAABiAwIAYgQCABIVI1UWAGIGAgASB00GBQdxMAsA"
+ "QwUMA25ACQAQMgwAIgEOAHAgCAABAGkBAQAOAA0AbhAHAAAAKPsAAAAAJAABAAEBDCUBAAEAAQAA"
+ "APUHAAAEAAAAcBAGAAAADgADAAIAAAAAAPoHAAADAAAAkAABAg8AAAAEAAEAAgAAAAEIAAAMAAAA"
+ "YgADABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAAAICAAACwAAABIgEjH8IAEAEAAKABJRcSAM"
+ "AAEADgAAAAAAAAAAAAAAAgAAAAAAAAACAAAAzAIAAAQAAADUAgAAAgAAAAAAAAADAAAABwAKABIA"
+ "AAADAAAABwAHABYAAAABAAAAAAAAAAEAAAAPAAAAAQAAABcACDxjbGluaXQ+AAY8aW5pdD4ACkdF"
+ "VF9TVEFUSUMAAUkAA0lJSQABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMv"
+ "Q2FsbGVkQnlJbnZva2VDdXN0b207ADBMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5r"
+ "ZXJGaWVsZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRs"
+ "ZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwMi9UZXN0"
+ "czsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEUxq"
+ "YXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVjdDsA"
+ "EkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABVMamF2YS9sYW5nL1Rocm93"
+ "YWJsZTsAG0xqYXZhL2xhbmcvaW52b2tlL0NhbGxTaXRlOwAjTGphdmEvbGFuZy9pbnZva2UvQ29u"
+ "c3RhbnRDYWxsU2l0ZTsAH0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTsAJ0xqYXZhL2xh"
+ "bmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwOwAgTGphdmEvbGFuZy9pbnZva2UvTWV0aG9k"
+ "SGFuZGxlczsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ABhManVuaXQvZnJhbWV3b3Jr"
+ "L0Fzc2VydDsAEExvcmcvanVuaXQvVGVzdDsABFRZUEUAClRlc3RzLmphdmEAAVYAAlZJAANWSUkA"
+ "AlZMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1l"
+ "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQuMC1lbmcADWVuY2xvc2luZ1R5"
+ "cGUADWZpZWxkQ2FsbFNpdGUAEWZpZWxkTWV0aG9kSGFuZGxlAApmaW5kU3RhdGljAARraW5kAAZs"
+ "b29rdXAABG1haW4ACm1ldGhvZFR5cGUABG5hbWUAA291dAAPcHJpbnRTdGFja1RyYWNlAAdwcmlu"
+ "dGxuAApyZXR1cm5UeXBlAAR0ZXN0AAV2YWx1ZQAoAAcOAR0PAnh3Jh4AIQAHDgA2AgAABw4APwEA"
+ "Bw60ADsABw6lAAABBCQcAhgAGAApHAEdAgMnGAQrGwAvFygvFyMzGAACBQE1HAEYDAEUAAMWABcj"
+ "FQABAAQBAQkAiIAE4AUBgYAE0AYBCugGAQmABwQBqAcAAAATAAAAAAAAAAEAAAAAAAAAAQAAADYA"
+ "AABwAAAAAgAAABgAAABIAQAAAwAAAAkAAACoAQAABAAAAAQAAAAUAgAABQAAAA0AAAA0AgAABwAA"
+ "AAIAAACcAgAABgAAAAEAAACkAgAACAAAAAEAAADEAgAAAxAAAAIAAADMAgAAASAAAAUAAADgAgAA"
+ "BiAAAAEAAADQAwAAARAAAAYAAADwAwAAAiAAADYAAAAmBAAAAyAAAAUAAADoBwAABCAAAAMAAAAO"
+ "CAAABSAAAAEAAAA4CAAAACAAAAEAAAA/CAAAABAAAAEAAABgCAAA",
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test003/Tests.java
+ "ZGV4CjAzOABjnhkFatj30/7cHTCJsfr7vAjz9/p+Y+TcCAAAcAAAAHhWNBIAAAAAAAAAAPQHAAAx"
+ "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAABEBgAAmAIAAOoD"
+ "AADyAwAA9QMAAP4DAAANBAAAEAQAABYEAABLBAAAfgQAAK8EAADkBAAAAAUAABcFAAAqBQAAPgUA"
+ "AFIFAABmBQAAfQUAAJoFAAC/BQAA4AUAAAkGAAArBgAASgYAAGQGAAB2BgAAggYAAIUGAACJBgAA"
+ "jgYAAJIGAACnBgAArAYAALsGAADJBgAA4AYAAO8GAAD+BgAACgcAAB4HAAAkBwAAMgcAADoHAABA"
+ "BwAARgcAAEsHAABUBwAAYAcAAGYHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A"
+ "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA"
+ "AAAAAACkAwAABQAAAAwAAAC0AwAABQAAAA4AAADAAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA"
+ "GwAAABQAAADMAwAAHAAAABQAAADUAwAAHQAAABQAAADcAwAAHQAAABQAAADkAwAAAwADAAMAAAAE"
+ "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA"
+ "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAM4HAADOBwAABAAA"
+ "AAEAAAAIAAAAAAAAABkAAAB8AwAA1QcAAAAAAAAEAAAAAgAAAAEAAACTBwAAAQAAAMMHAAACAAAA"
+ "wwcAAMsHAAABAAEAAQAAAG0HAAAEAAAAcBAGAAAADgAHAAYAAAAAAHIHAAAHAAAAkAABArAwsECw"
+ "ULBgDwAAAAUAAwAEAAAAfQcAABAAAABxAAkAAAAMABwBBABuQAgAEEMMACIBDQBwIAcAAQARAQgA"
+ "AQACAAAAhgcAABAAAABiBgIAEhASIRIyEkMSVBJl/QYAAAAACgBuIAUABgAOAAcAAQACAAAAjQcA"
+ "ABAAAAASEBIhEjISQxJUEmX9BgEAAAAKABMBFQBxIAoAAQAOAAAAAAAAAAAAAwAAAAAAAAABAAAA"
+ "mAIAAAIAAACgAgAABAAAAKgCAAAGAAAAAAAAAAAAAAAAAAAAAwAAAA8ACQARAAAAAwAAAAcACQAR"
+ "AAAAAQAAAAAAAAACAAAAAAAAAAEAAAAOAAAAAQAAABUABjxpbml0PgABSQAHSUlJSUlJSQANSU5W"
+ "T0tFX1NUQVRJQwABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvQ2FsbGVk"
+ "QnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5rZXJNZXRo"
+ "b2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9NZXRob2RIYW5kbGVLaW5k"
+ "OwAzTGNvbS9hbmRyb2lkL2phY2svamF2YTcvaW52b2tlY3VzdG9tL3Rlc3QwMDMvVGVzdHM7ABpM"
+ "ZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABFMamF2YS9s"
+ "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh"
+ "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D"
+ "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n"
+ "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv"
+ "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r"
+ "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7"
+ "AApUZXN0cy5qYXZhAAFWAAJWSQADVklJAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwADYWRkAA1h"
+ "cmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAFWVtaXR0ZXI6IGphY2stNC4wLWVuZwANZW5jbG9z"
+ "aW5nVHlwZQANZmllbGRDYWxsU2l0ZQAKZmluZFN0YXRpYwASaW52b2tlTWV0aG9kSGFuZGxlAARr"
+ "aW5kAAxsaW5rZXJNZXRob2QABmxvb2t1cAAEbWFpbgAEbmFtZQADb3V0AAdwcmludGxuAApyZXR1"
+ "cm5UeXBlAAR0ZXN0AAV2YWx1ZQAiAAcOAC8GAAAAAAAABw4ANQMAAAAHDqUAPwEABw7wADsABw7w"
+ "AAABBCAcBhgAGAAYABgAGAAYACYcAR0CBCAcAxgPGAkYESMYBCcbACsXKCsXHy4YAAIFATAcARgL"
+ "ARMAAxYAFx8VAAEABAEBCQCBgAS0BQEKzAUBCuwFAQmcBgQBzAYAAAATAAAAAAAAAAEAAAAAAAAA"
+ "AQAAADEAAABwAAAAAgAAABYAAAA0AQAAAwAAAAkAAACMAQAABAAAAAMAAAD4AQAABQAAAAsAAAAQ"
+ "AgAABwAAAAIAAABoAgAABgAAAAEAAABwAgAACAAAAAEAAACQAgAAAxAAAAMAAACYAgAAASAAAAUA"
+ "AAC0AgAABiAAAAEAAAB8AwAAARAAAAcAAACkAwAAAiAAADEAAADqAwAAAyAAAAUAAABtBwAABCAA"
+ "AAMAAACTBwAABSAAAAEAAADOBwAAACAAAAEAAADVBwAAABAAAAEAAAD0BwAA",
+ // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test004/Tests.java
+ "ZGV4CjAzOABvUVfbV74qWbSOEsgKP+EzahlNQLW2/8TMDAAAcAAAAHhWNBIAAAAAAAAAAOQLAABS"
+ "AAAAcAAAAB8AAAC4AQAAEAAAADQCAAADAAAA9AIAABIAAAAMAwAAAQAAAKQDAAAACQAAzAMAANYF"
+ "AADZBQAA4QUAAOkFAADsBQAA7wUAAPIFAAD1BQAA/AUAAP8FAAAEBgAAEwYAABYGAAAZBgAAHwYA"
+ "AC8GAABkBgAAjQYAAMAGAADxBgAAJgcAAEUHAABhBwAAeAcAAIoHAACdBwAAsQcAAMUHAADZBwAA"
+ "8AcAAA0IAAAyCAAAUwgAAHwIAACeCAAAvQgAANcIAADpCAAA7AgAAPgIAAD7CAAAAAkAAAYJAAAM"
+ "CQAAEAkAABUJAAAaCQAAHgkAACMJAAAnCQAAKgkAADMJAABICQAATQkAAFwJAABqCQAAdgkAAIQJ"
+ "AACPCQAAmgkAAKYJAACzCQAAygkAANkJAADoCQAA9AkAAAAKAAAKCgAAHgoAACQKAAAyCgAAPQoA"
+ "AEUKAABLCgAAYgoAAGgKAABtCgAAdgoAAIIKAACOCgAAmwoAAKEKAAADAAAABAAAAAUAAAAGAAAA"
+ "CAAAAAsAAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABgAAAAZAAAAGgAAABsAAAAc"
+ "AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJwAAADEAAAAzAAAACQAAAAQA"
+ "AABMBQAADgAAABMAAABUBQAADQAAABUAAAB0BQAADAAAABYAAAAAAAAAJwAAABwAAAAAAAAAKAAA"
+ "ABwAAACABQAAKQAAABwAAACIBQAAKgAAABwAAACUBQAAKwAAABwAAACgBQAALAAAABwAAABMBQAA"
+ "LQAAABwAAACoBQAALwAAABwAAACwBQAALwAAABwAAAC4BQAALgAAABwAAADABQAAMAAAABwAAADI"
+ "BQAALgAAABwAAADQBQAACQAJAAoAAAAKABMAPwAAABEADQBLAAAACgAEAAIAAAAKAAAANAAAAAoA"
+ "AQBFAAAACgAPAEgAAAAKAAQAUAAAAA0ACABMAAAADwAEAAIAAAAUAA0AAgAAABYAAgBAAAAAFwAD"
+ "AEcAAAAZAAUANgAAABkABgA2AAAAGQAHADYAAAAZAAkANgAAABkACgA2AAAAGQALADYAAAAZAAwA"
+ "NgAAABkADgA3AAAAnQsAAJ0LAAAKAAAAAQAAAA8AAAAAAAAAJgAAACQFAADGCwAAAAAAAAQAAAAC"
+ "AAAAAQAAAN4KAAACAAAAegsAAJILAAACAAAAkgsAAJoLAAABAAEAAQAAAKgKAAAEAAAAcBAGAAAA"
+ "DgADAAIAAAAAAK0KAAADAAAAkAABAg8AAAAYAA8ABgAAALQKAABTAAAAcRARAAwAEhJxIA0A0gAT"
+ "AmEAcSAKAOIAEwIABHEgDQDyABISAgAQAHEgDQACABICFAOamTFBAgARAHEwDAADAhYGAAAYApqZ"
+ "mZmZmQFABQQSAHcGCwACABsCBwAAAAgAFABxIBAAAgAcAgoACAAVAHEgDwACABcCFc1bBwUAFgBx"
+ "QA4AMhBxAAkAAAAMAhwDCgBuQAgAMroMAiIDFABwIAcAIwARAwAABAABAAIAAADRCgAADAAAAGIA"
+ "AgASIRIy/CAAACEACgFuIAUAEAAOAAMAAQACAAAA2AoAAAsAAAASIBIx/CABABAACgASUXEgDQAB"
+ "AA4AAAAAAAAAAAAAAAMAAAAAAAAAAQAAAMwDAAACAAAA1AMAAAQAAADgAwAAAgAAAAQABAANAAAA"
+ "FgAQABgAHQAAAAEAGwAEAAMAAgAQAA4ABQAAAAMAAAAOABAAGAAAAAIAAAABAAEAAwAAAAIAAgAC"
+ "AAAAAwAAAAMAAwADAAAAAQAAAAQAAAACAAAABQAFAAIAAAAPAA8AAgAAABAAEAABAAAAFQAAAAEA"
+ "AAAdAAAAAQAAAB4AASgABjwqPjtKKQAGPGluaXQ+AAFCAAFDAAFEAAFGAAVIZWxsbwABSQADSUlJ"
+ "AA1JTlZPS0VfU1RBVElDAAFKAAFMAARMTExMAA5MTExMWkJDU0lGRExMSgAzTGNvbS9hbmRyb2lk"
+ "L2phY2svYW5ub3RhdGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ACdMY29tL2FuZHJvaWQvamFj"
+ "ay9hbm5vdGF0aW9ucy9Db25zdGFudDsAMUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xp"
+ "bmtlck1ldGhvZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhh"
+ "bmRsZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwNC9U"
+ "ZXN0czsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlrL2Fubm90YXRpb24v"
+ "VGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s"
+ "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh"
+ "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D"
+ "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n"
+ "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv"
+ "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r"
+ "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7"
+ "AAFTAApUZXN0cy5qYXZhAAFWAANWQ0MABFZEREQABFZGRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZM"
+ "TAACVloAAVoAB1pCQ1NJRkQAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1lbnRUeXBl"
+ "cwAMYXNzZXJ0RXF1YWxzAAphc3NlcnRUcnVlAAxib29sZWFuVmFsdWUACWJ5dGVWYWx1ZQAJY2hh"
+ "clZhbHVlAApjbGFzc1ZhbHVlAAtkb3VibGVWYWx1ZQAVZW1pdHRlcjogamFjay00LjAtZW5nAA1l"
+ "bmNsb3NpbmdUeXBlAA1maWVsZENhbGxTaXRlAApmaW5kU3RhdGljAApmbG9hdFZhbHVlAAhpbnRW"
+ "YWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAxsaW5rZXJNZXRob2QACWxvbmdWYWx1ZQAG"
+ "bG9va3VwAARtYWluABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MABG5hbWUAA291dAAHcHJpbnRsbgAK"
+ "cmV0dXJuVHlwZQAKc2hvcnRWYWx1ZQALc3RyaW5nVmFsdWUABHRlc3QABXZhbHVlACMABw4ANwIA"
+ "AAcOAD4NAAAAAAAAAAAAAAAAAAcOPEtaWmmWw4d4h6UAUgEABw60AE4ABw6lAAAGBTUcAhgEGARD"
+ "HAEdCAQ1HA0YFhgQGBgYHRgAGAEYGxgEGAMYAhgQGA4YBT4YCkQbAEoXRUkcCh0HATgcAT8dBwE5"
+ "HAEAAR0HATocAQNhHQcBThwBIgAEHQcBQhwBBAEdBwFBHAFwmpkxQR0HATwcAfGamZmZmZkBQB0H"
+ "AU8cARcHHQcBOxwBGAodBwFGHAFmFc1bB0oXNE0YBAILAVEcCRcAFyAXGhciFzIXGhcXFwEXHQIM"
+ "AVEcARgSARoADRYAFzQVAAQBBAEEYSQABAQBcJqZMUHxmpmZmZmZAUAXBxgKZhXNWwcBAAQBAQkA"
+ "gYAE7AcBCoQIAQqcCAEJ1AkEAfwJAAATAAAAAAAAAAEAAAAAAAAAAQAAAFIAAABwAAAAAgAAAB8A"
+ "AAC4AQAAAwAAABAAAAA0AgAABAAAAAMAAAD0AgAABQAAABIAAAAMAwAABwAAAAIAAACcAwAABgAA"
+ "AAEAAACkAwAACAAAAAEAAADEAwAAAxAAAAMAAADMAwAAASAAAAUAAADsAwAABiAAAAEAAAAkBQAA"
+ "ARAAAA0AAABMBQAAAiAAAFIAAADWBQAAAyAAAAUAAACoCgAABCAAAAQAAADeCgAABSAAAAEAAACd"
+ "CwAAACAAAAEAAADGCwAAABAAAAEAAADkCwAA"
+};
+
+TEST_F(DexFileVerifierTest, InvokeCustomDexSamples) {
+ for (size_t i = 0; i < arraysize(kInvokeCustomDexFiles); ++i) {
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kInvokeCustomDexFiles[i], &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "good checksum, verify",
+ /*verify_checksum*/ true,
+ &error_msg));
+ // TODO(oth): Test corruptions (b/35308502)
+ }
+}
+
+TEST_F(DexFileVerifierTest, BadStaticFieldInitialValuesArray) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LBadStaticFieldInitialValuesArray;
+ // .super Ljava/lang/Object;
+ //
+ // # static fields
+ // .field static final c:C = 'c'
+ // .field static final i:I = 0x1
+ // .field static final s:Ljava/lang/String; = "s"
+ //
+ // # direct methods
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ // Output file was hex edited so that static field "i" has string typing in initial values array.
+ static const char kDexBase64[] =
+ "ZGV4CjAzNQBrMi4cCPcMvvXNRw0uI6RRubwMPwgEYXIsAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAL"
+ "AAAAcAAAAAYAAACcAAAAAQAAALQAAAADAAAAwAAAAAIAAADYAAAAAQAAAOgAAAAkAQAACAEAACAB"
+ "AAAoAQAAMAEAADMBAAA2AQAAOwEAAE8BAABjAQAAZgEAAGkBAABsAQAAAgAAAAMAAAAEAAAABQAA"
+ "AAYAAAAHAAAABwAAAAUAAAAAAAAAAgAAAAgAAAACAAEACQAAAAIABAAKAAAAAgAAAAAAAAADAAAA"
+ "AAAAAAIAAAABAAAAAwAAAAAAAAABAAAAAAAAAHsBAAB0AQAAAQABAAEAAABvAQAABAAAAHAQAQAA"
+ "AA4ABjxpbml0PgAGQS5qYXZhAAFDAAFJAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv"
+ "bGFuZy9TdHJpbmc7AAFWAAFjAAFpAAFzAAEABw4AAwNjFwoXCgMAAQAAGAEYARgAgYAEiAIADQAA"
+ "AAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAGAAAAnAAAAAMAAAABAAAAtAAAAAQAAAADAAAA"
+ "wAAAAAUAAAACAAAA2AAAAAYAAAABAAAA6AAAAAEgAAABAAAACAEAAAIgAAALAAAAIAEAAAMgAAAB"
+ "AAAAbwEAAAUgAAABAAAAdAEAAAAgAAABAAAAewEAAAAQAAABAAAAjAEAAA==";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad static field initial values array",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, GoodStaticFieldInitialValuesArray) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LGoodStaticFieldInitialValuesArray;
+ // .super Ljava/lang/Object;
+ //
+ // # static fields
+ // .field static final b:B = 0x1t
+ // .field static final c:C = 'c'
+ // .field static final d:D = 0.6
+ // .field static final f:F = 0.5f
+ // .field static final i:I = 0x3
+ // .field static final j:J = 0x4L
+ // .field static final l1:Ljava/lang/String;
+ // .field static final l2:Ljava/lang/String; = "s"
+ // .field static final l3:Ljava/lang/Class; = Ljava/lang/String;
+ // .field static final s:S = 0x2s
+ // .field static final z:Z = true
+ //
+ // # direct methods
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ static const char kDexBase64[] =
+ "ZGV4CjAzNQAwWxLbdhFa1NGiFWjsy5fhUCHxe5QHtPY8AwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAZ"
+ "AAAAcAAAAA0AAADUAAAAAQAAAAgBAAALAAAAFAEAAAIAAABsAQAAAQAAAHwBAACgAQAAnAEAAJwB"
+ "AACkAQAApwEAAKoBAACtAQAAsAEAALMBAAC2AQAA2wEAAO4BAAACAgAAFgIAABkCAAAcAgAAHwIA"
+ "ACICAAAlAgAAKAIAACsCAAAuAgAAMQIAADUCAAA5AgAAPQIAAEACAAABAAAAAgAAAAMAAAAEAAAA"
+ "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADAAAAAsAAAAAAAAABgAAAA4AAAAG"
+ "AAEADwAAAAYAAgAQAAAABgADABEAAAAGAAQAEgAAAAYABQATAAAABgAJABQAAAAGAAkAFQAAAAYA"
+ "BwAWAAAABgAKABcAAAAGAAwAGAAAAAYAAAAAAAAACAAAAAAAAAAGAAAAAQAAAAgAAAAAAAAA////"
+ "/wAAAAB8AgAARAIAAAY8aW5pdD4AAUIAAUMAAUQAAUYAAUkAAUoAI0xHb29kU3RhdGljRmllbGRJ"
+ "bml0aWFsVmFsdWVzQXJyYXk7ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7"
+ "ABJMamF2YS9sYW5nL1N0cmluZzsAAVMAAVYAAVoAAWIAAWMAAWQAAWYAAWkAAWoAAmwxAAJsMgAC"
+ "bDMAAXMAAXoAAAsAAQNj8TMzMzMzM+M/ED8EAwYEHhcXGAkCAj8AAAAAAQABAAEAAAAAAAAABAAA"
+ "AHAQAQAAAA4ACwABAAAYARgBGAEYARgBGAEYARgBGAEYARgAgYAE5AQNAAAAAAAAAAEAAAAAAAAA"
+ "AQAAABkAAABwAAAAAgAAAA0AAADUAAAAAwAAAAEAAAAIAQAABAAAAAsAAAAUAQAABQAAAAIAAABs"
+ "AQAABgAAAAEAAAB8AQAAAiAAABkAAACcAQAABSAAAAEAAABEAgAAAxAAAAEAAABgAgAAASAAAAEA"
+ "AABkAgAAACAAAAEAAAB8AgAAABAAAAEAAACcAgAA";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "good static field initial values array",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+} // namespace art
diff --git a/libdexfile/dex/dex_instruction-inl.h b/libdexfile/dex/dex_instruction-inl.h
new file mode 100644
index 0000000000..6bef18c85f
--- /dev/null
+++ b/libdexfile/dex/dex_instruction-inl.h
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_INL_H_
+#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_INL_H_
+
+#include "dex_instruction.h"
+
+namespace art {
+
+//------------------------------------------------------------------------------
+// VRegA
+//------------------------------------------------------------------------------
+inline bool Instruction::HasVRegA() const {
+ switch (FormatOf(Opcode())) {
+ case k10t: return true;
+ case k10x: return true;
+ case k11n: return true;
+ case k11x: return true;
+ case k12x: return true;
+ case k20t: return true;
+ case k21c: return true;
+ case k21h: return true;
+ case k21s: return true;
+ case k21t: return true;
+ case k22b: return true;
+ case k22c: return true;
+ case k22s: return true;
+ case k22t: return true;
+ case k22x: return true;
+ case k23x: return true;
+ case k30t: return true;
+ case k31c: return true;
+ case k31i: return true;
+ case k31t: return true;
+ case k32x: return true;
+ case k35c: return true;
+ case k3rc: return true;
+ case k45cc: return true;
+ case k4rcc: return true;
+ case k51l: return true;
+ default: return false;
+ }
+}
+
+inline int32_t Instruction::VRegA() const {
+ switch (FormatOf(Opcode())) {
+ case k10t: return VRegA_10t();
+ case k10x: return VRegA_10x();
+ case k11n: return VRegA_11n();
+ case k11x: return VRegA_11x();
+ case k12x: return VRegA_12x();
+ case k20t: return VRegA_20t();
+ case k21c: return VRegA_21c();
+ case k21h: return VRegA_21h();
+ case k21s: return VRegA_21s();
+ case k21t: return VRegA_21t();
+ case k22b: return VRegA_22b();
+ case k22c: return VRegA_22c();
+ case k22s: return VRegA_22s();
+ case k22t: return VRegA_22t();
+ case k22x: return VRegA_22x();
+ case k23x: return VRegA_23x();
+ case k30t: return VRegA_30t();
+ case k31c: return VRegA_31c();
+ case k31i: return VRegA_31i();
+ case k31t: return VRegA_31t();
+ case k32x: return VRegA_32x();
+ case k35c: return VRegA_35c();
+ case k3rc: return VRegA_3rc();
+ case k45cc: return VRegA_45cc();
+ case k4rcc: return VRegA_4rcc();
+ case k51l: return VRegA_51l();
+ default:
+ LOG(FATAL) << "Tried to access vA of instruction " << Name() << " which has no A operand.";
+ exit(EXIT_FAILURE);
+ }
+}
+
+inline int8_t Instruction::VRegA_10t(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k10t);
+ return static_cast<int8_t>(InstAA(inst_data));
+}
+
+inline uint8_t Instruction::VRegA_10x(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k10x);
+ return InstAA(inst_data);
+}
+
+inline uint4_t Instruction::VRegA_11n(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k11n);
+ return InstA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_11x(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k11x);
+ return InstAA(inst_data);
+}
+
+inline uint4_t Instruction::VRegA_12x(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k12x);
+ return InstA(inst_data);
+}
+
+inline int16_t Instruction::VRegA_20t() const {
+ DCHECK_EQ(FormatOf(Opcode()), k20t);
+ return static_cast<int16_t>(Fetch16(1));
+}
+
+inline uint8_t Instruction::VRegA_21c(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k21c);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_21h(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k21h);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_21s(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k21s);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_21t(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k21t);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_22b(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22b);
+ return InstAA(inst_data);
+}
+
+inline uint4_t Instruction::VRegA_22c(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22c);
+ return InstA(inst_data);
+}
+
+inline uint4_t Instruction::VRegA_22s(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22s);
+ return InstA(inst_data);
+}
+
+inline uint4_t Instruction::VRegA_22t(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22t);
+ return InstA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_22x(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22x);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_23x(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k23x);
+ return InstAA(inst_data);
+}
+
+inline int32_t Instruction::VRegA_30t() const {
+ DCHECK_EQ(FormatOf(Opcode()), k30t);
+ return static_cast<int32_t>(Fetch32(1));
+}
+
+inline uint8_t Instruction::VRegA_31c(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k31c);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_31i(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k31i);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_31t(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k31t);
+ return InstAA(inst_data);
+}
+
+inline uint16_t Instruction::VRegA_32x() const {
+ DCHECK_EQ(FormatOf(Opcode()), k32x);
+ return Fetch16(1);
+}
+
+inline uint4_t Instruction::VRegA_35c(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k35c);
+ return InstB(inst_data); // This is labeled A in the spec.
+}
+
+inline uint8_t Instruction::VRegA_3rc(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k3rc);
+ return InstAA(inst_data);
+}
+
+inline uint8_t Instruction::VRegA_51l(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k51l);
+ return InstAA(inst_data);
+}
+
+inline uint4_t Instruction::VRegA_45cc(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return InstB(inst_data); // This is labeled A in the spec.
+}
+
+inline uint8_t Instruction::VRegA_4rcc(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return InstAA(inst_data);
+}
+
+//------------------------------------------------------------------------------
+// VRegB
+//------------------------------------------------------------------------------
+inline bool Instruction::HasVRegB() const {
+ switch (FormatOf(Opcode())) {
+ case k11n: return true;
+ case k12x: return true;
+ case k21c: return true;
+ case k21h: return true;
+ case k21s: return true;
+ case k21t: return true;
+ case k22b: return true;
+ case k22c: return true;
+ case k22s: return true;
+ case k22t: return true;
+ case k22x: return true;
+ case k23x: return true;
+ case k31c: return true;
+ case k31i: return true;
+ case k31t: return true;
+ case k32x: return true;
+ case k35c: return true;
+ case k3rc: return true;
+ case k45cc: return true;
+ case k4rcc: return true;
+ case k51l: return true;
+ default: return false;
+ }
+}
+
+inline bool Instruction::HasWideVRegB() const {
+ return FormatOf(Opcode()) == k51l;
+}
+
+inline int32_t Instruction::VRegB() const {
+ switch (FormatOf(Opcode())) {
+ case k11n: return VRegB_11n();
+ case k12x: return VRegB_12x();
+ case k21c: return VRegB_21c();
+ case k21h: return VRegB_21h();
+ case k21s: return VRegB_21s();
+ case k21t: return VRegB_21t();
+ case k22b: return VRegB_22b();
+ case k22c: return VRegB_22c();
+ case k22s: return VRegB_22s();
+ case k22t: return VRegB_22t();
+ case k22x: return VRegB_22x();
+ case k23x: return VRegB_23x();
+ case k31c: return VRegB_31c();
+ case k31i: return VRegB_31i();
+ case k31t: return VRegB_31t();
+ case k32x: return VRegB_32x();
+ case k35c: return VRegB_35c();
+ case k3rc: return VRegB_3rc();
+ case k45cc: return VRegB_45cc();
+ case k4rcc: return VRegB_4rcc();
+ case k51l: return VRegB_51l();
+ default:
+ LOG(FATAL) << "Tried to access vB of instruction " << Name() << " which has no B operand.";
+ exit(EXIT_FAILURE);
+ }
+}
+
+inline uint64_t Instruction::WideVRegB() const {
+ return VRegB_51l();
+}
+
+inline int4_t Instruction::VRegB_11n(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k11n);
+ return static_cast<int4_t>((InstB(inst_data) << 28) >> 28);
+}
+
+inline uint4_t Instruction::VRegB_12x(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k12x);
+ return InstB(inst_data);
+}
+
+inline uint16_t Instruction::VRegB_21c() const {
+ DCHECK_EQ(FormatOf(Opcode()), k21c);
+ return Fetch16(1);
+}
+
+inline uint16_t Instruction::VRegB_21h() const {
+ DCHECK_EQ(FormatOf(Opcode()), k21h);
+ return Fetch16(1);
+}
+
+inline int16_t Instruction::VRegB_21s() const {
+ DCHECK_EQ(FormatOf(Opcode()), k21s);
+ return static_cast<int16_t>(Fetch16(1));
+}
+
+inline int16_t Instruction::VRegB_21t() const {
+ DCHECK_EQ(FormatOf(Opcode()), k21t);
+ return static_cast<int16_t>(Fetch16(1));
+}
+
+inline uint8_t Instruction::VRegB_22b() const {
+ DCHECK_EQ(FormatOf(Opcode()), k22b);
+ return static_cast<uint8_t>(Fetch16(1) & 0xff);
+}
+
+inline uint4_t Instruction::VRegB_22c(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22c);
+ return InstB(inst_data);
+}
+
+inline uint4_t Instruction::VRegB_22s(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22s);
+ return InstB(inst_data);
+}
+
+inline uint4_t Instruction::VRegB_22t(uint16_t inst_data) const {
+ DCHECK_EQ(FormatOf(Opcode()), k22t);
+ return InstB(inst_data);
+}
+
+inline uint16_t Instruction::VRegB_22x() const {
+ DCHECK_EQ(FormatOf(Opcode()), k22x);
+ return Fetch16(1);
+}
+
+inline uint8_t Instruction::VRegB_23x() const {
+ DCHECK_EQ(FormatOf(Opcode()), k23x);
+ return static_cast<uint8_t>(Fetch16(1) & 0xff);
+}
+
+inline uint32_t Instruction::VRegB_31c() const {
+ DCHECK_EQ(FormatOf(Opcode()), k31c);
+ return Fetch32(1);
+}
+
+inline int32_t Instruction::VRegB_31i() const {
+ DCHECK_EQ(FormatOf(Opcode()), k31i);
+ return static_cast<int32_t>(Fetch32(1));
+}
+
+inline int32_t Instruction::VRegB_31t() const {
+ DCHECK_EQ(FormatOf(Opcode()), k31t);
+ return static_cast<int32_t>(Fetch32(1));
+}
+
+inline uint16_t Instruction::VRegB_32x() const {
+ DCHECK_EQ(FormatOf(Opcode()), k32x);
+ return Fetch16(2);
+}
+
+inline uint16_t Instruction::VRegB_35c() const {
+ DCHECK_EQ(FormatOf(Opcode()), k35c);
+ return Fetch16(1);
+}
+
+inline uint16_t Instruction::VRegB_3rc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k3rc);
+ return Fetch16(1);
+}
+
+inline uint16_t Instruction::VRegB_45cc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return Fetch16(1);
+}
+
+inline uint16_t Instruction::VRegB_4rcc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return Fetch16(1);
+}
+
+inline uint64_t Instruction::VRegB_51l() const {
+ DCHECK_EQ(FormatOf(Opcode()), k51l);
+ uint64_t vB_wide = Fetch32(1) | ((uint64_t) Fetch32(3) << 32);
+ return vB_wide;
+}
+
+//------------------------------------------------------------------------------
+// VRegC
+//------------------------------------------------------------------------------
+inline bool Instruction::HasVRegC() const {
+ switch (FormatOf(Opcode())) {
+ case k22b: return true;
+ case k22c: return true;
+ case k22s: return true;
+ case k22t: return true;
+ case k23x: return true;
+ case k35c: return true;
+ case k3rc: return true;
+ case k45cc: return true;
+ case k4rcc: return true;
+ default: return false;
+ }
+}
+
+inline int32_t Instruction::VRegC() const {
+ switch (FormatOf(Opcode())) {
+ case k22b: return VRegC_22b();
+ case k22c: return VRegC_22c();
+ case k22s: return VRegC_22s();
+ case k22t: return VRegC_22t();
+ case k23x: return VRegC_23x();
+ case k35c: return VRegC_35c();
+ case k3rc: return VRegC_3rc();
+ case k45cc: return VRegC_45cc();
+ case k4rcc: return VRegC_4rcc();
+ default:
+ LOG(FATAL) << "Tried to access vC of instruction " << Name() << " which has no C operand.";
+ exit(EXIT_FAILURE);
+ }
+}
+
+inline int8_t Instruction::VRegC_22b() const {
+ DCHECK_EQ(FormatOf(Opcode()), k22b);
+ return static_cast<int8_t>(Fetch16(1) >> 8);
+}
+
+inline uint16_t Instruction::VRegC_22c() const {
+ DCHECK_EQ(FormatOf(Opcode()), k22c);
+ return Fetch16(1);
+}
+
+inline int16_t Instruction::VRegC_22s() const {
+ DCHECK_EQ(FormatOf(Opcode()), k22s);
+ return static_cast<int16_t>(Fetch16(1));
+}
+
+inline int16_t Instruction::VRegC_22t() const {
+ DCHECK_EQ(FormatOf(Opcode()), k22t);
+ return static_cast<int16_t>(Fetch16(1));
+}
+
+inline uint8_t Instruction::VRegC_23x() const {
+ DCHECK_EQ(FormatOf(Opcode()), k23x);
+ return static_cast<uint8_t>(Fetch16(1) >> 8);
+}
+
+inline uint4_t Instruction::VRegC_35c() const {
+ DCHECK_EQ(FormatOf(Opcode()), k35c);
+ return static_cast<uint4_t>(Fetch16(2) & 0x0f);
+}
+
+inline uint16_t Instruction::VRegC_3rc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k3rc);
+ return Fetch16(2);
+}
+
+inline uint4_t Instruction::VRegC_45cc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return static_cast<uint4_t>(Fetch16(2) & 0x0f);
+}
+
+inline uint16_t Instruction::VRegC_4rcc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return Fetch16(2);
+}
+
+//------------------------------------------------------------------------------
+// VRegH
+//------------------------------------------------------------------------------
+inline bool Instruction::HasVRegH() const {
+ switch (FormatOf(Opcode())) {
+ case k45cc: return true;
+ case k4rcc: return true;
+ default : return false;
+ }
+}
+
+inline int32_t Instruction::VRegH() const {
+ switch (FormatOf(Opcode())) {
+ case k45cc: return VRegH_45cc();
+ case k4rcc: return VRegH_4rcc();
+ default :
+ LOG(FATAL) << "Tried to access vH of instruction " << Name() << " which has no H operand.";
+ exit(EXIT_FAILURE);
+ }
+}
+
+inline uint16_t Instruction::VRegH_45cc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k45cc);
+ return Fetch16(3);
+}
+
+inline uint16_t Instruction::VRegH_4rcc() const {
+ DCHECK_EQ(FormatOf(Opcode()), k4rcc);
+ return Fetch16(3);
+}
+
+inline bool Instruction::HasVarArgs() const {
+ return (FormatOf(Opcode()) == k35c) || (FormatOf(Opcode()) == k45cc);
+}
+
+inline void Instruction::GetVarArgs(uint32_t arg[kMaxVarArgRegs], uint16_t inst_data) const {
+ DCHECK(HasVarArgs());
+
+ /*
+ * Note that the fields mentioned in the spec don't appear in
+ * their "usual" positions here compared to most formats. This
+ * was done so that the field names for the argument count and
+ * reference index match between this format and the corresponding
+ * range formats (3rc and friends).
+ *
+ * Bottom line: The argument count is always in vA, and the
+ * method constant (or equivalent) is always in vB.
+ */
+ uint16_t regList = Fetch16(2);
+ uint4_t count = InstB(inst_data); // This is labeled A in the spec.
+ DCHECK_LE(count, 5U) << "Invalid arg count in 35c (" << count << ")";
+
+ /*
+ * Copy the argument registers into the arg[] array, and
+ * also copy the first argument (if any) into vC. (The
+ * DecodedInstruction structure doesn't have separate
+ * fields for {vD, vE, vF, vG}, so there's no need to make
+ * copies of those.) Note that cases 5..2 fall through.
+ */
+ switch (count) {
+ case 5:
+ arg[4] = InstA(inst_data);
+ FALLTHROUGH_INTENDED;
+ case 4:
+ arg[3] = (regList >> 12) & 0x0f;
+ FALLTHROUGH_INTENDED;
+ case 3:
+ arg[2] = (regList >> 8) & 0x0f;
+ FALLTHROUGH_INTENDED;
+ case 2:
+ arg[1] = (regList >> 4) & 0x0f;
+ FALLTHROUGH_INTENDED;
+ case 1:
+ arg[0] = regList & 0x0f;
+ break;
+ default: // case 0
+ break; // Valid, but no need to do anything.
+ }
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_INL_H_
diff --git a/libdexfile/dex/dex_instruction.cc b/libdexfile/dex/dex_instruction.cc
new file mode 100644
index 0000000000..886218129e
--- /dev/null
+++ b/libdexfile/dex/dex_instruction.cc
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_instruction-inl.h"
+
+#include <inttypes.h>
+
+#include <iomanip>
+#include <sstream>
+
+#include "android-base/stringprintf.h"
+
+#include "dex_file-inl.h"
+#include "utf.h"
+
+namespace art {
+
+using android::base::StringPrintf;
+
+const char* const Instruction::kInstructionNames[] = {
+#define INSTRUCTION_NAME(o, c, pname, f, i, a, e, v) pname,
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_NAME)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_NAME
+};
+
+static_assert(sizeof(Instruction::InstructionDescriptor) == 8u, "Unexpected descriptor size");
+
+static constexpr int8_t InstructionSizeInCodeUnitsByOpcode(Instruction::Code opcode,
+ Instruction::Format format) {
+ if (opcode == Instruction::Code::NOP) {
+ return -1;
+ } else if ((format >= Instruction::Format::k10x) && (format <= Instruction::Format::k10t)) {
+ return 1;
+ } else if ((format >= Instruction::Format::k20t) && (format <= Instruction::Format::k22c)) {
+ return 2;
+ } else if ((format >= Instruction::Format::k32x) && (format <= Instruction::Format::k3rc)) {
+ return 3;
+ } else if ((format >= Instruction::Format::k45cc) && (format <= Instruction::Format::k4rcc)) {
+ return 4;
+ } else if (format == Instruction::Format::k51l) {
+ return 5;
+ } else {
+ return -1;
+ }
+}
+
+Instruction::InstructionDescriptor const Instruction::kInstructionDescriptors[] = {
+#define INSTRUCTION_DESCR(opcode, c, p, format, index, flags, eflags, vflags) \
+ { vflags, \
+ format, \
+ index, \
+ flags, \
+ InstructionSizeInCodeUnitsByOpcode((c), (format)), \
+ },
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_DESCR)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_DESCR
+};
+
+int32_t Instruction::GetTargetOffset() const {
+ switch (FormatOf(Opcode())) {
+ // Cases for conditional branches follow.
+ case k22t: return VRegC_22t();
+ case k21t: return VRegB_21t();
+ // Cases for unconditional branches follow.
+ case k10t: return VRegA_10t();
+ case k20t: return VRegA_20t();
+ case k30t: return VRegA_30t();
+ default: LOG(FATAL) << "Tried to access the branch offset of an instruction " << Name() <<
+ " which does not have a target operand.";
+ }
+ return 0;
+}
+
+bool Instruction::CanFlowThrough() const {
+ const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
+ uint16_t insn = *insns;
+ Code opcode = static_cast<Code>(insn & 0xFF);
+ return FlagsOf(opcode) & Instruction::kContinue;
+}
+
+size_t Instruction::SizeInCodeUnitsComplexOpcode() const {
+ const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
+ // Handle special NOP encoded variable length sequences.
+ switch (*insns) {
+ case kPackedSwitchSignature:
+ return (4 + insns[1] * 2);
+ case kSparseSwitchSignature:
+ return (2 + insns[1] * 4);
+ case kArrayDataSignature: {
+ uint16_t element_size = insns[1];
+ uint32_t length = insns[2] | (((uint32_t)insns[3]) << 16);
+ // The plus 1 is to round up for odd size and width.
+ return (4 + (element_size * length + 1) / 2);
+ }
+ default:
+ if ((*insns & 0xFF) == 0) {
+ return 1; // NOP.
+ } else {
+ LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
+ UNREACHABLE();
+ }
+ }
+}
+
+size_t Instruction::CodeUnitsRequiredForSizeOfComplexOpcode() const {
+ const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
+ // Handle special NOP encoded variable length sequences.
+ switch (*insns) {
+ case kPackedSwitchSignature:
+ FALLTHROUGH_INTENDED;
+ case kSparseSwitchSignature:
+ return 2;
+ case kArrayDataSignature:
+ return 4;
+ default:
+ if ((*insns & 0xFF) == 0) {
+ return 1; // NOP.
+ } else {
+ LOG(FATAL) << "Unreachable: " << DumpString(nullptr);
+ UNREACHABLE();
+ }
+ }
+}
+
+std::string Instruction::DumpHex(size_t code_units) const {
+ size_t inst_length = SizeInCodeUnits();
+ if (inst_length > code_units) {
+ inst_length = code_units;
+ }
+ std::ostringstream os;
+ const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
+ for (size_t i = 0; i < inst_length; i++) {
+ os << StringPrintf("0x%04x", insn[i]) << " ";
+ }
+ for (size_t i = inst_length; i < code_units; i++) {
+ os << " ";
+ }
+ return os.str();
+}
+
+std::string Instruction::DumpHexLE(size_t instr_code_units) const {
+ size_t inst_length = SizeInCodeUnits();
+ if (inst_length > instr_code_units) {
+ inst_length = instr_code_units;
+ }
+ std::ostringstream os;
+ const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
+ for (size_t i = 0; i < inst_length; i++) {
+ os << StringPrintf("%02x%02x", static_cast<uint8_t>(insn[i] & 0x00FF),
+ static_cast<uint8_t>((insn[i] & 0xFF00) >> 8)) << " ";
+ }
+ for (size_t i = inst_length; i < instr_code_units; i++) {
+ os << " ";
+ }
+ return os.str();
+}
+
+std::string Instruction::DumpString(const DexFile* file) const {
+ std::ostringstream os;
+ const char* opcode = kInstructionNames[Opcode()];
+ switch (FormatOf(Opcode())) {
+ case k10x: os << opcode; break;
+ case k12x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_12x(), VRegB_12x()); break;
+ case k11n: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_11n(), VRegB_11n()); break;
+ case k11x: os << StringPrintf("%s v%d", opcode, VRegA_11x()); break;
+ case k10t: os << StringPrintf("%s %+d", opcode, VRegA_10t()); break;
+ case k20t: os << StringPrintf("%s %+d", opcode, VRegA_20t()); break;
+ case k22x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_22x(), VRegB_22x()); break;
+ case k21t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_21t(), VRegB_21t()); break;
+ case k21s: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_21s(), VRegB_21s()); break;
+ case k21h: {
+ // op vAA, #+BBBB0000[00000000]
+ if (Opcode() == CONST_HIGH16) {
+ uint32_t value = VRegB_21h() << 16;
+ os << StringPrintf("%s v%d, #int %+d // 0x%x", opcode, VRegA_21h(), value, value);
+ } else {
+ uint64_t value = static_cast<uint64_t>(VRegB_21h()) << 48;
+ os << StringPrintf("%s v%d, #long %+" PRId64 " // 0x%" PRIx64, opcode, VRegA_21h(),
+ value, value);
+ }
+ }
+ break;
+ case k21c: {
+ switch (Opcode()) {
+ case CONST_STRING:
+ if (file != nullptr) {
+ uint32_t string_idx = VRegB_21c();
+ if (string_idx < file->NumStringIds()) {
+ os << StringPrintf(
+ "const-string v%d, %s // string@%d",
+ VRegA_21c(),
+ PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(),
+ string_idx);
+ } else {
+ os << StringPrintf("const-string v%d, <<invalid-string-idx-%d>> // string@%d",
+ VRegA_21c(),
+ string_idx,
+ string_idx);
+ }
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case CHECK_CAST:
+ case CONST_CLASS:
+ case NEW_INSTANCE:
+ if (file != nullptr) {
+ dex::TypeIndex type_idx(VRegB_21c());
+ os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", "
+ << file->PrettyType(type_idx) << " // type@" << type_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case SGET:
+ case SGET_WIDE:
+ case SGET_OBJECT:
+ case SGET_BOOLEAN:
+ case SGET_BYTE:
+ case SGET_CHAR:
+ case SGET_SHORT:
+ if (file != nullptr) {
+ uint32_t field_idx = VRegB_21c();
+ os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true)
+ << " // field@" << field_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case SPUT:
+ case SPUT_WIDE:
+ case SPUT_OBJECT:
+ case SPUT_BOOLEAN:
+ case SPUT_BYTE:
+ case SPUT_CHAR:
+ case SPUT_SHORT:
+ if (file != nullptr) {
+ uint32_t field_idx = VRegB_21c();
+ os << opcode << " v" << static_cast<int>(VRegA_21c()) << ", " << file->PrettyField(field_idx, true)
+ << " // field@" << field_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ default:
+ os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_21c(), VRegB_21c());
+ break;
+ }
+ break;
+ }
+ case k23x: os << StringPrintf("%s v%d, v%d, v%d", opcode, VRegA_23x(), VRegB_23x(), VRegC_23x()); break;
+ case k22b: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22b(), VRegB_22b(), VRegC_22b()); break;
+ case k22t: os << StringPrintf("%s v%d, v%d, %+d", opcode, VRegA_22t(), VRegB_22t(), VRegC_22t()); break;
+ case k22s: os << StringPrintf("%s v%d, v%d, #%+d", opcode, VRegA_22s(), VRegB_22s(), VRegC_22s()); break;
+ case k22c: {
+ switch (Opcode()) {
+ case IGET:
+ case IGET_WIDE:
+ case IGET_OBJECT:
+ case IGET_BOOLEAN:
+ case IGET_BYTE:
+ case IGET_CHAR:
+ case IGET_SHORT:
+ if (file != nullptr) {
+ uint32_t field_idx = VRegC_22c();
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+ << file->PrettyField(field_idx, true) << " // field@" << field_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case IGET_QUICK:
+ case IGET_OBJECT_QUICK:
+ if (file != nullptr) {
+ uint32_t field_idx = VRegC_22c();
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+ << "// offset@" << field_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case IPUT:
+ case IPUT_WIDE:
+ case IPUT_OBJECT:
+ case IPUT_BOOLEAN:
+ case IPUT_BYTE:
+ case IPUT_CHAR:
+ case IPUT_SHORT:
+ if (file != nullptr) {
+ uint32_t field_idx = VRegC_22c();
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+ << file->PrettyField(field_idx, true) << " // field@" << field_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case IPUT_QUICK:
+ case IPUT_OBJECT_QUICK:
+ if (file != nullptr) {
+ uint32_t field_idx = VRegC_22c();
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v" << static_cast<int>(VRegB_22c()) << ", "
+ << "// offset@" << field_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case INSTANCE_OF:
+ if (file != nullptr) {
+ dex::TypeIndex type_idx(VRegC_22c());
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
+ << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
+ << " // type@" << type_idx.index_;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case NEW_ARRAY:
+ if (file != nullptr) {
+ dex::TypeIndex type_idx(VRegC_22c());
+ os << opcode << " v" << static_cast<int>(VRegA_22c()) << ", v"
+ << static_cast<int>(VRegB_22c()) << ", " << file->PrettyType(type_idx)
+ << " // type@" << type_idx.index_;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ default:
+ os << StringPrintf("%s v%d, v%d, thing@%d", opcode, VRegA_22c(), VRegB_22c(), VRegC_22c());
+ break;
+ }
+ break;
+ }
+ case k32x: os << StringPrintf("%s v%d, v%d", opcode, VRegA_32x(), VRegB_32x()); break;
+ case k30t: os << StringPrintf("%s %+d", opcode, VRegA_30t()); break;
+ case k31t: os << StringPrintf("%s v%d, %+d", opcode, VRegA_31t(), VRegB_31t()); break;
+ case k31i: os << StringPrintf("%s v%d, #%+d", opcode, VRegA_31i(), VRegB_31i()); break;
+ case k31c:
+ if (Opcode() == CONST_STRING_JUMBO) {
+ uint32_t string_idx = VRegB_31c();
+ if (file != nullptr) {
+ if (string_idx < file->NumStringIds()) {
+ os << StringPrintf(
+ "%s v%d, %s // string@%d",
+ opcode,
+ VRegA_31c(),
+ PrintableString(file->StringDataByIdx(dex::StringIndex(string_idx))).c_str(),
+ string_idx);
+ } else {
+ os << StringPrintf("%s v%d, <<invalid-string-idx-%d>> // string@%d",
+ opcode,
+ VRegA_31c(),
+ string_idx,
+ string_idx);
+ }
+ } else {
+ os << StringPrintf("%s v%d, string@%d", opcode, VRegA_31c(), string_idx);
+ }
+ } else {
+ os << StringPrintf("%s v%d, thing@%d", opcode, VRegA_31c(), VRegB_31c()); break;
+ }
+ break;
+ case k35c: {
+ uint32_t arg[kMaxVarArgRegs];
+ GetVarArgs(arg);
+ auto DumpArgs = [&](size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ if (i != 0) {
+ os << ", ";
+ }
+ os << "v" << arg[i];
+ }
+ };
+ switch (Opcode()) {
+ case FILLED_NEW_ARRAY:
+ {
+ os << opcode << " {";
+ DumpArgs(VRegA_35c());
+ os << "}, type@" << VRegB_35c();
+ }
+ break;
+
+ case INVOKE_VIRTUAL:
+ case INVOKE_SUPER:
+ case INVOKE_DIRECT:
+ case INVOKE_STATIC:
+ case INVOKE_INTERFACE:
+ if (file != nullptr) {
+ os << opcode << " {";
+ uint32_t method_idx = VRegB_35c();
+ DumpArgs(VRegA_35c());
+ os << "}, " << file->PrettyMethod(method_idx) << " // method@" << method_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case INVOKE_VIRTUAL_QUICK:
+ if (file != nullptr) {
+ os << opcode << " {";
+ uint32_t method_idx = VRegB_35c();
+ DumpArgs(VRegA_35c());
+ os << "}, // vtable@" << method_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case INVOKE_CUSTOM:
+ if (file != nullptr) {
+ os << opcode << " {";
+ uint32_t call_site_idx = VRegB_35c();
+ DumpArgs(VRegA_35c());
+ os << "}, // call_site@" << call_site_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ default:
+ os << opcode << " {";
+ DumpArgs(VRegA_35c());
+ os << "}, thing@" << VRegB_35c();
+ break;
+ }
+ break;
+ }
+ case k3rc: {
+ uint16_t first_reg = VRegC_3rc();
+ uint16_t last_reg = VRegC_3rc() + VRegA_3rc() - 1;
+ switch (Opcode()) {
+ case INVOKE_VIRTUAL_RANGE:
+ case INVOKE_SUPER_RANGE:
+ case INVOKE_DIRECT_RANGE:
+ case INVOKE_STATIC_RANGE:
+ case INVOKE_INTERFACE_RANGE:
+ if (file != nullptr) {
+ uint32_t method_idx = VRegB_3rc();
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << file->PrettyMethod(method_idx) << " // method@" << method_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case INVOKE_VIRTUAL_RANGE_QUICK:
+ if (file != nullptr) {
+ uint32_t method_idx = VRegB_3rc();
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << "// vtable@" << method_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case INVOKE_CUSTOM_RANGE:
+ if (file != nullptr) {
+ uint32_t call_site_idx = VRegB_3rc();
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << "// call_site@" << call_site_idx;
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ default:
+ os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg)
+ << "thing@" << VRegB_3rc();
+ break;
+ }
+ break;
+ }
+ case k45cc: {
+ uint32_t arg[kMaxVarArgRegs];
+ GetVarArgs(arg);
+ uint32_t method_idx = VRegB_45cc();
+ uint32_t proto_idx = VRegH_45cc();
+ os << opcode << " {";
+ for (int i = 0; i < VRegA_45cc(); ++i) {
+ if (i != 0) {
+ os << ", ";
+ }
+ os << "v" << arg[i];
+ }
+ os << "}";
+ if (file != nullptr) {
+ os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
+ << " // ";
+ } else {
+ os << ", ";
+ }
+ os << "method@" << method_idx << ", proto@" << proto_idx;
+ break;
+ }
+ case k4rcc:
+ switch (Opcode()) {
+ case INVOKE_POLYMORPHIC_RANGE: {
+ if (file != nullptr) {
+ uint32_t method_idx = VRegB_4rcc();
+ uint32_t proto_idx = VRegH_4rcc();
+ os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
+ << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx)
+ << " // method@" << method_idx << ", proto@" << proto_idx;
+ break;
+ }
+ }
+ FALLTHROUGH_INTENDED;
+ default: {
+ uint32_t method_idx = VRegB_4rcc();
+ uint32_t proto_idx = VRegH_4rcc();
+ os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc())
+ << "}, method@" << method_idx << ", proto@" << proto_idx;
+ }
+ }
+ break;
+ case k51l: os << StringPrintf("%s v%d, #%+" PRId64, opcode, VRegA_51l(), VRegB_51l()); break;
+ }
+ return os.str();
+}
+
+// Add some checks that ensure the flags make sense. We need a subclass to be in the context of
+// Instruction. Otherwise the flags from the instruction list don't work.
+struct InstructionStaticAsserts : private Instruction {
+ #define IMPLIES(a, b) (!(a) || (b))
+
+ #define VAR_ARGS_CHECK(o, c, pname, f, i, a, e, v) \
+ static_assert(IMPLIES((f) == k35c || (f) == k45cc, \
+ ((v) & (kVerifyVarArg | kVerifyVarArgNonZero)) != 0), \
+ "Missing var-arg verification");
+ #include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(VAR_ARGS_CHECK)
+ #undef DEX_INSTRUCTION_LIST
+ #undef VAR_ARGS_CHECK
+
+ #define VAR_ARGS_RANGE_CHECK(o, c, pname, f, i, a, e, v) \
+ static_assert(IMPLIES((f) == k3rc || (f) == k4rcc, \
+ ((v) & (kVerifyVarArgRange | kVerifyVarArgRangeNonZero)) != 0), \
+ "Missing var-arg verification");
+ #include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(VAR_ARGS_RANGE_CHECK)
+ #undef DEX_INSTRUCTION_LIST
+ #undef VAR_ARGS_RANGE_CHECK
+
+ #define EXPERIMENTAL_CHECK(o, c, pname, f, i, a, e, v) \
+ static_assert(kHaveExperimentalInstructions || (((a) & kExperimental) == 0), \
+ "Unexpected experimental instruction.");
+ #include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(EXPERIMENTAL_CHECK)
+ #undef DEX_INSTRUCTION_LIST
+ #undef EXPERIMENTAL_CHECK
+};
+
+std::ostream& operator<<(std::ostream& os, const Instruction::Code& code) {
+ return os << Instruction::Name(code);
+}
+
+uint32_t RangeInstructionOperands::GetOperand(size_t operand_index) const {
+ DCHECK_LT(operand_index, GetNumberOfOperands());
+ return first_operand_ + operand_index;
+}
+
+uint32_t VarArgsInstructionOperands::GetOperand(size_t operand_index) const {
+ DCHECK_LT(operand_index, GetNumberOfOperands());
+ return operands_[operand_index];
+}
+
+uint32_t NoReceiverInstructionOperands::GetOperand(size_t operand_index) const {
+ DCHECK_LT(GetNumberOfOperands(), inner_->GetNumberOfOperands());
+ // The receiver is the first operand and since we're skipping it, we need to
+ // add 1 to the operand_index.
+ return inner_->GetOperand(operand_index + 1);
+}
+
+} // namespace art
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
new file mode 100644
index 0000000000..c9533656d3
--- /dev/null
+++ b/libdexfile/dex/dex_instruction.h
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_H_
+#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_H_
+
+#include <android-base/logging.h>
+
+#include "base/macros.h"
+#include "globals.h"
+
+typedef uint8_t uint4_t;
+typedef int8_t int4_t;
+
+namespace art {
+
+class DexFile;
+
+enum {
+ kNumPackedOpcodes = 0x100
+};
+
+class Instruction {
+ public:
+ // NOP-encoded switch-statement signatures.
+ enum Signatures {
+ kPackedSwitchSignature = 0x0100,
+ kSparseSwitchSignature = 0x0200,
+ kArrayDataSignature = 0x0300,
+ };
+
+ struct PACKED(4) PackedSwitchPayload {
+ const uint16_t ident;
+ const uint16_t case_count;
+ const int32_t first_key;
+ const int32_t targets[];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PackedSwitchPayload);
+ };
+
+ struct PACKED(4) SparseSwitchPayload {
+ const uint16_t ident;
+ const uint16_t case_count;
+ const int32_t keys_and_targets[];
+
+ public:
+ const int32_t* GetKeys() const {
+ return keys_and_targets;
+ }
+
+ const int32_t* GetTargets() const {
+ return keys_and_targets + case_count;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SparseSwitchPayload);
+ };
+
+ struct PACKED(4) ArrayDataPayload {
+ const uint16_t ident;
+ const uint16_t element_width;
+ const uint32_t element_count;
+ const uint8_t data[];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArrayDataPayload);
+ };
+
+ enum Code { // private marker to avoid generate-operator-out.py from processing.
+#define INSTRUCTION_ENUM(opcode, cname, p, f, i, a, e, v) cname = (opcode),
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_ENUM
+ RSUB_INT_LIT16 = RSUB_INT,
+ };
+
+ enum Format : uint8_t {
+ k10x, // op
+ k12x, // op vA, vB
+ k11n, // op vA, #+B
+ k11x, // op vAA
+ k10t, // op +AA
+ k20t, // op +AAAA
+ k22x, // op vAA, vBBBB
+ k21t, // op vAA, +BBBB
+ k21s, // op vAA, #+BBBB
+ k21h, // op vAA, #+BBBB00000[00000000]
+ k21c, // op vAA, thing@BBBB
+ k23x, // op vAA, vBB, vCC
+ k22b, // op vAA, vBB, #+CC
+ k22t, // op vA, vB, +CCCC
+ k22s, // op vA, vB, #+CCCC
+ k22c, // op vA, vB, thing@CCCC
+ k32x, // op vAAAA, vBBBB
+ k30t, // op +AAAAAAAA
+ k31t, // op vAA, +BBBBBBBB
+ k31i, // op vAA, #+BBBBBBBB
+ k31c, // op vAA, thing@BBBBBBBB
+ k35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG)
+ k3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+
+ // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH (A: count)
+ // format: AG op BBBB FEDC HHHH
+ k45cc,
+
+ // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
+ // format: AA op BBBB CCCC HHHH
+ k4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH (AA: count)
+
+ k51l, // op vAA, #+BBBBBBBBBBBBBBBB
+ };
+
+ enum IndexType : uint8_t {
+ kIndexUnknown = 0,
+ kIndexNone, // has no index
+ kIndexTypeRef, // type reference index
+ kIndexStringRef, // string reference index
+ kIndexMethodRef, // method reference index
+ kIndexFieldRef, // field reference index
+ kIndexFieldOffset, // field offset (for static linked fields)
+ kIndexVtableOffset, // vtable offset (for static linked methods)
+ kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic)
+ kIndexCallSiteRef, // call site reference index
+ kIndexMethodHandleRef, // constant method handle reference index
+ kIndexProtoRef, // prototype reference index
+ };
+
+ enum Flags : uint8_t {
+ kBranch = 0x01, // conditional or unconditional branch
+ kContinue = 0x02, // flow can continue to next statement
+ kSwitch = 0x04, // switch statement
+ kThrow = 0x08, // could cause an exception to be thrown
+ kReturn = 0x10, // returns, no additional statements
+ kInvoke = 0x20, // a flavor of invoke
+ kUnconditional = 0x40, // unconditional branch
+ kExperimental = 0x80, // is an experimental opcode
+ };
+
+ // Old flags. Keeping them around in case we might need them again some day.
+ enum ExtendedFlags : uint32_t {
+ kAdd = 0x0000080, // addition
+ kSubtract = 0x0000100, // subtract
+ kMultiply = 0x0000200, // multiply
+ kDivide = 0x0000400, // division
+ kRemainder = 0x0000800, // remainder
+ kAnd = 0x0001000, // and
+ kOr = 0x0002000, // or
+ kXor = 0x0004000, // xor
+ kShl = 0x0008000, // shl
+ kShr = 0x0010000, // shr
+ kUshr = 0x0020000, // ushr
+ kCast = 0x0040000, // cast
+ kStore = 0x0080000, // store opcode
+ kLoad = 0x0100000, // load opcode
+ kClobber = 0x0200000, // clobbers memory in a big way (not just a write)
+ kRegCFieldOrConstant = 0x0400000, // is the third virtual register a field or literal constant (vC)
+ kRegBFieldOrConstant = 0x0800000, // is the second virtual register a field or literal constant (vB)
+ };
+
+ enum VerifyFlag : uint32_t {
+ kVerifyNone = 0x0000000,
+ kVerifyRegA = 0x0000001,
+ kVerifyRegAWide = 0x0000002,
+ kVerifyRegB = 0x0000004,
+ kVerifyRegBField = 0x0000008,
+ kVerifyRegBMethod = 0x0000010,
+ kVerifyRegBNewInstance = 0x0000020,
+ kVerifyRegBString = 0x0000040,
+ kVerifyRegBType = 0x0000080,
+ kVerifyRegBWide = 0x0000100,
+ kVerifyRegC = 0x0000200,
+ kVerifyRegCField = 0x0000400,
+ kVerifyRegCNewArray = 0x0000800,
+ kVerifyRegCType = 0x0001000,
+ kVerifyRegCWide = 0x0002000,
+ kVerifyArrayData = 0x0004000,
+ kVerifyBranchTarget = 0x0008000,
+ kVerifySwitchTargets = 0x0010000,
+ kVerifyVarArg = 0x0020000,
+ kVerifyVarArgNonZero = 0x0040000,
+ kVerifyVarArgRange = 0x0080000,
+ kVerifyVarArgRangeNonZero = 0x0100000,
+ kVerifyRuntimeOnly = 0x0200000,
+ kVerifyError = 0x0400000,
+ kVerifyRegHPrototype = 0x0800000,
+ kVerifyRegBCallSite = 0x1000000,
+ kVerifyRegBMethodHandle = 0x2000000,
+ kVerifyRegBPrototype = 0x4000000,
+ };
+
+ // Collect the enums in a struct for better locality.
+ struct InstructionDescriptor {
+ uint32_t verify_flags; // Set of VerifyFlag.
+ Format format;
+ IndexType index_type;
+ uint8_t flags; // Set of Flags.
+ int8_t size_in_code_units;
+ };
+
+ static constexpr uint32_t kMaxVarArgRegs = 5;
+
+ static constexpr bool kHaveExperimentalInstructions = false;
+
+ // Returns the size (in 2 byte code units) of this instruction.
+ size_t SizeInCodeUnits() const {
+ int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units;
+ if (UNLIKELY(result < 0)) {
+ return SizeInCodeUnitsComplexOpcode();
+ } else {
+ return static_cast<size_t>(result);
+ }
+ }
+
+ // Code units required to calculate the size of the instruction.
+ size_t CodeUnitsRequiredForSizeComputation() const {
+ const int8_t result = kInstructionDescriptors[Opcode()].size_in_code_units;
+ return UNLIKELY(result < 0) ? CodeUnitsRequiredForSizeOfComplexOpcode() : 1;
+ }
+
+ // Reads an instruction out of the stream at the specified address.
+ static const Instruction* At(const uint16_t* code) {
+ DCHECK(code != nullptr);
+ return reinterpret_cast<const Instruction*>(code);
+ }
+
+ // Reads an instruction out of the stream from the current address plus an offset.
+ const Instruction* RelativeAt(int32_t offset) const WARN_UNUSED {
+ return At(reinterpret_cast<const uint16_t*>(this) + offset);
+ }
+
+ // Returns a pointer to the next instruction in the stream.
+ const Instruction* Next() const {
+ return RelativeAt(SizeInCodeUnits());
+ }
+
+ // Returns a pointer to the instruction after this 1xx instruction in the stream.
+ const Instruction* Next_1xx() const {
+ DCHECK(FormatOf(Opcode()) >= k10x && FormatOf(Opcode()) <= k10t);
+ return RelativeAt(1);
+ }
+
+ // Returns a pointer to the instruction after this 2xx instruction in the stream.
+ const Instruction* Next_2xx() const {
+ DCHECK(FormatOf(Opcode()) >= k20t && FormatOf(Opcode()) <= k22c);
+ return RelativeAt(2);
+ }
+
+ // Returns a pointer to the instruction after this 3xx instruction in the stream.
+ const Instruction* Next_3xx() const {
+ DCHECK(FormatOf(Opcode()) >= k32x && FormatOf(Opcode()) <= k3rc);
+ return RelativeAt(3);
+ }
+
+ // Returns a pointer to the instruction after this 4xx instruction in the stream.
+ const Instruction* Next_4xx() const {
+ DCHECK(FormatOf(Opcode()) >= k45cc && FormatOf(Opcode()) <= k4rcc);
+ return RelativeAt(4);
+ }
+
+ // Returns a pointer to the instruction after this 51l instruction in the stream.
+ const Instruction* Next_51l() const {
+ DCHECK(FormatOf(Opcode()) == k51l);
+ return RelativeAt(5);
+ }
+
+ // Returns the name of this instruction's opcode.
+ const char* Name() const {
+ return Instruction::Name(Opcode());
+ }
+
+ // Returns the name of the given opcode.
+ static const char* Name(Code opcode) {
+ return kInstructionNames[opcode];
+ }
+
+ // VRegA
+ bool HasVRegA() const;
+ ALWAYS_INLINE int32_t VRegA() const;
+
+ int8_t VRegA_10t() const {
+ return VRegA_10t(Fetch16(0));
+ }
+ uint8_t VRegA_10x() const {
+ return VRegA_10x(Fetch16(0));
+ }
+ uint4_t VRegA_11n() const {
+ return VRegA_11n(Fetch16(0));
+ }
+ uint8_t VRegA_11x() const {
+ return VRegA_11x(Fetch16(0));
+ }
+ uint4_t VRegA_12x() const {
+ return VRegA_12x(Fetch16(0));
+ }
+ int16_t VRegA_20t() const;
+ uint8_t VRegA_21c() const {
+ return VRegA_21c(Fetch16(0));
+ }
+ uint8_t VRegA_21h() const {
+ return VRegA_21h(Fetch16(0));
+ }
+ uint8_t VRegA_21s() const {
+ return VRegA_21s(Fetch16(0));
+ }
+ uint8_t VRegA_21t() const {
+ return VRegA_21t(Fetch16(0));
+ }
+ uint8_t VRegA_22b() const {
+ return VRegA_22b(Fetch16(0));
+ }
+ uint4_t VRegA_22c() const {
+ return VRegA_22c(Fetch16(0));
+ }
+ uint4_t VRegA_22s() const {
+ return VRegA_22s(Fetch16(0));
+ }
+ uint4_t VRegA_22t() const {
+ return VRegA_22t(Fetch16(0));
+ }
+ uint8_t VRegA_22x() const {
+ return VRegA_22x(Fetch16(0));
+ }
+ uint8_t VRegA_23x() const {
+ return VRegA_23x(Fetch16(0));
+ }
+ int32_t VRegA_30t() const;
+ uint8_t VRegA_31c() const {
+ return VRegA_31c(Fetch16(0));
+ }
+ uint8_t VRegA_31i() const {
+ return VRegA_31i(Fetch16(0));
+ }
+ uint8_t VRegA_31t() const {
+ return VRegA_31t(Fetch16(0));
+ }
+ uint16_t VRegA_32x() const;
+ uint4_t VRegA_35c() const {
+ return VRegA_35c(Fetch16(0));
+ }
+ uint8_t VRegA_3rc() const {
+ return VRegA_3rc(Fetch16(0));
+ }
+ uint8_t VRegA_51l() const {
+ return VRegA_51l(Fetch16(0));
+ }
+ uint4_t VRegA_45cc() const {
+ return VRegA_45cc(Fetch16(0));
+ }
+ uint8_t VRegA_4rcc() const {
+ return VRegA_4rcc(Fetch16(0));
+ }
+
+ // The following methods return the vA operand for various instruction formats. The "inst_data"
+ // parameter holds the first 16 bits of instruction which the returned value is decoded from.
+ int8_t VRegA_10t(uint16_t inst_data) const;
+ uint8_t VRegA_10x(uint16_t inst_data) const;
+ uint4_t VRegA_11n(uint16_t inst_data) const;
+ uint8_t VRegA_11x(uint16_t inst_data) const;
+ uint4_t VRegA_12x(uint16_t inst_data) const;
+ uint8_t VRegA_21c(uint16_t inst_data) const;
+ uint8_t VRegA_21h(uint16_t inst_data) const;
+ uint8_t VRegA_21s(uint16_t inst_data) const;
+ uint8_t VRegA_21t(uint16_t inst_data) const;
+ uint8_t VRegA_22b(uint16_t inst_data) const;
+ uint4_t VRegA_22c(uint16_t inst_data) const;
+ uint4_t VRegA_22s(uint16_t inst_data) const;
+ uint4_t VRegA_22t(uint16_t inst_data) const;
+ uint8_t VRegA_22x(uint16_t inst_data) const;
+ uint8_t VRegA_23x(uint16_t inst_data) const;
+ uint8_t VRegA_31c(uint16_t inst_data) const;
+ uint8_t VRegA_31i(uint16_t inst_data) const;
+ uint8_t VRegA_31t(uint16_t inst_data) const;
+ uint4_t VRegA_35c(uint16_t inst_data) const;
+ uint8_t VRegA_3rc(uint16_t inst_data) const;
+ uint8_t VRegA_51l(uint16_t inst_data) const;
+ uint4_t VRegA_45cc(uint16_t inst_data) const;
+ uint8_t VRegA_4rcc(uint16_t inst_data) const;
+
+ // VRegB
+ bool HasVRegB() const;
+ int32_t VRegB() const;
+
+ bool HasWideVRegB() const;
+ uint64_t WideVRegB() const;
+
+ int4_t VRegB_11n() const {
+ return VRegB_11n(Fetch16(0));
+ }
+ uint4_t VRegB_12x() const {
+ return VRegB_12x(Fetch16(0));
+ }
+ uint16_t VRegB_21c() const;
+ uint16_t VRegB_21h() const;
+ int16_t VRegB_21s() const;
+ int16_t VRegB_21t() const;
+ uint8_t VRegB_22b() const;
+ uint4_t VRegB_22c() const {
+ return VRegB_22c(Fetch16(0));
+ }
+ uint4_t VRegB_22s() const {
+ return VRegB_22s(Fetch16(0));
+ }
+ uint4_t VRegB_22t() const {
+ return VRegB_22t(Fetch16(0));
+ }
+ uint16_t VRegB_22x() const;
+ uint8_t VRegB_23x() const;
+ uint32_t VRegB_31c() const;
+ int32_t VRegB_31i() const;
+ int32_t VRegB_31t() const;
+ uint16_t VRegB_32x() const;
+ uint16_t VRegB_35c() const;
+ uint16_t VRegB_3rc() const;
+ uint64_t VRegB_51l() const; // vB_wide
+ uint16_t VRegB_45cc() const;
+ uint16_t VRegB_4rcc() const;
+
+ // The following methods return the vB operand for all instruction formats where it is encoded in
+ // the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned
+ // value is decoded from it.
+ int4_t VRegB_11n(uint16_t inst_data) const;
+ uint4_t VRegB_12x(uint16_t inst_data) const;
+ uint4_t VRegB_22c(uint16_t inst_data) const;
+ uint4_t VRegB_22s(uint16_t inst_data) const;
+ uint4_t VRegB_22t(uint16_t inst_data) const;
+
+ // VRegC
+ bool HasVRegC() const;
+ int32_t VRegC() const;
+
+ int8_t VRegC_22b() const;
+ uint16_t VRegC_22c() const;
+ int16_t VRegC_22s() const;
+ int16_t VRegC_22t() const;
+ uint8_t VRegC_23x() const;
+ uint4_t VRegC_35c() const;
+ uint16_t VRegC_3rc() const;
+ uint4_t VRegC_45cc() const;
+ uint16_t VRegC_4rcc() const;
+
+
+ // VRegH
+ bool HasVRegH() const;
+ int32_t VRegH() const;
+ uint16_t VRegH_45cc() const;
+ uint16_t VRegH_4rcc() const;
+
+ // Fills the given array with the 'arg' array of the instruction.
+ bool HasVarArgs() const;
+ void GetVarArgs(uint32_t args[kMaxVarArgRegs], uint16_t inst_data) const;
+ void GetVarArgs(uint32_t args[kMaxVarArgRegs]) const {
+ return GetVarArgs(args, Fetch16(0));
+ }
+
+ // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first
+ // 16 bits of instruction.
+ Code Opcode(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<Code>(inst_data & 0xFF);
+ }
+
+ // Returns the opcode field of the instruction from the first 16 bits of instruction.
+ Code Opcode() const {
+ return Opcode(Fetch16(0));
+ }
+
+ void SetOpcode(Code opcode) {
+ DCHECK_LT(static_cast<uint16_t>(opcode), 256u);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[0] = (insns[0] & 0xff00) | static_cast<uint16_t>(opcode);
+ }
+
+ void SetVRegA_10x(uint8_t val) {
+ DCHECK(FormatOf(Opcode()) == k10x);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[0] = (val << 8) | (insns[0] & 0x00ff);
+ }
+
+ void SetVRegB_3rc(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k3rc);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
+ void SetVRegB_35c(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k35c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
+ void SetVRegC_22c(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k22c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
+ void SetVRegA_21c(uint8_t val) {
+ DCHECK(FormatOf(Opcode()) == k21c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[0] = (val << 8) | (insns[0] & 0x00ff);
+ }
+
+ void SetVRegB_21c(uint16_t val) {
+ DCHECK(FormatOf(Opcode()) == k21c);
+ uint16_t* insns = reinterpret_cast<uint16_t*>(this);
+ insns[1] = val;
+ }
+
+ // Returns the format of the given opcode.
+ static Format FormatOf(Code opcode) {
+ return kInstructionDescriptors[opcode].format;
+ }
+
+ // Returns the index type of the given opcode.
+ static IndexType IndexTypeOf(Code opcode) {
+ return kInstructionDescriptors[opcode].index_type;
+ }
+
+ // Returns the flags for the given opcode.
+ static uint8_t FlagsOf(Code opcode) {
+ return kInstructionDescriptors[opcode].flags;
+ }
+
+ // Return the verify flags for the given opcode.
+ static uint32_t VerifyFlagsOf(Code opcode) {
+ return kInstructionDescriptors[opcode].verify_flags;
+ }
+
+ // Returns true if this instruction is a branch.
+ bool IsBranch() const {
+ return (kInstructionDescriptors[Opcode()].flags & kBranch) != 0;
+ }
+
+ // Returns true if this instruction is a unconditional branch.
+ bool IsUnconditional() const {
+ return (kInstructionDescriptors[Opcode()].flags & kUnconditional) != 0;
+ }
+
+ // Returns the branch offset if this instruction is a branch.
+ int32_t GetTargetOffset() const;
+
+ // Returns true if the instruction allows control flow to go to the following instruction.
+ bool CanFlowThrough() const;
+
+ // Returns true if the instruction is a quickened instruction.
+ bool IsQuickened() const {
+ return (kInstructionDescriptors[Opcode()].index_type == kIndexFieldOffset) ||
+ (kInstructionDescriptors[Opcode()].index_type == kIndexVtableOffset);
+ }
+
+ // Returns true if this instruction is a switch.
+ bool IsSwitch() const {
+ return (kInstructionDescriptors[Opcode()].flags & kSwitch) != 0;
+ }
+
+ // Returns true if this instruction can throw.
+ bool IsThrow() const {
+ return (kInstructionDescriptors[Opcode()].flags & kThrow) != 0;
+ }
+
+ // Determine if the instruction is any of 'return' instructions.
+ bool IsReturn() const {
+ return (kInstructionDescriptors[Opcode()].flags & kReturn) != 0;
+ }
+
+ // Determine if this instruction ends execution of its basic block.
+ bool IsBasicBlockEnd() const {
+ return IsBranch() || IsReturn() || Opcode() == THROW;
+ }
+
+ // Determine if this instruction is an invoke.
+ bool IsInvoke() const {
+ return (kInstructionDescriptors[Opcode()].flags & kInvoke) != 0;
+ }
+
+ // Determine if this instruction is experimental.
+ bool IsExperimental() const {
+ return (kInstructionDescriptors[Opcode()].flags & kExperimental) != 0;
+ }
+
+ int GetVerifyTypeArgumentA() const {
+ return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegA | kVerifyRegAWide));
+ }
+
+ int GetVerifyTypeArgumentB() const {
+ return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegB | kVerifyRegBField |
+ kVerifyRegBMethod | kVerifyRegBNewInstance | kVerifyRegBString | kVerifyRegBType |
+ kVerifyRegBWide));
+ }
+
+ int GetVerifyTypeArgumentC() const {
+ return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyRegC | kVerifyRegCField |
+ kVerifyRegCNewArray | kVerifyRegCType | kVerifyRegCWide));
+ }
+
+ int GetVerifyTypeArgumentH() const {
+ return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRegHPrototype);
+ }
+
+ int GetVerifyExtraFlags() const {
+ return (kInstructionDescriptors[Opcode()].verify_flags & (kVerifyArrayData |
+ kVerifyBranchTarget | kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgNonZero |
+ kVerifyVarArgRange | kVerifyVarArgRangeNonZero | kVerifyError));
+ }
+
+ bool GetVerifyIsRuntimeOnly() const {
+ return (kInstructionDescriptors[Opcode()].verify_flags & kVerifyRuntimeOnly) != 0;
+ }
+
+ // Get the dex PC of this instruction as a offset in code units from the beginning of insns.
+ uint32_t GetDexPc(const uint16_t* insns) const {
+ return (reinterpret_cast<const uint16_t*>(this) - insns);
+ }
+
+ // Dump decoded version of instruction
+ std::string DumpString(const DexFile*) const;
+
+ // Dump code_units worth of this instruction, padding to code_units for shorter instructions
+ std::string DumpHex(size_t code_units) const;
+
+ // Little-endian dump code_units worth of this instruction, padding to code_units for
+ // shorter instructions
+ std::string DumpHexLE(size_t instr_code_units) const;
+
+ uint16_t Fetch16(size_t offset) const {
+ const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
+ return insns[offset];
+ }
+
+ private:
+ size_t SizeInCodeUnitsComplexOpcode() const;
+
+ // Return how many code unit words are required to compute the size of the opcode.
+ size_t CodeUnitsRequiredForSizeOfComplexOpcode() const;
+
+ uint32_t Fetch32(size_t offset) const {
+ return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16));
+ }
+
+ uint4_t InstA() const {
+ return InstA(Fetch16(0));
+ }
+
+ uint4_t InstB() const {
+ return InstB(Fetch16(0));
+ }
+
+ uint8_t InstAA() const {
+ return InstAA(Fetch16(0));
+ }
+
+ uint4_t InstA(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint4_t>((inst_data >> 8) & 0x0f);
+ }
+
+ uint4_t InstB(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint4_t>(inst_data >> 12);
+ }
+
+ uint8_t InstAA(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint8_t>(inst_data >> 8);
+ }
+
+ static const char* const kInstructionNames[];
+
+ static const InstructionDescriptor kInstructionDescriptors[];
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction);
+};
+std::ostream& operator<<(std::ostream& os, const Instruction::Code& code);
+std::ostream& operator<<(std::ostream& os, const Instruction::Format& format);
+std::ostream& operator<<(std::ostream& os, const Instruction::Flags& flags);
+std::ostream& operator<<(std::ostream& os, const Instruction::VerifyFlag& vflags);
+
+// Base class for accessing instruction operands. Unifies operand
+// access for instructions that have range and varargs forms
+// (e.g. invoke-polymoprhic/range and invoke-polymorphic).
+class InstructionOperands {
+ public:
+ explicit InstructionOperands(size_t num_operands) : num_operands_(num_operands) {}
+ virtual ~InstructionOperands() {}
+ virtual uint32_t GetOperand(size_t index) const = 0;
+ size_t GetNumberOfOperands() const { return num_operands_; }
+
+ private:
+ const size_t num_operands_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(InstructionOperands);
+};
+
+// Class for accessing operands for instructions with a range format
+// (e.g. 3rc and 4rcc).
+class RangeInstructionOperands FINAL : public InstructionOperands {
+ public:
+ RangeInstructionOperands(uint32_t first_operand, size_t num_operands)
+ : InstructionOperands(num_operands), first_operand_(first_operand) {}
+ ~RangeInstructionOperands() {}
+ uint32_t GetOperand(size_t operand_index) const OVERRIDE;
+
+ private:
+ const uint32_t first_operand_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RangeInstructionOperands);
+};
+
+// Class for accessing operands for instructions with a variable
+// number of arguments format (e.g. 35c and 45cc).
+class VarArgsInstructionOperands FINAL : public InstructionOperands {
+ public:
+ VarArgsInstructionOperands(const uint32_t (&operands)[Instruction::kMaxVarArgRegs],
+ size_t num_operands)
+ : InstructionOperands(num_operands), operands_(operands) {}
+ ~VarArgsInstructionOperands() {}
+ uint32_t GetOperand(size_t operand_index) const OVERRIDE;
+
+ private:
+ const uint32_t (&operands_)[Instruction::kMaxVarArgRegs];
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(VarArgsInstructionOperands);
+};
+
+// Class for accessing operands without the receiver by wrapping an
+// existing InstructionOperands instance.
+class NoReceiverInstructionOperands FINAL : public InstructionOperands {
+ public:
+ explicit NoReceiverInstructionOperands(InstructionOperands* inner)
+ : InstructionOperands(inner->GetNumberOfOperands() - 1), inner_(inner) {}
+ ~NoReceiverInstructionOperands() {}
+ uint32_t GetOperand(size_t operand_index) const OVERRIDE;
+
+ private:
+ const InstructionOperands* const inner_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NoReceiverInstructionOperands);
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_H_
diff --git a/libdexfile/dex/dex_instruction_iterator.h b/libdexfile/dex/dex_instruction_iterator.h
new file mode 100644
index 0000000000..db3ff95e02
--- /dev/null
+++ b/libdexfile/dex/dex_instruction_iterator.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_
+#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_
+
+#include <iterator>
+
+#include <android-base/logging.h>
+
+#include "base/macros.h"
+#include "dex_instruction.h"
+
+namespace art {
+
+class DexInstructionPcPair {
+ public:
+ ALWAYS_INLINE const Instruction& Inst() const {
+ return *Instruction::At(instructions_ + DexPc());
+ }
+
+ ALWAYS_INLINE const Instruction* operator->() const {
+ return &Inst();
+ }
+
+ ALWAYS_INLINE uint32_t DexPc() const {
+ return dex_pc_;
+ }
+
+ ALWAYS_INLINE const uint16_t* Instructions() const {
+ return instructions_;
+ }
+
+ protected:
+ explicit DexInstructionPcPair(const uint16_t* instructions, uint32_t dex_pc)
+ : instructions_(instructions), dex_pc_(dex_pc) {}
+
+ const uint16_t* instructions_ = nullptr;
+ uint32_t dex_pc_ = 0;
+
+ friend class DexInstructionIteratorBase;
+ friend class DexInstructionIterator;
+ friend class SafeDexInstructionIterator;
+};
+
+// Base helper class to prevent duplicated comparators.
+class DexInstructionIteratorBase : public
+ std::iterator<std::forward_iterator_tag, DexInstructionPcPair> {
+ public:
+ using value_type = std::iterator<std::forward_iterator_tag, DexInstructionPcPair>::value_type;
+ using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type;
+
+ DexInstructionIteratorBase() = default;
+ explicit DexInstructionIteratorBase(const Instruction* inst, uint32_t dex_pc)
+ : data_(reinterpret_cast<const uint16_t*>(inst), dex_pc) {}
+
+ const Instruction& Inst() const {
+ return data_.Inst();
+ }
+
+ // Return the dex pc for an iterator compared to the code item begin.
+ ALWAYS_INLINE uint32_t DexPc() const {
+ return data_.DexPc();
+ }
+
+ // Instructions from the start of the code item.
+ ALWAYS_INLINE const uint16_t* Instructions() const {
+ return data_.Instructions();
+ }
+
+ protected:
+ DexInstructionPcPair data_;
+};
+
+static ALWAYS_INLINE inline bool operator==(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items.";
+ return lhs.DexPc() == rhs.DexPc();
+}
+
+static inline bool operator!=(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return !(lhs == rhs);
+}
+
+static inline bool operator<(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items.";
+ return lhs.DexPc() < rhs.DexPc();
+}
+
+static inline bool operator>(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return rhs < lhs;
+}
+
+static inline bool operator<=(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return !(rhs < lhs);
+}
+
+static inline bool operator>=(const DexInstructionIteratorBase& lhs,
+ const DexInstructionIteratorBase& rhs) {
+ return !(lhs < rhs);
+}
+
+// A helper class for a code_item's instructions using range based for loop syntax.
+class DexInstructionIterator : public DexInstructionIteratorBase {
+ public:
+ using DexInstructionIteratorBase::DexInstructionIteratorBase;
+
+ explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc)
+ : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {}
+
+ explicit DexInstructionIterator(const DexInstructionPcPair& pair)
+ : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {}
+
+ // Value after modification.
+ DexInstructionIterator& operator++() {
+ data_.dex_pc_ += Inst().SizeInCodeUnits();
+ return *this;
+ }
+
+ // Value before modification.
+ DexInstructionIterator operator++(int) {
+ DexInstructionIterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ const value_type& operator*() const {
+ return data_;
+ }
+
+ const Instruction* operator->() const {
+ return &data_.Inst();
+ }
+
+ // Return the dex pc for the iterator.
+ ALWAYS_INLINE uint32_t DexPc() const {
+ return data_.DexPc();
+ }
+};
+
+// A safe version of DexInstructionIterator that is guaranteed to not go past the end of the code
+// item.
+class SafeDexInstructionIterator : public DexInstructionIteratorBase {
+ public:
+ explicit SafeDexInstructionIterator(const DexInstructionIteratorBase& start,
+ const DexInstructionIteratorBase& end)
+ : DexInstructionIteratorBase(&start.Inst(), start.DexPc())
+ , num_code_units_(end.DexPc()) {
+ DCHECK_EQ(start.Instructions(), end.Instructions())
+ << "start and end must be in the same code item.";
+ }
+
+ // Value after modification, does not read past the end of the allowed region. May increment past
+ // the end of the code item though.
+ SafeDexInstructionIterator& operator++() {
+ AssertValid();
+ const size_t size_code_units = Inst().CodeUnitsRequiredForSizeComputation();
+ const size_t available = NumCodeUnits() - DexPc();
+ if (UNLIKELY(size_code_units > available)) {
+ error_state_ = true;
+ return *this;
+ }
+ const size_t instruction_code_units = Inst().SizeInCodeUnits();
+ if (UNLIKELY(instruction_code_units > available)) {
+ error_state_ = true;
+ return *this;
+ }
+ data_.dex_pc_ += instruction_code_units;
+ return *this;
+ }
+
+ // Value before modification.
+ SafeDexInstructionIterator operator++(int) {
+ SafeDexInstructionIterator temp = *this;
+ ++*this;
+ return temp;
+ }
+
+ const value_type& operator*() const {
+ AssertValid();
+ return data_;
+ }
+
+ const Instruction* operator->() const {
+ AssertValid();
+ return &data_.Inst();
+ }
+
+ // Return the current instruction of the iterator.
+ ALWAYS_INLINE const Instruction& Inst() const {
+ return data_.Inst();
+ }
+
+ const uint16_t* Instructions() const {
+ return data_.Instructions();
+ }
+
+ // Returns true if the iterator is in an error state. This occurs when an instruction couldn't
+ // have its size computed without reading past the end iterator.
+ bool IsErrorState() const {
+ return error_state_;
+ }
+
+ private:
+ ALWAYS_INLINE void AssertValid() const {
+ DCHECK(!IsErrorState());
+ DCHECK_LT(DexPc(), NumCodeUnits());
+ }
+
+ ALWAYS_INLINE uint32_t NumCodeUnits() const {
+ return num_code_units_;
+ }
+
+ const uint32_t num_code_units_ = 0;
+ bool error_state_ = false;
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_
diff --git a/libdexfile/dex/dex_instruction_list.h b/libdexfile/dex/dex_instruction_list.h
new file mode 100644
index 0000000000..9f0aba421a
--- /dev/null
+++ b/libdexfile/dex/dex_instruction_list.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_LIST_H_
+#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_LIST_H_
+
+// V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags);
+#define DEX_INSTRUCTION_LIST(V) \
+ V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \
+ V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x04, MOVE_WIDE, "move-wide", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x05, MOVE_WIDE_FROM16, "move-wide/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x06, MOVE_WIDE_16, "move-wide/16", k32x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x07, MOVE_OBJECT, "move-object", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x08, MOVE_OBJECT_FROM16, "move-object/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x09, MOVE_OBJECT_16, "move-object/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x0A, MOVE_RESULT, "move-result", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
+ V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \
+ V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
+ V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
+ V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+ V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
+ V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \
+ V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
+ V(0x12, CONST_4, "const/4", k11n, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
+ V(0x13, CONST_16, "const/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
+ V(0x14, CONST, "const", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
+ V(0x15, CONST_HIGH16, "const/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegA) \
+ V(0x16, CONST_WIDE_16, "const-wide/16", k21s, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
+ V(0x17, CONST_WIDE_32, "const-wide/32", k31i, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
+ V(0x18, CONST_WIDE, "const-wide", k51l, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
+ V(0x19, CONST_WIDE_HIGH16, "const-wide/high16", k21h, kIndexNone, kContinue, kRegBFieldOrConstant, kVerifyRegAWide) \
+ V(0x1A, CONST_STRING, "const-string", k21c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \
+ V(0x1B, CONST_STRING_JUMBO, "const-string/jumbo", k31c, kIndexStringRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBString) \
+ V(0x1C, CONST_CLASS, "const-class", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \
+ V(0x1D, MONITOR_ENTER, "monitor-enter", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \
+ V(0x1E, MONITOR_EXIT, "monitor-exit", k11x, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA) \
+ V(0x1F, CHECK_CAST, "check-cast", k21c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBType) \
+ V(0x20, INSTANCE_OF, "instance-of", k22c, kIndexTypeRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB | kVerifyRegCType) \
+ V(0x21, ARRAY_LENGTH, "array-length", k12x, kIndexNone, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x22, NEW_INSTANCE, "new-instance", k21c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegBNewInstance) \
+ V(0x23, NEW_ARRAY, "new-array", k22c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegB | kVerifyRegCNewArray) \
+ V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArg) \
+ V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArgRange) \
+ V(0x26, FILL_ARRAY_DATA, "fill-array-data", k31t, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyArrayData) \
+ V(0x27, THROW, "throw", k11x, kIndexNone, kThrow, 0, kVerifyRegA) \
+ V(0x28, GOTO, "goto", k10t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
+ V(0x29, GOTO_16, "goto/16", k20t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
+ V(0x2A, GOTO_32, "goto/32", k30t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
+ V(0x2B, PACKED_SWITCH, "packed-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \
+ V(0x2C, SPARSE_SWITCH, "sparse-switch", k31t, kIndexNone, kContinue | kSwitch, 0, kVerifyRegA | kVerifySwitchTargets) \
+ V(0x2D, CMPL_FLOAT, "cmpl-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x2E, CMPG_FLOAT, "cmpg-float", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x2F, CMPL_DOUBLE, "cmpl-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x30, CMPG_DOUBLE, "cmpg-double", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x31, CMP_LONG, "cmp-long", k23x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x32, IF_EQ, "if-eq", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x33, IF_NE, "if-ne", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x34, IF_LT, "if-lt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x35, IF_GE, "if-ge", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x36, IF_GT, "if-gt", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x37, IF_LE, "if-le", k22t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x38, IF_EQZ, "if-eqz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x39, IF_NEZ, "if-nez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3A, IF_LTZ, "if-ltz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3B, IF_GEZ, "if-gez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3C, IF_GTZ, "if-gtz", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3D, IF_LEZ, "if-lez", k21t, kIndexNone, kContinue | kBranch, 0, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3E, UNUSED_3E, "unused-3e", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x3F, UNUSED_3F, "unused-3f", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x40, UNUSED_40, "unused-40", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x41, UNUSED_41, "unused-41", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x42, UNUSED_42, "unused-42", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x43, UNUSED_43, "unused-43", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x44, AGET, "aget", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x45, AGET_WIDE, "aget-wide", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
+ V(0x46, AGET_OBJECT, "aget-object", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x47, AGET_BOOLEAN, "aget-boolean", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x48, AGET_BYTE, "aget-byte", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x49, AGET_CHAR, "aget-char", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4A, AGET_SHORT, "aget-short", k23x, kIndexNone, kContinue | kThrow, kLoad, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4B, APUT, "aput", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4C, APUT_WIDE, "aput-wide", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
+ V(0x4D, APUT_OBJECT, "aput-object", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4E, APUT_BOOLEAN, "aput-boolean", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4F, APUT_BYTE, "aput-byte", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x50, APUT_CHAR, "aput-char", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x51, APUT_SHORT, "aput-short", k23x, kIndexNone, kContinue | kThrow, kStore, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x52, IGET, "iget", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x53, IGET_WIDE, "iget-wide", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
+ V(0x54, IGET_OBJECT, "iget-object", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x55, IGET_BOOLEAN, "iget-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x56, IGET_BYTE, "iget-byte", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x57, IGET_CHAR, "iget-char", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x58, IGET_SHORT, "iget-short", k22c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x59, IPUT, "iput", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5A, IPUT_WIDE, "iput-wide", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
+ V(0x5B, IPUT_OBJECT, "iput-object", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5C, IPUT_BOOLEAN, "iput-boolean", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5D, IPUT_BYTE, "iput-byte", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5E, IPUT_CHAR, "iput-char", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5F, IPUT_SHORT, "iput-short", k22c, kIndexFieldRef, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x60, SGET, "sget", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x61, SGET_WIDE, "sget-wide", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \
+ V(0x62, SGET_OBJECT, "sget-object", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x63, SGET_BOOLEAN, "sget-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x64, SGET_BYTE, "sget-byte", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x65, SGET_CHAR, "sget-char", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x66, SGET_SHORT, "sget-short", k21c, kIndexFieldRef, kContinue | kThrow, kLoad | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x67, SPUT, "sput", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x68, SPUT_WIDE, "sput-wide", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegAWide | kVerifyRegBField) \
+ V(0x69, SPUT_OBJECT, "sput-object", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x6A, SPUT_BOOLEAN, "sput-boolean", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x6B, SPUT_BYTE, "sput-byte", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x6C, SPUT_CHAR, "sput-char", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x6D, SPUT_SHORT, "sput-short", k21c, kIndexFieldRef, kContinue | kThrow, kStore | kRegBFieldOrConstant, kVerifyRegA | kVerifyRegBField) \
+ V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
+ V(0x6F, INVOKE_SUPER, "invoke-super", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
+ V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
+ V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \
+ V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
+ V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+ V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
+ V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
+ V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
+ V(0x77, INVOKE_STATIC_RANGE, "invoke-static/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRange) \
+ V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
+ V(0x79, UNUSED_79, "unused-79", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x7A, UNUSED_7A, "unused-7a", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0x7B, NEG_INT, "neg-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x7C, NOT_INT, "not-int", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x7D, NEG_LONG, "neg-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x7E, NOT_LONG, "not-long", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x7F, NEG_FLOAT, "neg-float", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
+ V(0x80, NEG_DOUBLE, "neg-double", k12x, kIndexNone, kContinue, 0, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x81, INT_TO_LONG, "int-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
+ V(0x82, INT_TO_FLOAT, "int-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
+ V(0x83, INT_TO_DOUBLE, "int-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
+ V(0x84, LONG_TO_INT, "long-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
+ V(0x85, LONG_TO_FLOAT, "long-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
+ V(0x86, LONG_TO_DOUBLE, "long-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x87, FLOAT_TO_INT, "float-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
+ V(0x88, FLOAT_TO_LONG, "float-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
+ V(0x89, FLOAT_TO_DOUBLE, "float-to-double", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegB) \
+ V(0x8A, DOUBLE_TO_INT, "double-to-int", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
+ V(0x8B, DOUBLE_TO_LONG, "double-to-long", k12x, kIndexNone, kContinue, kCast, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x8C, DOUBLE_TO_FLOAT, "double-to-float", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegBWide) \
+ V(0x8D, INT_TO_BYTE, "int-to-byte", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
+ V(0x8E, INT_TO_CHAR, "int-to-char", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
+ V(0x8F, INT_TO_SHORT, "int-to-short", k12x, kIndexNone, kContinue, kCast, kVerifyRegA | kVerifyRegB) \
+ V(0x90, ADD_INT, "add-int", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x91, SUB_INT, "sub-int", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x92, MUL_INT, "mul-int", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x93, DIV_INT, "div-int", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x94, REM_INT, "rem-int", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x95, AND_INT, "and-int", k23x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x96, OR_INT, "or-int", k23x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x97, XOR_INT, "xor-int", k23x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x98, SHL_INT, "shl-int", k23x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x99, SHR_INT, "shr-int", k23x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x9A, USHR_INT, "ushr-int", k23x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x9B, ADD_LONG, "add-long", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9C, SUB_LONG, "sub-long", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9D, MUL_LONG, "mul-long", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9E, DIV_LONG, "div-long", k23x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9F, REM_LONG, "rem-long", k23x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA0, AND_LONG, "and-long", k23x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA1, OR_LONG, "or-long", k23x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA2, XOR_LONG, "xor-long", k23x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA3, SHL_LONG, "shl-long", k23x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
+ V(0xA4, SHR_LONG, "shr-long", k23x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
+ V(0xA5, USHR_LONG, "ushr-long", k23x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
+ V(0xA6, ADD_FLOAT, "add-float", k23x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xA7, SUB_FLOAT, "sub-float", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xA8, MUL_FLOAT, "mul-float", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xA9, DIV_FLOAT, "div-float", k23x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xAA, REM_FLOAT, "rem-float", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xAB, ADD_DOUBLE, "add-double", k23x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAC, SUB_DOUBLE, "sub-double", k23x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAD, MUL_DOUBLE, "mul-double", k23x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAE, DIV_DOUBLE, "div-double", k23x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAF, REM_DOUBLE, "rem-double", k23x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xB0, ADD_INT_2ADDR, "add-int/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \
+ V(0xB1, SUB_INT_2ADDR, "sub-int/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \
+ V(0xB2, MUL_INT_2ADDR, "mul-int/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \
+ V(0xB3, DIV_INT_2ADDR, "div-int/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegA | kVerifyRegB) \
+ V(0xB4, REM_INT_2ADDR, "rem-int/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegA | kVerifyRegB) \
+ V(0xB5, AND_INT_2ADDR, "and-int/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegA | kVerifyRegB) \
+ V(0xB6, OR_INT_2ADDR, "or-int/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegA | kVerifyRegB) \
+ V(0xB7, XOR_INT_2ADDR, "xor-int/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegA | kVerifyRegB) \
+ V(0xB8, SHL_INT_2ADDR, "shl-int/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegA | kVerifyRegB) \
+ V(0xB9, SHR_INT_2ADDR, "shr-int/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegA | kVerifyRegB) \
+ V(0xBA, USHR_INT_2ADDR, "ushr-int/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegA | kVerifyRegB) \
+ V(0xBB, ADD_LONG_2ADDR, "add-long/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBC, SUB_LONG_2ADDR, "sub-long/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBD, MUL_LONG_2ADDR, "mul-long/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBE, DIV_LONG_2ADDR, "div-long/2addr", k12x, kIndexNone, kContinue | kThrow, kDivide, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBF, REM_LONG_2ADDR, "rem-long/2addr", k12x, kIndexNone, kContinue | kThrow, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC0, AND_LONG_2ADDR, "and-long/2addr", k12x, kIndexNone, kContinue, kAnd, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC1, OR_LONG_2ADDR, "or-long/2addr", k12x, kIndexNone, kContinue, kOr, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC2, XOR_LONG_2ADDR, "xor-long/2addr", k12x, kIndexNone, kContinue, kXor, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC3, SHL_LONG_2ADDR, "shl-long/2addr", k12x, kIndexNone, kContinue, kShl, kVerifyRegAWide | kVerifyRegB) \
+ V(0xC4, SHR_LONG_2ADDR, "shr-long/2addr", k12x, kIndexNone, kContinue, kShr, kVerifyRegAWide | kVerifyRegB) \
+ V(0xC5, USHR_LONG_2ADDR, "ushr-long/2addr", k12x, kIndexNone, kContinue, kUshr, kVerifyRegAWide | kVerifyRegB) \
+ V(0xC6, ADD_FLOAT_2ADDR, "add-float/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegA | kVerifyRegB) \
+ V(0xC7, SUB_FLOAT_2ADDR, "sub-float/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegA | kVerifyRegB) \
+ V(0xC8, MUL_FLOAT_2ADDR, "mul-float/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegA | kVerifyRegB) \
+ V(0xC9, DIV_FLOAT_2ADDR, "div-float/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegA | kVerifyRegB) \
+ V(0xCA, REM_FLOAT_2ADDR, "rem-float/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegA | kVerifyRegB) \
+ V(0xCB, ADD_DOUBLE_2ADDR, "add-double/2addr", k12x, kIndexNone, kContinue, kAdd, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCC, SUB_DOUBLE_2ADDR, "sub-double/2addr", k12x, kIndexNone, kContinue, kSubtract, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCD, MUL_DOUBLE_2ADDR, "mul-double/2addr", k12x, kIndexNone, kContinue, kMultiply, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCE, DIV_DOUBLE_2ADDR, "div-double/2addr", k12x, kIndexNone, kContinue, kDivide, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCF, REM_DOUBLE_2ADDR, "rem-double/2addr", k12x, kIndexNone, kContinue, kRemainder, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xD0, ADD_INT_LIT16, "add-int/lit16", k22s, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD1, RSUB_INT, "rsub-int", k22s, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD2, MUL_INT_LIT16, "mul-int/lit16", k22s, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD3, DIV_INT_LIT16, "div-int/lit16", k22s, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD4, REM_INT_LIT16, "rem-int/lit16", k22s, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD5, AND_INT_LIT16, "and-int/lit16", k22s, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD6, OR_INT_LIT16, "or-int/lit16", k22s, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD7, XOR_INT_LIT16, "xor-int/lit16", k22s, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD8, ADD_INT_LIT8, "add-int/lit8", k22b, kIndexNone, kContinue, kAdd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xD9, RSUB_INT_LIT8, "rsub-int/lit8", k22b, kIndexNone, kContinue, kSubtract | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xDA, MUL_INT_LIT8, "mul-int/lit8", k22b, kIndexNone, kContinue, kMultiply | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xDB, DIV_INT_LIT8, "div-int/lit8", k22b, kIndexNone, kContinue | kThrow, kDivide | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xDC, REM_INT_LIT8, "rem-int/lit8", k22b, kIndexNone, kContinue | kThrow, kRemainder | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xDD, AND_INT_LIT8, "and-int/lit8", k22b, kIndexNone, kContinue, kAnd | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xDE, OR_INT_LIT8, "or-int/lit8", k22b, kIndexNone, kContinue, kOr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xDF, XOR_INT_LIT8, "xor-int/lit8", k22b, kIndexNone, kContinue, kXor | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, kIndexNone, kContinue, kShl | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, kIndexNone, kContinue, kShr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, kIndexNone, kContinue, kUshr | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB) \
+ V(0xE3, IGET_QUICK, "iget-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xE4, IGET_WIDE_QUICK, "iget-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xE5, IGET_OBJECT_QUICK, "iget-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xE6, IPUT_QUICK, "iput-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xE7, IPUT_WIDE_QUICK, "iput-wide-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \
+ V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, kIndexVtableOffset, kContinue | kThrow | kInvoke, 0, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \
+ V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xEF, IGET_BOOLEAN_QUICK, "iget-boolean-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xF0, IGET_BYTE_QUICK, "iget-byte-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xF1, IGET_CHAR_QUICK, "iget-char-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xF2, IGET_SHORT_QUICK, "iget-short-quick", k22c, kIndexFieldOffset, kContinue | kThrow, kLoad | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xF3, UNUSED_F3, "unused-f3", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0xF4, UNUSED_F4, "unused-f4", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0xF5, UNUSED_F5, "unused-f5", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0xF6, UNUSED_F6, "unused-f6", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0xF7, UNUSED_F7, "unused-f7", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0xF8, UNUSED_F8, "unused-f8", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, 0, kVerifyError) \
+ V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \
+ V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \
+ V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArg) \
+ V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, 0, kVerifyRegBCallSite | kVerifyVarArgRange) \
+ V(0xFE, CONST_METHOD_HANDLE, "const-method-handle", k21c, kIndexMethodHandleRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBMethodHandle) \
+ V(0xFF, CONST_METHOD_TYPE, "const-method-type", k21c, kIndexProtoRef, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegBPrototype)
+
+#define DEX_INSTRUCTION_FORMAT_LIST(V) \
+ V(k10x) \
+ V(k12x) \
+ V(k11n) \
+ V(k11x) \
+ V(k10t) \
+ V(k20t) \
+ V(k22x) \
+ V(k21t) \
+ V(k21s) \
+ V(k21h) \
+ V(k21c) \
+ V(k23x) \
+ V(k22b) \
+ V(k22t) \
+ V(k22s) \
+ V(k22c) \
+ V(k32x) \
+ V(k30t) \
+ V(k31t) \
+ V(k31i) \
+ V(k31c) \
+ V(k35c) \
+ V(k3rc) \
+ V(k45cc) \
+ V(k4rcc) \
+ V(k51l)
+
+#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_LIST_H_
+#undef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_LIST_H_ // the guard in this file is just for cpplint
diff --git a/libdexfile/dex/dex_instruction_test.cc b/libdexfile/dex/dex_instruction_test.cc
new file mode 100644
index 0000000000..c944085b9e
--- /dev/null
+++ b/libdexfile/dex/dex_instruction_test.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_instruction-inl.h"
+#include "dex_instruction_iterator.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(StaticGetters, PropertiesOfNopTest) {
+ Instruction::Code nop = Instruction::NOP;
+ EXPECT_STREQ("nop", Instruction::Name(nop));
+ EXPECT_EQ(Instruction::k10x, Instruction::FormatOf(nop));
+ EXPECT_EQ(Instruction::kIndexNone, Instruction::IndexTypeOf(nop));
+ EXPECT_EQ(Instruction::kContinue, Instruction::FlagsOf(nop));
+ EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop));
+}
+
+static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx,
+ uint16_t arg_regs, uint16_t* out) {
+ // A = num argument registers
+ // B = method_idx
+ // C - G = argument registers
+ // H = proto_idx
+ //
+ // op = 0xFA
+ //
+ // format:
+ // AG op BBBB FEDC HHHH
+ out[0] = 0;
+ out[0] |= (num_args << 12);
+ out[0] |= 0x00FA;
+
+ out[1] = method_idx;
+ out[2] = arg_regs;
+ out[3] = proto_idx;
+}
+
+static void Build4rcc(uint16_t num_args, uint16_t method_idx, uint16_t proto_idx,
+ uint16_t arg_regs_start, uint16_t* out) {
+ // A = num argument registers
+ // B = method_idx
+ // C = first argument register
+ // H = proto_idx
+ //
+ // op = 0xFB
+ //
+ // format:
+ // AA op BBBB CCCC HHHH
+ out[0] = 0;
+ out[0] |= (num_args << 8);
+ out[0] |= 0x00FB;
+
+ out[1] = method_idx;
+ out[2] = arg_regs_start;
+ out[3] = proto_idx;
+}
+
+TEST(Instruction, PropertiesOf45cc) {
+ uint16_t instruction[4];
+ Build45cc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
+ 0xcafe /* arg_regs */, instruction);
+
+ DexInstructionIterator ins(instruction, /*dex_pc*/ 0u);
+ ASSERT_EQ(4u, ins->SizeInCodeUnits());
+
+ ASSERT_TRUE(ins->HasVRegA());
+ ASSERT_EQ(4, ins->VRegA());
+ ASSERT_EQ(4u, ins->VRegA_45cc());
+ ASSERT_EQ(4u, ins->VRegA_45cc(instruction[0]));
+
+ ASSERT_TRUE(ins->HasVRegB());
+ ASSERT_EQ(16, ins->VRegB());
+ ASSERT_EQ(16u, ins->VRegB_45cc());
+
+ ASSERT_TRUE(ins->HasVRegC());
+ ASSERT_EQ(0xe, ins->VRegC());
+ ASSERT_EQ(0xe, ins->VRegC_45cc());
+
+ ASSERT_TRUE(ins->HasVRegH());
+ ASSERT_EQ(32, ins->VRegH());
+ ASSERT_EQ(32, ins->VRegH_45cc());
+
+ ASSERT_TRUE(ins->HasVarArgs());
+
+ uint32_t arg_regs[Instruction::kMaxVarArgRegs];
+ ins->GetVarArgs(arg_regs);
+ ASSERT_EQ(0xeu, arg_regs[0]);
+ ASSERT_EQ(0xfu, arg_regs[1]);
+ ASSERT_EQ(0xau, arg_regs[2]);
+ ASSERT_EQ(0xcu, arg_regs[3]);
+}
+
+TEST(Instruction, PropertiesOf4rcc) {
+ uint16_t instruction[4];
+ Build4rcc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
+ 0xcafe /* arg_regs */, instruction);
+
+ DexInstructionIterator ins(instruction, /*dex_pc*/ 0u);
+ ASSERT_EQ(4u, ins->SizeInCodeUnits());
+
+ ASSERT_TRUE(ins->HasVRegA());
+ ASSERT_EQ(4, ins->VRegA());
+ ASSERT_EQ(4u, ins->VRegA_4rcc());
+ ASSERT_EQ(4u, ins->VRegA_4rcc(instruction[0]));
+
+ ASSERT_TRUE(ins->HasVRegB());
+ ASSERT_EQ(16, ins->VRegB());
+ ASSERT_EQ(16u, ins->VRegB_4rcc());
+
+ ASSERT_TRUE(ins->HasVRegC());
+ ASSERT_EQ(0xcafe, ins->VRegC());
+ ASSERT_EQ(0xcafe, ins->VRegC_4rcc());
+
+ ASSERT_TRUE(ins->HasVRegH());
+ ASSERT_EQ(32, ins->VRegH());
+ ASSERT_EQ(32, ins->VRegH_4rcc());
+
+ ASSERT_FALSE(ins->HasVarArgs());
+}
+
+static void Build35c(uint16_t* out,
+ Instruction::Code code,
+ uint16_t method_idx,
+ std::vector<uint16_t> args) {
+ out[0] = 0;
+ out[0] |= (args.size() << 12);
+ out[0] |= static_cast<uint16_t>(code);
+ out[1] = method_idx;
+ size_t i = 0;
+ out[2] = 0;
+ for (; i < 4 && i < args.size(); ++i) {
+ out[2] |= args[i] << (i * 4);
+ }
+ if (args.size() == 5) {
+ out[0] |= args[4] << 8;
+ }
+}
+
+static std::string DumpInst35c(Instruction::Code code,
+ uint16_t method_idx,
+ std::vector<uint16_t> args) {
+ uint16_t inst[6] = {};
+ Build35c(inst, code, method_idx, args);
+ return Instruction::At(inst)->DumpString(nullptr);
+}
+
+TEST(Instruction, DumpString) {
+ EXPECT_EQ(DumpInst35c(Instruction::FILLED_NEW_ARRAY, 1234, {3, 2}),
+ "filled-new-array {v3, v2}, type@1234");
+ EXPECT_EQ(DumpInst35c(Instruction::INVOKE_VIRTUAL, 1234, {3, 2, 1, 5, 6}),
+ "invoke-virtual {v3, v2, v1, v5, v6}, thing@1234");
+ EXPECT_EQ(DumpInst35c(Instruction::INVOKE_VIRTUAL_QUICK, 1234, {3, 2, 1, 5}),
+ "invoke-virtual-quick {v3, v2, v1, v5}, thing@1234");
+ EXPECT_EQ(DumpInst35c(Instruction::INVOKE_CUSTOM, 1234, {3, 2, 1}),
+ "invoke-custom {v3, v2, v1}, thing@1234");
+}
+
+} // namespace art
diff --git a/libdexfile/dex/dex_instruction_utils.h b/libdexfile/dex/dex_instruction_utils.h
new file mode 100644
index 0000000000..e7614ada31
--- /dev/null
+++ b/libdexfile/dex/dex_instruction_utils.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_UTILS_H_
+#define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_UTILS_H_
+
+#include "dex_instruction.h"
+
+namespace art {
+
+// Dex invoke type corresponds to the ordering of INVOKE instructions;
+// this order is the same for range and non-range invokes.
+enum DexInvokeType : uint8_t {
+ kDexInvokeVirtual = 0, // invoke-virtual, invoke-virtual-range
+ kDexInvokeSuper, // invoke-super, invoke-super-range
+ kDexInvokeDirect, // invoke-direct, invoke-direct-range
+ kDexInvokeStatic, // invoke-static, invoke-static-range
+ kDexInvokeInterface, // invoke-interface, invoke-interface-range
+ kDexInvokeTypeCount
+};
+
+// Dex instruction memory access types correspond to the ordering of GET/PUT instructions;
+// this order is the same for IGET, IPUT, SGET, SPUT, AGET and APUT.
+enum DexMemAccessType : uint8_t {
+ kDexMemAccessWord = 0, // op 0; int or float, the actual type is not encoded.
+ kDexMemAccessWide, // op_WIDE 1; long or double, the actual type is not encoded.
+ kDexMemAccessObject, // op_OBJECT 2; the actual reference type is not encoded.
+ kDexMemAccessBoolean, // op_BOOLEAN 3
+ kDexMemAccessByte, // op_BYTE 4
+ kDexMemAccessChar, // op_CHAR 5
+ kDexMemAccessShort, // op_SHORT 6
+ kDexMemAccessTypeCount
+};
+
+std::ostream& operator<<(std::ostream& os, const DexMemAccessType& type);
+
+// NOTE: The following functions disregard quickened instructions.
+
+// By "direct" const we mean to exclude const-string and const-class
+// which load data from somewhere else, i.e. indirectly.
+constexpr bool IsInstructionDirectConst(Instruction::Code opcode) {
+ return Instruction::CONST_4 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16;
+}
+
+constexpr bool IsInstructionConstWide(Instruction::Code opcode) {
+ return Instruction::CONST_WIDE_16 <= opcode && opcode <= Instruction::CONST_WIDE_HIGH16;
+}
+
+constexpr bool IsInstructionReturn(Instruction::Code opcode) {
+ return Instruction::RETURN_VOID <= opcode && opcode <= Instruction::RETURN_OBJECT;
+}
+
+constexpr bool IsInstructionInvoke(Instruction::Code opcode) {
+ return Instruction::INVOKE_VIRTUAL <= opcode && opcode <= Instruction::INVOKE_INTERFACE_RANGE &&
+ opcode != Instruction::RETURN_VOID_NO_BARRIER;
+}
+
+constexpr bool IsInstructionQuickInvoke(Instruction::Code opcode) {
+ return opcode == Instruction::INVOKE_VIRTUAL_QUICK ||
+ opcode == Instruction::INVOKE_VIRTUAL_RANGE_QUICK;
+}
+
+constexpr bool IsInstructionInvokeStatic(Instruction::Code opcode) {
+ return opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE;
+}
+
+constexpr bool IsInstructionGoto(Instruction::Code opcode) {
+ return Instruction::GOTO <= opcode && opcode <= Instruction::GOTO_32;
+}
+
+constexpr bool IsInstructionIfCc(Instruction::Code opcode) {
+ return Instruction::IF_EQ <= opcode && opcode <= Instruction::IF_LE;
+}
+
+constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) {
+ return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ;
+}
+
+constexpr bool IsInstructionIGet(Instruction::Code code) {
+ return Instruction::IGET <= code && code <= Instruction::IGET_SHORT;
+}
+
+constexpr bool IsInstructionIPut(Instruction::Code code) {
+ return Instruction::IPUT <= code && code <= Instruction::IPUT_SHORT;
+}
+
+constexpr bool IsInstructionSGet(Instruction::Code code) {
+ return Instruction::SGET <= code && code <= Instruction::SGET_SHORT;
+}
+
+constexpr bool IsInstructionSPut(Instruction::Code code) {
+ return Instruction::SPUT <= code && code <= Instruction::SPUT_SHORT;
+}
+
+constexpr bool IsInstructionAGet(Instruction::Code code) {
+ return Instruction::AGET <= code && code <= Instruction::AGET_SHORT;
+}
+
+constexpr bool IsInstructionAPut(Instruction::Code code) {
+ return Instruction::APUT <= code && code <= Instruction::APUT_SHORT;
+}
+
+constexpr bool IsInstructionIGetOrIPut(Instruction::Code code) {
+ return Instruction::IGET <= code && code <= Instruction::IPUT_SHORT;
+}
+
+constexpr bool IsInstructionIGetQuickOrIPutQuick(Instruction::Code code) {
+ return (code >= Instruction::IGET_QUICK && code <= Instruction::IPUT_OBJECT_QUICK) ||
+ (code >= Instruction::IPUT_BOOLEAN_QUICK && code <= Instruction::IGET_SHORT_QUICK);
+}
+
+constexpr bool IsInstructionSGetOrSPut(Instruction::Code code) {
+ return Instruction::SGET <= code && code <= Instruction::SPUT_SHORT;
+}
+
+constexpr bool IsInstructionAGetOrAPut(Instruction::Code code) {
+ return Instruction::AGET <= code && code <= Instruction::APUT_SHORT;
+}
+
+constexpr bool IsInstructionBinOp2Addr(Instruction::Code code) {
+ return Instruction::ADD_INT_2ADDR <= code && code <= Instruction::REM_DOUBLE_2ADDR;
+}
+
+constexpr bool IsInvokeInstructionRange(Instruction::Code opcode) {
+ DCHECK(IsInstructionInvoke(opcode));
+ return opcode >= Instruction::INVOKE_VIRTUAL_RANGE;
+}
+
+constexpr DexInvokeType InvokeInstructionType(Instruction::Code opcode) {
+ DCHECK(IsInstructionInvoke(opcode));
+ return static_cast<DexInvokeType>(IsInvokeInstructionRange(opcode)
+ ? (opcode - Instruction::INVOKE_VIRTUAL_RANGE)
+ : (opcode - Instruction::INVOKE_VIRTUAL));
+}
+
+constexpr DexMemAccessType IGetMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionIGet(code));
+ return static_cast<DexMemAccessType>(code - Instruction::IGET);
+}
+
+constexpr DexMemAccessType IPutMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionIPut(code));
+ return static_cast<DexMemAccessType>(code - Instruction::IPUT);
+}
+
+constexpr DexMemAccessType SGetMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionSGet(code));
+ return static_cast<DexMemAccessType>(code - Instruction::SGET);
+}
+
+constexpr DexMemAccessType SPutMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionSPut(code));
+ return static_cast<DexMemAccessType>(code - Instruction::SPUT);
+}
+
+constexpr DexMemAccessType AGetMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionAGet(code));
+ return static_cast<DexMemAccessType>(code - Instruction::AGET);
+}
+
+constexpr DexMemAccessType APutMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionAPut(code));
+ return static_cast<DexMemAccessType>(code - Instruction::APUT);
+}
+
+constexpr DexMemAccessType IGetOrIPutMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionIGetOrIPut(code));
+ return (code >= Instruction::IPUT) ? IPutMemAccessType(code) : IGetMemAccessType(code);
+}
+
+inline DexMemAccessType IGetQuickOrIPutQuickMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionIGetQuickOrIPutQuick(code));
+ switch (code) {
+ case Instruction::IGET_QUICK: case Instruction::IPUT_QUICK:
+ return kDexMemAccessWord;
+ case Instruction::IGET_WIDE_QUICK: case Instruction::IPUT_WIDE_QUICK:
+ return kDexMemAccessWide;
+ case Instruction::IGET_OBJECT_QUICK: case Instruction::IPUT_OBJECT_QUICK:
+ return kDexMemAccessObject;
+ case Instruction::IGET_BOOLEAN_QUICK: case Instruction::IPUT_BOOLEAN_QUICK:
+ return kDexMemAccessBoolean;
+ case Instruction::IGET_BYTE_QUICK: case Instruction::IPUT_BYTE_QUICK:
+ return kDexMemAccessByte;
+ case Instruction::IGET_CHAR_QUICK: case Instruction::IPUT_CHAR_QUICK:
+ return kDexMemAccessChar;
+ case Instruction::IGET_SHORT_QUICK: case Instruction::IPUT_SHORT_QUICK:
+ return kDexMemAccessShort;
+ default:
+ LOG(FATAL) << code;
+ UNREACHABLE();
+ }
+}
+
+constexpr DexMemAccessType SGetOrSPutMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionSGetOrSPut(code));
+ return (code >= Instruction::SPUT) ? SPutMemAccessType(code) : SGetMemAccessType(code);
+}
+
+constexpr DexMemAccessType AGetOrAPutMemAccessType(Instruction::Code code) {
+ DCHECK(IsInstructionAGetOrAPut(code));
+ return (code >= Instruction::APUT) ? APutMemAccessType(code) : AGetMemAccessType(code);
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_UTILS_H_
diff --git a/libdexfile/dex/invoke_type.h b/libdexfile/dex/invoke_type.h
new file mode 100644
index 0000000000..9b3af673a8
--- /dev/null
+++ b/libdexfile/dex/invoke_type.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_INVOKE_TYPE_H_
+#define ART_LIBDEXFILE_DEX_INVOKE_TYPE_H_
+
+#include <iosfwd>
+
+namespace art {
+
+enum InvokeType : uint32_t {
+ kStatic, // <<static>>
+ kDirect, // <<direct>>
+ kVirtual, // <<virtual>>
+ kSuper, // <<super>>
+ kInterface, // <<interface>>
+ kPolymorphic, // <<polymorphic>>
+ kMaxInvokeType = kPolymorphic
+};
+
+std::ostream& operator<<(std::ostream& os, const InvokeType& rhs);
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_INVOKE_TYPE_H_
diff --git a/libdexfile/dex/modifiers.cc b/libdexfile/dex/modifiers.cc
new file mode 100644
index 0000000000..30daefb172
--- /dev/null
+++ b/libdexfile/dex/modifiers.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "modifiers.h"
+
+namespace art {
+
+std::string PrettyJavaAccessFlags(uint32_t access_flags) {
+ std::string result;
+ if ((access_flags & kAccPublic) != 0) {
+ result += "public ";
+ }
+ if ((access_flags & kAccProtected) != 0) {
+ result += "protected ";
+ }
+ if ((access_flags & kAccPrivate) != 0) {
+ result += "private ";
+ }
+ if ((access_flags & kAccFinal) != 0) {
+ result += "final ";
+ }
+ if ((access_flags & kAccStatic) != 0) {
+ result += "static ";
+ }
+ if ((access_flags & kAccAbstract) != 0) {
+ result += "abstract ";
+ }
+ if ((access_flags & kAccInterface) != 0) {
+ result += "interface ";
+ }
+ if ((access_flags & kAccTransient) != 0) {
+ result += "transient ";
+ }
+ if ((access_flags & kAccVolatile) != 0) {
+ result += "volatile ";
+ }
+ if ((access_flags & kAccSynchronized) != 0) {
+ result += "synchronized ";
+ }
+ return result;
+}
+
+} // namespace art
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
new file mode 100644
index 0000000000..2425a588df
--- /dev/null
+++ b/libdexfile/dex/modifiers.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_MODIFIERS_H_
+#define ART_LIBDEXFILE_DEX_MODIFIERS_H_
+
+#include <stdint.h>
+
+namespace art {
+
+static constexpr uint32_t kAccPublic = 0x0001; // class, field, method, ic
+static constexpr uint32_t kAccPrivate = 0x0002; // field, method, ic
+static constexpr uint32_t kAccProtected = 0x0004; // field, method, ic
+static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
+static constexpr uint32_t kAccFinal = 0x0010; // class, field, method, ic
+static constexpr uint32_t kAccSynchronized = 0x0020; // method (only allowed on natives)
+static constexpr uint32_t kAccSuper = 0x0020; // class (not used in dex)
+static constexpr uint32_t kAccVolatile = 0x0040; // field
+static constexpr uint32_t kAccBridge = 0x0040; // method (1.5)
+static constexpr uint32_t kAccTransient = 0x0080; // field
+static constexpr uint32_t kAccVarargs = 0x0080; // method (1.5)
+static constexpr uint32_t kAccNative = 0x0100; // method
+static constexpr uint32_t kAccInterface = 0x0200; // class, ic
+static constexpr uint32_t kAccAbstract = 0x0400; // class, method, ic
+static constexpr uint32_t kAccStrict = 0x0800; // method
+static constexpr uint32_t kAccSynthetic = 0x1000; // class, field, method, ic
+static constexpr uint32_t kAccAnnotation = 0x2000; // class, ic (1.5)
+static constexpr uint32_t kAccEnum = 0x4000; // class, field, ic (1.5)
+
+static constexpr uint32_t kAccJavaFlagsMask = 0xffff; // bits set from Java sources (low 16)
+
+// The following flags are used to insert hidden API access flags into boot
+// class path dex files. They are decoded by DexFile::ClassDataItemIterator and
+// removed from the access flags before used by the runtime.
+static constexpr uint32_t kAccDexHiddenBit = 0x00000020; // field, method (not native)
+static constexpr uint32_t kAccDexHiddenBitNative = 0x00000200; // method (native)
+
+static constexpr uint32_t kAccConstructor = 0x00010000; // method (dex only) <(cl)init>
+static constexpr uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
+static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
+// Set to indicate that the ArtMethod is obsolete and has a different DexCache + DexFile from its
+// declaring class. This flag may only be applied to methods.
+static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime)
+// Used by a method to denote that its execution does not need to go through slow path interpreter.
+static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native)
+// Used by a class to denote that the verifier has attempted to check it at least once.
+static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
+// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
+// that it was copied from its declaring class into another class. All methods marked kAccMiranda
+// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
+// array of a concrete class will also have this bit set.
+static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime)
+static constexpr uint32_t kAccMiranda = 0x00200000; // method (runtime, not native)
+static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
+// Native method flags are set when linking the methods based on the presence of the
+// @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility.
+// Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods.
+static constexpr uint32_t kAccFastNative = 0x00080000; // method (runtime; native only)
+static constexpr uint32_t kAccCriticalNative = 0x00200000; // method (runtime; native only)
+
+// Set by the JIT when clearing profiling infos to denote that a method was previously warm.
+static constexpr uint32_t kAccPreviouslyWarm = 0x00800000; // method (runtime)
+
+// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
+// if any particular method needs to be a default conflict. Used to figure out at runtime if
+// invoking this method will throw an exception.
+static constexpr uint32_t kAccDefaultConflict = 0x01000000; // method (runtime)
+
+// Set by the verifier for a method we do not want the compiler to compile.
+static constexpr uint32_t kAccCompileDontBother = 0x02000000; // method (runtime)
+
+// Set by the verifier for a method that could not be verified to follow structured locking.
+static constexpr uint32_t kAccMustCountLocks = 0x04000000; // method (runtime)
+
+// Set by the class linker for a method that has only one implementation for a
+// virtual call.
+static constexpr uint32_t kAccSingleImplementation = 0x08000000; // method (runtime)
+
+static constexpr uint32_t kAccHiddenApiBits = 0x30000000; // field, method
+
+// Not currently used, except for intrinsic methods where these bits
+// are part of the intrinsic ordinal.
+static constexpr uint32_t kAccMayBeUnusedBits = 0x40000000;
+
+// Set by the compiler driver when compiling boot classes with instrinsic methods.
+static constexpr uint32_t kAccIntrinsic = 0x80000000; // method (runtime)
+
+// Special runtime-only flags.
+// Interface and all its super-interfaces with default methods have been recursively initialized.
+static constexpr uint32_t kAccRecursivelyInitialized = 0x20000000;
+// Interface declares some default method.
+static constexpr uint32_t kAccHasDefaultMethod = 0x40000000;
+// class/ancestor overrides finalize()
+static constexpr uint32_t kAccClassIsFinalizable = 0x80000000;
+
+// Continuous sequence of bits used to hold the ordinal of an intrinsic method. Flags
+// which overlap are not valid when kAccIntrinsic is set.
+static constexpr uint32_t kAccIntrinsicBits = kAccMayBeUnusedBits | kAccHiddenApiBits |
+ kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccDefaultConflict |
+ kAccPreviouslyWarm;
+
+// Valid (meaningful) bits for a field.
+static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected |
+ kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum;
+
+// Valid (meaningful) bits for a method.
+static constexpr uint32_t kAccValidMethodFlags = kAccPublic | kAccPrivate | kAccProtected |
+ kAccStatic | kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative |
+ kAccAbstract | kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized;
+static_assert(((kAccIntrinsic | kAccIntrinsicBits) & kAccValidMethodFlags) == 0,
+ "Intrinsic bits and valid dex file method access flags must not overlap.");
+
+// Valid (meaningful) bits for a class (not interface).
+// Note 1. These are positive bits. Other bits may have to be zero.
+// Note 2. Inner classes can expose more access flags to Java programs. That is handled by libcore.
+static constexpr uint32_t kAccValidClassFlags = kAccPublic | kAccFinal | kAccSuper |
+ kAccAbstract | kAccSynthetic | kAccEnum;
+
+// Valid (meaningful) bits for an interface.
+// Note 1. Annotations are interfaces.
+// Note 2. These are positive bits. Other bits may have to be zero.
+// Note 3. Inner classes can expose more access flags to Java programs. That is handled by libcore.
+static constexpr uint32_t kAccValidInterfaceFlags = kAccPublic | kAccInterface |
+ kAccAbstract | kAccSynthetic | kAccAnnotation;
+
+static constexpr uint32_t kAccVisibilityFlags = kAccPublic | kAccPrivate | kAccProtected;
+
+// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
+// (note the trailing whitespace).
+std::string PrettyJavaAccessFlags(uint32_t access_flags);
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_MODIFIERS_H_
+
diff --git a/libdexfile/dex/standard_dex_file.cc b/libdexfile/dex/standard_dex_file.cc
new file mode 100644
index 0000000000..f7317eb997
--- /dev/null
+++ b/libdexfile/dex/standard_dex_file.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "standard_dex_file.h"
+
+#include "base/casts.h"
+#include "code_item_accessors-inl.h"
+#include "dex_file-inl.h"
+#include "leb128.h"
+
+namespace art {
+
+const uint8_t StandardDexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
+const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersions]
+ [StandardDexFile::kDexVersionLen] = {
+ {'0', '3', '5', '\0'},
+ // Dex version 036 skipped because of an old dalvik bug on some versions of android where dex
+ // files with that version number would erroneously be accepted and run.
+ {'0', '3', '7', '\0'},
+ // Dex version 038: Android "O" and beyond.
+ {'0', '3', '8', '\0'},
+ // Dex verion 039: Beyond Android "O".
+ {'0', '3', '9', '\0'},
+};
+
+void StandardDexFile::WriteMagic(uint8_t* magic) {
+ std::copy_n(kDexMagic, kDexMagicSize, magic);
+}
+
+void StandardDexFile::WriteCurrentVersion(uint8_t* magic) {
+ std::copy_n(kDexMagicVersions[StandardDexFile::kDexVersionLen - 1],
+ kDexVersionLen,
+ magic + kDexMagicSize);
+}
+
+bool StandardDexFile::IsMagicValid(const uint8_t* magic) {
+ return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0);
+}
+
+bool StandardDexFile::IsVersionValid(const uint8_t* magic) {
+ const uint8_t* version = &magic[sizeof(kDexMagic)];
+ for (uint32_t i = 0; i < kNumDexVersions; i++) {
+ if (memcmp(version, kDexMagicVersions[i], kDexVersionLen) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool StandardDexFile::IsMagicValid() const {
+ return IsMagicValid(header_->magic_);
+}
+
+bool StandardDexFile::IsVersionValid() const {
+ return IsVersionValid(header_->magic_);
+}
+
+bool StandardDexFile::SupportsDefaultMethods() const {
+ return GetDexVersion() >= DexFile::kDefaultMethodsVersion;
+}
+
+uint32_t StandardDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const {
+ DCHECK(IsInDataSection(&item));
+ return reinterpret_cast<uintptr_t>(CodeItemDataAccessor(*this, &item).CodeItemDataEnd()) -
+ reinterpret_cast<uintptr_t>(&item);
+}
+
+} // namespace art
diff --git a/libdexfile/dex/standard_dex_file.h b/libdexfile/dex/standard_dex_file.h
new file mode 100644
index 0000000000..9b13caa2be
--- /dev/null
+++ b/libdexfile/dex/standard_dex_file.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_STANDARD_DEX_FILE_H_
+#define ART_LIBDEXFILE_DEX_STANDARD_DEX_FILE_H_
+
+#include <iosfwd>
+
+#include "dex_file.h"
+
+namespace art {
+
+class OatDexFile;
+
+// Standard dex file. This is the format that is packaged in APKs and produced by tools.
+class StandardDexFile : public DexFile {
+ public:
+ class Header : public DexFile::Header {
+ // Same for now.
+ };
+
+ struct CodeItem : public DexFile::CodeItem {
+ static constexpr size_t kAlignment = 4;
+
+ private:
+ CodeItem() = default;
+
+ uint16_t registers_size_; // the number of registers used by this code
+ // (locals + parameters)
+ uint16_t ins_size_; // the number of words of incoming arguments to the method
+ // that this code is for
+ uint16_t outs_size_; // the number of words of outgoing argument space required
+ // by this code for method invocation
+ uint16_t tries_size_; // the number of try_items for this instance. If non-zero,
+ // then these appear as the tries array just after the
+ // insns in this instance.
+ uint32_t debug_info_off_; // Holds file offset to debug info stream.
+
+ uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units
+ uint16_t insns_[1]; // actual array of bytecode.
+
+ ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
+ friend class CodeItemDataAccessor;
+ friend class CodeItemDebugInfoAccessor;
+ friend class CodeItemInstructionAccessor;
+ friend class DexWriter;
+ friend class StandardDexFile;
+ DISALLOW_COPY_AND_ASSIGN(CodeItem);
+ };
+
+ // Write the standard dex specific magic.
+ static void WriteMagic(uint8_t* magic);
+
+ // Write the current version, note that the input is the address of the magic.
+ static void WriteCurrentVersion(uint8_t* magic);
+
+ static const uint8_t kDexMagic[kDexMagicSize];
+ static constexpr size_t kNumDexVersions = 4;
+ static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen];
+
+ // Returns true if the byte string points to the magic value.
+ static bool IsMagicValid(const uint8_t* magic);
+ virtual bool IsMagicValid() const OVERRIDE;
+
+ // Returns true if the byte string after the magic is the correct value.
+ static bool IsVersionValid(const uint8_t* magic);
+ virtual bool IsVersionValid() const OVERRIDE;
+
+ virtual bool SupportsDefaultMethods() const OVERRIDE;
+
+ uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
+
+ virtual size_t GetDequickenedSize() const OVERRIDE {
+ return Size();
+ }
+
+ private:
+ StandardDexFile(const uint8_t* base,
+ size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ DexFileContainer* container)
+ : DexFile(base,
+ size,
+ /*data_begin*/ base,
+ /*data_size*/ size,
+ location,
+ location_checksum,
+ oat_dex_file,
+ container,
+ /*is_compact_dex*/ false) {}
+
+ friend class DexFileLoader;
+ friend class DexFileVerifierTest;
+
+ ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor
+ friend class OptimizingUnitTestHelper; // for constructor
+
+ DISALLOW_COPY_AND_ASSIGN(StandardDexFile);
+};
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_STANDARD_DEX_FILE_H_
diff --git a/libdexfile/dex/utf-inl.h b/libdexfile/dex/utf-inl.h
new file mode 100644
index 0000000000..5355766aae
--- /dev/null
+++ b/libdexfile/dex/utf-inl.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_UTF_INL_H_
+#define ART_LIBDEXFILE_DEX_UTF_INL_H_
+
+#include "utf.h"
+
+namespace art {
+
+inline uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) {
+ return static_cast<uint16_t>(maybe_pair >> 16);
+}
+
+inline uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) {
+ return static_cast<uint16_t>(maybe_pair & 0x0000FFFF);
+}
+
+inline uint32_t GetUtf16FromUtf8(const char** utf8_data_in) {
+ const uint8_t one = *(*utf8_data_in)++;
+ if ((one & 0x80) == 0) {
+ // one-byte encoding
+ return one;
+ }
+
+ const uint8_t two = *(*utf8_data_in)++;
+ if ((one & 0x20) == 0) {
+ // two-byte encoding
+ return ((one & 0x1f) << 6) | (two & 0x3f);
+ }
+
+ const uint8_t three = *(*utf8_data_in)++;
+ if ((one & 0x10) == 0) {
+ return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
+ }
+
+ // Four byte encodings need special handling. We'll have
+ // to convert them into a surrogate pair.
+ const uint8_t four = *(*utf8_data_in)++;
+
+ // Since this is a 4 byte UTF-8 sequence, it will lie between
+ // U+10000 and U+1FFFFF.
+ //
+ // TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The
+ // spec says they're invalid but nobody appears to check for them.
+ const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12)
+ | ((three & 0x3f) << 6) | (four & 0x3f);
+
+ uint32_t surrogate_pair = 0;
+ // Step two: Write out the high (leading) surrogate to the bottom 16 bits
+ // of the of the 32 bit type.
+ surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff;
+ // Step three : Write out the low (trailing) surrogate to the top 16 bits.
+ surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16;
+
+ return surrogate_pair;
+}
+
+inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1,
+ const char* utf8_2) {
+ uint32_t c1, c2;
+ do {
+ c1 = *utf8_1;
+ c2 = *utf8_2;
+ // Did we reach a terminating character?
+ if (c1 == 0) {
+ return (c2 == 0) ? 0 : -1;
+ } else if (c2 == 0) {
+ return 1;
+ }
+
+ c1 = GetUtf16FromUtf8(&utf8_1);
+ c2 = GetUtf16FromUtf8(&utf8_2);
+ } while (c1 == c2);
+
+ const uint32_t leading_surrogate_diff = GetLeadingUtf16Char(c1) - GetLeadingUtf16Char(c2);
+ if (leading_surrogate_diff != 0) {
+ return static_cast<int>(leading_surrogate_diff);
+ }
+
+ return GetTrailingUtf16Char(c1) - GetTrailingUtf16Char(c2);
+}
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_UTF_INL_H_
diff --git a/libdexfile/dex/utf.cc b/libdexfile/dex/utf.cc
new file mode 100644
index 0000000000..772a610140
--- /dev/null
+++ b/libdexfile/dex/utf.cc
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utf.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "base/casts.h"
+#include "utf-inl.h"
+
+namespace art {
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+// This is used only from debugger and test code.
+size_t CountModifiedUtf8Chars(const char* utf8) {
+ return CountModifiedUtf8Chars(utf8, strlen(utf8));
+}
+
+/*
+ * This does not validate UTF8 rules (nor did older code). But it gets the right answer
+ * for valid UTF-8 and that's fine because it's used only to size a buffer for later
+ * conversion.
+ *
+ * Modified UTF-8 consists of a series of bytes up to 21 bit Unicode code points as follows:
+ * U+0001 - U+007F 0xxxxxxx
+ * U+0080 - U+07FF 110xxxxx 10xxxxxx
+ * U+0800 - U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
+ * U+10000 - U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ *
+ * U+0000 is encoded using the 2nd form to avoid nulls inside strings (this differs from
+ * standard UTF-8).
+ * The four byte encoding converts to two utf16 characters.
+ */
+size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) {
+ DCHECK_LE(byte_count, strlen(utf8));
+ size_t len = 0;
+ const char* end = utf8 + byte_count;
+ for (; utf8 < end; ++utf8) {
+ int ic = *utf8;
+ len++;
+ if (LIKELY((ic & 0x80) == 0)) {
+ // One-byte encoding.
+ continue;
+ }
+ // Two- or three-byte encoding.
+ utf8++;
+ if ((ic & 0x20) == 0) {
+ // Two-byte encoding.
+ continue;
+ }
+ utf8++;
+ if ((ic & 0x10) == 0) {
+ // Three-byte encoding.
+ continue;
+ }
+
+ // Four-byte encoding: needs to be converted into a surrogate
+ // pair.
+ utf8++;
+ len++;
+ }
+ return len;
+}
+
+// This is used only from debugger and test code.
+void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in) {
+ while (*utf8_data_in != '\0') {
+ const uint32_t ch = GetUtf16FromUtf8(&utf8_data_in);
+ const uint16_t leading = GetLeadingUtf16Char(ch);
+ const uint16_t trailing = GetTrailingUtf16Char(ch);
+
+ *utf16_data_out++ = leading;
+ if (trailing != 0) {
+ *utf16_data_out++ = trailing;
+ }
+ }
+}
+
+void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, size_t out_chars,
+ const char* utf8_data_in, size_t in_bytes) {
+ const char *in_start = utf8_data_in;
+ const char *in_end = utf8_data_in + in_bytes;
+ uint16_t *out_p = utf16_data_out;
+
+ if (LIKELY(out_chars == in_bytes)) {
+ // Common case where all characters are ASCII.
+ for (const char *p = in_start; p < in_end;) {
+ // Safe even if char is signed because ASCII characters always have
+ // the high bit cleared.
+ *out_p++ = dchecked_integral_cast<uint16_t>(*p++);
+ }
+ return;
+ }
+
+ // String contains non-ASCII characters.
+ for (const char *p = in_start; p < in_end;) {
+ const uint32_t ch = GetUtf16FromUtf8(&p);
+ const uint16_t leading = GetLeadingUtf16Char(ch);
+ const uint16_t trailing = GetTrailingUtf16Char(ch);
+
+ *out_p++ = leading;
+ if (trailing != 0) {
+ *out_p++ = trailing;
+ }
+ }
+}
+
+void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count,
+ const uint16_t* utf16_in, size_t char_count) {
+ if (LIKELY(byte_count == char_count)) {
+ // Common case where all characters are ASCII.
+ const uint16_t *utf16_end = utf16_in + char_count;
+ for (const uint16_t *p = utf16_in; p < utf16_end;) {
+ *utf8_out++ = dchecked_integral_cast<char>(*p++);
+ }
+ return;
+ }
+
+ // String contains non-ASCII characters.
+ while (char_count--) {
+ const uint16_t ch = *utf16_in++;
+ if (ch > 0 && ch <= 0x7f) {
+ *utf8_out++ = ch;
+ } else {
+ // Char_count == 0 here implies we've encountered an unpaired
+ // surrogate and we have no choice but to encode it as 3-byte UTF
+ // sequence. Note that unpaired surrogates can occur as a part of
+ // "normal" operation.
+ if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
+ const uint16_t ch2 = *utf16_in;
+
+ // Check if the other half of the pair is within the expected
+ // range. If it isn't, we will have to emit both "halves" as
+ // separate 3 byte sequences.
+ if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
+ utf16_in++;
+ char_count--;
+ const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
+ *utf8_out++ = (code_point >> 18) | 0xf0;
+ *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
+ *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
+ *utf8_out++ = (code_point & 0x3f) | 0x80;
+ continue;
+ }
+ }
+
+ if (ch > 0x07ff) {
+ // Three byte encoding.
+ *utf8_out++ = (ch >> 12) | 0xe0;
+ *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
+ *utf8_out++ = (ch & 0x3f) | 0x80;
+ } else /*(ch > 0x7f || ch == 0)*/ {
+ // Two byte encoding.
+ *utf8_out++ = (ch >> 6) | 0xc0;
+ *utf8_out++ = (ch & 0x3f) | 0x80;
+ }
+ }
+ }
+}
+
+int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length) {
+ uint32_t hash = 0;
+ while (utf16_length != 0u) {
+ const uint32_t pair = GetUtf16FromUtf8(&utf8);
+ const uint16_t first = GetLeadingUtf16Char(pair);
+ hash = hash * 31 + first;
+ --utf16_length;
+ const uint16_t second = GetTrailingUtf16Char(pair);
+ if (second != 0) {
+ hash = hash * 31 + second;
+ DCHECK_NE(utf16_length, 0u);
+ --utf16_length;
+ }
+ }
+ return static_cast<int32_t>(hash);
+}
+
+uint32_t ComputeModifiedUtf8Hash(const char* chars) {
+ uint32_t hash = 0;
+ while (*chars != '\0') {
+ hash = hash * 31 + *chars++;
+ }
+ return static_cast<int32_t>(hash);
+}
+
+int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16,
+ size_t utf16_length) {
+ for (;;) {
+ if (*utf8 == '\0') {
+ return (utf16_length == 0) ? 0 : -1;
+ } else if (utf16_length == 0) {
+ return 1;
+ }
+
+ const uint32_t pair = GetUtf16FromUtf8(&utf8);
+
+ // First compare the leading utf16 char.
+ const uint16_t lhs = GetLeadingUtf16Char(pair);
+ const uint16_t rhs = *utf16++;
+ --utf16_length;
+ if (lhs != rhs) {
+ return lhs > rhs ? 1 : -1;
+ }
+
+ // Then compare the trailing utf16 char. First check if there
+ // are any characters left to consume.
+ const uint16_t lhs2 = GetTrailingUtf16Char(pair);
+ if (lhs2 != 0) {
+ if (utf16_length == 0) {
+ return 1;
+ }
+
+ const uint16_t rhs2 = *utf16++;
+ --utf16_length;
+ if (lhs2 != rhs2) {
+ return lhs2 > rhs2 ? 1 : -1;
+ }
+ }
+ }
+}
+
+size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+ size_t result = 0;
+ const uint16_t *end = chars + char_count;
+ while (chars < end) {
+ const uint16_t ch = *chars++;
+ if (LIKELY(ch != 0 && ch < 0x80)) {
+ result++;
+ continue;
+ }
+ if (ch < 0x800) {
+ result += 2;
+ continue;
+ }
+ if (ch >= 0xd800 && ch < 0xdc00) {
+ if (chars < end) {
+ const uint16_t ch2 = *chars;
+ // If we find a properly paired surrogate, we emit it as a 4 byte
+ // UTF sequence. If we find an unpaired leading or trailing surrogate,
+ // we emit it as a 3 byte sequence like would have done earlier.
+ if (ch2 >= 0xdc00 && ch2 < 0xe000) {
+ chars++;
+ result += 4;
+ continue;
+ }
+ }
+ }
+ result += 3;
+ }
+ return result;
+}
+
+static inline constexpr bool NeedsEscaping(uint16_t ch) {
+ return (ch < ' ' || ch > '~');
+}
+
+std::string PrintableChar(uint16_t ch) {
+ std::string result;
+ result += '\'';
+ if (NeedsEscaping(ch)) {
+ StringAppendF(&result, "\\u%04x", ch);
+ } else {
+ result += static_cast<std::string::value_type>(ch);
+ }
+ result += '\'';
+ return result;
+}
+
+std::string PrintableString(const char* utf) {
+ std::string result;
+ result += '"';
+ const char* p = utf;
+ size_t char_count = CountModifiedUtf8Chars(p);
+ for (size_t i = 0; i < char_count; ++i) {
+ uint32_t ch = GetUtf16FromUtf8(&p);
+ if (ch == '\\') {
+ result += "\\\\";
+ } else if (ch == '\n') {
+ result += "\\n";
+ } else if (ch == '\r') {
+ result += "\\r";
+ } else if (ch == '\t') {
+ result += "\\t";
+ } else {
+ const uint16_t leading = GetLeadingUtf16Char(ch);
+
+ if (NeedsEscaping(leading)) {
+ StringAppendF(&result, "\\u%04x", leading);
+ } else {
+ result += static_cast<std::string::value_type>(leading);
+ }
+
+ const uint32_t trailing = GetTrailingUtf16Char(ch);
+ if (trailing != 0) {
+ // All high surrogates will need escaping.
+ StringAppendF(&result, "\\u%04x", trailing);
+ }
+ }
+ }
+ result += '"';
+ return result;
+}
+
+} // namespace art
diff --git a/libdexfile/dex/utf.h b/libdexfile/dex/utf.h
new file mode 100644
index 0000000000..c86b389175
--- /dev/null
+++ b/libdexfile/dex/utf.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBDEXFILE_DEX_UTF_H_
+#define ART_LIBDEXFILE_DEX_UTF_H_
+
+#include "base/macros.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+/*
+ * All UTF-8 in art is actually modified UTF-8. Mostly, this distinction
+ * doesn't matter.
+ *
+ * See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details.
+ */
+namespace art {
+
+/*
+ * Returns the number of UTF-16 characters in the given modified UTF-8 string.
+ */
+size_t CountModifiedUtf8Chars(const char* utf8);
+size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count);
+
+/*
+ * Returns the number of modified UTF-8 bytes needed to represent the given
+ * UTF-16 string.
+ */
+size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count);
+
+/*
+ * Convert from Modified UTF-8 to UTF-16.
+ */
+void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in);
+void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, size_t out_chars,
+ const char* utf8_in, size_t in_bytes);
+
+/*
+ * Compare two modified UTF-8 strings as UTF-16 code point values in a non-locale sensitive manner
+ */
+ALWAYS_INLINE int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1,
+ const char* utf8_2);
+
+/*
+ * Compare a null-terminated modified UTF-8 string with a UTF-16 string (not null-terminated)
+ * as code point values in a non-locale sensitive manner.
+ */
+int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16,
+ size_t utf16_length);
+
+/*
+ * Convert from UTF-16 to Modified UTF-8. Note that the output is _not_
+ * NUL-terminated. You probably need to call CountUtf8Bytes before calling
+ * this anyway, so if you want a NUL-terminated string, you know where to
+ * put the NUL byte.
+ */
+void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count,
+ const uint16_t* utf16_in, size_t char_count);
+
+/*
+ * The java.lang.String hashCode() algorithm.
+ */
+template<typename MemoryType>
+int32_t ComputeUtf16Hash(const MemoryType* chars, size_t char_count) {
+ uint32_t hash = 0;
+ while (char_count--) {
+ hash = hash * 31 + *chars++;
+ }
+ return static_cast<int32_t>(hash);
+}
+
+int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length);
+
+// Compute a hash code of a modified UTF-8 string. Not the standard java hash since it returns a
+// uint32_t and hashes individual chars instead of codepoint words.
+uint32_t ComputeModifiedUtf8Hash(const char* chars);
+
+/*
+ * Retrieve the next UTF-16 character or surrogate pair from a UTF-8 string.
+ * single byte, 2-byte and 3-byte UTF-8 sequences result in a single UTF-16
+ * character (possibly one half of a surrogate) whereas 4-byte UTF-8 sequences
+ * result in a surrogate pair. Use GetLeadingUtf16Char and GetTrailingUtf16Char
+ * to process the return value of this function.
+ *
+ * Advances "*utf8_data_in" to the start of the next character.
+ *
+ * WARNING: If a string is corrupted by dropping a '\0' in the middle
+ * of a multi byte sequence, you can end up overrunning the buffer with
+ * reads (and possibly with the writes if the length was computed and
+ * cached before the damage). For performance reasons, this function
+ * assumes that the string being parsed is known to be valid (e.g., by
+ * already being verified). Most strings we process here are coming
+ * out of dex files or other internal translations, so the only real
+ * risk comes from the JNI NewStringUTF call.
+ */
+uint32_t GetUtf16FromUtf8(const char** utf8_data_in);
+
+/**
+ * Gets the leading UTF-16 character from a surrogate pair, or the sole
+ * UTF-16 character from the return value of GetUtf16FromUtf8.
+ */
+ALWAYS_INLINE uint16_t GetLeadingUtf16Char(uint32_t maybe_pair);
+
+/**
+ * Gets the trailing UTF-16 character from a surrogate pair, or 0 otherwise
+ * from the return value of GetUtf16FromUtf8.
+ */
+ALWAYS_INLINE uint16_t GetTrailingUtf16Char(uint32_t maybe_pair);
+
+// Returns a printable (escaped) version of a character.
+std::string PrintableChar(uint16_t ch);
+
+// Returns an ASCII string corresponding to the given UTF-8 string.
+// Java escapes are used for non-ASCII characters.
+std::string PrintableString(const char* utf8);
+
+} // namespace art
+
+#endif // ART_LIBDEXFILE_DEX_UTF_H_
diff --git a/libdexfile/dex/utf_test.cc b/libdexfile/dex/utf_test.cc
new file mode 100644
index 0000000000..d2f22d16ef
--- /dev/null
+++ b/libdexfile/dex/utf_test.cc
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utf.h"
+
+#include <map>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "utf-inl.h"
+
+namespace art {
+
+class UtfTest : public testing::Test {};
+
+TEST_F(UtfTest, GetLeadingUtf16Char) {
+ EXPECT_EQ(0xffff, GetLeadingUtf16Char(0xeeeeffff));
+}
+
+TEST_F(UtfTest, GetTrailingUtf16Char) {
+ EXPECT_EQ(0xffff, GetTrailingUtf16Char(0xffffeeee));
+ EXPECT_EQ(0, GetTrailingUtf16Char(0x0000aaaa));
+}
+
+#define EXPECT_ARRAY_POSITION(expected, end, start) \
+ EXPECT_EQ(static_cast<uintptr_t>(expected), \
+ reinterpret_cast<uintptr_t>(end) - reinterpret_cast<uintptr_t>(start));
+
+// A test string containing one, two, three and four byte UTF-8 sequences.
+static const uint8_t kAllSequences[] = {
+ 0x24,
+ 0xc2, 0xa2,
+ 0xe2, 0x82, 0xac,
+ 0xf0, 0x9f, 0x8f, 0xa0,
+ 0x00
+};
+
+// A test string that contains a UTF-8 encoding of a surrogate pair
+// (code point = U+10400).
+static const uint8_t kSurrogateEncoding[] = {
+ 0xed, 0xa0, 0x81,
+ 0xed, 0xb0, 0x80,
+ 0x00
+};
+
+TEST_F(UtfTest, GetUtf16FromUtf8) {
+ const char* const start = reinterpret_cast<const char*>(kAllSequences);
+ const char* ptr = start;
+ uint32_t pair = 0;
+
+ // Single byte sequence.
+ pair = GetUtf16FromUtf8(&ptr);
+ EXPECT_EQ(0x24, GetLeadingUtf16Char(pair));
+ EXPECT_EQ(0, GetTrailingUtf16Char(pair));
+ EXPECT_ARRAY_POSITION(1, ptr, start);
+
+ // Two byte sequence.
+ pair = GetUtf16FromUtf8(&ptr);
+ EXPECT_EQ(0xa2, GetLeadingUtf16Char(pair));
+ EXPECT_EQ(0, GetTrailingUtf16Char(pair));
+ EXPECT_ARRAY_POSITION(3, ptr, start);
+
+ // Three byte sequence.
+ pair = GetUtf16FromUtf8(&ptr);
+ EXPECT_EQ(0x20ac, GetLeadingUtf16Char(pair));
+ EXPECT_EQ(0, GetTrailingUtf16Char(pair));
+ EXPECT_ARRAY_POSITION(6, ptr, start);
+
+ // Four byte sequence
+ pair = GetUtf16FromUtf8(&ptr);
+ EXPECT_EQ(0xd83c, GetLeadingUtf16Char(pair));
+ EXPECT_EQ(0xdfe0, GetTrailingUtf16Char(pair));
+ EXPECT_ARRAY_POSITION(10, ptr, start);
+
+ // Null terminator.
+ pair = GetUtf16FromUtf8(&ptr);
+ EXPECT_EQ(0, GetLeadingUtf16Char(pair));
+ EXPECT_EQ(0, GetTrailingUtf16Char(pair));
+ EXPECT_ARRAY_POSITION(11, ptr, start);
+}
+
+TEST_F(UtfTest, GetUtf16FromUtf8_SurrogatesPassThrough) {
+ const char* const start = reinterpret_cast<const char *>(kSurrogateEncoding);
+ const char* ptr = start;
+ uint32_t pair = 0;
+
+ pair = GetUtf16FromUtf8(&ptr);
+ EXPECT_EQ(0xd801, GetLeadingUtf16Char(pair));
+ EXPECT_EQ(0, GetTrailingUtf16Char(pair));
+ EXPECT_ARRAY_POSITION(3, ptr, start);
+
+ pair = GetUtf16FromUtf8(&ptr);
+ EXPECT_EQ(0xdc00, GetLeadingUtf16Char(pair));
+ EXPECT_EQ(0, GetTrailingUtf16Char(pair));
+ EXPECT_ARRAY_POSITION(6, ptr, start);
+}
+
+TEST_F(UtfTest, CountModifiedUtf8Chars) {
+ EXPECT_EQ(5u, CountModifiedUtf8Chars(reinterpret_cast<const char *>(kAllSequences)));
+ EXPECT_EQ(2u, CountModifiedUtf8Chars(reinterpret_cast<const char *>(kSurrogateEncoding)));
+}
+
+static void AssertConversion(const std::vector<uint16_t>& input,
+ const std::vector<uint8_t>& expected) {
+ ASSERT_EQ(expected.size(), CountUtf8Bytes(&input[0], input.size()));
+
+ std::vector<uint8_t> output(expected.size());
+ ConvertUtf16ToModifiedUtf8(reinterpret_cast<char*>(&output[0]), expected.size(),
+ &input[0], input.size());
+ EXPECT_EQ(expected, output);
+}
+
+TEST_F(UtfTest, CountAndConvertUtf8Bytes) {
+ // Surrogate pairs will be converted into 4 byte sequences.
+ AssertConversion({ 0xd801, 0xdc00 }, { 0xf0, 0x90, 0x90, 0x80 });
+
+ // Three byte encodings that are below & above the leading surrogate
+ // range respectively.
+ AssertConversion({ 0xdef0 }, { 0xed, 0xbb, 0xb0 });
+ AssertConversion({ 0xdcff }, { 0xed, 0xb3, 0xbf });
+ // Two byte encoding.
+ AssertConversion({ 0x0101 }, { 0xc4, 0x81 });
+
+ // Two byte special case : 0 must use an overlong encoding.
+ AssertConversion({ 0x0101, 0x0000 }, { 0xc4, 0x81, 0xc0, 0x80 });
+
+ // One byte encoding.
+ AssertConversion({ 'h', 'e', 'l', 'l', 'o' }, { 0x68, 0x65, 0x6c, 0x6c, 0x6f });
+
+ AssertConversion({
+ 0xd802, 0xdc02, // Surrogate pair.
+ 0xdef0, 0xdcff, // Three byte encodings.
+ 0x0101, 0x0000, // Two byte encodings.
+ 'p' , 'p' // One byte encoding.
+ }, {
+ 0xf0, 0x90, 0xa0, 0x82,
+ 0xed, 0xbb, 0xb0, 0xed, 0xb3, 0xbf,
+ 0xc4, 0x81, 0xc0, 0x80,
+ 0x70, 0x70
+ });
+}
+
+TEST_F(UtfTest, CountAndConvertUtf8Bytes_UnpairedSurrogate) {
+ // Unpaired trailing surrogate at the end of input.
+ AssertConversion({ 'h', 'e', 0xd801 }, { 'h', 'e', 0xed, 0xa0, 0x81 });
+ // Unpaired (or incorrectly paired) surrogates in the middle of the input.
+ const std::map<std::vector<uint16_t>, std::vector<uint8_t>> prefixes {
+ {{ 'h' }, { 'h' }},
+ {{ 0 }, { 0xc0, 0x80 }},
+ {{ 0x81 }, { 0xc2, 0x81 }},
+ {{ 0x801 }, { 0xe0, 0xa0, 0x81 }},
+ };
+ const std::map<std::vector<uint16_t>, std::vector<uint8_t>> suffixes {
+ {{ 'e' }, { 'e' }},
+ {{ 0 }, { 0xc0, 0x80 }},
+ {{ 0x7ff }, { 0xdf, 0xbf }},
+ {{ 0xffff }, { 0xef, 0xbf, 0xbf }},
+ };
+ const std::map<std::vector<uint16_t>, std::vector<uint8_t>> tests {
+ {{ 0xd801 }, { 0xed, 0xa0, 0x81 }},
+ {{ 0xdc00 }, { 0xed, 0xb0, 0x80 }},
+ {{ 0xd801, 0xd801 }, { 0xed, 0xa0, 0x81, 0xed, 0xa0, 0x81 }},
+ {{ 0xdc00, 0xdc00 }, { 0xed, 0xb0, 0x80, 0xed, 0xb0, 0x80 }},
+ };
+ for (const auto& prefix : prefixes) {
+ const std::vector<uint16_t>& prefix_in = prefix.first;
+ const std::vector<uint8_t>& prefix_out = prefix.second;
+ for (const auto& test : tests) {
+ const std::vector<uint16_t>& test_in = test.first;
+ const std::vector<uint8_t>& test_out = test.second;
+ for (const auto& suffix : suffixes) {
+ const std::vector<uint16_t>& suffix_in = suffix.first;
+ const std::vector<uint8_t>& suffix_out = suffix.second;
+ std::vector<uint16_t> in = prefix_in;
+ in.insert(in.end(), test_in.begin(), test_in.end());
+ in.insert(in.end(), suffix_in.begin(), suffix_in.end());
+ std::vector<uint8_t> out = prefix_out;
+ out.insert(out.end(), test_out.begin(), test_out.end());
+ out.insert(out.end(), suffix_out.begin(), suffix_out.end());
+ AssertConversion(in, out);
+ }
+ }
+ }
+}
+
+// Old versions of functions, here to compare answers with optimized versions.
+
+size_t CountModifiedUtf8Chars_reference(const char* utf8) {
+ size_t len = 0;
+ int ic;
+ while ((ic = *utf8++) != '\0') {
+ len++;
+ if ((ic & 0x80) == 0) {
+ // one-byte encoding
+ continue;
+ }
+ // two- or three-byte encoding
+ utf8++;
+ if ((ic & 0x20) == 0) {
+ // two-byte encoding
+ continue;
+ }
+ utf8++;
+ if ((ic & 0x10) == 0) {
+ // three-byte encoding
+ continue;
+ }
+
+ // four-byte encoding: needs to be converted into a surrogate
+ // pair.
+ utf8++;
+ len++;
+ }
+ return len;
+}
+
+static size_t CountUtf8Bytes_reference(const uint16_t* chars, size_t char_count) {
+ size_t result = 0;
+ while (char_count--) {
+ const uint16_t ch = *chars++;
+ if (ch > 0 && ch <= 0x7f) {
+ ++result;
+ } else if (ch >= 0xd800 && ch <= 0xdbff) {
+ if (char_count > 0) {
+ const uint16_t ch2 = *chars;
+ // If we find a properly paired surrogate, we emit it as a 4 byte
+ // UTF sequence. If we find an unpaired leading or trailing surrogate,
+ // we emit it as a 3 byte sequence like would have done earlier.
+ if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
+ chars++;
+ char_count--;
+
+ result += 4;
+ } else {
+ result += 3;
+ }
+ } else {
+ // This implies we found an unpaired trailing surrogate at the end
+ // of a string.
+ result += 3;
+ }
+ } else if (ch > 0x7ff) {
+ result += 3;
+ } else {
+ result += 2;
+ }
+ }
+ return result;
+}
+
+static void ConvertUtf16ToModifiedUtf8_reference(char* utf8_out, const uint16_t* utf16_in,
+ size_t char_count) {
+ while (char_count--) {
+ const uint16_t ch = *utf16_in++;
+ if (ch > 0 && ch <= 0x7f) {
+ *utf8_out++ = ch;
+ } else {
+ // Char_count == 0 here implies we've encountered an unpaired
+ // surrogate and we have no choice but to encode it as 3-byte UTF
+ // sequence. Note that unpaired surrogates can occur as a part of
+ // "normal" operation.
+ if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
+ const uint16_t ch2 = *utf16_in;
+
+ // Check if the other half of the pair is within the expected
+ // range. If it isn't, we will have to emit both "halves" as
+ // separate 3 byte sequences.
+ if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
+ utf16_in++;
+ char_count--;
+ const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
+ *utf8_out++ = (code_point >> 18) | 0xf0;
+ *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
+ *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
+ *utf8_out++ = (code_point & 0x3f) | 0x80;
+ continue;
+ }
+ }
+
+ if (ch > 0x07ff) {
+ // Three byte encoding.
+ *utf8_out++ = (ch >> 12) | 0xe0;
+ *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
+ *utf8_out++ = (ch & 0x3f) | 0x80;
+ } else /*(ch > 0x7f || ch == 0)*/ {
+ // Two byte encoding.
+ *utf8_out++ = (ch >> 6) | 0xc0;
+ *utf8_out++ = (ch & 0x3f) | 0x80;
+ }
+ }
+ }
+}
+
+// Exhaustive test of converting a single code point to UTF-16, then UTF-8, and back again.
+
+static void codePointToSurrogatePair(uint32_t code_point, uint16_t &first, uint16_t &second) {
+ first = (code_point >> 10) + 0xd7c0;
+ second = (code_point & 0x03ff) + 0xdc00;
+}
+
+static void testConversions(uint16_t *buf, int char_count) {
+ char bytes_test[8] = { 0 }, bytes_reference[8] = { 0 };
+ uint16_t out_buf_test[4] = { 0 }, out_buf_reference[4] = { 0 };
+ int byte_count_test, byte_count_reference;
+ int char_count_test, char_count_reference;
+
+ // Calculate the number of utf-8 bytes for the utf-16 chars.
+ byte_count_reference = CountUtf8Bytes_reference(buf, char_count);
+ byte_count_test = CountUtf8Bytes(buf, char_count);
+ EXPECT_EQ(byte_count_reference, byte_count_test);
+
+ // Convert the utf-16 string to utf-8 bytes.
+ ConvertUtf16ToModifiedUtf8_reference(bytes_reference, buf, char_count);
+ ConvertUtf16ToModifiedUtf8(bytes_test, byte_count_test, buf, char_count);
+ for (int i = 0; i < byte_count_test; ++i) {
+ EXPECT_EQ(bytes_reference[i], bytes_test[i]);
+ }
+
+ // Calculate the number of utf-16 chars from the utf-8 bytes.
+ bytes_reference[byte_count_reference] = 0; // Reference function needs null termination.
+ char_count_reference = CountModifiedUtf8Chars_reference(bytes_reference);
+ char_count_test = CountModifiedUtf8Chars(bytes_test, byte_count_test);
+ EXPECT_EQ(char_count, char_count_reference);
+ EXPECT_EQ(char_count, char_count_test);
+
+ // Convert the utf-8 bytes back to utf-16 chars.
+ // Does not need copied _reference version of the function because the original
+ // function with the old API is retained for debug/testing code.
+ ConvertModifiedUtf8ToUtf16(out_buf_reference, bytes_reference);
+ ConvertModifiedUtf8ToUtf16(out_buf_test, char_count_test, bytes_test, byte_count_test);
+ for (int i = 0; i < char_count_test; ++i) {
+ EXPECT_EQ(buf[i], out_buf_reference[i]);
+ EXPECT_EQ(buf[i], out_buf_test[i]);
+ }
+}
+
+TEST_F(UtfTest, ExhaustiveBidirectionalCodePointCheck) {
+ for (int codePoint = 0; codePoint <= 0x10ffff; ++codePoint) {
+ uint16_t buf[4] = { 0 };
+ if (codePoint <= 0xffff) {
+ if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
+ // According to the Unicode standard, no character will ever
+ // be assigned to these code points, and they cannot be encoded
+ // into either utf-16 or utf-8.
+ continue;
+ }
+ buf[0] = 'h';
+ buf[1] = codePoint;
+ buf[2] = 'e';
+ testConversions(buf, 2);
+ testConversions(buf, 3);
+ testConversions(buf + 1, 1);
+ testConversions(buf + 1, 2);
+ } else {
+ buf[0] = 'h';
+ codePointToSurrogatePair(codePoint, buf[1], buf[2]);
+ buf[3] = 'e';
+ testConversions(buf, 2);
+ testConversions(buf, 3);
+ testConversions(buf, 4);
+ testConversions(buf + 1, 1);
+ testConversions(buf + 1, 2);
+ testConversions(buf + 1, 3);
+ }
+ }
+}
+
+} // namespace art