tools/redexdump/DumpTables.cpp (731 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "DexDebugInstruction.h" #include "DexEncoding.h" #include "Formatters.h" #include "PrintUtil.h" #include "RedexDump.h" #include "utils/Unicode.h" #include <sstream> #include <string.h> #include <string> #include <vector> /** * Return a proto string in the form * [shorty] (argTypes)returnType */ static std::string get_proto(ddump_data* rd, uint32_t idx, bool with_shorty = true) { std::ostringstream ss; dex_proto_id* proto = rd->dex_proto_ids + idx; if (with_shorty) { ss << dex_string_by_idx(rd, proto->shortyidx) << " "; } ss << "("; if (proto->param_off) { uint32_t* tl = (uint32_t*)(rd->dexmmap + proto->param_off); int count = (int)*tl++; uint16_t* types = (uint16_t*)tl; for (int i = 0; i < count; i++) { if (i != 0) ss << " "; ss << dex_string_by_type_idx(rd, *types++); } } ss << ")" << dex_string_by_type_idx(rd, proto->rtypeidx); return ss.str(); } /** * Return a field in the form: * class field_type field_name */ static std::string get_field(ddump_data* rd, uint32_t idx) { std::ostringstream ss; dex_field_id* field = rd->dex_field_ids + idx; ss << dex_string_by_type_idx(rd, field->classidx) << " " << dex_string_by_type_idx(rd, field->typeidx) << " " << dex_string_by_idx(rd, field->nameidx); return ss.str(); } /** * Return a method in the form: * class method_proto_no_shorty method_name */ static std::string get_method(ddump_data* rd, uint32_t idx) { std::ostringstream ss; dex_method_id* method = rd->dex_method_ids + idx; ss << dex_string_by_type_idx(rd, method->classidx) << " " << dex_string_by_idx(rd, method->nameidx) << " " << get_proto(rd, method->protoidx, false); return ss.str(); } /** * Return flags. */ static std::string get_flags(uint32_t flags, bool cls = true, bool method = false) { std::ostringstream ss; if (flags & DexAccessFlags::ACC_PUBLIC) { ss << "public "; } if (flags & DexAccessFlags::ACC_PRIVATE) { ss << "private "; } if (flags & DexAccessFlags::ACC_PROTECTED) { ss << "protected "; } if (flags & DexAccessFlags::ACC_STATIC) { ss << "static "; } if (flags & DexAccessFlags::ACC_FINAL) { ss << "final "; } if (flags & DexAccessFlags::ACC_INTERFACE) { ss << "interface "; } else if (flags & DexAccessFlags::ACC_ABSTRACT) { ss << "abstract "; } if (flags & DexAccessFlags::ACC_ENUM) { ss << "enum "; } if (flags & DexAccessFlags::ACC_SYNCHRONIZED) { ss << "synchronized "; } if (flags & DexAccessFlags::ACC_VOLATILE) { ss << (cls || method ? "bridge " : "volatile "); } if (flags & DexAccessFlags::ACC_NATIVE) { ss << "native "; } if (flags & DexAccessFlags::ACC_TRANSIENT) { ss << (method ? "varargs " : "transient "); } if (flags & DexAccessFlags::ACC_SYNTHETIC) { ss << "synthetic "; } return ss.str(); } /** * Return a class def in the form: * flags class 'extends' superclass ['implements' interfaces] * [file: <filename>, anno: annotation_off, data: class_data_off, static * values: static_value_off] */ static std::string get_class_def(ddump_data* rd, uint32_t idx, bool metadata = true) { std::ostringstream ss; dex_class_def* cls_def = rd->dex_class_defs + idx; ss << get_flags(cls_def->access_flags) << dex_string_by_type_idx(rd, cls_def->typeidx); if (cls_def->super_idx != DEX_NO_INDEX) { ss << " extends " << dex_string_by_type_idx(rd, cls_def->super_idx); } if (cls_def->interfaces_off) { ss << " implements "; auto interfaces = (uint32_t*)(rd->dexmmap + cls_def->interfaces_off); auto size = *interfaces++; auto types = (uint16_t*)interfaces; for (uint32_t i = 0; i < size; i++) { ss << dex_string_by_type_idx(rd, *types++); } } if (metadata) { ss << "\n\t"; if (cls_def->source_file_idx != DEX_NO_INDEX) { ss << "file: " << dex_string_by_idx(rd, cls_def->source_file_idx); } else { ss << "<no_file>"; } if (cls_def->annotations_off) { ss << ", anno: " << "0x" << std::hex << cls_def->annotations_off; } ss << ", data: " << "0x" << std::hex << cls_def->class_data_offset; if (cls_def->static_values_off) { ss << ", static values: " << "0x" << std::hex << cls_def->static_values_off; } } return ss.str(); } /** * Return a class data items in the form: * class * sfields: <count> * 'field' * ... * ifields: <count> * 'field' * ... * dmethods: <count> * 'method' * ... * vmethods: <count> * 'method' * ... */ static std::string get_class_data_item(ddump_data* rd, uint32_t idx) { std::ostringstream ss; const dex_class_def* class_defs = (dex_class_def*)(rd->dexmmap + rd->dexh->class_defs_off) + idx; auto cls_off = class_defs->class_data_offset; if (!cls_off) return ""; ss << dex_string_by_type_idx(rd, class_defs->typeidx) << "\n"; const uint8_t* class_data = reinterpret_cast<const uint8_t*>(rd->dexmmap + cls_off); uint32_t sfield_count = read_uleb128(&class_data); uint32_t ifield_count = read_uleb128(&class_data); uint32_t dmethod_count = read_uleb128(&class_data); uint32_t vmethod_count = read_uleb128(&class_data); ss << "sfields: " << sfield_count << "\n"; uint32_t fidx = 0; for (uint32_t i = 0; i < sfield_count; i++) { fidx += read_uleb128(&class_data); auto flags = read_uleb128(&class_data); ss << get_flags(flags, false) << get_field(rd, fidx) << "\n"; } ss << "ifields: " << ifield_count << "\n"; fidx = 0; for (uint32_t i = 0; i < ifield_count; i++) { fidx += read_uleb128(&class_data); auto flags = read_uleb128(&class_data); ss << get_flags(flags, false) << get_field(rd, fidx) << "\n"; } ss << "dmethods: " << dmethod_count << "\n"; uint32_t meth_idx = 0; for (uint32_t i = 0; i < dmethod_count; i++) { meth_idx += read_uleb128(&class_data); auto flags = read_uleb128(&class_data); auto code = read_uleb128(&class_data); ss << get_flags(flags, false, true) << "- " << get_method(rd, meth_idx) << " - " << "0x" << std::hex << code << "\n"; } ss << "vmethods: " << vmethod_count << "\n"; meth_idx = 0; for (uint32_t i = 0; i < vmethod_count; i++) { meth_idx += read_uleb128(&class_data); auto flags = read_uleb128(&class_data); auto code = read_uleb128(&class_data); ss << get_flags(flags, false, true) << "- " << get_method(rd, meth_idx) << " - " << "0x" << std::hex << code << "\n"; } return ss.str(); } static std::string get_code_item(dex_code_item** pcode_item) { dex_code_item* code_item = *pcode_item; std::ostringstream ss; ss << "registers_size: " << code_item->registers_size << ", " << "ins_size: " << code_item->ins_size << ", " << "outs_size: " << code_item->outs_size << ", " << "tries_size: " << code_item->tries_size << ", " << "debug_info_off: 0x" << std::hex << code_item->debug_info_off << ", " << "insns_size: " << std::dec << code_item->insns_size << "\n"; const uint16_t* dexptr = (const uint16_t*)(code_item + 1) + code_item->insns_size; *pcode_item = (dex_code_item*)dexptr; if (code_item->tries_size) { if (code_item->insns_size & 1) dexptr++; // padding before tries const dex_tries_item* tries = (const dex_tries_item*)dexptr; const uint8_t* handlers = (const uint8_t*)(tries + code_item->tries_size); for (auto i = 0; i < code_item->tries_size; i++) { ss << "\tstart_addr: " << tries->start_addr << ", insn_count: " << tries->insn_count << ", handler_off: " << tries->handler_off << "\n"; if (tries->handler_off) { const uint8_t* cur_handler = handlers + tries->handler_off; auto size = read_sleb128(&cur_handler); ss << "\t\t\thandlers size: " << size << ", "; for (auto j = 0; j < abs(size); j++) { auto type = read_uleb128(&cur_handler); auto addr = read_uleb128(&cur_handler); ss << "(type_idx: " << type << ", addr: " << addr << ") "; } if (size <= 0) { ss << ", catch_all_addr: " << read_uleb128(&cur_handler); } ss << "\n"; if (cur_handler > (uint8_t*)*pcode_item) { *pcode_item = (dex_code_item*)cur_handler; } } tries++; } } *pcode_item = (dex_code_item*)(((uintptr_t)*pcode_item + 3) & ~3); return ss.str(); } class DexDebugInstructionReader { protected: virtual void handle_advance_pc(DexDebugItemOpcode op, uint32_t arg) { return handle_default(op); } virtual void handle_advance_line(DexDebugItemOpcode op, int32_t arg) { return handle_default(op); } virtual void handle_start_local(DexDebugItemOpcode op, uint32_t arg1, uint32_t arg2, uint32_t arg3) { return handle_default(op); } virtual void handle_start_local_extended(DexDebugItemOpcode op, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) { return handle_default(op); } virtual void handle_end_local(DexDebugItemOpcode op, uint32_t arg1) { return handle_default(op); } virtual void handle_restart_local(DexDebugItemOpcode op, uint32_t arg1) { return handle_default(op); } virtual void handle_set_file(DexDebugItemOpcode op, uint32_t arg) { return handle_default(op); } virtual void handle_set_prologue_end(DexDebugItemOpcode op) { return handle_default(op); } virtual void handle_set_epilogue_begin(DexDebugItemOpcode op) { return handle_default(op); } virtual void handle_default(DexDebugItemOpcode op) = 0; public: virtual ~DexDebugInstructionReader() {} void read(const uint8_t*& data) { uint32_t u1, u2, u3, u4; int32_t s1; while (true) { DexDebugItemOpcode op = (DexDebugItemOpcode)*data++; switch (op) { case DBG_END_SEQUENCE: return; case DBG_ADVANCE_PC: u1 = read_uleb128(&data); handle_advance_pc(op, u1); break; case DBG_ADVANCE_LINE: s1 = read_sleb128(&data); handle_advance_line(op, s1); break; case DBG_START_LOCAL: u1 = read_uleb128(&data); u2 = read_uleb128(&data); u3 = read_uleb128(&data); handle_start_local(op, u1, u2, u3); break; case DBG_START_LOCAL_EXTENDED: u1 = read_uleb128(&data); u2 = read_uleb128(&data); u3 = read_uleb128(&data); u4 = read_uleb128(&data); handle_start_local_extended(op, u1, u2, u3, u4); break; case DBG_END_LOCAL: u1 = read_uleb128(&data); handle_end_local(op, u1); break; case DBG_RESTART_LOCAL: u1 = read_uleb128(&data); handle_restart_local(op, u1); break; case DBG_SET_PROLOGUE_END: handle_set_prologue_end(op); break; case DBG_SET_EPILOGUE_BEGIN: handle_set_epilogue_begin(op); break; case DBG_SET_FILE: u1 = read_uleb128(&data); handle_set_file(op, u1); break; default: // special opcodes handle_default(op); break; }; } } }; uint32_t count_debug_instructions(const uint8_t*& encdata) { struct DexDebugInstructionCounter : public DexDebugInstructionReader { int sum; void handle_default(DexDebugItemOpcode op) override { sum++; } }; auto counter = DexDebugInstructionCounter(); counter.read(encdata); return counter.sum; } void disassemble_debug(ddump_data* rd, uint32_t offset) { redump("Disassembling debug opcodes at 0x%x\n", offset); auto data = (const uint8_t*)(rd->dexmmap + offset); auto line_start = read_uleb128(&data); auto parameters_size = read_uleb128(&data); redump("line_start: %d, parameters_size: %d\n", line_start, parameters_size); for (unsigned int i = 0; i < parameters_size; ++i) { read_uleb128(&data); } struct DexDebugInstructionPrinter : public DexDebugInstructionReader { void handle_advance_pc(DexDebugItemOpcode op, uint32_t arg) override { redump("DBG_ADVANCE_PC %u\n", arg); } void handle_advance_line(DexDebugItemOpcode op, int32_t arg) override { redump("DBG_ADVANCE_LINE %d\n", arg); } void handle_start_local(DexDebugItemOpcode op, uint32_t reg, uint32_t name_idx, uint32_t type_idx) override { redump("DBG_START_LOCAL %d\n", reg); } void handle_start_local_extended(DexDebugItemOpcode op, uint32_t reg, uint32_t, uint32_t, uint32_t) override { redump("DBG_START_LOCAL_EXTENDED %d\n", reg); } void handle_end_local(DexDebugItemOpcode op, uint32_t reg) override { redump("DBG_END_LOCAL %d\n", reg); } void handle_restart_local(DexDebugItemOpcode op, uint32_t reg) override { redump("DBG_RESTART_LOCAL %d\n", reg); } void handle_set_file(DexDebugItemOpcode op, uint32_t arg) override { redump("DBG_SET_FILE\n"); } void handle_set_prologue_end(DexDebugItemOpcode op) override { redump("DBG_SET_PROLOGUE_END\n"); } void handle_set_epilogue_begin(DexDebugItemOpcode op) override { redump("DBG_SET_EPILOGUE_BEGIN\n"); } void handle_default(DexDebugItemOpcode op) override { redump("DBG_SPECIAL 0x%02x\n", (uint32_t)op); } }; DexDebugInstructionPrinter().read(data); } static std::string get_debug_item(const uint8_t** pdebug_item) { auto line_start = read_uleb128(pdebug_item); auto parameters_size = read_uleb128(pdebug_item); for (unsigned int i = 0; i < parameters_size; ++i) { read_uleb128(pdebug_item); } auto num_opcodes = count_debug_instructions(*pdebug_item); std::ostringstream ss; ss << "line_start: " << line_start << ", " << "parameters_size: " << parameters_size << ", " << "num_opcodes: " << num_opcodes << "\n"; return ss.str(); } // Dump a string_data_item (i.e., an entry in the string data // section), advancing POS_INOUT over the item. static const char string_data_header[] = "u16len [contents]"; static void dump_string_data_item(const uint8_t** pos_inout) { const uint8_t* pos = *pos_inout; uint32_t utf16_code_point_count = read_uleb128(&pos); // Not byte count! size_t utf8_length = strlen((char*)pos); std::string cleansed_data; const char* string_to_print; if (raw) { // Output whatever bytes we have string_to_print = (char*)pos; } else if (escape) { // Escape non-printable characters. cleansed_data.reserve(utf8_length); // Avoid some reallocation. for (size_t i = 0; i < utf8_length; i++) { if (isprint(pos[i])) { cleansed_data.push_back(pos[i]); } else { char buf[5]; sprintf(buf, "\\x%02x", pos[i]); cleansed_data.append(buf); } } string_to_print = cleansed_data.c_str(); } else { // Translate to UTF-8; strip control characters std::vector<char32_t> code_points; const char* enc_pos = (char*)pos; uint32_t cp; while ((cp = mutf8_next_code_point(enc_pos))) { if (cp < ' ' || cp == 255 /* DEL */) { cp = '.'; } code_points.push_back(cp); } ssize_t nr_utf8_bytes = utf32_to_utf8_length(&code_points[0], code_points.size()); if (nr_utf8_bytes < 0 && utf8_length == 0) { cleansed_data = ""; } else if (nr_utf8_bytes < 0) { cleansed_data = "{invalid encoding?}"; } else { cleansed_data.resize(nr_utf8_bytes); utf32_to_utf8(&code_points[0], code_points.size(), &cleansed_data[0]); } string_to_print = cleansed_data.c_str(); } redump("%03u [%s]\n", (unsigned)utf16_code_point_count, string_to_print); *pos_inout = pos + utf8_length + 1; } void dump_stringdata(ddump_data* rd, bool print_headers) { if (print_headers) { redump("\nRAW STRING DATA\n"); redump("%s\n", string_data_header); } dex_map_item* string_data = get_dex_map_item(rd, TYPE_STRING_DATA_ITEM); if (string_data == nullptr) { redump("!!!! No string data section found\n"); return; } // Recall that for dex_map_item, size is really a count. Each item // in the string data section is a ULEB128 length followed by a // NUL-terminated modified-UTF-8 encoded string. const uint8_t* str_data_ptr = (uint8_t*)(rd->dexmmap) + string_data->offset; for (uint32_t i = 0; i < string_data->size; ++i) { dump_string_data_item(&str_data_ptr); } } // // Table dumpers... // void dump_strings(ddump_data* rd, bool print_headers) { auto offset = rd->dexh->string_ids_off; const uint8_t* str_id_ptr = (uint8_t*)(rd->dexmmap) + offset; auto size = rd->dexh->string_ids_size; auto length = 0; uint32_t tmp_str_id_off = 0; for (uint32_t i = 0; i < size; ++i) { const uint8_t* str_data_ptr = (uint8_t*)(rd->dexmmap) + tmp_str_id_off; length += strlen((char*)str_data_ptr); tmp_str_id_off += 4; } if (print_headers) { redump("\nSTRING IDS TABLE: %d %d\n", size, length); redump("%s\n", string_data_header); } for (uint32_t i = 0; i < size; ++i) { auto str_data_off = *(uint32_t*)str_id_ptr; str_id_ptr += 4; const uint8_t* str_data_ptr = (uint8_t*)(rd->dexmmap) + str_data_off; dump_string_data_item(&str_data_ptr); } } void dump_types(ddump_data* rd) { auto offset = rd->dexh->type_ids_off; const uint32_t* type_id_ptr = (uint32_t*)(rd->dexmmap + offset); const uint32_t* type_ptr = type_id_ptr; auto size = rd->dexh->type_ids_size; redump("\nTYPE IDS TABLE: %d\n", size); redump("[type_ids_off] type name\n"); for (uint32_t i = 0; i < size; ++i) { auto name_off = *(uint32_t*)type_ptr; redump((uint32_t)(type_ptr - type_id_ptr), "%s\n", dex_string_by_idx(rd, name_off)); type_ptr++; } } void dump_protos(ddump_data* rd, bool print_headers) { auto size = rd->dexh->proto_ids_size; if (print_headers) { redump("\nPROTO IDS TABLE: %d\n", size); redump("[proto_ids_off] shorty proto\n"); } for (uint32_t i = 0; i < size; i++) { redump(i, "%s\n", get_proto(rd, i).c_str()); } } void dump_fields(ddump_data* rd, bool print_headers) { auto size = rd->dexh->field_ids_size; if (print_headers) { redump("\nFIELD IDS TABLE: %d\n", size); redump("[field_ids_off] class type name\n"); } for (uint32_t i = 0; i < size; i++) { redump(i, "%s\n", get_field(rd, i).c_str()); } } void dump_methods(ddump_data* rd, bool print_headers) { uint32_t size = rd->dexh->method_ids_size; if (print_headers) { redump("\nMETHOD IDS TABLE: %d\n", size); redump("[method_ids_off] class name proto_no_shorty\n"); } for (uint32_t i = 0; i < size; i++) { redump(i, "%s\n", get_method(rd, i).c_str()); } } void dump_clsdefs(ddump_data* rd, bool print_headers) { auto size = rd->dexh->class_defs_size; if (print_headers) { redump("\nCLASS DEFS TABLE: %d\n", size); redump( "[class_def_off] flags class 'extends' superclass" "['implements' interfaces]\n" "\t[file: <filename>] [anno: annotation_off] data: class_data_off " "[static values: static_value_off]\n"); } for (uint32_t i = 0; i < size; i++) { redump(i, "%s\n", get_class_def(rd, i).c_str()); } } void dump_clsdata(ddump_data* rd, bool print_headers) { auto size = rd->dexh->class_defs_size; if (print_headers) { redump("\nCLASS DATA TABLE: %d\n", size); redump( "[cls_data_off] class\n" "sfields: <count> followed by sfields\n" "ifields: <count> followed by ifields\n" "dmethods: <count> followed by dmethods\n" "vmethods: <count> followed by vmethods\n"); } for (uint32_t i = 0; i < size; i++) { const dex_class_def* class_defs = (dex_class_def*)(rd->dexmmap + rd->dexh->class_defs_off) + i; redump(class_defs->class_data_offset, "%s", get_class_data_item(rd, i).c_str()); } } void dump_callsites(ddump_data* rd, bool print_headers) { auto map = rd->dexmmap + rd->dexh->map_off; const dex_map_list* map_list = reinterpret_cast<const dex_map_list*>(map); const dex_callsite_id* callsites = nullptr; int count = 0; for (uint32_t i = 0; i < map_list->size; i++) { const auto& item = map_list->items[i]; if (item.type == TYPE_CALL_SITE_ID_ITEM) { count = item.size; callsites = reinterpret_cast<dex_callsite_id*>(rd->dexmmap + item.offset); } } // TODO(T58569493) - emit full call site info if (print_headers) { redump("\nCALL SITE TABLE: %d\n", count); for (int i = 0; i < count; ++i) { const uint8_t* ptr = reinterpret_cast<uint8_t*>(rd->dexmmap + callsites[i].callsite_off); redump("[%0x] offset:0x%0x %s\n", i, callsites[i].callsite_off, format_callsite(rd, &ptr).c_str()); } } } void dump_methodhandles(ddump_data* rd, bool print_headers) { auto map = rd->dexmmap + rd->dexh->map_off; const dex_map_list* map_list = reinterpret_cast<const dex_map_list*>(map); const dex_methodhandle_id* methodhandles = nullptr; int count = 0; for (uint32_t i = 0; i < map_list->size; i++) { const auto& item = map_list->items[i]; if (item.type == TYPE_METHOD_HANDLE_ITEM) { count = item.size; methodhandles = reinterpret_cast<dex_methodhandle_id*>(rd->dexmmap + item.offset); } } // TODO(T58569493) - emit full method handle info if (print_headers) { redump("\nMETHOD HANDLE TABLE: %d\n", count); for (int i = 0; i < count; ++i) { redump("[0x%x] field_or_method_id:0x%x type:%s\n", i, methodhandles[i].field_or_method_id, format_method_handle_type( (MethodHandleType)methodhandles[i].method_handle_type) .c_str()); } } } static void dump_code_items(ddump_data* rd, dex_code_item* code_items, uint32_t size) { for (uint32_t i = 0; i < size; i++) { auto offset = reinterpret_cast<char*>(code_items) - rd->dexmmap; redump(offset, "%s", get_code_item(&code_items).c_str()); } } static void dump_debug_items(ddump_data* rd, const uint8_t* debug_items, uint32_t size) { for (uint32_t i = 0; i < size; i++) { auto offset = reinterpret_cast<const char*>(debug_items) - rd->dexmmap; redump(offset, "%s", get_debug_item(&debug_items).c_str()); } } void dump_code(ddump_data* rd) { unsigned count; dex_map_item* maps; get_dex_map_items(rd, &count, &maps); redump("\nCODE ITEM: %d\n", count); redump( "[code_item_off] meth_id " "registers_size: <count>," "ins_size: <count>," "outs_size: <count>," "tries_size: <count>," "debug_info_off: <addr>," "insns_size: <count>\n"); for (unsigned i = 0; i < count; i++) { if (maps[i].type == TYPE_CODE_ITEM) { auto code_items = (dex_code_item*)(rd->dexmmap + maps[i].offset); dump_code_items(rd, code_items, maps[i].size); return; } } } static void dump_annotation_set_item(ddump_data* rd, uint32_t* aset) { uint32_t count = *aset++; if (count == 0) { redump("Empty Aset\n"); } for (uint32_t i = 0; i < count; i++) { const uint8_t* aitem = (uint8_t*)(rd->dexmmap + aset[i]); redump(format_annotation_item(rd, &aitem).c_str()); } } static void dump_class_annotations(ddump_data* rd, dex_class_def* df) { char* cname = dex_string_by_type_idx(rd, df->typeidx); if (df->annotations_off) { uint32_t* diritem = (uint32_t*)(rd->dexmmap + df->annotations_off); uint32_t aclass, afields, amethods, aparams; aclass = *diritem++; afields = *diritem++; amethods = *diritem++; aparams = *diritem++; redump(df->typeidx, "Class '%s':\n", cname); if (aclass) { redump(" Class Annotations:\n"); dump_annotation_set_item(rd, (uint32_t*)(rd->dexmmap + aclass)); } while (afields--) { uint32_t fidx = *diritem++; uint32_t aoff = *diritem++; const char* ftype = dex_string_by_type_idx(rd, rd->dex_field_ids[fidx].typeidx); const char* fname = dex_string_by_idx(rd, rd->dex_field_ids[fidx].nameidx); redump(" Field '%s', Type '%s' Annotations:\n", ftype, fname); dump_annotation_set_item(rd, (uint32_t*)(rd->dexmmap + aoff)); } while (amethods--) { uint32_t midx = *diritem++; uint32_t aoff = *diritem++; const char* mtype = dex_string_by_type_idx(rd, rd->dex_method_ids[midx].classidx); const char* mname = dex_string_by_idx(rd, rd->dex_method_ids[midx].nameidx); redump(" Method '%s', Type '%s' Annotations:\n", mtype, mname); dump_annotation_set_item(rd, (uint32_t*)(rd->dexmmap + aoff)); } while (aparams--) { uint32_t midx = *diritem++; uint32_t asrefoff = *diritem++; uint32_t* asref = (uint32_t*)(rd->dexmmap + asrefoff); uint32_t asrefsize = *asref++; const char* mtype = dex_string_by_type_idx(rd, rd->dex_method_ids[midx].classidx); const char* mname = dex_string_by_idx(rd, rd->dex_method_ids[midx].nameidx); redump(" Method '%s', Type '%s' Parameter Annotations:\n", mtype, mname); int param = 0; while (asrefsize--) { uint32_t aoff = *asref++; redump("%d: ", param++); dump_annotation_set_item(rd, (uint32_t*)(rd->dexmmap + aoff)); } } } } void dump_anno(ddump_data* rd) { for (uint32_t i = 0; i < rd->dexh->class_defs_size; i++) { dump_class_annotations(rd, &rd->dex_class_defs[i]); } } void dump_debug(ddump_data* rd) { unsigned count; dex_map_item* maps; get_dex_map_items(rd, &count, &maps); for (unsigned i = 0; i < count; i++) { if (maps[i].type == TYPE_DEBUG_INFO_ITEM) { auto debug_items = (uint8_t*)(rd->dexmmap + maps[i].offset); dump_debug_items(rd, debug_items, maps[i].size); return; } } } void dump_enarr(ddump_data* rd) { unsigned count; dex_map_item* maps; get_dex_map_items(rd, &count, &maps); for (unsigned i = 0; i < count; i++) { if (maps[i].type == TYPE_ENCODED_ARRAY_ITEM) { auto ptr = (const uint8_t*)(rd->dexmmap + maps[i].offset); for (unsigned j = 0; j < maps[i].size; j++) { redump((uint32_t)(ptr - (const uint8_t*)rd->dexmmap), ": "); uint32_t earray_size = read_uleb128(&ptr); for (uint32_t k = 0; k < earray_size; k++) { redump("%s", format_encoded_value(rd, &ptr).c_str()); } redump("\n"); } return; } } }