/* * 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. */ #pragma once #include "common.h" #include "memview.h" #include "arrayview.h" #include "dex_format.h" #include "dex_leb128.h" #include "buffer.h" #include "index_map.h" #include "hash_table.h" #include #include #include #include #include // A simple, lightweight IR to abstract the key .dex structures // // 1. All the cross-IR references are modeled as plain pointers. // 2. Newly allocated nodes are mem-zeroed first // // This IR can mirror any .dex file, although for JVMTI BCI // it's expected to construct the IR for the single modified class only // (and include only the nodes referenced from that class) #define SLICER_IR_TYPE \ using Node::Node; \ friend struct DexFile; #define SLICER_IR_INDEXED_TYPE \ using IndexedNode::IndexedNode; \ friend struct DexFile; namespace ir { // convenience notation template using own = std::unique_ptr; struct Node; struct IndexedNode; struct EncodedValue; struct EncodedArray; struct String; struct Type; struct TypeList; struct Proto; struct FieldDecl; struct EncodedField; struct DebugInfo; struct Code; struct MethodDecl; struct EncodedMethod; struct AnnotationElement; struct Annotation; struct AnnotationSet; struct AnnotationSetRefList; struct FieldAnnotation; struct MethodAnnotation; struct ParamAnnotation; struct AnnotationsDirectory; struct Class; struct DexFile; // The base class for all the .dex IR types: // This is not a polymorphic interface, but // a way to constrain the allocation and ownership // of .dex IR nodes. struct Node { void* operator new(size_t size) { return ::calloc(1, size); } void* operator new[](size_t size) { return ::calloc(1, size); } void operator delete(void* ptr) { ::free(ptr); } void operator delete[](void* ptr) { ::free(ptr); } public: Node(const Node&) = delete; Node& operator=(const Node&) = delete; protected: Node() = default; ~Node() = default; }; // a concession for the convenience of the .dex writer // // TODO: consider moving the indexing to the writer. // struct IndexedNode : public Node { SLICER_IR_TYPE; // this is the index in the generated image // (not the original index) dex::u4 index; // original indexe // (from the source .dex image or allocated post reader) dex::u4 orig_index; }; struct EncodedValue : public Node { SLICER_IR_TYPE; dex::u1 type; union { int8_t byte_value; int16_t short_value; uint16_t char_value; int32_t int_value; int64_t long_value; float float_value; double double_value; String* string_value; Type* type_value; FieldDecl* field_value; MethodDecl* method_value; FieldDecl* enum_value; EncodedArray* array_value; Annotation* annotation_value; bool bool_value; } u; SLICER_EXTRA(slicer::MemView original); }; struct EncodedArray : public Node { SLICER_IR_TYPE; std::vector values; }; struct String : public IndexedNode { SLICER_IR_INDEXED_TYPE; // opaque DEX "string_data_item" slicer::MemView data; const char* c_str() const { const dex::u1* strData = data.ptr(); dex::ReadULeb128(&strData); return reinterpret_cast(strData); } }; struct Type : public IndexedNode { SLICER_IR_INDEXED_TYPE; enum class Category { Void, Scalar, WideScalar, Reference }; String* descriptor; Class* class_def; std::string Decl() const; Category GetCategory() const; }; struct TypeList : public Node { SLICER_IR_TYPE; std::vector types; }; struct Proto : public IndexedNode { SLICER_IR_INDEXED_TYPE; String* shorty; Type* return_type; TypeList* param_types; std::string Signature() const; }; struct FieldDecl : public IndexedNode { SLICER_IR_INDEXED_TYPE; String* name; Type* type; Type* parent; }; struct EncodedField : public Node { SLICER_IR_TYPE; FieldDecl* decl; dex::u4 access_flags; }; struct DebugInfo : public Node { SLICER_IR_TYPE; dex::u4 line_start; std::vector param_names; // original debug info opcodes stream // (must be "relocated" when creating a new .dex image) slicer::MemView data; }; struct Code : public Node { SLICER_IR_TYPE; dex::u2 registers; dex::u2 ins_count; dex::u2 outs_count; slicer::ArrayView instructions; slicer::ArrayView try_blocks; slicer::MemView catch_handlers; DebugInfo* debug_info; }; struct MethodDecl : public IndexedNode { SLICER_IR_INDEXED_TYPE; String* name; Proto* prototype; Type* parent; }; struct EncodedMethod : public Node { SLICER_IR_TYPE; MethodDecl* decl; Code* code; dex::u4 access_flags; }; struct AnnotationElement : public Node { SLICER_IR_TYPE; String* name; EncodedValue* value; }; struct Annotation : public Node { SLICER_IR_TYPE; Type* type; std::vector elements; dex::u1 visibility; }; struct AnnotationSet : public Node { SLICER_IR_TYPE; std::vector annotations; }; struct AnnotationSetRefList : public Node { SLICER_IR_TYPE; std::vector annotations; }; struct FieldAnnotation : public Node { SLICER_IR_TYPE; FieldDecl* field_decl; AnnotationSet* annotations; }; struct MethodAnnotation : public Node { SLICER_IR_TYPE; MethodDecl* method_decl; AnnotationSet* annotations; }; struct ParamAnnotation : public Node { SLICER_IR_TYPE; MethodDecl* method_decl; AnnotationSetRefList* annotations; }; struct AnnotationsDirectory : public Node { SLICER_IR_TYPE; AnnotationSet* class_annotation; std::vector field_annotations; std::vector method_annotations; std::vector param_annotations; }; struct Class : public IndexedNode { SLICER_IR_INDEXED_TYPE; Type* type; dex::u4 access_flags; Type* super_class; TypeList* interfaces; String* source_file; AnnotationsDirectory* annotations; EncodedArray* static_init; std::vector static_fields; std::vector instance_fields; std::vector direct_methods; std::vector virtual_methods; }; // ir::String hashing struct StringsHasher { const char* GetKey(const String* string) const { return string->c_str(); } uint32_t Hash(const char* string_key) const; bool Compare(const char* string_key, const String* string) const; }; // ir::Proto hashing struct ProtosHasher { std::string GetKey(const Proto* proto) const { return proto->Signature(); } uint32_t Hash(const std::string& proto_key) const; bool Compare(const std::string& proto_key, const Proto* proto) const; }; // ir::EncodedMethod hashing struct MethodKey { String* class_descriptor = nullptr; String* method_name = nullptr; Proto* prototype = nullptr; }; struct MethodsHasher { MethodKey GetKey(const EncodedMethod* method) const; uint32_t Hash(const MethodKey& method_key) const; bool Compare(const MethodKey& method_key, const EncodedMethod* method) const; }; using StringsLookup = slicer::HashTable; using PrototypesLookup = slicer::HashTable; using MethodsLookup = slicer::HashTable; // The main container/root for a .dex IR struct DexFile { // indexed structures std::vector> strings; std::vector> types; std::vector> protos; std::vector> fields; std::vector> methods; std::vector> classes; // data segment structures std::vector> encoded_fields; std::vector> encoded_methods; std::vector> type_lists; std::vector> code; std::vector> debug_info; std::vector> encoded_values; std::vector> encoded_arrays; std::vector> annotations; std::vector> annotation_elements; std::vector> annotation_sets; std::vector> annotation_set_ref_lists; std::vector> annotations_directories; std::vector> field_annotations; std::vector> method_annotations; std::vector> param_annotations; // original index to IR node mappings // // CONSIDER: we only need to carry around // the relocation for the referenced items // std::map types_map; std::map strings_map; std::map protos_map; std::map fields_map; std::map methods_map; std::map classes_map; // original .dex header "magic" signature slicer::MemView magic; // keep track of the used index values // (so we can easily allocate new ones) IndexMap strings_indexes; IndexMap types_indexes; IndexMap protos_indexes; IndexMap fields_indexes; IndexMap methods_indexes; IndexMap classes_indexes; // lookup hash tables StringsLookup strings_lookup; MethodsLookup methods_lookup; PrototypesLookup prototypes_lookup; public: DexFile() = default; // No copy/move semantics DexFile(const DexFile&) = delete; DexFile& operator=(const DexFile&) = delete; template T* Alloc() { T* p = new T(); Track(p); return p; } void AttachBuffer(slicer::Buffer&& buffer) { buffers_.push_back(std::move(buffer)); } void Normalize(); private: void TopSortClassIndex(Class* irClass, dex::u4* nextIndex); void SortClassIndexes(); template void PushOwn(std::vector>& v, T* p) { v.push_back(own(p)); } void Track(String* p) { PushOwn(strings, p); } void Track(Type* p) { PushOwn(types, p); } void Track(Proto* p) { PushOwn(protos, p); } void Track(FieldDecl* p) { PushOwn(fields, p); } void Track(MethodDecl* p) { PushOwn(methods, p); } void Track(Class* p) { PushOwn(classes, p); } void Track(EncodedField* p) { PushOwn(encoded_fields, p); } void Track(EncodedMethod* p) { PushOwn(encoded_methods, p); } void Track(TypeList* p) { PushOwn(type_lists, p); } void Track(Code* p) { PushOwn(code, p); } void Track(DebugInfo* p) { PushOwn(debug_info, p); } void Track(EncodedValue* p) { PushOwn(encoded_values, p); } void Track(EncodedArray* p) { PushOwn(encoded_arrays, p); } void Track(Annotation* p) { PushOwn(annotations, p); } void Track(AnnotationElement* p) { PushOwn(annotation_elements, p); } void Track(AnnotationSet* p) { PushOwn(annotation_sets, p); } void Track(AnnotationSetRefList* p) { PushOwn(annotation_set_ref_lists, p); } void Track(AnnotationsDirectory* p) { PushOwn(annotations_directories, p); } void Track(FieldAnnotation* p) { PushOwn(field_annotations, p); } void Track(MethodAnnotation* p) { PushOwn(method_annotations, p); } void Track(ParamAnnotation* p) { PushOwn(param_annotations, p); } private: // additional memory buffers owned by this .dex IR std::vector buffers_; }; } // namespace ir #undef SLICER_IR_TYPE #undef SLICER_IR_INDEXED_TYPE