mirror of
https://github.com/LSPosed/DexBuilder.git
synced 2024-11-22 06:06:28 +00:00
476 lines
12 KiB
C++
476 lines
12 KiB
C++
/*
|
|
* 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
|