/** * Check collisions in method definition. */ EscapeReason OptimizationImpl::check_method_collision(DexType* intf, SingleImplData& data) { for (auto method : data.methoddefs) { auto meth_it = new_methods.find(method); if (meth_it != new_methods.end()) method = meth_it->second; auto proto = get_or_make_proto(intf, data.cls, method->get_proto()); assert(proto != method->get_proto()); DexMethod* collision = find_collision(method->get_name(), proto, type_class(method->get_class()), method->is_virtual()); if (collision) return SIG_COLLISION; } return NO_ESCAPE; }
/** * Rewrite all methods sigs by creating new ones. Remove old methods and push * the new to the proper class method list. */ void OptimizationImpl::set_method_defs(DexType* intf, SingleImplData& data) { for (auto method : data.methoddefs) { TRACE(INTF, 3, "(MDEF) %s\n", SHOW(method)); auto meth = method; auto meth_it = new_methods.find(meth); if (meth_it != new_methods.end()) { // if a method rewrite existed it must not be on a single impl // given we have escaped to next pass all collisions always_assert(meth_it->second->is_def()); meth = static_cast<DexMethod*>(meth_it->second); TRACE(INTF, 4, "(MDEF) current: %s\n", SHOW(meth)); assert(!single_impls->is_single_impl(method->get_class()) || single_impls->is_escaped(method->get_class())); assert(!single_impls->is_single_impl(meth->get_class()) || single_impls->is_escaped(meth->get_class())); } auto proto = get_or_make_proto(intf, data.cls, meth->get_proto()); TRACE(INTF, 5, "(MDEF) make_method: %s.%s - %s => %s\n", SHOW(meth->get_class()), SHOW(meth->get_name()), SHOW(meth->get_proto()), SHOW(proto)); assert(proto != meth->get_proto()); auto new_meth = static_cast<DexMethod*>(DexMethod::make_method( meth->get_class(), meth->get_name(), proto)); // new_meth may have already existed in RedexContext, so // we need to make sure it isn't concrete. // TODO: this is horrible. After we remove methods, we shouldn't // have these zombies lying around. new_meth->clear_annotations(); new_meth->make_non_concrete(); new_meth->set_deobfuscated_name(meth->get_deobfuscated_name()); new_meth->rstate = meth->rstate; assert(new_meth != meth); setup_method(meth, new_meth); new_methods[method] = new_meth; auto owner = type_class(new_meth->get_class()); owner->remove_method(meth); owner->add_method(new_meth); TRACE(INTF, 3, "(MDEF)\t=> %s\n", SHOW(new_meth)); } }
/** * Rewrite all method refs. */ void OptimizationImpl::set_method_refs(DexType* intf, SingleImplData& data) { for (auto mrefit : data.methodrefs) { auto method = mrefit.first; TRACE(INTF, 3, "(MREF) %s\n", SHOW(method)); auto new_meth = method; auto meth_it = new_methods.find(new_meth); if (meth_it != new_methods.end()) { // if a method rewrite existed it must not be on a single impl // given we have escaped to "next pass" all collisions new_meth = meth_it->second; TRACE(INTF, 4, "current: %s\n", SHOW(new_meth)); assert(!single_impls->is_single_impl(method->get_class()) || single_impls->is_escaped(method->get_class())); assert(!single_impls->is_single_impl(new_meth->get_class()) || single_impls->is_escaped(new_meth->get_class())); } // next 2 lines will generate no new proto or method when the ref matches // a def, which should be very common. // However it does not seem too much of an overkill and more // straightforward that any logic that manages that. // Both creation of protos or methods is "interned" and so the same // method would be returned if there is nothing to change and create. // Still we need to change the opcodes where we assert the new method // to go in the opcode is in fact different than what was there. auto proto = get_or_make_proto(intf, data.cls, new_meth->get_proto()); auto created_meth = DexMethod::make_method( new_meth->get_class(), new_meth->get_name(), proto); if (created_meth->is_def() && method->is_def()) { static_cast<DexMethod*>(created_meth)->set_deobfuscated_name( static_cast<DexMethod*>(method)->get_deobfuscated_name()); static_cast<DexMethod*>(created_meth)->rstate = static_cast<DexMethod*>(method)->rstate; } new_methods[method] = created_meth; TRACE(INTF, 3, "(MREF)\t=> %s\n", SHOW(created_meth)); for (auto opcode : mrefit.second) { TRACE(INTF, 3, "(MREF) %s\n", SHOW(opcode)); assert(opcode->get_method() != created_meth); opcode->set_method(created_meth); TRACE(INTF, 3, "(MREF) \t=> %s\n", SHOW(opcode)); } } }
/** * Rewrite all methods sigs by creating new ones. Remove old methods and push * the new to the proper class method list. */ void OptimizationImpl::rewrite_method_defs(DexType* intf, SingleImplData& data) { for (auto method : data.methoddefs) { TRACE(INTF, 3, "(MDEF) %s\n", SHOW(method)); auto meth = method; auto meth_it = new_methods.find(meth); if (meth_it != new_methods.end()) { // if a method rewrite existed it must not be on a single impl // given we have escaped to next pass all collisions meth = meth_it->second; TRACE(INTF, 4, "(MDEF) current: %s\n", SHOW(meth)); assert(!single_impls->is_single_impl(method->get_class()) || single_impls->is_escaped(method->get_class())); assert(!single_impls->is_single_impl(meth->get_class()) || single_impls->is_escaped(meth->get_class())); } auto proto = get_or_make_proto(intf, data.cls, meth->get_proto()); TRACE(INTF, 5, "(MDEF) make_method: %s.%s - %s => %s\n", SHOW(meth->get_class()), SHOW(meth->get_name()), SHOW(meth->get_proto()), SHOW(proto)); assert(proto != meth->get_proto()); auto new_meth = DexMethod::make_method( meth->get_class(), meth->get_name(), proto); assert(new_meth != meth); setup_method(meth, new_meth); new_methods[method] = new_meth; auto owner = type_class(new_meth->get_class()); owner->remove_method(meth); owner->add_method(new_meth); TRACE(INTF, 3, "(MDEF)\t=> %s\n", SHOW(new_meth)); } }