/*
* 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 "slicer/reader.h"
#include "slicer/dex_bytecode.h"
#include "slicer/chronometer.h"
#include "slicer/dex_leb128.h"
#include <assert.h>
#include <string.h>
#include <type_traits>
#include <cstdlib>
namespace dex {
Reader::Reader(const dex::u1* image, size_t size) : image_(image), size_(size) {
// init the header reference
header_ = ptr<dex::Header>(0);
ValidateHeader();
// start with an "empty" .dex IR
dex_ir_ = std::make_shared<ir::DexFile>();
dex_ir_->magic = slicer::MemView(header_, sizeof(dex::Header::magic));
}
slicer::ArrayView<const dex::ClassDef> Reader::ClassDefs() const {
return section<dex::ClassDef>(header_->class_defs_off,
header_->class_defs_size);
}
slicer::ArrayView<const dex::StringId> Reader::StringIds() const {
return section<dex::StringId>(header_->string_ids_off,
header_->string_ids_size);
}
slicer::ArrayView<const dex::TypeId> Reader::TypeIds() const {
return section<dex::TypeId>(header_->type_ids_off,
header_->type_ids_size);
}
slicer::ArrayView<const dex::FieldId> Reader::FieldIds() const {
return section<dex::FieldId>(header_->field_ids_off,
header_->field_ids_size);
}
slicer::ArrayView<const dex::MethodId> Reader::MethodIds() const {
return section<dex::MethodId>(header_->method_ids_off,
header_->method_ids_size);
}
slicer::ArrayView<const dex::ProtoId> Reader::ProtoIds() const {
return section<dex::ProtoId>(header_->proto_ids_off,
header_->proto_ids_size);
}
const dex::MapList* Reader::DexMapList() const {
return dataPtr<dex::MapList>(header_->map_off);
}
const char* Reader::GetStringMUTF8(dex::u4 index) const {
if (index == dex::kNoIndex) {
return "<no_string>";
}
const dex::u1* strData = GetStringData(index);
dex::ReadULeb128(&strData);
return reinterpret_cast<const char*>(strData);
}
void Reader::CreateFullIr() {
size_t classCount = ClassDefs().size();
for (size_t i = 0; i < classCount; ++i) {
CreateClassIr(i);
}
}
void Reader::CreateClassIr(dex::u4 index) {
auto ir_class = GetClass(index);
SLICER_CHECK(ir_class != nullptr);
}
// Returns the index of the class with the specified
// descriptor, or kNoIndex if not found
dex::u4 Reader::FindClassIndex(const char* class_descriptor) const {
auto classes = ClassDefs();
auto types = TypeIds();
for (dex::u4 i = 0; i < classes.size(); ++i) {
auto typeId = types[classes[i].class_idx];
const char* descriptor = GetStringMUTF8(typeId.descriptor_idx);
if (strcmp(class_descriptor, descriptor) == 0) {
return i;
}
}
return dex::kNoIndex;
}
// map a .dex index to corresponding .dex IR node
//
// NOTES:
// 1. the mapping beween an index and the indexed
// .dex IR nodes is 1:1
// 2. we do a single index lookup for both existing
// nodes as well as new nodes
// 3. dummy is an invalid, but non-null pointer value
// used to check that the mapping loookup/update is atomic
// 4. there should be no recursion with the same index
// (we use the dummy value to guard against this too)
//
ir::Class* Reader::GetClass(dex::u4 index) {
SLICER_CHECK(index != dex::kNoIndex);
auto& p = dex_ir_->classes_map[index];
auto dummy = reinterpret_cast<ir::Class*>(1);
if (p == nullptr) {
p = dummy;
auto newClass = ParseClass(index);
SLICER_CHECK(p == dummy);
p = newClass;
dex_ir_->classes_indexes.MarkUsedIndex(index);
}
SLICER_CHECK(p != dummy);
return p;
}
// map a .dex index to corresponding .dex IR node
// (see the Reader::GetClass() comments)
ir::Type* Reader::GetType(dex::u4 index) {
SLICER_CHECK(index != dex::kNoIndex);
auto& p = dex_ir_->types_map[index];
auto dummy = reinterpret_cast<ir::Type*>(1);
if (p == nullptr) {
p = dummy;
auto newType = ParseType(index);
SLICER_CHECK(p == dummy);
p = newType;
dex_ir_->types_indexes.MarkUsedIndex(index);
}
SLICER_CHECK(p != dummy);
return p;
}
// map a .dex index to corresponding .dex IR node
// (see the Reader::GetClass() comments)
ir::FieldDecl* Reader::GetFieldDecl(dex::u4 index) {
SLICER_CHECK(index != dex::kNoIndex);
auto& p = dex_ir_->fields_map[index];
auto dummy = reinterpret_cast<ir::FieldDecl*>(1);
if (p == nullptr) {
p = dummy;
auto newField = ParseFieldDecl(index);
SLICER_CHECK(p == dummy);
p = newField;
dex_ir_->fields_indexes.MarkUsedIndex(index);
}
SLICER_CHECK(p != dummy);
return p;
}
// map a .dex index to corresponding .dex IR node
// (see the Reader::GetClass() comments)
ir::MethodDecl* Reader::GetMethodDecl(dex::u4 index) {
SLICER_CHECK(index != dex::kNoIndex);
auto& p = dex_ir_->methods_map[index];
auto dummy = reinterpret_cast<ir::MethodDecl*>(1);
if (p == nullptr) {
p = dummy;
auto newMethod = ParseMethodDecl(index);
SLICER_CHECK(p == dummy);
p = newMethod;
dex_ir_->methods_indexes.MarkUsedIndex(index);
}
SLICER_CHECK(p != dummy);
return p;
}
// map a .dex index to corresponding .dex IR node
// (see the Reader::GetClass() comments)
ir::Proto* Reader::GetProto(dex::u4 index) {
SLICER_CHECK(index != dex::kNoIndex);
auto& p = dex_ir_->protos_map[index];
auto dummy = reinterpret_cast<ir::Proto*>(1);
if (p == nullptr) {
p = dummy;
auto newProto = ParseProto(index);
SLICER_CHECK(p == dummy);
p = newProto;
dex_ir_->protos_indexes.MarkUsedIndex(index);
}
SLICER_CHECK(p != dummy);
return p;
}
// map a .dex index to corresponding .dex IR node
// (see the Reader::GetClass() comments)
ir::String* Reader::GetString(dex::u4 index) {
SLICER_CHECK(index != dex::kNoIndex);
auto& p = dex_ir_->strings_map[index];
auto dummy = reinterpret_cast<ir::String*>(1);
if (p == nullptr) {
p = dummy;
auto newString = ParseString(index);
SLICER_CHECK(p == dummy);
p = newString;
dex_ir_->strings_indexes.MarkUsedIndex(index);
}
SLICER_CHECK(p != dummy);
return p;
}
ir::Class* Reader::ParseClass(dex::u4 index) {
auto& dex_class_def = ClassDefs()[index];
auto ir_class = dex_ir_->Alloc<ir::Class>();
ir_class->type = GetType(dex_class_def.class_idx);
assert(ir_class->type->class_def == nullptr);
ir_class->type->class_def = ir_class;
ir_class->access_flags = dex_class_def.access_flags;
ir_class->interfaces = ExtractTypeList(dex_class_def.interfaces_off);
if (dex_class_def.superclass_idx != dex::kNoIndex) {
ir_class->super_class = GetType(dex_class_def.superclass_idx);
}
if (dex_class_def.source_file_idx != dex::kNoIndex) {
ir_class->source_file = GetString(dex_class_def.source_file_idx);
}
if (dex_class_def.class_data_off != 0) {
const dex::u1* class_data = dataPtr<dex::u1>(dex_class_def.class_data_off);
dex::u4 static_fields_count = dex::ReadULeb128(&class_data);
dex::u4 instance_fields_count = dex::ReadULeb128(&class_data);
dex::u4 direct_methods_count = dex::ReadULeb128(&class_data);
dex::u4 virtual_methods_count = dex::ReadULeb128(&class_data);
dex::u4 base_index = dex::kNoIndex;
for (dex::u4 i = 0; i < static_fields_count; ++i) {
auto field = ParseEncodedField(&class_data, &base_index);
ir_class->static_fields.push_back(field);
}
base_index = dex::kNoIndex;
for (dex::u4 i = 0; i < instance_fields_count; ++i) {
auto field = ParseEncodedField(&class_data, &base_index);
ir_class->instance_fields.push_back(field);
}
base_index = dex::kNoIndex;
for (dex::u4 i = 0; i < direct_methods_count; ++i) {
auto method = ParseEncodedMethod(&class_data, &base_index);
ir_class->direct_methods.push_back(method);
}
base_index = dex::kNoIndex;
for (dex::u4 i = 0; i < virtual_methods_count; ++i) {
auto method = ParseEncodedMethod(&class_data, &base_index);
ir_class->virtual_methods.push_back(method);
}
}
ir_class->static_init = ExtractEncodedArray(dex_class_def.static_values_off);
ir_class->annotations = ExtractAnnotations(dex_class_def.annotations_off);
ir_class->orig_index = index;
return ir_class;
}
ir::AnnotationsDirectory* Reader::ExtractAnnotations(dex::u4 offset) {
if (offset == 0) {
return nullptr;
}
SLICER_CHECK(offset % 4 == 0);
// first check if we already extracted the same "annotations_directory_item"
auto& ir_annotations = annotations_directories_[offset];
if (ir_annotations == nullptr) {
ir_annotations = dex_ir_->Alloc<ir::AnnotationsDirectory>();
auto dex_annotations = dataPtr<dex::AnnotationsDirectoryItem>(offset);
ir_annotations->class_annotation =
ExtractAnnotationSet(dex_annotations->class_annotations_off);
const dex::u1* ptr = reinterpret_cast<const dex::u1*>(dex_annotations + 1);
for (dex::u4 i = 0; i < dex_annotations->fields_size; ++i) {
ir_annotations->field_annotations.push_back(ParseFieldAnnotation(&ptr));
}
for (dex::u4 i = 0; i < dex_annotations->methods_size; ++i) {
ir_annotations->method_annotations.push_back(ParseMethodAnnotation(&ptr));
}
for (dex::u4 i = 0; i < dex_annotations->parameters_size; ++i) {
ir_annotations->param_annotations.push_back(ParseParamAnnotation(&ptr));
}
}
return ir_annotations;
}
ir::Annotation* Reader::ExtractAnnotationItem(dex::u4 offset) {
SLICER_CHECK(offset != 0);
// first check if we already extracted the same "annotation_item"
auto& ir_annotation = annotations_[offset];
if (ir_annotation == nullptr) {
auto dexAnnotationItem = dataPtr<dex::AnnotationItem>(offset);
const dex::u1* ptr = dexAnnotationItem->annotation;
ir_annotation = ParseAnnotation(&ptr);
ir_annotation->visibility = dexAnnotationItem->visibility;
}
return ir_annotation;
}
ir::AnnotationSet* Reader::ExtractAnnotationSet(dex::u4 offset) {
if (offset == 0) {
return nullptr;
}
SLICER_CHECK(offset % 4 == 0);
// first check if we already extracted the same "annotation_set_item"
auto& ir_annotation_set = annotation_sets_[offset];
if (ir_annotation_set == nullptr) {
ir_annotation_set = dex_ir_->Alloc<ir::AnnotationSet>();
auto dex_annotation_set = dataPtr<dex::AnnotationSetItem>(offset);
for (dex::u4 i = 0; i < dex_annotation_set->size; ++i) {
auto ir_annotation = ExtractAnnotationItem(dex_annotation_set->entries[i]);
assert(ir_annotation != nullptr);
ir_annotation_set->annotations.push_back(ir_annotation);
}
}
return ir_annotation_set;
}
ir::AnnotationSetRefList* Reader::ExtractAnnotationSetRefList(dex::u4 offset) {
SLICER_CHECK(offset % 4 == 0);
auto dex_annotation_set_ref_list = dataPtr<dex::AnnotationSetRefList>(offset);
auto ir_annotation_set_ref_list = dex_ir_->Alloc<ir::AnnotationSetRefList>();
for (dex::u4 i = 0; i < dex_annotation_set_ref_list->size; ++i) {
dex::u4 entry_offset = dex_annotation_set_ref_list->list[i].annotations_off;
if (entry_offset != 0) {
auto ir_annotation_set = ExtractAnnotationSet(entry_offset);
SLICER_CHECK(ir_annotation_set != nullptr);
ir_annotation_set_ref_list->annotations.push_back(ir_annotation_set);
}
}
return ir_annotation_set_ref_list;
}
ir::FieldAnnotation* Reader::ParseFieldAnnotation(const dex::u1** pptr) {
auto dex_field_annotation = reinterpret_cast<const dex::FieldAnnotationsItem*>(*pptr);
auto ir_field_annotation = dex_ir_->Alloc<ir::FieldAnnotation>();
ir_field_annotation->field_decl = GetFieldDecl(dex_field_annotation->field_idx);
ir_field_annotation->annotations =
ExtractAnnotationSet(dex_field_annotation->annotations_off);
SLICER_CHECK(ir_field_annotation->annotations != nullptr);
*pptr += sizeof(dex::FieldAnnotationsItem);
return ir_field_annotation;
}
ir::MethodAnnotation* Reader::ParseMethodAnnotation(const dex::u1** pptr) {
auto dex_method_annotation =
reinterpret_cast<const dex::MethodAnnotationsItem*>(*pptr);
auto ir_method_annotation = dex_ir_->Alloc<ir::MethodAnnotation>();
ir_method_annotation->method_decl = GetMethodDecl(dex_method_annotation->method_idx);
ir_method_annotation->annotations =
ExtractAnnotationSet(dex_method_annotation->annotations_off);
SLICER_CHECK(ir_method_annotation->annotations != nullptr);
*pptr += sizeof(dex::MethodAnnotationsItem);
return ir_method_annotation;
}
ir::ParamAnnotation* Reader::ParseParamAnnotation(const dex::u1** pptr) {
auto dex_param_annotation =
reinterpret_cast<const dex::ParameterAnnotationsItem*>(*pptr);
auto ir_param_annotation = dex_ir_->Alloc<ir::ParamAnnotation>();
ir_param_annotation->method_decl = GetMethodDecl(dex_param_annotation->method_idx);
ir_param_annotation->annotations =
ExtractAnnotationSetRefList(dex_param_annotation->annotations_off);
SLICER_CHECK(ir_param_annotation->annotations != nullptr);
*pptr += sizeof(dex::ParameterAnnotationsItem);
return ir_param_annotation;
}
ir::EncodedField* Reader::ParseEncodedField(const dex::u1** pptr, dex::u4* base_index) {
auto ir_encoded_field = dex_ir_->Alloc<ir::EncodedField>();
auto field_index = dex::ReadULeb128(pptr);
SLICER_CHECK(field_index != dex::kNoIndex);
if (*base_index != dex::kNoIndex) {
SLICER_CHECK(field_index != 0);
field_index += *base_index;
}
*base_index = field_index;
ir_encoded_field->decl = GetFieldDecl(field_index);
ir_encoded_field->access_flags = dex::ReadULeb128(pptr);
return ir_encoded_field;
}
// Parse an encoded variable-length integer value
// (sign-extend signed types, zero-extend unsigned types)
template <class T>
static T ParseIntValue(const dex::u1** pptr, size_t size) {
static_assert(std::is_integral<T>::value, "must be an integral type");
SLICER_CHECK(size > 0);
SLICER_CHECK(size <= sizeof(T));
T value = 0;
for (int i = 0; i < size; ++i) {
value |= T(*(*pptr)++) << (i * 8);
}
// sign-extend?
if (std::is_signed<T>::value) {
size_t shift = (sizeof(T) - size) * 8;
value = T(value << shift) >> shift;
}
return value;
}
// Parse an encoded variable-length floating point value
// (zero-extend to the right)
template <class T>
static T ParseFloatValue(const dex::u1** pptr, size_t size) {
SLICER_CHECK(size > 0);
SLICER_CHECK(size <= sizeof(T));
T value = 0;
int start_byte = sizeof(T) - size;
for (dex::u1* p = reinterpret_cast<dex::u1*>(&value) + start_byte; size > 0;
--size) {
*p++ = *(*pptr)++;
}
return value;
}
ir::EncodedValue* Reader::ParseEncodedValue(const dex::u1** pptr) {
auto ir_encoded_value = dex_ir_->Alloc<ir::EncodedValue>();
SLICER_EXTRA(auto base_ptr = *pptr);
dex::u1 header = *(*pptr)++;
dex::u1 type = header & dex::kEncodedValueTypeMask;
dex::u1 arg = header >> dex::kEncodedValueArgShift;
ir_encoded_value->type = type;
switch (type) {
case dex::kEncodedByte:
ir_encoded_value->u.byte_value = ParseIntValue<int8_t>(pptr, arg + 1);
break;
case dex::kEncodedShort:
ir_encoded_value->u.short_value = ParseIntValue<int16_t>(pptr, arg + 1);
break;
case dex::kEncodedChar:
ir_encoded_value->u.char_value = ParseIntValue<uint16_t>(pptr, arg + 1);
break;
case dex::kEncodedInt:
ir_encoded_value->u.int_value = ParseIntValue<int32_t>(pptr, arg + 1);
break;
case dex::kEncodedLong:
ir_encoded_value->u.long_value = ParseIntValue<int64_t>(pptr, arg + 1);
break;
case dex::kEncodedFloat:
ir_encoded_value->u.float_value = ParseFloatValue<float>(pptr, arg + 1);
break;
case dex::kEncodedDouble:
ir_encoded_value->u.double_value = ParseFloatValue<double>(pptr, arg + 1);
break;
case dex::kEncodedString: {
dex::u4 index = ParseIntValue<dex::u4>(pptr, arg + 1);
ir_encoded_value->u.string_value = GetString(index);
} break;
case dex::kEncodedType: {
dex::u4 index = ParseIntValue<dex::u4>(pptr, arg + 1);
ir_encoded_value->u.type_value = GetType(index);
} break;
case dex::kEncodedField: {
dex::u4 index = ParseIntValue<dex::u4>(pptr, arg + 1);
ir_encoded_value->u.field_value = GetFieldDecl(index);
} break;
case dex::kEncodedMethod: {
dex::u4 index = ParseIntValue<dex::u4>(pptr, arg + 1);
ir_encoded_value->u.method_value = GetMethodDecl(index);
} break;
case dex::kEncodedEnum: {
dex::u4 index = ParseIntValue<dex::u4>(pptr, arg + 1);
ir_encoded_value->u.enum_value = GetFieldDecl(index);
} break;
case dex::kEncodedArray:
SLICER_CHECK(arg == 0);
ir_encoded_value->u.array_value = ParseEncodedArray(pptr);
break;
case dex::kEncodedAnnotation:
SLICER_CHECK(arg == 0);
ir_encoded_value->u.annotation_value = ParseAnnotation(pptr);
break;
case dex::kEncodedNull:
SLICER_CHECK(arg == 0);
break;
case dex::kEncodedBoolean:
SLICER_CHECK(arg < 2);
ir_encoded_value->u.bool_value = (arg == 1);
break;
default:
SLICER_CHECK(!"unexpected value type");
}
SLICER_EXTRA(ir_encoded_value->original = slicer::MemView(base_ptr, *pptr - base_ptr));
return ir_encoded_value;
}
ir::Annotation* Reader::ParseAnnotation(const dex::u1** pptr) {
auto ir_annotation = dex_ir_->Alloc<ir::Annotation>();
dex::u4 type_index = dex::ReadULeb128(pptr);
dex::u4 elements_count = dex::ReadULeb128(pptr);
ir_annotation->type = GetType(type_index);
ir_annotation->visibility = dex::kVisibilityEncoded;
for (dex::u4 i = 0; i < elements_count; ++i) {
auto ir_element = dex_ir_->Alloc<ir::AnnotationElement>();
ir_element->name = GetString(dex::ReadULeb128(pptr));
ir_element->value = ParseEncodedValue(pptr);
ir_annotation->elements.push_back(ir_element);
}
return ir_annotation;
}
ir::EncodedArray* Reader::ParseEncodedArray(const dex::u1** pptr) {
auto ir_encoded_array = dex_ir_->Alloc<ir::EncodedArray>();
dex::u4 count = dex::ReadULeb128(pptr);
for (dex::u4 i = 0; i < count; ++i) {
ir_encoded_array->values.push_back(ParseEncodedValue(pptr));
}
return ir_encoded_array;
}
ir::EncodedArray* Reader::ExtractEncodedArray(dex::u4 offset) {
if (offset == 0) {
return nullptr;
}
// first check if we already extracted the same "annotation_item"
auto& ir_encoded_array = encoded_arrays_[offset];
if (ir_encoded_array == nullptr) {
auto ptr = dataPtr<dex::u1>(offset);
ir_encoded_array = ParseEncodedArray(&ptr);
}
return ir_encoded_array;
}
ir::DebugInfo* Reader::ExtractDebugInfo(dex::u4 offset) {
if (offset == 0) {
return nullptr;
}
auto ir_debug_info = dex_ir_->Alloc<ir::DebugInfo>();
const dex::u1* ptr = dataPtr<dex::u1>(offset);
ir_debug_info->line_start = dex::ReadULeb128(&ptr);
// TODO: implicit this param for non-static methods?
dex::u4 param_count = dex::ReadULeb128(&ptr);
for (dex::u4 i = 0; i < param_count; ++i) {
dex::u4 name_index = dex::ReadULeb128(&ptr) - 1;
auto ir_string =
(name_index == dex::kNoIndex) ? nullptr : GetString(name_index);
ir_debug_info->param_names.push_back(ir_string);
}
// parse the debug info opcodes and note the
// references to strings and types (to make sure the IR
// is the full closure of all referenced items)
//
// TODO: design a generic debug info iterator?
//
auto base_ptr = ptr;
dex::u1 opcode = 0;
while ((opcode = *ptr++) != dex::DBG_END_SEQUENCE) {
switch (opcode) {
case dex::DBG_ADVANCE_PC:
// addr_diff
dex::ReadULeb128(&ptr);
break;
case dex::DBG_ADVANCE_LINE:
// line_diff
dex::ReadSLeb128(&ptr);
break;
case dex::DBG_START_LOCAL: {
// register_num
dex::ReadULeb128(&ptr);
dex::u4 name_index = dex::ReadULeb128(&ptr) - 1;
if (name_index != dex::kNoIndex) {
GetString(name_index);
}
dex::u4 type_index = dex::ReadULeb128(&ptr) - 1;
if (type_index != dex::kNoIndex) {
GetType(type_index);
}
} break;
case dex::DBG_START_LOCAL_EXTENDED: {
// register_num
dex::ReadULeb128(&ptr);
dex::u4 name_index = dex::ReadULeb128(&ptr) - 1;
if (name_index != dex::kNoIndex) {
GetString(name_index);
}
dex::u4 type_index = dex::ReadULeb128(&ptr) - 1;
if (type_index != dex::kNoIndex) {
GetType(type_index);
}
dex::u4 sig_index = dex::ReadULeb128(&ptr) - 1;
if (sig_index != dex::kNoIndex) {
GetString(sig_index);
}
} break;
case dex::DBG_END_LOCAL:
case dex::DBG_RESTART_LOCAL:
// register_num
dex::ReadULeb128(&ptr);
break;
case dex::DBG_SET_FILE: {
dex::u4 name_index = dex::ReadULeb128(&ptr) - 1;
if (name_index != dex::kNoIndex) {
GetString(name_index);
}
} break;
}
}
ir_debug_info->data = slicer::MemView(base_ptr, ptr - base_ptr);
return ir_debug_info;
}
ir::Code* Reader::ExtractCode(dex::u4 offset) {
if (offset == 0) {
return nullptr;
}
SLICER_CHECK(offset % 4 == 0);
auto dex_code = dataPtr<dex::Code>(offset);
auto ir_code = dex_ir_->Alloc<ir::Code>();
ir_code->registers = dex_code->registers_size;
ir_code->ins_count = dex_code->ins_size;
ir_code->outs_count = dex_code->outs_size;
// instructions array
ir_code->instructions =
slicer::ArrayView<const dex::u2>(dex_code->insns, dex_code->insns_size);
// parse the instructions to discover references to other
// IR nodes (see debug info stream parsing too)
ParseInstructions(ir_code->instructions);
// try blocks & handlers
//
// TODO: a generic try/catch blocks iterator?
//
if (dex_code->tries_size != 0) {
dex::u4 aligned_count = (dex_code->insns_size + 1) / 2 * 2;
auto tries =
reinterpret_cast<const dex::TryBlock*>(dex_code->insns + aligned_count);
auto handlers_list =
reinterpret_cast<const dex::u1*>(tries + dex_code->tries_size);
ir_code->try_blocks =
slicer::ArrayView<const dex::TryBlock>(tries, dex_code->tries_size);
// parse the handlers list (and discover embedded references)
auto ptr = handlers_list;
dex::u4 handlers_count = dex::ReadULeb128(&ptr);
SLICER_WEAK_CHECK(handlers_count <= dex_code->tries_size);
for (dex::u4 handler_index = 0; handler_index < handlers_count; ++handler_index) {
int catch_count = dex::ReadSLeb128(&ptr);
for (int catch_index = 0; catch_index < std::abs(catch_count); ++catch_index) {
dex::u4 type_index = dex::ReadULeb128(&ptr);
GetType(type_index);
// address
dex::ReadULeb128(&ptr);
}
if (catch_count < 1) {
// catch_all_addr
dex::ReadULeb128(&ptr);
}
}
ir_code->catch_handlers = slicer::MemView(handlers_list, ptr - handlers_list);
}
ir_code->debug_info = ExtractDebugInfo(dex_code->debug_info_off);
return ir_code;
}
ir::EncodedMethod* Reader::ParseEncodedMethod(const dex::u1** pptr, dex::u4* base_index) {
auto ir_encoded_method = dex_ir_->Alloc<ir::EncodedMethod>();
auto method_index = dex::ReadULeb128(pptr);
SLICER_CHECK(method_index != dex::kNoIndex);
if (*base_index != dex::kNoIndex) {
SLICER_CHECK(method_index != 0);
method_index += *base_index;
}
*base_index = method_index;
ir_encoded_method->decl = GetMethodDecl(method_index);
ir_encoded_method->access_flags = dex::ReadULeb128(pptr);
dex::u4 code_offset = dex::ReadULeb128(pptr);
ir_encoded_method->code = ExtractCode(code_offset);
// update the methods lookup table
dex_ir_->methods_lookup.Insert(ir_encoded_method);
return ir_encoded_method;
}
ir::Type* Reader::ParseType(dex::u4 index) {
auto& dex_type = TypeIds()[index];
auto ir_type = dex_ir_->Alloc<ir::Type>();
ir_type->descriptor = GetString(dex_type.descriptor_idx);
ir_type->orig_index = index;
return ir_type;
}
ir::FieldDecl* Reader::ParseFieldDecl(dex::u4 index) {
auto& dex_field = FieldIds()[index];
auto ir_field = dex_ir_->Alloc<ir::FieldDecl>();
ir_field->name = GetString(dex_field.name_idx);
ir_field->type = GetType(dex_field.type_idx);
ir_field->parent = GetType(dex_field.class_idx);
ir_field->orig_index = index;
return ir_field;
}
ir::MethodDecl* Reader::ParseMethodDecl(dex::u4 index) {
auto& dex_method = MethodIds()[index];
auto ir_method = dex_ir_->Alloc<ir::MethodDecl>();
ir_method->name = GetString(dex_method.name_idx);
ir_method->prototype = GetProto(dex_method.proto_idx);
ir_method->parent = GetType(dex_method.class_idx);
ir_method->orig_index = index;
return ir_method;
}
ir::TypeList* Reader::ExtractTypeList(dex::u4 offset) {
if (offset == 0) {
return nullptr;
}
// first check to see if we already extracted the same "type_list"
auto& ir_type_list = type_lists_[offset];
if (ir_type_list == nullptr) {
ir_type_list = dex_ir_->Alloc<ir::TypeList>();
auto dex_type_list = dataPtr<dex::TypeList>(offset);
SLICER_WEAK_CHECK(dex_type_list->size > 0);
for (dex::u4 i = 0; i < dex_type_list->size; ++i) {
ir_type_list->types.push_back(GetType(dex_type_list->list[i].type_idx));
}
}
return ir_type_list;
}
ir::Proto* Reader::ParseProto(dex::u4 index) {
auto& dex_proto = ProtoIds()[index];
auto ir_proto = dex_ir_->Alloc<ir::Proto>();
ir_proto->shorty = GetString(dex_proto.shorty_idx);
ir_proto->return_type = GetType(dex_proto.return_type_idx);
ir_proto->param_types = ExtractTypeList(dex_proto.parameters_off);
ir_proto->orig_index = index;
// update the prototypes lookup table
dex_ir_->prototypes_lookup.Insert(ir_proto);
return ir_proto;
}
ir::String* Reader::ParseString(dex::u4 index) {
auto ir_string = dex_ir_->Alloc<ir::String>();
auto data = GetStringData(index);
auto cstr = data;
dex::ReadULeb128(&cstr);
size_t size = (cstr - data) + ::strlen(reinterpret_cast<const char*>(cstr)) + 1;
ir_string->data = slicer::MemView(data, size);
ir_string->orig_index = index;
// update the strings lookup table
dex_ir_->strings_lookup.Insert(ir_string);
return ir_string;
}
void Reader::ParseInstructions(slicer::ArrayView<const dex::u2> code) {
const dex::u2* ptr = code.begin();
while (ptr < code.end()) {
auto dex_instr = dex::DecodeInstruction(ptr);
dex::u4 index = dex::kNoIndex;
switch (dex::GetFormatFromOpcode(dex_instr.opcode)) {
case dex::k20bc:
case dex::k21c:
case dex::k31c:
case dex::k35c:
case dex::k3rc:
index = dex_instr.vB;
break;
case dex::k22c:
index = dex_instr.vC;
break;
default:
break;
}
switch (GetIndexTypeFromOpcode(dex_instr.opcode)) {
case dex::kIndexStringRef:
GetString(index);
break;
case dex::kIndexTypeRef:
GetType(index);
break;
case dex::kIndexFieldRef:
GetFieldDecl(index);
break;
case dex::kIndexMethodRef:
GetMethodDecl(index);
break;
default:
break;
}
auto isize = dex::GetWidthFromBytecode(ptr);
SLICER_CHECK(isize > 0);
ptr += isize;
}
SLICER_CHECK(ptr == code.end());
}
// Basic .dex header structural checks
void Reader::ValidateHeader() {
SLICER_CHECK(size_ > sizeof(dex::Header));
// Known issue: For performance reasons the initial size_ passed to jvmti events might be an
// estimate. b/72402467
SLICER_CHECK(header_->file_size <= size_);
SLICER_CHECK(header_->header_size == sizeof(dex::Header));
SLICER_CHECK(header_->endian_tag == dex::kEndianConstant);
SLICER_CHECK(header_->data_size % 4 == 0);
// Known issue: The fields might be slighly corrupted b/65452964
// SLICER_CHECK(header_->data_off + header_->data_size <= size_);
SLICER_CHECK(header_->string_ids_off % 4 == 0);
SLICER_CHECK(header_->type_ids_size < 65536);
SLICER_CHECK(header_->type_ids_off % 4 == 0);
SLICER_CHECK(header_->proto_ids_size < 65536);
SLICER_CHECK(header_->proto_ids_off % 4 == 0);
SLICER_CHECK(header_->field_ids_off % 4 == 0);
SLICER_CHECK(header_->method_ids_off % 4 == 0);
SLICER_CHECK(header_->class_defs_off % 4 == 0);
SLICER_CHECK(header_->map_off >= header_->data_off && header_->map_off < size_);
SLICER_CHECK(header_->link_size == 0);
SLICER_CHECK(header_->link_off == 0);
SLICER_CHECK(header_->data_off % 4 == 0);
SLICER_CHECK(header_->map_off % 4 == 0);
// we seem to have .dex files with extra bytes at the end ...
// Known issue: For performance reasons the initial size_ passed to jvmti events might be an
// estimate. b/72402467
SLICER_WEAK_CHECK(header_->data_off + header_->data_size <= size_);
// but we should still have the whole data section
// Known issue: The fields might be slighly corrupted b/65452964
// Known issue: For performance reasons the initial size_ passed to jvmti events might be an
// estimate. b/72402467
// SLICER_CHECK(header_->data_off + header_->data_size <= size_);
// validate the map
// (map section size = sizeof(MapList::size) + sizeof(MapList::list[size])
auto map_list = ptr<dex::MapList>(header_->map_off);
SLICER_CHECK(map_list->size > 0);
auto map_section_size =
sizeof(dex::u4) + sizeof(dex::MapItem) * map_list->size;
SLICER_CHECK(header_->map_off + map_section_size <= size_);
}
} // namespace dex