/* * Matches the pattern: * sget-TYPE vA, FIELD * return-TYPE vA */ DexField* trivial_get_static_field_wrapper(DexMethod* m) { DexCode* code = m->get_code(); if (code == nullptr) return nullptr; auto& insns = code->get_instructions(); auto it = insns.begin(); if (!is_sget((*it)->opcode())) return nullptr; auto sget = static_cast<DexOpcodeField*>(*it); uint16_t sget_dest = sget->dest(); ++it; if (!is_return_value((*it)->opcode())) return nullptr; uint16_t ret_reg = (*it)->src(0); if (ret_reg != sget_dest) return nullptr; ++it; if (it != insns.end()) return nullptr; // Check to make sure we have a concrete field reference. auto def = resolve_field(sget->field(), FieldSearch::Static); if (def == nullptr) return nullptr; if (!def->is_concrete()) { return nullptr; } return def; }
/* * Matches the pattern: * invoke-(direct|static) {vA, ..., vB} METHOD * ( move-result-TYPE v0 * return-TYPE v0 * | return-void ) */ DexMethod* trivial_method_wrapper(DexMethod* m) { DexCode* code = m->get_code(); if (code == nullptr) return nullptr; auto& insns = code->get_instructions(); auto it = insns.begin(); bool is_direct = (*it)->opcode() == OPCODE_INVOKE_DIRECT; bool is_static = (*it)->opcode() == OPCODE_INVOKE_STATIC; if (!is_direct && !is_static) return nullptr; auto invoke = static_cast<DexOpcodeMethod*>(*it); auto method = invoke->get_method(); if (is_static) { method = resolve_static(type_class(method->get_class()), method->get_name(), method->get_proto()); } if (!method) return nullptr; if (!method->is_concrete()) return nullptr; auto collision = find_collision_excepting(method, method->get_name(), method->get_proto(), type_class(method->get_class()), true, true); if (collision) { TRACE(SYNT, 5, "wrapper blocked:%s\nwrapped method:%s\nconflicts with:%s\n", SHOW(m), SHOW(method), SHOW(collision)); return nullptr; } if (!passes_args_through(invoke, code)) return nullptr; if (++it == insns.end()) return nullptr; if (is_move_result((*it)->opcode())) { if (++it == insns.end()) return nullptr; if (!is_return_value((*it)->opcode())) return nullptr; if (++it != insns.end()) return nullptr; // exception handling code return method; } if ((*it)->opcode() == OPCODE_RETURN_VOID) { if (++it != insns.end()) return nullptr; // exception handling code return method; } return nullptr; }
/* * Matches the pattern: * invoke-direct {v0...} Lclass;.<init> * return-void */ DexMethod* trivial_ctor_wrapper(DexMethod* m) { DexCode* code = m->get_code(); if (code == nullptr) return nullptr; auto& insns = code->get_instructions(); auto it = insns.begin(); if ((*it)->opcode() != OPCODE_INVOKE_DIRECT) { TRACE(SYNT, 5, "Rejecting, not direct: %s\n", SHOW(m)); return nullptr; } auto invoke = static_cast<DexOpcodeMethod*>(*it); if (!passes_args_through(invoke, code, 1)) { TRACE(SYNT, 5, "Rejecting, not passthrough: %s\n", SHOW(m)); return nullptr; } if (++it == insns.end()) return nullptr; if ((*it)->opcode() != OPCODE_RETURN_VOID) return nullptr; auto method = invoke->get_method(); if (!method->is_concrete() || !is_constructor(method)) return nullptr; return method; }
void exclude_referenced_bridgee(DexMethod* code_method, const DexCode& code) { auto const& insts = code.get_instructions(); for (auto inst : insts) { if (!is_invoke(inst->opcode())) continue; auto method = static_cast<DexOpcodeMethod*>(inst)->get_method(); auto range = m_potential_bridgee_refs.equal_range( MethodRef(method->get_class(), method->get_name(), method->get_proto())); for (auto it = range.first; it != range.second; ++it) { auto referenced_bridge = it->second; // Don't count the bridge itself if (referenced_bridge == code_method) continue; TRACE(BRIDGE, 5, "Rejecting, reference `%s.%s.%s' in `%s' blocks `%s'\n", SHOW(method->get_class()), SHOW(method->get_name()), SHOW(method->get_proto()), SHOW(code_method), SHOW(referenced_bridge)); m_bridges_to_bridgees.erase(referenced_bridge); } } }
void replace_wrappers(DexMethod* caller_method, DexCode& code, WrapperMethods& ssms) { std::vector<std::tuple<DexOpcodeMethod*, DexInstruction*, DexField*>> getter_calls; std::vector<std::pair<DexOpcodeMethod*, DexMethod*>> wrapper_calls; std::vector<std::pair<DexOpcodeMethod*, DexMethod*>> wrapped_calls; std::vector<std::pair<DexOpcodeMethod*, DexMethod*>> ctor_calls; TRACE(SYNT, 4, "Replacing wrappers in %s\n", SHOW(caller_method)); auto& insns = code.get_instructions(); for (auto it = insns.begin(); it != insns.end(); ++it) { auto insn = *it; if (insn->opcode() == OPCODE_INVOKE_STATIC) { // Replace calls to static getters and wrappers auto const meth_insn = static_cast<DexOpcodeMethod*>(insn); auto const callee = resolve_method( meth_insn->get_method(), MethodSearch::Static); if (callee == nullptr) continue; auto const found_get = ssms.getters.find(callee); if (found_get != ssms.getters.end()) { auto const move_result = *(std::next(it)); if (!is_move_result(move_result->opcode())) { ssms.keepers.emplace(callee); continue; } auto field = found_get->second; getter_calls.emplace_back(meth_insn, move_result, field); continue; } auto const found_wrap = ssms.wrappers.find(callee); if (found_wrap != ssms.wrappers.end()) { auto method = found_wrap->second; wrapper_calls.emplace_back(meth_insn, method); continue; } always_assert_log( ssms.wrapped.find(callee) == ssms.wrapped.end(), "caller: %s\ncallee: %s\ninsn: %s\n", SHOW(caller_method), SHOW(callee), SHOW(insn)); ssms.keepers.emplace(callee); } else if (insn->opcode() == OPCODE_INVOKE_DIRECT || insn->opcode() == OPCODE_INVOKE_DIRECT_RANGE) { auto const meth_insn = static_cast<DexOpcodeMethod*>(insn); auto const callee = resolve_method(meth_insn->get_method(), MethodSearch::Direct); if (callee == nullptr) continue; auto const found_get = ssms.getters.find(callee); if (found_get != ssms.getters.end()) { auto const move_result = *(std::next(it)); if (!is_move_result(move_result->opcode())) { ssms.keepers.emplace(callee); continue; } auto field = found_get->second; getter_calls.emplace_back(meth_insn, move_result, field); continue; } auto const found_wrap = ssms.wrappers.find(callee); if (found_wrap != ssms.wrappers.end()) { auto method = found_wrap->second; wrapper_calls.emplace_back(meth_insn, method); continue; } auto const found_wrappee = ssms.wrapped.find(callee); if (found_wrappee != ssms.wrapped.end()) { auto wrapper = found_wrappee->second.first; wrapped_calls.emplace_back(meth_insn, wrapper); continue; } auto const found_ctor = ssms.ctors.find(callee); if (found_ctor != ssms.ctors.end()) { auto ctor = found_ctor->second; ctor_calls.emplace_back(meth_insn, ctor); continue; } } else if (insn->opcode() == OPCODE_INVOKE_STATIC_RANGE) { // We don't handle this yet, but it's not hard. auto const meth_insn = static_cast<DexOpcodeMethod*>(insn); auto const callee = resolve_method( meth_insn->get_method(), MethodSearch::Static); if (callee == nullptr) continue; ssms.keepers.emplace(callee); } } // Prune out wrappers that are invalid due to naming conflicts. std::unordered_set<DexMethod*> bad_wrappees; std::unordered_multimap<DexMethod*, DexMethod*> wrappees_to_wrappers; for (auto wpair : wrapper_calls) { auto call_inst = wpair.first; auto wrapper = call_inst->get_method(); auto wrappee = wpair.second; wrappees_to_wrappers.emplace(wrappee, wrapper); if (!can_update_wrappee(wrappee, wrapper)) { bad_wrappees.emplace(wrappee); } } for (auto wpair : wrapped_calls) { auto call_inst = wpair.first; auto wrapper = wpair.second; auto wrappee = call_inst->get_method(); wrappees_to_wrappers.emplace(wrappee, wrapper); if (!can_update_wrappee(wrappee, wrapper)) { bad_wrappees.emplace(wrappee); } } for (auto bw : bad_wrappees) { auto range = wrappees_to_wrappers.equal_range(bw); for (auto it = range.first; it != range.second; ++it) { ssms.keepers.emplace(it->second); } } wrapper_calls.erase( std::remove_if( wrapper_calls.begin(), wrapper_calls.end(), [&](const std::pair<DexOpcodeMethod*, DexMethod*>& p) { return bad_wrappees.count(p.second); }), wrapper_calls.end() ); wrapped_calls.erase( std::remove_if( wrapped_calls.begin(), wrapped_calls.end(), [&](const std::pair<DexOpcodeMethod*, DexMethod*>& p) { return bad_wrappees.count(p.first->get_method()); }), wrapped_calls.end() ); // Fix up everything left. if (getter_calls.empty() && wrapper_calls.empty() && ctor_calls.empty() && wrapped_calls.empty()) { return; } MethodTransformer transform(caller_method); for (auto g : getter_calls) { using std::get; if (!replace_getter_wrapper(transform, get<0>(g), get<1>(g), get<2>(g))) { ssms.keepers.emplace(get<0>(g)->get_method()); } } for (auto wpair : wrapper_calls) { auto call_inst = wpair.first; auto wrapper = call_inst->get_method(); auto wrappee = wpair.second; auto success = replace_method_wrapper( transform, call_inst, wrapper, wrappee, ssms); if (!success) { ssms.keepers.emplace(wpair.first->get_method()); } } for (auto wpair : wrapped_calls) { auto call_inst = wpair.first; auto wrapper = wpair.second; auto wrappee = call_inst->get_method(); auto success = replace_method_wrapper( transform, call_inst, wrapper, wrappee, ssms); if (!success) { ssms.keepers.emplace(wpair.first->get_method()); } } for (auto cpair : ctor_calls) { replace_ctor_wrapper(transform, cpair.first, cpair.second); } }
void DexLoader::gather_input_stats(dex_stats_t* stats, const dex_header* dh) { stats->num_types += dh->type_ids_size; stats->num_classes += dh->class_defs_size; stats->num_method_refs += dh->method_ids_size; stats->num_field_refs += dh->field_ids_size; stats->num_strings += dh->string_ids_size; stats->num_protos += dh->proto_ids_size; stats->num_bytes += dh->file_size; std::unordered_set<DexEncodedValueArray, boost::hash<DexEncodedValueArray>> enc_arrays; std::set<DexTypeList*, dextypelists_comparator> type_lists; std::unordered_set<uint32_t> anno_offsets; for (uint32_t cidx = 0; cidx < dh->class_defs_size; ++cidx) { auto* clz = m_classes->at(cidx); auto* class_def = &m_class_defs[cidx]; auto anno_off = class_def->annotations_off; if (anno_off) { const dex_annotations_directory_item* anno_dir = (const dex_annotations_directory_item*)m_idx->get_uint_data(anno_off); auto class_anno_off = anno_dir->class_annotations_off; if (class_anno_off) { const uint32_t* anno_data = m_idx->get_uint_data(class_anno_off); uint32_t count = *anno_data++; for (uint32_t aidx = 0; aidx < count; ++aidx) { anno_offsets.insert(anno_data[aidx]); } } const uint32_t* anno_data = (uint32_t*)(anno_dir + 1); for (uint32_t fidx = 0; fidx < anno_dir->fields_size; ++fidx) { anno_data++; anno_offsets.insert(*anno_data++); } for (uint32_t midx = 0; midx < anno_dir->methods_size; ++midx) { anno_data++; anno_offsets.insert(*anno_data++); } for (uint32_t pidx = 0; pidx < anno_dir->parameters_size; ++pidx) { anno_data++; uint32_t xrefoff = *anno_data++; if (xrefoff != 0) { const uint32_t* annoxref = m_idx->get_uint_data(xrefoff); uint32_t count = *annoxref++; for (uint32_t j = 0; j < count; j++) { uint32_t off = annoxref[j]; anno_offsets.insert(off); } } } } auto* interfaces_type_list = clz->get_interfaces(); type_lists.insert(interfaces_type_list); std::unique_ptr<DexEncodedValueArray> deva(clz->get_static_values()); if (deva) { if (!enc_arrays.count(*deva)) { enc_arrays.emplace(std::move(*deva.release())); stats->num_static_values++; } } stats->num_fields += clz->get_ifields().size() + clz->get_sfields().size(); stats->num_methods += clz->get_vmethods().size() + clz->get_dmethods().size(); for (auto* meth : clz->get_vmethods()) { DexCode* code = meth->get_dex_code(); if (code) { stats->num_instructions += code->get_instructions().size(); } } for (auto* meth : clz->get_dmethods()) { DexCode* code = meth->get_dex_code(); if (code) { stats->num_instructions += code->get_instructions().size(); } } } for (uint32_t meth_idx = 0; meth_idx < dh->method_ids_size; ++meth_idx) { auto* meth = m_idx->get_methodidx(meth_idx); DexProto* proto = meth->get_proto(); type_lists.insert(proto->get_args()); } stats->num_annotations += anno_offsets.size(); stats->num_type_lists += type_lists.size(); }