diff options
Diffstat (limited to 'libdexfile/dex')
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, + ®isters_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, ¶m_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(¤t_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(¤t_data_)); + handler_.address_ = DecodeUnsignedLeb128(¤t_data_); + remaining_count_--; + return; + } + + if (catch_all_) { + handler_.type_idx_ = dex::TypeIndex(DexFile::kDexNoIndex16); + handler_.address_ = DecodeUnsignedLeb128(¤t_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 |