| /* |
| * 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 <stdlib.h> |
| #include <map> |
| #include <memory> |
| #include <vector> |
| #include <string> |
| |
| // 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 <class T> |
| using own = std::unique_ptr<T>; |
| |
| 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<EncodedValue*> 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::u1>(); |
| dex::ReadULeb128(&strData); |
| return reinterpret_cast<const char*>(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<Type*> 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<String*> 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<const dex::u2> instructions; |
| slicer::ArrayView<const dex::TryBlock> 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<AnnotationElement*> elements; |
| dex::u1 visibility; |
| }; |
| |
| struct AnnotationSet : public Node { |
| SLICER_IR_TYPE; |
| |
| std::vector<Annotation*> annotations; |
| }; |
| |
| struct AnnotationSetRefList : public Node { |
| SLICER_IR_TYPE; |
| |
| std::vector<AnnotationSet*> 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<FieldAnnotation*> field_annotations; |
| std::vector<MethodAnnotation*> method_annotations; |
| std::vector<ParamAnnotation*> 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<EncodedField*> static_fields; |
| std::vector<EncodedField*> instance_fields; |
| std::vector<EncodedMethod*> direct_methods; |
| std::vector<EncodedMethod*> 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<const char*, String, StringsHasher>; |
| using PrototypesLookup = slicer::HashTable<const std::string&, Proto, ProtosHasher>; |
| using MethodsLookup = slicer::HashTable<const MethodKey&, EncodedMethod, MethodsHasher>; |
| |
| // The main container/root for a .dex IR |
| struct DexFile { |
| // indexed structures |
| std::vector<own<String>> strings; |
| std::vector<own<Type>> types; |
| std::vector<own<Proto>> protos; |
| std::vector<own<FieldDecl>> fields; |
| std::vector<own<MethodDecl>> methods; |
| std::vector<own<Class>> classes; |
| |
| // data segment structures |
| std::vector<own<EncodedField>> encoded_fields; |
| std::vector<own<EncodedMethod>> encoded_methods; |
| std::vector<own<TypeList>> type_lists; |
| std::vector<own<Code>> code; |
| std::vector<own<DebugInfo>> debug_info; |
| std::vector<own<EncodedValue>> encoded_values; |
| std::vector<own<EncodedArray>> encoded_arrays; |
| std::vector<own<Annotation>> annotations; |
| std::vector<own<AnnotationElement>> annotation_elements; |
| std::vector<own<AnnotationSet>> annotation_sets; |
| std::vector<own<AnnotationSetRefList>> annotation_set_ref_lists; |
| std::vector<own<AnnotationsDirectory>> annotations_directories; |
| std::vector<own<FieldAnnotation>> field_annotations; |
| std::vector<own<MethodAnnotation>> method_annotations; |
| std::vector<own<ParamAnnotation>> param_annotations; |
| |
| // original index to IR node mappings |
| // |
| // CONSIDER: we only need to carry around |
| // the relocation for the referenced items |
| // |
| std::map<dex::u4, Type*> types_map; |
| std::map<dex::u4, String*> strings_map; |
| std::map<dex::u4, Proto*> protos_map; |
| std::map<dex::u4, FieldDecl*> fields_map; |
| std::map<dex::u4, MethodDecl*> methods_map; |
| std::map<dex::u4, Class*> 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 <class T> |
| 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 <class T> |
| void PushOwn(std::vector<own<T>>& v, T* p) { |
| v.push_back(own<T>(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<slicer::Buffer> buffers_; |
| }; |
| |
| } // namespace ir |
| |
| #undef SLICER_IR_TYPE |
| #undef SLICER_IR_INDEXED_TYPE |