You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1116 lines
34 KiB
1116 lines
34 KiB
/*
|
|
* 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/writer.h"
|
|
#include "slicer/common.h"
|
|
#include "slicer/scopeguard.h"
|
|
#include "slicer/dex_bytecode.h"
|
|
#include "slicer/dex_format.h"
|
|
#include "slicer/dex_ir.h"
|
|
#include "slicer/dex_leb128.h"
|
|
|
|
#include <assert.h>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
#include <cstdlib>
|
|
#include <string.h>
|
|
#include <algorithm>
|
|
|
|
namespace dex {
|
|
|
|
// Returns the IR node index, or kNoIndex for null IR nodes
|
|
template <class T>
|
|
static dex::u4 OptIndex(const T* ir_node) {
|
|
return ir_node != nullptr ? ir_node->index : dex::kNoIndex;
|
|
}
|
|
|
|
// Helper for creating the header of an encoded value
|
|
static void WriteEncodedValueHeader(dex::u1 type, int arg, Section& data) {
|
|
assert((type & ~dex::kEncodedValueTypeMask) == 0);
|
|
assert(arg >= 0 && arg < 8);
|
|
dex::u1 header = dex::u1(type | (arg << dex::kEncodedValueArgShift));
|
|
data.Push<dex::u1>(header);
|
|
}
|
|
|
|
// Writes an integer encoded value
|
|
template <class T>
|
|
static void WriteIntValue(dex::u1 type, T value, Section& data) {
|
|
dex::u1 buff[sizeof(T)] = {};
|
|
dex::u1* dst = buff;
|
|
|
|
if (std::is_signed<T>::value) {
|
|
const bool positive = (value >= 0);
|
|
while (positive ? value >= 0x80 : value < -0x80) {
|
|
*dst++ = value & 0xff;
|
|
value >>= 8;
|
|
}
|
|
*dst++ = value & 0xff;
|
|
} else {
|
|
do {
|
|
*dst++ = value & 0xff;
|
|
value >>= 8;
|
|
} while (value != 0);
|
|
}
|
|
|
|
size_t size = dst - buff;
|
|
assert(size > 0 && size <= sizeof(T));
|
|
WriteEncodedValueHeader(type, size - 1, data);
|
|
data.Push(buff, size);
|
|
}
|
|
|
|
// Writes a floating point encoded value
|
|
template <class T>
|
|
static void WriteFloatValue(dex::u1 type, T value, Section& data) {
|
|
dex::u1 buff[sizeof(T)] = {};
|
|
auto src = reinterpret_cast<const dex::u1*>(&value);
|
|
size_t size = sizeof(T);
|
|
|
|
// skip "rightmost" zero bytes
|
|
while (size > 1 && *src == 0) {
|
|
--size;
|
|
++src;
|
|
}
|
|
|
|
// copy the rest...
|
|
for (size_t i = 0; i < size; ++i) {
|
|
buff[i] = src[i];
|
|
}
|
|
|
|
assert(size > 0 && size <= sizeof(T));
|
|
WriteEncodedValueHeader(type, size - 1, data);
|
|
data.Push(buff, size);
|
|
}
|
|
|
|
static void WriteEncodedArray(const ir::EncodedArray* ir_array, Section& data);
|
|
static void WriteAnnotation(const ir::Annotation* ir_annotation, Section& data);
|
|
|
|
// "encoded_value"
|
|
static void WriteEncodedValue(const ir::EncodedValue* ir_value, Section& data) {
|
|
SLICER_EXTRA(auto offset = data.size());
|
|
|
|
dex::u1 type = ir_value->type;
|
|
switch (type) {
|
|
case dex::kEncodedByte:
|
|
WriteIntValue(type, ir_value->u.byte_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedShort:
|
|
WriteIntValue(type, ir_value->u.short_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedChar:
|
|
WriteIntValue(type, ir_value->u.char_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedInt:
|
|
WriteIntValue(type, ir_value->u.int_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedLong:
|
|
WriteIntValue(type, ir_value->u.long_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedFloat:
|
|
WriteFloatValue(type, ir_value->u.float_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedDouble:
|
|
WriteFloatValue(type, ir_value->u.double_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedString:
|
|
WriteIntValue<dex::u4>(type, ir_value->u.string_value->index, data);
|
|
break;
|
|
|
|
case dex::kEncodedType:
|
|
WriteIntValue<dex::u4>(type, ir_value->u.type_value->index, data);
|
|
break;
|
|
|
|
case dex::kEncodedField:
|
|
WriteIntValue<dex::u4>(type, ir_value->u.field_value->index, data);
|
|
break;
|
|
|
|
case dex::kEncodedMethod:
|
|
WriteIntValue<dex::u4>(type, ir_value->u.method_value->index, data);
|
|
break;
|
|
|
|
case dex::kEncodedEnum:
|
|
WriteIntValue<dex::u4>(type, ir_value->u.enum_value->index, data);
|
|
break;
|
|
|
|
case dex::kEncodedArray:
|
|
WriteEncodedValueHeader(type, 0, data);
|
|
WriteEncodedArray(ir_value->u.array_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedAnnotation:
|
|
WriteEncodedValueHeader(type, 0, data);
|
|
WriteAnnotation(ir_value->u.annotation_value, data);
|
|
break;
|
|
|
|
case dex::kEncodedNull:
|
|
WriteEncodedValueHeader(type, 0, data);
|
|
break;
|
|
|
|
case dex::kEncodedBoolean: {
|
|
int arg = ir_value->u.bool_value ? 1 : 0;
|
|
WriteEncodedValueHeader(type, arg, data);
|
|
} break;
|
|
|
|
default:
|
|
SLICER_CHECK(!"unexpected value type");
|
|
}
|
|
|
|
// optionally check the encoding against the original one
|
|
// (if possible, some of the values contain relocated indexes)
|
|
SLICER_EXTRA({
|
|
switch (type) {
|
|
case dex::kEncodedByte:
|
|
case dex::kEncodedShort:
|
|
case dex::kEncodedChar:
|
|
case dex::kEncodedInt:
|
|
case dex::kEncodedLong:
|
|
case dex::kEncodedFloat:
|
|
case dex::kEncodedDouble:
|
|
case dex::kEncodedNull:
|
|
case dex::kEncodedBoolean:
|
|
auto ptr = data.ptr<const dex::u1>(offset);
|
|
auto size = data.size() - offset;
|
|
SLICER_CHECK(size == ir_value->original.size());
|
|
SLICER_CHECK(memcmp(ptr, ir_value->original.ptr(), size) == 0);
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
|
|
// "encoded_annotation"
|
|
static void WriteAnnotation(const ir::Annotation* ir_annotation, Section& data) {
|
|
data.PushULeb128(ir_annotation->type->index);
|
|
data.PushULeb128(ir_annotation->elements.size());
|
|
for (auto irAnnotationElement : ir_annotation->elements) {
|
|
data.PushULeb128(irAnnotationElement->name->index);
|
|
WriteEncodedValue(irAnnotationElement->value, data);
|
|
}
|
|
}
|
|
|
|
// "encoded_array"
|
|
static void WriteEncodedArray(const ir::EncodedArray* ir_array, Section& data) {
|
|
const auto& values = ir_array->values;
|
|
data.PushULeb128(values.size());
|
|
for (auto irEncodedValue : values) {
|
|
WriteEncodedValue(irEncodedValue, data);
|
|
}
|
|
}
|
|
|
|
// helper for concatenating .dex sections into the final image
|
|
template <class T>
|
|
static void CopySection(const T& section, dex::u1* image, dex::u4 image_size) {
|
|
if (section.size() == 0) {
|
|
SLICER_CHECK(section.ItemsCount() == 0);
|
|
return;
|
|
}
|
|
|
|
SLICER_CHECK(section.ItemsCount() > 0);
|
|
dex::u4 offset = section.SectionOffset();
|
|
dex::u4 size = section.size();
|
|
SLICER_CHECK(offset >= sizeof(dex::Header));
|
|
SLICER_CHECK(offset + size <= image_size);
|
|
|
|
::memcpy(image + offset, section.data(), size);
|
|
}
|
|
|
|
static u4 ReadU4(const u2* ptr) { return ptr[0] | (u4(ptr[1]) << 16); }
|
|
|
|
static void WriteU4(u2* ptr, u4 val) {
|
|
ptr[0] = val & 0xffff;
|
|
ptr[1] = val >> 16;
|
|
}
|
|
|
|
// This is the main interface for the .dex writer
|
|
// (returns nullptr on failure)
|
|
dex::u1* Writer::CreateImage(Allocator* allocator, size_t* new_image_size) {
|
|
// create a new DexImage
|
|
dex_.reset(new DexImage);
|
|
|
|
SLICER_SCOPE_EXIT {
|
|
dex_.reset();
|
|
};
|
|
|
|
// TODO: revisit IR normalization
|
|
// (ideally we shouldn't change the IR while generating an image)
|
|
dex_ir_->Normalize();
|
|
|
|
// track the current offset within the .dex image
|
|
dex::u4 offset = 0;
|
|
|
|
// allocate the image and index sections
|
|
// (they will be back-filled)
|
|
offset += sizeof(dex::Header);
|
|
offset += dex_->string_ids.Init(offset, dex_ir_->strings.size());
|
|
offset += dex_->type_ids.Init(offset, dex_ir_->types.size());
|
|
offset += dex_->proto_ids.Init(offset, dex_ir_->protos.size());
|
|
offset += dex_->field_ids.Init(offset, dex_ir_->fields.size());
|
|
offset += dex_->method_ids.Init(offset, dex_ir_->methods.size());
|
|
offset += dex_->class_defs.Init(offset, dex_ir_->classes.size());
|
|
|
|
// the base offset for the "data" meta-section
|
|
SLICER_CHECK(offset % 4 == 0);
|
|
const dex::u4 data_offset = offset;
|
|
|
|
// we must create the sections in a very specific
|
|
// order due to file pointers across sections
|
|
offset += CreateStringDataSection(offset);
|
|
offset += CreateTypeListsSection(offset);
|
|
offset += CreateDebugInfoSection(offset);
|
|
offset += CreateEncodedArrayItemSection(offset);
|
|
offset += CreateCodeItemSection(offset);
|
|
offset += CreateClassDataSection(offset);
|
|
offset += CreateAnnItemSection(offset);
|
|
offset += CreateAnnSetsSection(offset);
|
|
offset += CreateAnnSetRefListsSection(offset);
|
|
offset += CreateAnnDirectoriesSection(offset);
|
|
offset += CreateMapSection(offset);
|
|
|
|
// back-fill the indexes
|
|
FillTypes();
|
|
FillFields();
|
|
FillProtos();
|
|
FillMethods();
|
|
FillClassDefs();
|
|
|
|
// allocate the final buffer for the .dex image
|
|
SLICER_CHECK(offset % 4 == 0);
|
|
const dex::u4 image_size = offset;
|
|
dex::u1* image = static_cast<dex::u1*>(allocator->Allocate(image_size));
|
|
if (image == nullptr) {
|
|
// memory allocation failed, bailing out...
|
|
return nullptr;
|
|
}
|
|
memset(image, 0, image_size);
|
|
|
|
// finally, back-fill the header
|
|
SLICER_CHECK(image_size > sizeof(dex::Header));
|
|
|
|
dex::Header* header = reinterpret_cast<dex::Header*>(image + 0);
|
|
|
|
// magic signature
|
|
memcpy(header->magic, dex_ir_->magic.ptr(), dex_ir_->magic.size());
|
|
|
|
header->file_size = image_size;
|
|
header->header_size = sizeof(dex::Header);
|
|
header->endian_tag = dex::kEndianConstant;
|
|
|
|
header->link_size = 0;
|
|
header->link_off = 0;
|
|
|
|
header->map_off = dex_->map_list.SectionOffset();
|
|
header->string_ids_size = dex_->string_ids.ItemsCount();
|
|
header->string_ids_off = dex_->string_ids.SectionOffset();
|
|
header->type_ids_size = dex_->type_ids.ItemsCount();
|
|
header->type_ids_off = dex_->type_ids.SectionOffset();
|
|
header->proto_ids_size = dex_->proto_ids.ItemsCount();
|
|
header->proto_ids_off = dex_->proto_ids.SectionOffset();
|
|
header->field_ids_size = dex_->field_ids.ItemsCount();
|
|
header->field_ids_off = dex_->field_ids.SectionOffset();
|
|
header->method_ids_size = dex_->method_ids.ItemsCount();
|
|
header->method_ids_off = dex_->method_ids.SectionOffset();
|
|
header->class_defs_size = dex_->class_defs.ItemsCount();
|
|
header->class_defs_off = dex_->class_defs.SectionOffset();
|
|
header->data_size = image_size - data_offset;
|
|
header->data_off = data_offset;
|
|
|
|
// copy the individual sections to the final image
|
|
CopySection(dex_->string_ids, image, image_size);
|
|
CopySection(dex_->type_ids, image, image_size);
|
|
CopySection(dex_->proto_ids, image, image_size);
|
|
CopySection(dex_->field_ids, image, image_size);
|
|
CopySection(dex_->method_ids, image, image_size);
|
|
CopySection(dex_->class_defs, image, image_size);
|
|
CopySection(dex_->string_data, image, image_size);
|
|
CopySection(dex_->type_lists, image, image_size);
|
|
CopySection(dex_->debug_info, image, image_size);
|
|
CopySection(dex_->encoded_arrays, image, image_size);
|
|
CopySection(dex_->code, image, image_size);
|
|
CopySection(dex_->class_data, image, image_size);
|
|
CopySection(dex_->ann_directories, image, image_size);
|
|
CopySection(dex_->ann_set_ref_lists, image, image_size);
|
|
CopySection(dex_->ann_sets, image, image_size);
|
|
CopySection(dex_->ann_items, image, image_size);
|
|
CopySection(dex_->map_list, image, image_size);
|
|
|
|
// checksum
|
|
header->checksum = dex::ComputeChecksum(header);
|
|
|
|
*new_image_size = image_size;
|
|
return image;
|
|
}
|
|
|
|
// "string_id_item" + string data section
|
|
dex::u4 Writer::CreateStringDataSection(dex::u4 section_offset) {
|
|
auto& section = dex_->string_data;
|
|
section.SetOffset(section_offset);
|
|
|
|
const auto& strings = dex_ir_->strings;
|
|
for (size_t i = 0; i < strings.size(); ++i) {
|
|
const auto& ir_string = strings[i];
|
|
auto dexStringId = &dex_->string_ids[i];
|
|
|
|
dex::u4 offset = section.AddItem();
|
|
section.Push(ir_string->data);
|
|
dexStringId->string_data_off = section.AbsoluteOffset(offset);
|
|
}
|
|
|
|
dex::u4 size = section.Seal(4);
|
|
return size;
|
|
}
|
|
|
|
// Helper for creating the map section
|
|
template <class T>
|
|
static void AddMapItem(const T& section, std::vector<dex::MapItem>& items) {
|
|
if (section.ItemsCount() > 0) {
|
|
SLICER_CHECK(section.SectionOffset() >= sizeof(dex::Header));
|
|
dex::MapItem map_item = {};
|
|
map_item.type = section.MapEntryType();
|
|
map_item.size = section.ItemsCount();
|
|
map_item.offset = section.SectionOffset();
|
|
items.push_back(map_item);
|
|
}
|
|
}
|
|
|
|
// map_list section
|
|
dex::u4 Writer::CreateMapSection(dex::u4 section_offset) {
|
|
auto& section = dex_->map_list;
|
|
section.SetOffset(section_offset);
|
|
section.AddItem(4);
|
|
|
|
std::vector<dex::MapItem> map_items;
|
|
|
|
dex::MapItem headerItem = {};
|
|
headerItem.type = dex::kHeaderItem;
|
|
headerItem.size = 1;
|
|
headerItem.offset = 0;
|
|
map_items.push_back(headerItem);
|
|
|
|
AddMapItem(dex_->string_ids, map_items);
|
|
AddMapItem(dex_->type_ids, map_items);
|
|
AddMapItem(dex_->proto_ids, map_items);
|
|
AddMapItem(dex_->field_ids, map_items);
|
|
AddMapItem(dex_->method_ids, map_items);
|
|
AddMapItem(dex_->class_defs, map_items);
|
|
AddMapItem(dex_->string_data, map_items);
|
|
AddMapItem(dex_->type_lists, map_items);
|
|
AddMapItem(dex_->debug_info, map_items);
|
|
AddMapItem(dex_->encoded_arrays, map_items);
|
|
AddMapItem(dex_->code, map_items);
|
|
AddMapItem(dex_->class_data, map_items);
|
|
AddMapItem(dex_->ann_directories, map_items);
|
|
AddMapItem(dex_->ann_set_ref_lists, map_items);
|
|
AddMapItem(dex_->ann_sets, map_items);
|
|
AddMapItem(dex_->ann_items, map_items);
|
|
AddMapItem(dex_->map_list, map_items);
|
|
|
|
std::sort(map_items.begin(), map_items.end(),
|
|
[](const dex::MapItem& a, const dex::MapItem& b) {
|
|
SLICER_CHECK(a.offset != b.offset);
|
|
return a.offset < b.offset;
|
|
});
|
|
|
|
section.Push<dex::u4>(map_items.size());
|
|
section.Push(map_items);
|
|
return section.Seal(4);
|
|
}
|
|
|
|
// annotation_item section
|
|
dex::u4 Writer::CreateAnnItemSection(dex::u4 section_offset) {
|
|
dex_->ann_items.SetOffset(section_offset);
|
|
|
|
for (const auto& ir_node : dex_ir_->annotations) {
|
|
if (ir_node->visibility != dex::kVisibilityEncoded) {
|
|
// TODO: factor out the node_offset_ updating
|
|
dex::u4& offset = node_offset_[ir_node.get()];
|
|
SLICER_CHECK(offset == 0);
|
|
offset = WriteAnnotationItem(ir_node.get());
|
|
}
|
|
}
|
|
|
|
return dex_->ann_items.Seal(4);
|
|
}
|
|
|
|
// annotation_set_item section
|
|
dex::u4 Writer::CreateAnnSetsSection(dex::u4 section_offset) {
|
|
dex_->ann_sets.SetOffset(section_offset);
|
|
|
|
for (const auto& ir_node : dex_ir_->annotation_sets) {
|
|
dex::u4& offset = node_offset_[ir_node.get()];
|
|
SLICER_CHECK(offset == 0);
|
|
offset = WriteAnnotationSet(ir_node.get());
|
|
}
|
|
|
|
return dex_->ann_sets.Seal(4);
|
|
}
|
|
|
|
// annotation_set_ref_list section
|
|
dex::u4 Writer::CreateAnnSetRefListsSection(dex::u4 section_offset) {
|
|
dex_->ann_set_ref_lists.SetOffset(section_offset);
|
|
|
|
for (const auto& ir_node : dex_ir_->annotation_set_ref_lists) {
|
|
dex::u4& offset = node_offset_[ir_node.get()];
|
|
SLICER_CHECK(offset == 0);
|
|
offset = WriteAnnotationSetRefList(ir_node.get());
|
|
}
|
|
|
|
return dex_->ann_set_ref_lists.Seal(4);
|
|
}
|
|
|
|
// type_list section
|
|
dex::u4 Writer::CreateTypeListsSection(dex::u4 section_offset) {
|
|
dex_->type_lists.SetOffset(section_offset);
|
|
|
|
for (const auto& ir_type_list : dex_ir_->type_lists) {
|
|
dex::u4& offset = node_offset_[ir_type_list.get()];
|
|
SLICER_CHECK(offset == 0);
|
|
offset = WriteTypeList(ir_type_list->types);
|
|
}
|
|
|
|
return dex_->type_lists.Seal(4);
|
|
}
|
|
|
|
// code_item section
|
|
dex::u4 Writer::CreateCodeItemSection(dex::u4 section_offset) {
|
|
dex_->code.SetOffset(section_offset);
|
|
|
|
for (const auto& ir_node : dex_ir_->code) {
|
|
dex::u4& offset = node_offset_[ir_node.get()];
|
|
SLICER_CHECK(offset == 0);
|
|
offset = WriteCode(ir_node.get());
|
|
}
|
|
|
|
dex::u4 size = dex_->code.Seal(4);
|
|
return size;
|
|
}
|
|
|
|
// debug info section
|
|
dex::u4 Writer::CreateDebugInfoSection(dex::u4 section_offset) {
|
|
dex_->debug_info.SetOffset(section_offset);
|
|
|
|
for (const auto& ir_node : dex_ir_->debug_info) {
|
|
dex::u4& offset = node_offset_[ir_node.get()];
|
|
SLICER_CHECK(offset == 0);
|
|
offset = WriteDebugInfo(ir_node.get());
|
|
}
|
|
|
|
dex::u4 size = dex_->debug_info.Seal(4);
|
|
return size;
|
|
}
|
|
|
|
// class_data_item section
|
|
dex::u4 Writer::CreateClassDataSection(dex::u4 section_offset) {
|
|
dex_->class_data.SetOffset(section_offset);
|
|
|
|
const auto& classes = dex_ir_->classes;
|
|
for (size_t i = 0; i < classes.size(); ++i) {
|
|
auto ir_class = classes[i].get();
|
|
auto dex_class_def = &dex_->class_defs[i];
|
|
dex_class_def->class_data_off = WriteClassData(ir_class);
|
|
}
|
|
|
|
dex::u4 size = dex_->class_data.Seal(4);
|
|
return size;
|
|
}
|
|
|
|
// annotations_directory section
|
|
dex::u4 Writer::CreateAnnDirectoriesSection(dex::u4 section_offset) {
|
|
dex_->ann_directories.SetOffset(section_offset);
|
|
|
|
const auto& classes = dex_ir_->classes;
|
|
for (size_t i = 0; i < classes.size(); ++i) {
|
|
auto ir_class = classes[i].get();
|
|
auto dex_class_def = &dex_->class_defs[i];
|
|
dex_class_def->annotations_off = WriteClassAnnotations(ir_class);
|
|
}
|
|
|
|
return dex_->ann_directories.Seal(4);
|
|
}
|
|
|
|
// encoded_array_item section
|
|
dex::u4 Writer::CreateEncodedArrayItemSection(dex::u4 section_offset) {
|
|
dex_->encoded_arrays.SetOffset(section_offset);
|
|
|
|
const auto& classes = dex_ir_->classes;
|
|
for (size_t i = 0; i < classes.size(); ++i) {
|
|
auto ir_class = classes[i].get();
|
|
auto dex_class_def = &dex_->class_defs[i];
|
|
dex_class_def->static_values_off = WriteClassStaticValues(ir_class);
|
|
}
|
|
|
|
return dex_->encoded_arrays.Seal(4);
|
|
}
|
|
|
|
// "type_id_item"
|
|
void Writer::FillTypes() {
|
|
const auto& types = dex_ir_->types;
|
|
for (size_t i = 0; i < types.size(); ++i) {
|
|
const auto& ir_type = types[i];
|
|
auto dexTypeId = &dex_->type_ids[i];
|
|
// CONSIDER: an automatic index check would be nice
|
|
dexTypeId->descriptor_idx = ir_type->descriptor->index;
|
|
}
|
|
}
|
|
|
|
// "proto_id_item"
|
|
void Writer::FillProtos() {
|
|
const auto& protos = dex_ir_->protos;
|
|
for (size_t i = 0; i < protos.size(); ++i) {
|
|
const auto& irProto = protos[i];
|
|
auto dexProtoId = &dex_->proto_ids[i];
|
|
dexProtoId->shorty_idx = irProto->shorty->index;
|
|
dexProtoId->return_type_idx = irProto->return_type->index;
|
|
dexProtoId->parameters_off = FilePointer(irProto->param_types);
|
|
}
|
|
}
|
|
|
|
// "field_id_item"
|
|
void Writer::FillFields() {
|
|
const auto& fields = dex_ir_->fields;
|
|
for (size_t i = 0; i < fields.size(); ++i) {
|
|
const auto& ir_field = fields[i];
|
|
auto dexFieldId = &dex_->field_ids[i];
|
|
dexFieldId->class_idx = ir_field->parent->index;
|
|
dexFieldId->type_idx = ir_field->type->index;
|
|
dexFieldId->name_idx = ir_field->name->index;
|
|
}
|
|
}
|
|
|
|
// "method_id_item"
|
|
void Writer::FillMethods() {
|
|
const auto& methods = dex_ir_->methods;
|
|
for (size_t i = 0; i < methods.size(); ++i) {
|
|
const auto& ir_method = methods[i];
|
|
auto dexMethodId = &dex_->method_ids[i];
|
|
dexMethodId->class_idx = ir_method->parent->index;
|
|
dexMethodId->proto_idx = ir_method->prototype->index;
|
|
dexMethodId->name_idx = ir_method->name->index;
|
|
}
|
|
}
|
|
|
|
// "class_def_item"
|
|
void Writer::FillClassDefs() {
|
|
const auto& classes = dex_ir_->classes;
|
|
for (size_t i = 0; i < classes.size(); ++i) {
|
|
auto ir_class = classes[i].get();
|
|
auto dex_class_def = &dex_->class_defs[i];
|
|
dex_class_def->class_idx = ir_class->type->index;
|
|
dex_class_def->access_flags = ir_class->access_flags;
|
|
dex_class_def->superclass_idx = OptIndex(ir_class->super_class);
|
|
dex_class_def->source_file_idx = OptIndex(ir_class->source_file);
|
|
dex_class_def->interfaces_off = FilePointer(ir_class->interfaces);
|
|
|
|
// NOTE: we already set some offsets when we created the
|
|
// corresponding .dex section:
|
|
//
|
|
// ->annotations_off
|
|
// ->class_data_off
|
|
// ->static_values_off
|
|
}
|
|
}
|
|
|
|
// "type_list"
|
|
dex::u4 Writer::WriteTypeList(const std::vector<ir::Type*>& types) {
|
|
if (types.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& data = dex_->type_lists;
|
|
dex::u4 offset = data.AddItem(4);
|
|
data.Push<dex::u4>(types.size());
|
|
for (auto ir_type : types) {
|
|
data.Push<dex::u2>(ir_type->index);
|
|
}
|
|
return data.AbsoluteOffset(offset);
|
|
}
|
|
|
|
// "annotation_item"
|
|
dex::u4 Writer::WriteAnnotationItem(const ir::Annotation* ir_annotation) {
|
|
SLICER_CHECK(ir_annotation->visibility != dex::kVisibilityEncoded);
|
|
|
|
auto& data = dex_->ann_items;
|
|
dex::u4 offset = data.AddItem();
|
|
data.Push<dex::u1>(ir_annotation->visibility);
|
|
WriteAnnotation(ir_annotation, data);
|
|
return data.AbsoluteOffset(offset);
|
|
}
|
|
|
|
// "annotation_set_item"
|
|
dex::u4 Writer::WriteAnnotationSet(const ir::AnnotationSet* ir_annotation_set) {
|
|
SLICER_CHECK(ir_annotation_set != nullptr);
|
|
|
|
const auto& annotations = ir_annotation_set->annotations;
|
|
|
|
auto& data = dex_->ann_sets;
|
|
dex::u4 offset = data.AddItem(4);
|
|
data.Push<dex::u4>(annotations.size());
|
|
for (auto ir_annotation : annotations) {
|
|
data.Push<dex::u4>(FilePointer(ir_annotation));
|
|
}
|
|
return data.AbsoluteOffset(offset);
|
|
}
|
|
|
|
// "annotation_set_ref_list"
|
|
dex::u4 Writer::WriteAnnotationSetRefList(
|
|
const ir::AnnotationSetRefList* ir_annotation_set_ref_list) {
|
|
SLICER_CHECK(ir_annotation_set_ref_list != nullptr);
|
|
|
|
const auto& annotations = ir_annotation_set_ref_list->annotations;
|
|
|
|
auto& data = dex_->ann_set_ref_lists;
|
|
dex::u4 offset = data.AddItem(4);
|
|
data.Push<dex::u4>(annotations.size());
|
|
for (auto ir_annotation_set : annotations) {
|
|
data.Push<dex::u4>(FilePointer(ir_annotation_set));
|
|
}
|
|
return data.AbsoluteOffset(offset);
|
|
}
|
|
|
|
// "annotations_directory_item"
|
|
dex::u4 Writer::WriteClassAnnotations(const ir::Class* ir_class) {
|
|
if (ir_class->annotations == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
auto ir_annotations = ir_class->annotations;
|
|
|
|
dex::u4& offset = node_offset_[ir_annotations];
|
|
if (offset == 0) {
|
|
// in order to write a contiguous "annotations_directory_item" we do two
|
|
// passes :
|
|
// 1. write the field/method/params annotations content
|
|
// 2. write the directory (including the field/method/params arrays)
|
|
std::vector<dex::FieldAnnotationsItem> dex_field_annotations;
|
|
std::vector<dex::MethodAnnotationsItem> dex_method_annotations;
|
|
std::vector<dex::ParameterAnnotationsItem> dex_param_annotations;
|
|
|
|
for (auto irItem : ir_annotations->field_annotations) {
|
|
dex::FieldAnnotationsItem dex_item = {};
|
|
dex_item.field_idx = irItem->field_decl->index;
|
|
dex_item.annotations_off = FilePointer(irItem->annotations);
|
|
dex_field_annotations.push_back(dex_item);
|
|
}
|
|
|
|
for (auto irItem : ir_annotations->method_annotations) {
|
|
dex::MethodAnnotationsItem dex_item = {};
|
|
dex_item.method_idx = irItem->method_decl->index;
|
|
dex_item.annotations_off = FilePointer(irItem->annotations);
|
|
dex_method_annotations.push_back(dex_item);
|
|
}
|
|
|
|
for (auto irItem : ir_annotations->param_annotations) {
|
|
dex::ParameterAnnotationsItem dex_item = {};
|
|
dex_item.method_idx = irItem->method_decl->index;
|
|
dex_item.annotations_off = FilePointer(irItem->annotations);
|
|
dex_param_annotations.push_back(dex_item);
|
|
}
|
|
|
|
dex::u4 class_annotations_offset =
|
|
FilePointer(ir_annotations->class_annotation);
|
|
|
|
// now that the annotations content is written,
|
|
// we can write down the "annotations_directory_item"
|
|
dex::AnnotationsDirectoryItem dex_annotations = {};
|
|
dex_annotations.class_annotations_off = class_annotations_offset;
|
|
dex_annotations.fields_size = ir_annotations->field_annotations.size();
|
|
dex_annotations.methods_size = ir_annotations->method_annotations.size();
|
|
dex_annotations.parameters_size = ir_annotations->param_annotations.size();
|
|
|
|
auto& data = dex_->ann_directories;
|
|
offset = data.AddItem(4);
|
|
data.Push(dex_annotations);
|
|
data.Push(dex_field_annotations);
|
|
data.Push(dex_method_annotations);
|
|
data.Push(dex_param_annotations);
|
|
offset = data.AbsoluteOffset(offset);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
// "debug_info_item"
|
|
dex::u4 Writer::WriteDebugInfo(const ir::DebugInfo* ir_debug_info) {
|
|
SLICER_CHECK(ir_debug_info != nullptr);
|
|
|
|
auto& data = dex_->debug_info;
|
|
dex::u4 offset = data.AddItem();
|
|
|
|
// debug info "header"
|
|
data.PushULeb128(ir_debug_info->line_start);
|
|
data.PushULeb128(ir_debug_info->param_names.size());
|
|
for (auto ir_string : ir_debug_info->param_names) {
|
|
data.PushULeb128(OptIndex(ir_string) + 1);
|
|
}
|
|
|
|
// debug info "state machine bytecodes"
|
|
const dex::u1* src = ir_debug_info->data.ptr<dex::u1>();
|
|
dex::u1 opcode = 0;
|
|
while ((opcode = *src++) != dex::DBG_END_SEQUENCE) {
|
|
data.Push<dex::u1>(opcode);
|
|
|
|
switch (opcode) {
|
|
case dex::DBG_ADVANCE_PC:
|
|
// addr_diff
|
|
data.PushULeb128(dex::ReadULeb128(&src));
|
|
break;
|
|
|
|
case dex::DBG_ADVANCE_LINE:
|
|
// line_diff
|
|
data.PushSLeb128(dex::ReadSLeb128(&src));
|
|
break;
|
|
|
|
case dex::DBG_START_LOCAL: {
|
|
// register_num
|
|
data.PushULeb128(dex::ReadULeb128(&src));
|
|
|
|
dex::u4 name_index = dex::ReadULeb128(&src) - 1;
|
|
data.PushULeb128(MapStringIndex(name_index) + 1);
|
|
|
|
dex::u4 type_index = dex::ReadULeb128(&src) - 1;
|
|
data.PushULeb128(MapTypeIndex(type_index) + 1);
|
|
} break;
|
|
|
|
case dex::DBG_START_LOCAL_EXTENDED: {
|
|
// register_num
|
|
data.PushULeb128(dex::ReadULeb128(&src));
|
|
|
|
dex::u4 name_index = dex::ReadULeb128(&src) - 1;
|
|
data.PushULeb128(MapStringIndex(name_index) + 1);
|
|
|
|
dex::u4 type_index = dex::ReadULeb128(&src) - 1;
|
|
data.PushULeb128(MapTypeIndex(type_index) + 1);
|
|
|
|
dex::u4 sig_index = dex::ReadULeb128(&src) - 1;
|
|
data.PushULeb128(MapStringIndex(sig_index) + 1);
|
|
} break;
|
|
|
|
case dex::DBG_END_LOCAL:
|
|
case dex::DBG_RESTART_LOCAL:
|
|
// register_num
|
|
data.PushULeb128(dex::ReadULeb128(&src));
|
|
break;
|
|
|
|
case dex::DBG_SET_FILE: {
|
|
dex::u4 name_index = dex::ReadULeb128(&src) - 1;
|
|
data.PushULeb128(MapStringIndex(name_index) + 1);
|
|
} break;
|
|
}
|
|
}
|
|
data.Push<dex::u1>(dex::DBG_END_SEQUENCE);
|
|
|
|
return data.AbsoluteOffset(offset);
|
|
}
|
|
|
|
// instruction[] array
|
|
void Writer::WriteInstructions(slicer::ArrayView<const dex::u2> instructions) {
|
|
SLICER_CHECK(!instructions.empty());
|
|
|
|
auto offset = dex_->code.Push(instructions);
|
|
dex::u2* ptr = dex_->code.ptr<dex::u2>(offset);
|
|
dex::u2* const end = ptr + instructions.size();
|
|
|
|
// relocate the instructions
|
|
while (ptr < end) {
|
|
auto opcode = dex::OpcodeFromBytecode(*ptr);
|
|
dex::u2* idx = &ptr[1];
|
|
|
|
size_t idx_size = 0;
|
|
switch (dex::GetFormatFromOpcode(opcode)) {
|
|
case dex::k20bc:
|
|
case dex::k21c:
|
|
case dex::k35c:
|
|
case dex::k3rc:
|
|
case dex::k22c:
|
|
idx_size = 2;
|
|
break;
|
|
|
|
case dex::k31c:
|
|
idx_size = 4;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (dex::GetIndexTypeFromOpcode(opcode)) {
|
|
case dex::kIndexStringRef:
|
|
if (idx_size == 4) {
|
|
dex::u4 new_index = MapStringIndex(ReadU4(idx));
|
|
SLICER_CHECK(new_index != dex::kNoIndex);
|
|
WriteU4(idx, new_index);
|
|
} else {
|
|
SLICER_CHECK(idx_size == 2);
|
|
dex::u4 new_index = MapStringIndex(*idx);
|
|
SLICER_CHECK(new_index != dex::kNoIndex);
|
|
SLICER_CHECK(dex::u2(new_index) == new_index);
|
|
*idx = dex::u2(new_index);
|
|
}
|
|
break;
|
|
|
|
case dex::kIndexTypeRef: {
|
|
SLICER_CHECK(idx_size == 2);
|
|
dex::u4 new_index = MapTypeIndex(*idx);
|
|
SLICER_CHECK(new_index != dex::kNoIndex);
|
|
SLICER_CHECK(dex::u2(new_index) == new_index);
|
|
*idx = dex::u2(new_index);
|
|
} break;
|
|
|
|
case dex::kIndexFieldRef: {
|
|
SLICER_CHECK(idx_size == 2);
|
|
dex::u4 new_index = MapFieldIndex(*idx);
|
|
SLICER_CHECK(new_index != dex::kNoIndex);
|
|
SLICER_CHECK(dex::u2(new_index) == new_index);
|
|
*idx = dex::u2(new_index);
|
|
} break;
|
|
|
|
case dex::kIndexMethodRef: {
|
|
SLICER_CHECK(idx_size == 2);
|
|
dex::u4 new_index = MapMethodIndex(*idx);
|
|
SLICER_CHECK(new_index != dex::kNoIndex);
|
|
SLICER_CHECK(dex::u2(new_index) == new_index);
|
|
*idx = dex::u2(new_index);
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
auto isize = dex::GetWidthFromBytecode(ptr);
|
|
SLICER_CHECK(isize > 0);
|
|
ptr += isize;
|
|
}
|
|
SLICER_CHECK(ptr == end);
|
|
}
|
|
|
|
// "try_item[] + encoded_catch_handler_list"
|
|
void Writer::WriteTryBlocks(const ir::Code* irCode) {
|
|
SLICER_CHECK(!irCode->try_blocks.empty());
|
|
|
|
// use a temporary buffer to build the "encoded_catch_handler_list"
|
|
slicer::Buffer handlers_list;
|
|
auto original_list = irCode->catch_handlers.ptr<dex::u1>();
|
|
auto ptr = original_list;
|
|
std::map<dex::u2, dex::u2> handlers_offset_map;
|
|
|
|
dex::u4 handlers_count = dex::ReadULeb128(&ptr);
|
|
handlers_list.PushULeb128(handlers_count);
|
|
|
|
for (dex::u4 handler_index = 0; handler_index < handlers_count; ++handler_index) {
|
|
// track the oldOffset/newOffset mapping
|
|
handlers_offset_map[ptr - original_list] = handlers_list.size();
|
|
|
|
// parse each "encoded_catch_handler"
|
|
int catch_count = dex::ReadSLeb128(&ptr);
|
|
handlers_list.PushSLeb128(catch_count);
|
|
|
|
for (int catch_index = 0; catch_index < std::abs(catch_count); ++catch_index) {
|
|
// type_idx
|
|
dex::u4 type_index = dex::ReadULeb128(&ptr);
|
|
handlers_list.PushULeb128(MapTypeIndex(type_index));
|
|
|
|
// address
|
|
handlers_list.PushULeb128(dex::ReadULeb128(&ptr));
|
|
}
|
|
|
|
if (catch_count < 1) {
|
|
// catch_all_addr
|
|
handlers_list.PushULeb128(dex::ReadULeb128(&ptr));
|
|
}
|
|
}
|
|
|
|
handlers_list.Seal(1);
|
|
|
|
// now write everything (try_item[] and encoded_catch_handler_list)
|
|
auto& data = dex_->code;
|
|
dex::u4 tries_offset = data.size();
|
|
data.Push(irCode->try_blocks);
|
|
data.Push(handlers_list);
|
|
|
|
// finally relocate the offsets to handlers
|
|
for (dex::TryBlock& dex_try : slicer::ArrayView<dex::TryBlock>(
|
|
data.ptr<dex::TryBlock>(tries_offset), irCode->try_blocks.size())) {
|
|
dex::u2 new_Handler_offset = handlers_offset_map[dex_try.handler_off];
|
|
SLICER_CHECK(new_Handler_offset != 0);
|
|
dex_try.handler_off = new_Handler_offset;
|
|
}
|
|
}
|
|
|
|
// "code_item"
|
|
dex::u4 Writer::WriteCode(const ir::Code* irCode) {
|
|
SLICER_CHECK(irCode != nullptr);
|
|
|
|
dex::Code dex_code = {};
|
|
dex_code.registers_size = irCode->registers;
|
|
dex_code.ins_size = irCode->ins_count;
|
|
dex_code.outs_size = irCode->outs_count;
|
|
dex_code.tries_size = irCode->try_blocks.size();
|
|
dex_code.debug_info_off = FilePointer(irCode->debug_info);
|
|
dex_code.insns_size = irCode->instructions.size();
|
|
|
|
auto& data = dex_->code;
|
|
dex::u4 offset = data.AddItem(4);
|
|
data.Push(&dex_code, offsetof(dex::Code, insns));
|
|
WriteInstructions(irCode->instructions);
|
|
if (!irCode->try_blocks.empty()) {
|
|
data.Align(4);
|
|
WriteTryBlocks(irCode);
|
|
}
|
|
return data.AbsoluteOffset(offset);
|
|
}
|
|
|
|
// "encoded_field"
|
|
void Writer::WriteEncodedField(const ir::EncodedField* ir_encoded_field,
|
|
dex::u4* base_index) {
|
|
dex::u4 index_delta = ir_encoded_field->decl->index;
|
|
SLICER_CHECK(index_delta != dex::kNoIndex);
|
|
if (*base_index != dex::kNoIndex) {
|
|
SLICER_CHECK(index_delta > *base_index);
|
|
index_delta = index_delta - *base_index;
|
|
}
|
|
*base_index = ir_encoded_field->decl->index;
|
|
|
|
auto& data = dex_->class_data;
|
|
data.PushULeb128(index_delta);
|
|
data.PushULeb128(ir_encoded_field->access_flags);
|
|
}
|
|
|
|
// "encoded_method"
|
|
void Writer::WriteEncodedMethod(const ir::EncodedMethod* ir_encoded_method,
|
|
dex::u4* base_index) {
|
|
dex::u4 index_delta = ir_encoded_method->decl->index;
|
|
SLICER_CHECK(index_delta != dex::kNoIndex);
|
|
if (*base_index != dex::kNoIndex) {
|
|
SLICER_CHECK(index_delta > *base_index);
|
|
index_delta = index_delta - *base_index;
|
|
}
|
|
*base_index = ir_encoded_method->decl->index;
|
|
|
|
dex::u4 code_offset = FilePointer(ir_encoded_method->code);
|
|
|
|
auto& data = dex_->class_data;
|
|
data.PushULeb128(index_delta);
|
|
data.PushULeb128(ir_encoded_method->access_flags);
|
|
data.PushULeb128(code_offset);
|
|
}
|
|
|
|
// "class_data_item"
|
|
dex::u4 Writer::WriteClassData(const ir::Class* ir_class) {
|
|
if (ir_class->static_fields.empty() && ir_class->instance_fields.empty() &&
|
|
ir_class->direct_methods.empty() && ir_class->virtual_methods.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
auto& data = dex_->class_data;
|
|
dex::u4 offset = data.AddItem();
|
|
|
|
data.PushULeb128(ir_class->static_fields.size());
|
|
data.PushULeb128(ir_class->instance_fields.size());
|
|
data.PushULeb128(ir_class->direct_methods.size());
|
|
data.PushULeb128(ir_class->virtual_methods.size());
|
|
|
|
dex::u4 base_index = dex::kNoIndex;
|
|
for (auto ir_encoded_field : ir_class->static_fields) {
|
|
WriteEncodedField(ir_encoded_field, &base_index);
|
|
}
|
|
|
|
base_index = dex::kNoIndex;
|
|
for (auto ir_encoded_field : ir_class->instance_fields) {
|
|
WriteEncodedField(ir_encoded_field, &base_index);
|
|
}
|
|
|
|
base_index = dex::kNoIndex;
|
|
for (auto ir_encoded_method : ir_class->direct_methods) {
|
|
WriteEncodedMethod(ir_encoded_method, &base_index);
|
|
}
|
|
|
|
base_index = dex::kNoIndex;
|
|
for (auto ir_encoded_method : ir_class->virtual_methods) {
|
|
WriteEncodedMethod(ir_encoded_method, &base_index);
|
|
}
|
|
|
|
return data.AbsoluteOffset(offset);
|
|
}
|
|
|
|
// "encoded_array_item"
|
|
dex::u4 Writer::WriteClassStaticValues(const ir::Class* ir_class) {
|
|
if (ir_class->static_init == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
dex::u4& offset = node_offset_[ir_class->static_init];
|
|
if (offset == 0) {
|
|
auto& data = dex_->encoded_arrays;
|
|
offset = data.AddItem();
|
|
WriteEncodedArray(ir_class->static_init, data);
|
|
offset = data.AbsoluteOffset(offset);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
// Map an index from the original .dex to the new index
|
|
dex::u4 Writer::MapStringIndex(dex::u4 index) const {
|
|
if (index != dex::kNoIndex) {
|
|
index = dex_ir_->strings_map.at(index)->index;
|
|
SLICER_CHECK(index != dex::kNoIndex);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Map an index from the original .dex to the new index
|
|
dex::u4 Writer::MapTypeIndex(dex::u4 index) const {
|
|
if (index != dex::kNoIndex) {
|
|
index = dex_ir_->types_map.at(index)->index;
|
|
SLICER_CHECK(index != dex::kNoIndex);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Map an index from the original .dex to the new index
|
|
dex::u4 Writer::MapFieldIndex(dex::u4 index) const {
|
|
if (index != dex::kNoIndex) {
|
|
index = dex_ir_->fields_map.at(index)->index;
|
|
SLICER_CHECK(index != dex::kNoIndex);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Map an index from the original .dex to the new index
|
|
dex::u4 Writer::MapMethodIndex(dex::u4 index) const {
|
|
if (index != dex::kNoIndex) {
|
|
index = dex_ir_->methods_map.at(index)->index;
|
|
SLICER_CHECK(index != dex::kNoIndex);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// .dex IR node to file pointer (absolute offset)
|
|
dex::u4 Writer::FilePointer(const ir::Node* ir_node) const {
|
|
if (ir_node == nullptr) {
|
|
return 0;
|
|
}
|
|
auto it = node_offset_.find(ir_node);
|
|
SLICER_CHECK(it != node_offset_.end());
|
|
dex::u4 offset = it->second;
|
|
SLICER_CHECK(offset > 0);
|
|
return offset;
|
|
}
|
|
|
|
} // namespace dex
|