|  | /* | 
|  | * 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. | 
|  | * | 
|  | * Implementation file of dex ir verifier. | 
|  | * | 
|  | * Compares two dex files at the IR level, allowing differences in layout, but not in data. | 
|  | */ | 
|  |  | 
|  | #include "dex_verify.h" | 
|  |  | 
|  | #include <inttypes.h> | 
|  |  | 
|  | #include <set> | 
|  |  | 
|  | #include "android-base/stringprintf.h" | 
|  |  | 
|  | namespace art { | 
|  |  | 
|  | using android::base::StringPrintf; | 
|  |  | 
|  | bool VerifyOutputDexFile(dex_ir::Header* orig_header, | 
|  | dex_ir::Header* output_header, | 
|  | std::string* error_msg) { | 
|  | // Compare all id sections. They have a defined order that can't be changed by dexlayout. | 
|  | if (!VerifyIds(orig_header->StringIds(), output_header->StringIds(), "string ids", error_msg) || | 
|  | !VerifyIds(orig_header->TypeIds(), output_header->TypeIds(), "type ids", error_msg) || | 
|  | !VerifyIds(orig_header->ProtoIds(), output_header->ProtoIds(), "proto ids", error_msg) || | 
|  | !VerifyIds(orig_header->FieldIds(), output_header->FieldIds(), "field ids", error_msg) || | 
|  | !VerifyIds(orig_header->MethodIds(), output_header->MethodIds(), "method ids", error_msg)) { | 
|  | return false; | 
|  | } | 
|  | // Compare class defs. The order may have been changed by dexlayout. | 
|  | if (!VerifyClassDefs(orig_header->ClassDefs(), output_header->ClassDefs(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template<class T> bool VerifyIds(dex_ir::CollectionVector<T>& orig, | 
|  | dex_ir::CollectionVector<T>& output, | 
|  | const char* section_name, | 
|  | std::string* error_msg) { | 
|  | auto orig_iter = orig.begin(); | 
|  | auto output_iter = output.begin(); | 
|  | for (; orig_iter != orig.end() && output_iter != output.end(); ++orig_iter, ++output_iter) { | 
|  | if (!VerifyId(orig_iter->get(), output_iter->get(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | if (orig_iter != orig.end() || output_iter != output.end()) { | 
|  | const char* longer; | 
|  | if (orig_iter == orig.end()) { | 
|  | longer = "output"; | 
|  | } else { | 
|  | longer = "original"; | 
|  | } | 
|  | *error_msg = StringPrintf("Mismatch for %s section: %s is longer.", section_name, longer); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* error_msg) { | 
|  | if (strcmp(orig->Data(), output->Data()) != 0) { | 
|  | *error_msg = StringPrintf("Mismatched string data for string id %u at offset %x: %s vs %s.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Data(), | 
|  | output->Data()); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_msg) { | 
|  | if (orig->GetStringId()->GetIndex() != output->GetStringId()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched string index for type id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->GetStringId()->GetIndex(), | 
|  | output->GetStringId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error_msg) { | 
|  | if (orig->Shorty()->GetIndex() != output->Shorty()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched string index for proto id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Shorty()->GetIndex(), | 
|  | output->Shorty()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig->ReturnType()->GetIndex() != output->ReturnType()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched type index for proto id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->ReturnType()->GetIndex(), | 
|  | output->ReturnType()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyTypeList(orig->Parameters(), output->Parameters())) { | 
|  | *error_msg = StringPrintf("Mismatched type list for proto id %u at offset %x.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset()); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg) { | 
|  | if (orig->Class()->GetIndex() != output->Class()->GetIndex()) { | 
|  | *error_msg = | 
|  | StringPrintf("Mismatched class type index for field id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Class()->GetIndex(), | 
|  | output->Class()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig->Type()->GetIndex() != output->Type()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched type index for field id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Class()->GetIndex(), | 
|  | output->Class()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig->Name()->GetIndex() != output->Name()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched string index for field id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Name()->GetIndex(), | 
|  | output->Name()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg) { | 
|  | if (orig->Class()->GetIndex() != output->Class()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched type index for method id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Class()->GetIndex(), | 
|  | output->Class()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig->Proto()->GetIndex() != output->Proto()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched proto index for method id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Class()->GetIndex(), | 
|  | output->Class()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig->Name()->GetIndex() != output->Name()->GetIndex()) { | 
|  | *error_msg = | 
|  | StringPrintf("Mismatched string index for method id %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->Name()->GetIndex(), | 
|  | output->Name()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | struct ClassDefCompare { | 
|  | bool operator()(dex_ir::ClassDef* lhs, dex_ir::ClassDef* rhs) const { | 
|  | return lhs->ClassType()->GetIndex() < rhs->ClassType()->GetIndex(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // The class defs may have a new order due to dexlayout. Use the class's class_idx to uniquely | 
|  | // identify them and sort them for comparison. | 
|  | bool VerifyClassDefs(dex_ir::CollectionVector<dex_ir::ClassDef>& orig, | 
|  | dex_ir::CollectionVector<dex_ir::ClassDef>& output, | 
|  | std::string* error_msg) { | 
|  | // Store the class defs into sets sorted by the class's type index. | 
|  | std::set<dex_ir::ClassDef*, ClassDefCompare> orig_set; | 
|  | std::set<dex_ir::ClassDef*, ClassDefCompare> output_set; | 
|  | auto orig_iter = orig.begin(); | 
|  | auto output_iter = output.begin(); | 
|  | for (; orig_iter != orig.end() && output_iter != output.end(); ++orig_iter, ++output_iter) { | 
|  | orig_set.insert(orig_iter->get()); | 
|  | output_set.insert(output_iter->get()); | 
|  | } | 
|  | if (orig_iter != orig.end() || output_iter != output.end()) { | 
|  | const char* longer; | 
|  | if (orig_iter == orig.end()) { | 
|  | longer = "output"; | 
|  | } else { | 
|  | longer = "original"; | 
|  | } | 
|  | *error_msg = StringPrintf("Mismatch for class defs section: %s is longer.", longer); | 
|  | return false; | 
|  | } | 
|  | auto orig_set_iter = orig_set.begin(); | 
|  | auto output_set_iter = output_set.begin(); | 
|  | while (orig_set_iter != orig_set.end() && output_set_iter != output_set.end()) { | 
|  | if (!VerifyClassDef(*orig_set_iter, *output_set_iter, error_msg)) { | 
|  | return false; | 
|  | } | 
|  | orig_set_iter++; | 
|  | output_set_iter++; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyClassDef(dex_ir::ClassDef* orig, dex_ir::ClassDef* output, std::string* error_msg) { | 
|  | if (orig->ClassType()->GetIndex() != output->ClassType()->GetIndex()) { | 
|  | *error_msg = | 
|  | StringPrintf("Mismatched class type index for class def %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->ClassType()->GetIndex(), | 
|  | output->ClassType()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig->GetAccessFlags() != output->GetAccessFlags()) { | 
|  | *error_msg = | 
|  | StringPrintf("Mismatched access flags for class def %u at offset %x: %x vs %x.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig->GetAccessFlags(), | 
|  | output->GetAccessFlags()); | 
|  | return false; | 
|  | } | 
|  | uint32_t orig_super = orig->Superclass() == nullptr ? 0 : orig->Superclass()->GetIndex(); | 
|  | uint32_t output_super = output->Superclass() == nullptr ? 0 : output->Superclass()->GetIndex(); | 
|  | if (orig_super != output_super) { | 
|  | *error_msg = | 
|  | StringPrintf("Mismatched super class for class def %u at offset %x: %u vs %u.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig_super, | 
|  | output_super); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyTypeList(orig->Interfaces(), output->Interfaces())) { | 
|  | *error_msg = StringPrintf("Mismatched type list for class def %u at offset %x.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset()); | 
|  | return false; | 
|  | } | 
|  | const char* orig_source = orig->SourceFile() == nullptr ? "" : orig->SourceFile()->Data(); | 
|  | const char* output_source = output->SourceFile() == nullptr ? "" : output->SourceFile()->Data(); | 
|  | if (strcmp(orig_source, output_source) != 0) { | 
|  | *error_msg = StringPrintf("Mismatched source file for class def %u at offset %x: %s vs %s.", | 
|  | orig->GetIndex(), | 
|  | orig->GetOffset(), | 
|  | orig_source, | 
|  | output_source); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyAnnotationsDirectory(orig->Annotations(), output->Annotations(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | if (!VerifyClassData(orig->GetClassData(), output->GetClassData(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | return VerifyEncodedArray(orig->StaticValues(), output->StaticValues(), error_msg); | 
|  | } | 
|  |  | 
|  | bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | return orig == output; | 
|  | } | 
|  | const dex_ir::TypeIdVector* orig_list = orig->GetTypeList(); | 
|  | const dex_ir::TypeIdVector* output_list = output->GetTypeList(); | 
|  | if (orig_list->size() != output_list->size()) { | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig_list->size(); ++i) { | 
|  | if ((*orig_list)[i]->GetIndex() != (*output_list)[i]->GetIndex()) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyAnnotationsDirectory(dex_ir::AnnotationsDirectoryItem* orig, | 
|  | dex_ir::AnnotationsDirectoryItem* output, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty annotations directory."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (!VerifyAnnotationSet(orig->GetClassAnnotation(), output->GetClassAnnotation(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | if (!VerifyFieldAnnotations(orig->GetFieldAnnotations(), | 
|  | output->GetFieldAnnotations(), | 
|  | orig->GetOffset(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | if (!VerifyMethodAnnotations(orig->GetMethodAnnotations(), | 
|  | output->GetMethodAnnotations(), | 
|  | orig->GetOffset(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | return VerifyParameterAnnotations(orig->GetParameterAnnotations(), | 
|  | output->GetParameterAnnotations(), | 
|  | orig->GetOffset(), | 
|  | error_msg); | 
|  | } | 
|  |  | 
|  | bool VerifyFieldAnnotations(dex_ir::FieldAnnotationVector* orig, | 
|  | dex_ir::FieldAnnotationVector* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = StringPrintf( | 
|  | "Found unexpected empty field annotations for annotations directory at offset %x.", | 
|  | orig_offset); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (orig->size() != output->size()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched field annotations size for annotations directory at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig->size(), | 
|  | output->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig->size(); ++i) { | 
|  | dex_ir::FieldAnnotation* orig_field = (*orig)[i].get(); | 
|  | dex_ir::FieldAnnotation* output_field = (*output)[i].get(); | 
|  | if (orig_field->GetFieldId()->GetIndex() != output_field->GetFieldId()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched field annotation index for annotations directory at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_field->GetFieldId()->GetIndex(), | 
|  | output_field->GetFieldId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyAnnotationSet(orig_field->GetAnnotationSetItem(), | 
|  | output_field->GetAnnotationSetItem(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyMethodAnnotations(dex_ir::MethodAnnotationVector* orig, | 
|  | dex_ir::MethodAnnotationVector* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = StringPrintf( | 
|  | "Found unexpected empty method annotations for annotations directory at offset %x.", | 
|  | orig_offset); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (orig->size() != output->size()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched method annotations size for annotations directory at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig->size(), | 
|  | output->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig->size(); ++i) { | 
|  | dex_ir::MethodAnnotation* orig_method = (*orig)[i].get(); | 
|  | dex_ir::MethodAnnotation* output_method = (*output)[i].get(); | 
|  | if (orig_method->GetMethodId()->GetIndex() != output_method->GetMethodId()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched method annotation index for annotations directory at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_method->GetMethodId()->GetIndex(), | 
|  | output_method->GetMethodId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyAnnotationSet(orig_method->GetAnnotationSetItem(), | 
|  | output_method->GetAnnotationSetItem(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyParameterAnnotations(dex_ir::ParameterAnnotationVector* orig, | 
|  | dex_ir::ParameterAnnotationVector* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = StringPrintf( | 
|  | "Found unexpected empty parameter annotations for annotations directory at offset %x.", | 
|  | orig_offset); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (orig->size() != output->size()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched parameter annotations size for annotations directory at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig->size(), | 
|  | output->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig->size(); ++i) { | 
|  | dex_ir::ParameterAnnotation* orig_param = (*orig)[i].get(); | 
|  | dex_ir::ParameterAnnotation* output_param = (*output)[i].get(); | 
|  | if (orig_param->GetMethodId()->GetIndex() != output_param->GetMethodId()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched parameter annotation index for annotations directory at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_param->GetMethodId()->GetIndex(), | 
|  | output_param->GetMethodId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyAnnotationSetRefList(orig_param->GetAnnotations(), | 
|  | output_param->GetAnnotations(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyAnnotationSetRefList(dex_ir::AnnotationSetRefList* orig, | 
|  | dex_ir::AnnotationSetRefList* output, | 
|  | std::string* error_msg) { | 
|  | std::vector<dex_ir::AnnotationSetItem*>* orig_items = orig->GetItems(); | 
|  | std::vector<dex_ir::AnnotationSetItem*>* output_items = output->GetItems(); | 
|  | if (orig_items->size() != output_items->size()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched annotation set ref list size at offset %x: %zu vs %zu.", | 
|  | orig->GetOffset(), | 
|  | orig_items->size(), | 
|  | output_items->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig_items->size(); ++i) { | 
|  | if (!VerifyAnnotationSet((*orig_items)[i], (*output_items)[i], error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyAnnotationSet(dex_ir::AnnotationSetItem* orig, | 
|  | dex_ir::AnnotationSetItem* output, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty annotation set."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | std::vector<dex_ir::AnnotationItem*>* orig_items = orig->GetItems(); | 
|  | std::vector<dex_ir::AnnotationItem*>* output_items = output->GetItems(); | 
|  | if (orig_items->size() != output_items->size()) { | 
|  | *error_msg = StringPrintf("Mismatched size for annotation set at offset %x: %zu vs %zu.", | 
|  | orig->GetOffset(), | 
|  | orig_items->size(), | 
|  | output_items->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig_items->size(); ++i) { | 
|  | if (!VerifyAnnotation((*orig_items)[i], (*output_items)[i], error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyAnnotation(dex_ir::AnnotationItem* orig, | 
|  | dex_ir::AnnotationItem* output, | 
|  | std::string* error_msg) { | 
|  | if (orig->GetVisibility() != output->GetVisibility()) { | 
|  | *error_msg = StringPrintf("Mismatched visibility for annotation at offset %x: %u vs %u.", | 
|  | orig->GetOffset(), | 
|  | orig->GetVisibility(), | 
|  | output->GetVisibility()); | 
|  | return false; | 
|  | } | 
|  | return VerifyEncodedAnnotation(orig->GetAnnotation(), | 
|  | output->GetAnnotation(), | 
|  | orig->GetOffset(), | 
|  | error_msg); | 
|  | } | 
|  |  | 
|  | bool VerifyEncodedAnnotation(dex_ir::EncodedAnnotation* orig, | 
|  | dex_ir::EncodedAnnotation* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig->GetType()->GetIndex() != output->GetType()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded annotation type for annotation at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig->GetType()->GetIndex(), | 
|  | output->GetType()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | dex_ir::AnnotationElementVector* orig_elements = orig->GetAnnotationElements(); | 
|  | dex_ir::AnnotationElementVector* output_elements = output->GetAnnotationElements(); | 
|  | if (orig_elements->size() != output_elements->size()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded annotation size for annotation at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig_elements->size(), | 
|  | output_elements->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig_elements->size(); ++i) { | 
|  | if (!VerifyAnnotationElement((*orig_elements)[i].get(), | 
|  | (*output_elements)[i].get(), | 
|  | orig_offset, | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyAnnotationElement(dex_ir::AnnotationElement* orig, | 
|  | dex_ir::AnnotationElement* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig->GetName()->GetIndex() != output->GetName()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched annotation element name for annotation at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig->GetName()->GetIndex(), | 
|  | output->GetName()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | return VerifyEncodedValue(orig->GetValue(), output->GetValue(), orig_offset, error_msg); | 
|  | } | 
|  |  | 
|  | bool VerifyEncodedValue(dex_ir::EncodedValue* orig, | 
|  | dex_ir::EncodedValue* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig->Type() != output->Type()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded value type for annotation or encoded array at offset %x: %d vs %d.", | 
|  | orig_offset, | 
|  | orig->Type(), | 
|  | output->Type()); | 
|  | return false; | 
|  | } | 
|  | switch (orig->Type()) { | 
|  | case DexFile::kDexAnnotationByte: | 
|  | if (orig->GetByte() != output->GetByte()) { | 
|  | *error_msg = StringPrintf("Mismatched encoded byte for annotation at offset %x: %d vs %d.", | 
|  | orig_offset, | 
|  | orig->GetByte(), | 
|  | output->GetByte()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationShort: | 
|  | if (orig->GetShort() != output->GetShort()) { | 
|  | *error_msg = StringPrintf("Mismatched encoded short for annotation at offset %x: %d vs %d.", | 
|  | orig_offset, | 
|  | orig->GetShort(), | 
|  | output->GetShort()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationChar: | 
|  | if (orig->GetChar() != output->GetChar()) { | 
|  | *error_msg = StringPrintf("Mismatched encoded char for annotation at offset %x: %c vs %c.", | 
|  | orig_offset, | 
|  | orig->GetChar(), | 
|  | output->GetChar()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationInt: | 
|  | if (orig->GetInt() != output->GetInt()) { | 
|  | *error_msg = StringPrintf("Mismatched encoded int for annotation at offset %x: %d vs %d.", | 
|  | orig_offset, | 
|  | orig->GetInt(), | 
|  | output->GetInt()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationLong: | 
|  | if (orig->GetLong() != output->GetLong()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded long for annotation at offset %x: %" PRId64 " vs %" PRId64 ".", | 
|  | orig_offset, | 
|  | orig->GetLong(), | 
|  | output->GetLong()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationFloat: | 
|  | // The float value is encoded, so compare as if it's an int. | 
|  | if (orig->GetInt() != output->GetInt()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded float for annotation at offset %x: %x (encoded) vs %x (encoded).", | 
|  | orig_offset, | 
|  | orig->GetInt(), | 
|  | output->GetInt()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationDouble: | 
|  | // The double value is encoded, so compare as if it's a long. | 
|  | if (orig->GetLong() != output->GetLong()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded double for annotation at offset %x: %" PRIx64 | 
|  | " (encoded) vs %" PRIx64 " (encoded).", | 
|  | orig_offset, | 
|  | orig->GetLong(), | 
|  | output->GetLong()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationString: | 
|  | if (orig->GetStringId()->GetIndex() != output->GetStringId()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded string for annotation at offset %x: %s vs %s.", | 
|  | orig_offset, | 
|  | orig->GetStringId()->Data(), | 
|  | output->GetStringId()->Data()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationType: | 
|  | if (orig->GetTypeId()->GetIndex() != output->GetTypeId()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched encoded type for annotation at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig->GetTypeId()->GetIndex(), | 
|  | output->GetTypeId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationField: | 
|  | case DexFile::kDexAnnotationEnum: | 
|  | if (orig->GetFieldId()->GetIndex() != output->GetFieldId()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched encoded field for annotation at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig->GetFieldId()->GetIndex(), | 
|  | output->GetFieldId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationMethod: | 
|  | if (orig->GetMethodId()->GetIndex() != output->GetMethodId()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded method for annotation at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig->GetMethodId()->GetIndex(), | 
|  | output->GetMethodId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationArray: | 
|  | if (!VerifyEncodedArray(orig->GetEncodedArray(), output->GetEncodedArray(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationAnnotation: | 
|  | if (!VerifyEncodedAnnotation(orig->GetEncodedAnnotation(), | 
|  | output->GetEncodedAnnotation(), | 
|  | orig_offset, | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | case DexFile::kDexAnnotationNull: | 
|  | break; | 
|  | case DexFile::kDexAnnotationBoolean: | 
|  | if (orig->GetBoolean() != output->GetBoolean()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched encoded boolean for annotation at offset %x: %d vs %d.", | 
|  | orig_offset, | 
|  | orig->GetBoolean(), | 
|  | output->GetBoolean()); | 
|  | return false; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyEncodedArray(dex_ir::EncodedArrayItem* orig, | 
|  | dex_ir::EncodedArrayItem* output, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty encoded array."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | dex_ir::EncodedValueVector* orig_vector = orig->GetEncodedValues(); | 
|  | dex_ir::EncodedValueVector* output_vector = output->GetEncodedValues(); | 
|  | if (orig_vector->size() != output_vector->size()) { | 
|  | *error_msg = StringPrintf("Mismatched size for encoded array at offset %x: %zu vs %zu.", | 
|  | orig->GetOffset(), | 
|  | orig_vector->size(), | 
|  | output_vector->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig_vector->size(); ++i) { | 
|  | if (!VerifyEncodedValue((*orig_vector)[i].get(), | 
|  | (*output_vector)[i].get(), | 
|  | orig->GetOffset(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyClassData(dex_ir::ClassData* orig, dex_ir::ClassData* output, std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty class data."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (!VerifyFields(orig->StaticFields(), output->StaticFields(), orig->GetOffset(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | if (!VerifyFields(orig->InstanceFields(), | 
|  | output->InstanceFields(), | 
|  | orig->GetOffset(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | if (!VerifyMethods(orig->DirectMethods(), | 
|  | output->DirectMethods(), | 
|  | orig->GetOffset(), | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | return VerifyMethods(orig->VirtualMethods(), | 
|  | output->VirtualMethods(), | 
|  | orig->GetOffset(), | 
|  | error_msg); | 
|  | } | 
|  |  | 
|  | bool VerifyFields(dex_ir::FieldItemVector* orig, | 
|  | dex_ir::FieldItemVector* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig->size() != output->size()) { | 
|  | *error_msg = StringPrintf("Mismatched fields size for class data at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig->size(), | 
|  | output->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig->size(); ++i) { | 
|  | dex_ir::FieldItem* orig_field = &(*orig)[i]; | 
|  | dex_ir::FieldItem* output_field = &(*output)[i]; | 
|  | if (orig_field->GetFieldId()->GetIndex() != output_field->GetFieldId()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched field index for class data at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_field->GetFieldId()->GetIndex(), | 
|  | output_field->GetFieldId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig_field->GetAccessFlags() != output_field->GetAccessFlags()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched field access flags for class data at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_field->GetAccessFlags(), | 
|  | output_field->GetAccessFlags()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyMethods(dex_ir::MethodItemVector* orig, | 
|  | dex_ir::MethodItemVector* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig->size() != output->size()) { | 
|  | *error_msg = StringPrintf("Mismatched methods size for class data at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig->size(), | 
|  | output->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig->size(); ++i) { | 
|  | dex_ir::MethodItem* orig_method = &(*orig)[i]; | 
|  | dex_ir::MethodItem* output_method = &(*output)[i]; | 
|  | if (orig_method->GetMethodId()->GetIndex() != output_method->GetMethodId()->GetIndex()) { | 
|  | *error_msg = StringPrintf("Mismatched method index for class data at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_method->GetMethodId()->GetIndex(), | 
|  | output_method->GetMethodId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig_method->GetAccessFlags() != output_method->GetAccessFlags()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched method access flags for class data at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_method->GetAccessFlags(), | 
|  | output_method->GetAccessFlags()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyCode(orig_method->GetCodeItem(), output_method->GetCodeItem(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyCode(dex_ir::CodeItem* orig, dex_ir::CodeItem* output, std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty code item."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (orig->RegistersSize() != output->RegistersSize()) { | 
|  | *error_msg = StringPrintf("Mismatched registers size for code item at offset %x: %u vs %u.", | 
|  | orig->GetOffset(), | 
|  | orig->RegistersSize(), | 
|  | output->RegistersSize()); | 
|  | return false; | 
|  | } | 
|  | if (orig->InsSize() != output->InsSize()) { | 
|  | *error_msg = StringPrintf("Mismatched ins size for code item at offset %x: %u vs %u.", | 
|  | orig->GetOffset(), | 
|  | orig->InsSize(), | 
|  | output->InsSize()); | 
|  | return false; | 
|  | } | 
|  | if (orig->OutsSize() != output->OutsSize()) { | 
|  | *error_msg = StringPrintf("Mismatched outs size for code item at offset %x: %u vs %u.", | 
|  | orig->GetOffset(), | 
|  | orig->OutsSize(), | 
|  | output->OutsSize()); | 
|  | return false; | 
|  | } | 
|  | if (orig->TriesSize() != output->TriesSize()) { | 
|  | *error_msg = StringPrintf("Mismatched tries size for code item at offset %x: %u vs %u.", | 
|  | orig->GetOffset(), | 
|  | orig->TriesSize(), | 
|  | output->TriesSize()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyDebugInfo(orig->DebugInfo(), output->DebugInfo(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | if (orig->InsnsSize() != output->InsnsSize()) { | 
|  | *error_msg = StringPrintf("Mismatched insns size for code item at offset %x: %u vs %u.", | 
|  | orig->GetOffset(), | 
|  | orig->InsnsSize(), | 
|  | output->InsnsSize()); | 
|  | return false; | 
|  | } | 
|  | if (memcmp(orig->Insns(), output->Insns(), orig->InsnsSize()) != 0) { | 
|  | *error_msg = StringPrintf("Mismatched insns for code item at offset %x.", | 
|  | orig->GetOffset()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyTries(orig->Tries(), output->Tries(), orig->GetOffset(), error_msg)) { | 
|  | return false; | 
|  | } | 
|  | return VerifyHandlers(orig->Handlers(), output->Handlers(), orig->GetOffset(), error_msg); | 
|  | } | 
|  |  | 
|  | bool VerifyDebugInfo(dex_ir::DebugInfoItem* orig, | 
|  | dex_ir::DebugInfoItem* output, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty debug info."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | // TODO: Test for debug equivalence rather than byte array equality. | 
|  | uint32_t orig_size = orig->GetDebugInfoSize(); | 
|  | uint32_t output_size = output->GetDebugInfoSize(); | 
|  | if (orig_size != output_size) { | 
|  | *error_msg = "DebugInfoSize disagreed."; | 
|  | return false; | 
|  | } | 
|  | uint8_t* orig_data = orig->GetDebugInfo(); | 
|  | uint8_t* output_data = output->GetDebugInfo(); | 
|  | if ((orig_data == nullptr && output_data != nullptr) || | 
|  | (orig_data != nullptr && output_data == nullptr)) { | 
|  | *error_msg = "DebugInfo null/non-null mismatch."; | 
|  | return false; | 
|  | } | 
|  | if (orig_data != nullptr && memcmp(orig_data, output_data, orig_size) != 0) { | 
|  | *error_msg = "DebugInfo bytes mismatch."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyTries(dex_ir::TryItemVector* orig, | 
|  | dex_ir::TryItemVector* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty try items."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (orig->size() != output->size()) { | 
|  | *error_msg = StringPrintf("Mismatched tries size for code item at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig->size(), | 
|  | output->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig->size(); ++i) { | 
|  | const dex_ir::TryItem* orig_try = (*orig)[i].get(); | 
|  | const dex_ir::TryItem* output_try = (*output)[i].get(); | 
|  | if (orig_try->StartAddr() != output_try->StartAddr()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched try item start addr for code item at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_try->StartAddr(), | 
|  | output_try->StartAddr()); | 
|  | return false; | 
|  | } | 
|  | if (orig_try->InsnCount() != output_try->InsnCount()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched try item insn count for code item at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_try->InsnCount(), | 
|  | output_try->InsnCount()); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyHandler(orig_try->GetHandlers(), | 
|  | output_try->GetHandlers(), | 
|  | orig_offset, | 
|  | error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyHandlers(dex_ir::CatchHandlerVector* orig, | 
|  | dex_ir::CatchHandlerVector* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | if (orig == nullptr || output == nullptr) { | 
|  | if (orig != output) { | 
|  | *error_msg = "Found unexpected empty catch handlers."; | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | if (orig->size() != output->size()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched catch handlers size for code item at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig->size(), | 
|  | output->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig->size(); ++i) { | 
|  | if (!VerifyHandler((*orig)[i].get(), (*output)[i].get(), orig_offset, error_msg)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyHandler(const dex_ir::CatchHandler* orig, | 
|  | const dex_ir::CatchHandler* output, | 
|  | uint32_t orig_offset, | 
|  | std::string* error_msg) { | 
|  | dex_ir::TypeAddrPairVector* orig_handlers = orig->GetHandlers(); | 
|  | dex_ir::TypeAddrPairVector* output_handlers = output->GetHandlers(); | 
|  | if (orig_handlers->size() != output_handlers->size()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched number of catch handlers for code item at offset %x: %zu vs %zu.", | 
|  | orig_offset, | 
|  | orig_handlers->size(), | 
|  | output_handlers->size()); | 
|  | return false; | 
|  | } | 
|  | for (size_t i = 0; i < orig_handlers->size(); ++i) { | 
|  | const dex_ir::TypeAddrPair* orig_handler = (*orig_handlers)[i].get(); | 
|  | const dex_ir::TypeAddrPair* output_handler = (*output_handlers)[i].get(); | 
|  | if (orig_handler->GetTypeId() == nullptr || output_handler->GetTypeId() == nullptr) { | 
|  | if (orig_handler->GetTypeId() != output_handler->GetTypeId()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Found unexpected catch all catch handler for code item at offset %x.", | 
|  | orig_offset); | 
|  | return false; | 
|  | } | 
|  | } else if (orig_handler->GetTypeId()->GetIndex() != output_handler->GetTypeId()->GetIndex()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched catch handler type for code item at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_handler->GetTypeId()->GetIndex(), | 
|  | output_handler->GetTypeId()->GetIndex()); | 
|  | return false; | 
|  | } | 
|  | if (orig_handler->GetAddress() != output_handler->GetAddress()) { | 
|  | *error_msg = StringPrintf( | 
|  | "Mismatched catch handler address for code item at offset %x: %u vs %u.", | 
|  | orig_offset, | 
|  | orig_handler->GetAddress(), | 
|  | output_handler->GetAddress()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace art |