1*f0dffb02SXin Li /*
2*f0dffb02SXin Li * Copyright (C) 2017 The Android Open Source Project
3*f0dffb02SXin Li *
4*f0dffb02SXin Li * Licensed under the Apache License, Version 2.0 (the "License");
5*f0dffb02SXin Li * you may not use this file except in compliance with the License.
6*f0dffb02SXin Li * You may obtain a copy of the License at
7*f0dffb02SXin Li *
8*f0dffb02SXin Li * http://www.apache.org/licenses/LICENSE-2.0
9*f0dffb02SXin Li *
10*f0dffb02SXin Li * Unless required by applicable law or agreed to in writing, software
11*f0dffb02SXin Li * distributed under the License is distributed on an "AS IS" BASIS,
12*f0dffb02SXin Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*f0dffb02SXin Li * See the License for the specific language governing permissions and
14*f0dffb02SXin Li * limitations under the License.
15*f0dffb02SXin Li */
16*f0dffb02SXin Li
17*f0dffb02SXin Li #include "slicer/instrumentation.h"
18*f0dffb02SXin Li
19*f0dffb02SXin Li #include "slicer/dex_ir_builder.h"
20*f0dffb02SXin Li
21*f0dffb02SXin Li #include <iomanip>
22*f0dffb02SXin Li #include <sstream>
23*f0dffb02SXin Li
24*f0dffb02SXin Li namespace slicer {
25*f0dffb02SXin Li
26*f0dffb02SXin Li namespace {
27*f0dffb02SXin Li
28*f0dffb02SXin Li struct BytecodeConvertingVisitor : public lir::Visitor {
29*f0dffb02SXin Li lir::Bytecode* out = nullptr;
Visitslicer::__anon8c2a62890111::BytecodeConvertingVisitor30*f0dffb02SXin Li bool Visit(lir::Bytecode* bytecode) {
31*f0dffb02SXin Li out = bytecode;
32*f0dffb02SXin Li return true;
33*f0dffb02SXin Li }
34*f0dffb02SXin Li };
35*f0dffb02SXin Li
BoxValue(lir::Bytecode * bytecode,lir::CodeIr * code_ir,ir::Type * type,dex::u4 src_reg,dex::u4 dst_reg)36*f0dffb02SXin Li void BoxValue(lir::Bytecode* bytecode,
37*f0dffb02SXin Li lir::CodeIr* code_ir,
38*f0dffb02SXin Li ir::Type* type,
39*f0dffb02SXin Li dex::u4 src_reg,
40*f0dffb02SXin Li dex::u4 dst_reg) {
41*f0dffb02SXin Li bool is_wide = false;
42*f0dffb02SXin Li const char* boxed_type_name = nullptr;
43*f0dffb02SXin Li switch (*(type->descriptor)->c_str()) {
44*f0dffb02SXin Li case 'Z':
45*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Boolean;";
46*f0dffb02SXin Li break;
47*f0dffb02SXin Li case 'B':
48*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Byte;";
49*f0dffb02SXin Li break;
50*f0dffb02SXin Li case 'C':
51*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Character;";
52*f0dffb02SXin Li break;
53*f0dffb02SXin Li case 'S':
54*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Short;";
55*f0dffb02SXin Li break;
56*f0dffb02SXin Li case 'I':
57*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Integer;";
58*f0dffb02SXin Li break;
59*f0dffb02SXin Li case 'J':
60*f0dffb02SXin Li is_wide = true;
61*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Long;";
62*f0dffb02SXin Li break;
63*f0dffb02SXin Li case 'F':
64*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Float;";
65*f0dffb02SXin Li break;
66*f0dffb02SXin Li case 'D':
67*f0dffb02SXin Li is_wide = true;
68*f0dffb02SXin Li boxed_type_name = "Ljava/lang/Double;";
69*f0dffb02SXin Li break;
70*f0dffb02SXin Li }
71*f0dffb02SXin Li SLICER_CHECK_NE(boxed_type_name, nullptr);
72*f0dffb02SXin Li
73*f0dffb02SXin Li ir::Builder builder(code_ir->dex_ir);
74*f0dffb02SXin Li std::vector<ir::Type*> param_types;
75*f0dffb02SXin Li param_types.push_back(type);
76*f0dffb02SXin Li
77*f0dffb02SXin Li auto boxed_type = builder.GetType(boxed_type_name);
78*f0dffb02SXin Li auto ir_proto = builder.GetProto(boxed_type, builder.GetTypeList(param_types));
79*f0dffb02SXin Li
80*f0dffb02SXin Li auto ir_method_decl = builder.GetMethodDecl(
81*f0dffb02SXin Li builder.GetAsciiString("valueOf"), ir_proto, boxed_type);
82*f0dffb02SXin Li
83*f0dffb02SXin Li auto boxing_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
84*f0dffb02SXin Li
85*f0dffb02SXin Li auto args = code_ir->Alloc<lir::VRegRange>(src_reg, 1 + is_wide);
86*f0dffb02SXin Li auto boxing_invoke = code_ir->Alloc<lir::Bytecode>();
87*f0dffb02SXin Li boxing_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
88*f0dffb02SXin Li boxing_invoke->operands.push_back(args);
89*f0dffb02SXin Li boxing_invoke->operands.push_back(boxing_method);
90*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, boxing_invoke);
91*f0dffb02SXin Li
92*f0dffb02SXin Li auto move_result = code_ir->Alloc<lir::Bytecode>();
93*f0dffb02SXin Li move_result->opcode = dex::OP_MOVE_RESULT_OBJECT;
94*f0dffb02SXin Li move_result->operands.push_back(code_ir->Alloc<lir::VReg>(dst_reg));
95*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, move_result);
96*f0dffb02SXin Li }
97*f0dffb02SXin Li
MethodLabel(ir::EncodedMethod * ir_method)98*f0dffb02SXin Li std::string MethodLabel(ir::EncodedMethod* ir_method) {
99*f0dffb02SXin Li auto signature_str = ir_method->decl->prototype->Signature();
100*f0dffb02SXin Li return ir_method->decl->parent->Decl() + "->" + ir_method->decl->name->c_str() + signature_str;
101*f0dffb02SXin Li }
102*f0dffb02SXin Li
103*f0dffb02SXin Li } // namespace
104*f0dffb02SXin Li
Apply(lir::CodeIr * code_ir)105*f0dffb02SXin Li bool EntryHook::Apply(lir::CodeIr* code_ir) {
106*f0dffb02SXin Li lir::Bytecode* bytecode = nullptr;
107*f0dffb02SXin Li // find the first bytecode in the method body to insert the hook before it
108*f0dffb02SXin Li for (auto instr : code_ir->instructions) {
109*f0dffb02SXin Li BytecodeConvertingVisitor visitor;
110*f0dffb02SXin Li instr->Accept(&visitor);
111*f0dffb02SXin Li bytecode = visitor.out;
112*f0dffb02SXin Li if (bytecode != nullptr) {
113*f0dffb02SXin Li break;
114*f0dffb02SXin Li }
115*f0dffb02SXin Li }
116*f0dffb02SXin Li if (bytecode == nullptr) {
117*f0dffb02SXin Li return false;
118*f0dffb02SXin Li }
119*f0dffb02SXin Li if (tweak_ == Tweak::ArrayParams) {
120*f0dffb02SXin Li return InjectArrayParamsHook(code_ir, bytecode);
121*f0dffb02SXin Li }
122*f0dffb02SXin Li
123*f0dffb02SXin Li ir::Builder builder(code_ir->dex_ir);
124*f0dffb02SXin Li const auto ir_method = code_ir->ir_method;
125*f0dffb02SXin Li
126*f0dffb02SXin Li // construct the hook method declaration
127*f0dffb02SXin Li std::vector<ir::Type*> param_types;
128*f0dffb02SXin Li if ((ir_method->access_flags & dex::kAccStatic) == 0) {
129*f0dffb02SXin Li ir::Type* this_argument_type;
130*f0dffb02SXin Li switch (tweak_) {
131*f0dffb02SXin Li case Tweak::ThisAsObject:
132*f0dffb02SXin Li this_argument_type = builder.GetType("Ljava/lang/Object;");
133*f0dffb02SXin Li break;
134*f0dffb02SXin Li default:
135*f0dffb02SXin Li this_argument_type = ir_method->decl->parent;
136*f0dffb02SXin Li break;
137*f0dffb02SXin Li }
138*f0dffb02SXin Li param_types.push_back(this_argument_type);
139*f0dffb02SXin Li }
140*f0dffb02SXin Li if (ir_method->decl->prototype->param_types != nullptr) {
141*f0dffb02SXin Li const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
142*f0dffb02SXin Li param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
143*f0dffb02SXin Li }
144*f0dffb02SXin Li
145*f0dffb02SXin Li auto ir_proto = builder.GetProto(builder.GetType("V"),
146*f0dffb02SXin Li builder.GetTypeList(param_types));
147*f0dffb02SXin Li
148*f0dffb02SXin Li auto ir_method_decl = builder.GetMethodDecl(
149*f0dffb02SXin Li builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
150*f0dffb02SXin Li builder.GetType(hook_method_id_.class_descriptor));
151*f0dffb02SXin Li
152*f0dffb02SXin Li auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
153*f0dffb02SXin Li
154*f0dffb02SXin Li // argument registers
155*f0dffb02SXin Li auto regs = ir_method->code->registers;
156*f0dffb02SXin Li auto args_count = ir_method->code->ins_count;
157*f0dffb02SXin Li auto args = code_ir->Alloc<lir::VRegRange>(regs - args_count, args_count);
158*f0dffb02SXin Li
159*f0dffb02SXin Li // invoke hook bytecode
160*f0dffb02SXin Li auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
161*f0dffb02SXin Li hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
162*f0dffb02SXin Li hook_invoke->operands.push_back(args);
163*f0dffb02SXin Li hook_invoke->operands.push_back(hook_method);
164*f0dffb02SXin Li
165*f0dffb02SXin Li // insert the hook before the first bytecode in the method body
166*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, hook_invoke);
167*f0dffb02SXin Li return true;
168*f0dffb02SXin Li }
169*f0dffb02SXin Li
GenerateShiftParamsCode(lir::CodeIr * code_ir,lir::Instruction * position,dex::u4 shift)170*f0dffb02SXin Li void GenerateShiftParamsCode(lir::CodeIr* code_ir, lir::Instruction* position, dex::u4 shift) {
171*f0dffb02SXin Li const auto ir_method = code_ir->ir_method;
172*f0dffb02SXin Li
173*f0dffb02SXin Li // Since the goal is to relocate the registers when extra scratch registers are needed,
174*f0dffb02SXin Li // if there are no parameters this is a no-op.
175*f0dffb02SXin Li if (ir_method->code->ins_count == 0) {
176*f0dffb02SXin Li return;
177*f0dffb02SXin Li }
178*f0dffb02SXin Li
179*f0dffb02SXin Li // build a param list with the explicit "this" argument for non-static methods
180*f0dffb02SXin Li std::vector<ir::Type*> param_types;
181*f0dffb02SXin Li if ((ir_method->access_flags & dex::kAccStatic) == 0) {
182*f0dffb02SXin Li param_types.push_back(ir_method->decl->parent);
183*f0dffb02SXin Li }
184*f0dffb02SXin Li if (ir_method->decl->prototype->param_types != nullptr) {
185*f0dffb02SXin Li const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
186*f0dffb02SXin Li param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
187*f0dffb02SXin Li }
188*f0dffb02SXin Li
189*f0dffb02SXin Li const dex::u4 regs = ir_method->code->registers;
190*f0dffb02SXin Li const dex::u4 ins_count = ir_method->code->ins_count;
191*f0dffb02SXin Li SLICER_CHECK_GE(regs, ins_count);
192*f0dffb02SXin Li
193*f0dffb02SXin Li // generate the args "relocation" instructions
194*f0dffb02SXin Li dex::u4 reg = regs - ins_count;
195*f0dffb02SXin Li for (const auto& type : param_types) {
196*f0dffb02SXin Li auto move = code_ir->Alloc<lir::Bytecode>();
197*f0dffb02SXin Li switch (type->GetCategory()) {
198*f0dffb02SXin Li case ir::Type::Category::Reference:
199*f0dffb02SXin Li move->opcode = dex::OP_MOVE_OBJECT_16;
200*f0dffb02SXin Li move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
201*f0dffb02SXin Li move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
202*f0dffb02SXin Li reg += 1;
203*f0dffb02SXin Li break;
204*f0dffb02SXin Li case ir::Type::Category::Scalar:
205*f0dffb02SXin Li move->opcode = dex::OP_MOVE_16;
206*f0dffb02SXin Li move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
207*f0dffb02SXin Li move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
208*f0dffb02SXin Li reg += 1;
209*f0dffb02SXin Li break;
210*f0dffb02SXin Li case ir::Type::Category::WideScalar:
211*f0dffb02SXin Li move->opcode = dex::OP_MOVE_WIDE_16;
212*f0dffb02SXin Li move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg - shift));
213*f0dffb02SXin Li move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg));
214*f0dffb02SXin Li reg += 2;
215*f0dffb02SXin Li break;
216*f0dffb02SXin Li case ir::Type::Category::Void:
217*f0dffb02SXin Li SLICER_FATAL("void parameter type");
218*f0dffb02SXin Li }
219*f0dffb02SXin Li code_ir->instructions.InsertBefore(position, move);
220*f0dffb02SXin Li }
221*f0dffb02SXin Li }
222*f0dffb02SXin Li
InjectArrayParamsHook(lir::CodeIr * code_ir,lir::Bytecode * bytecode)223*f0dffb02SXin Li bool EntryHook::InjectArrayParamsHook(lir::CodeIr* code_ir, lir::Bytecode* bytecode) {
224*f0dffb02SXin Li ir::Builder builder(code_ir->dex_ir);
225*f0dffb02SXin Li const auto ir_method = code_ir->ir_method;
226*f0dffb02SXin Li auto param_types_list = ir_method->decl->prototype->param_types;
227*f0dffb02SXin Li auto param_types = param_types_list != nullptr ? param_types_list->types : std::vector<ir::Type*>();
228*f0dffb02SXin Li bool is_static = (ir_method->access_flags & dex::kAccStatic) != 0;
229*f0dffb02SXin Li
230*f0dffb02SXin Li // number of registers that we need to operate
231*f0dffb02SXin Li dex::u2 regs_count = 3;
232*f0dffb02SXin Li auto non_param_regs = ir_method->code->registers - ir_method->code->ins_count;
233*f0dffb02SXin Li
234*f0dffb02SXin Li // do we have enough registers to operate?
235*f0dffb02SXin Li bool needsExtraRegs = non_param_regs < regs_count;
236*f0dffb02SXin Li if (needsExtraRegs) {
237*f0dffb02SXin Li // we don't have enough registers, so we allocate more, we will shift
238*f0dffb02SXin Li // params to their original registers later.
239*f0dffb02SXin Li code_ir->ir_method->code->registers += regs_count - non_param_regs;
240*f0dffb02SXin Li }
241*f0dffb02SXin Li
242*f0dffb02SXin Li // use three first registers:
243*f0dffb02SXin Li // all three are needed when we "aput" a string/boxed-value (1) into an array (2) at an index (3)
244*f0dffb02SXin Li
245*f0dffb02SXin Li // register that will store size of during allocation
246*f0dffb02SXin Li // later will be reused to store index when do "aput"
247*f0dffb02SXin Li dex::u4 array_size_reg = 0;
248*f0dffb02SXin Li // register that will store an array that will be passed
249*f0dffb02SXin Li // as a parameter in entry hook
250*f0dffb02SXin Li dex::u4 array_reg = 1;
251*f0dffb02SXin Li // stores result of boxing (if it's needed); also stores the method signature string
252*f0dffb02SXin Li dex::u4 value_reg = 2;
253*f0dffb02SXin Li // array size bytecode
254*f0dffb02SXin Li auto const_size_op = code_ir->Alloc<lir::Bytecode>();
255*f0dffb02SXin Li const_size_op->opcode = dex::OP_CONST;
256*f0dffb02SXin Li const_size_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_size_reg));
257*f0dffb02SXin Li const_size_op->operands.push_back(code_ir->Alloc<lir::Const32>(
258*f0dffb02SXin Li 2 + param_types.size())); // method signature + params + "this" object
259*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, const_size_op);
260*f0dffb02SXin Li
261*f0dffb02SXin Li // allocate array
262*f0dffb02SXin Li const auto obj_array_type = builder.GetType("[Ljava/lang/Object;");
263*f0dffb02SXin Li auto allocate_array_op = code_ir->Alloc<lir::Bytecode>();
264*f0dffb02SXin Li allocate_array_op->opcode = dex::OP_NEW_ARRAY;
265*f0dffb02SXin Li allocate_array_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_reg));
266*f0dffb02SXin Li allocate_array_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_size_reg));
267*f0dffb02SXin Li allocate_array_op->operands.push_back(
268*f0dffb02SXin Li code_ir->Alloc<lir::Type>(obj_array_type, obj_array_type->orig_index));
269*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, allocate_array_op);
270*f0dffb02SXin Li
271*f0dffb02SXin Li // fill the array with parameters passed into function
272*f0dffb02SXin Li
273*f0dffb02SXin Li std::vector<ir::Type*> types;
274*f0dffb02SXin Li types.push_back(builder.GetType("Ljava/lang/String;")); // method signature string
275*f0dffb02SXin Li if (!is_static) {
276*f0dffb02SXin Li types.push_back(ir_method->decl->parent); // "this" object
277*f0dffb02SXin Li }
278*f0dffb02SXin Li
279*f0dffb02SXin Li types.insert(types.end(), param_types.begin(), param_types.end()); // parameters
280*f0dffb02SXin Li
281*f0dffb02SXin Li // register where params start
282*f0dffb02SXin Li dex::u4 current_reg = ir_method->code->registers - ir_method->code->ins_count;
283*f0dffb02SXin Li // reuse not needed anymore register to store indexes
284*f0dffb02SXin Li dex::u4 array_index_reg = array_size_reg;
285*f0dffb02SXin Li int i = 0;
286*f0dffb02SXin Li for (auto type: types) {
287*f0dffb02SXin Li dex::u4 src_reg = 0;
288*f0dffb02SXin Li if (i == 0) { // method signature string
289*f0dffb02SXin Li // e.g. const-string v2, "(I[Ljava/lang/String;)Ljava/lang/String;"
290*f0dffb02SXin Li // for (int, String[]) -> String
291*f0dffb02SXin Li auto const_str_op = code_ir->Alloc<lir::Bytecode>();
292*f0dffb02SXin Li const_str_op->opcode = dex::OP_CONST_STRING;
293*f0dffb02SXin Li const_str_op->operands.push_back(code_ir->Alloc<lir::VReg>(value_reg)); // dst
294*f0dffb02SXin Li auto method_label = builder.GetAsciiString(MethodLabel(ir_method).c_str());
295*f0dffb02SXin Li const_str_op->operands.push_back(
296*f0dffb02SXin Li code_ir->Alloc<lir::String>(method_label, method_label->orig_index)); // src
297*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, const_str_op);
298*f0dffb02SXin Li src_reg = value_reg;
299*f0dffb02SXin Li } else if (type->GetCategory() != ir::Type::Category::Reference) {
300*f0dffb02SXin Li BoxValue(bytecode, code_ir, type, current_reg, value_reg);
301*f0dffb02SXin Li src_reg = value_reg;
302*f0dffb02SXin Li current_reg += 1 + (type->GetCategory() == ir::Type::Category::WideScalar);
303*f0dffb02SXin Li } else {
304*f0dffb02SXin Li src_reg = current_reg;
305*f0dffb02SXin Li current_reg++;
306*f0dffb02SXin Li }
307*f0dffb02SXin Li
308*f0dffb02SXin Li auto index_const_op = code_ir->Alloc<lir::Bytecode>();
309*f0dffb02SXin Li index_const_op->opcode = dex::OP_CONST;
310*f0dffb02SXin Li index_const_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_index_reg));
311*f0dffb02SXin Li index_const_op->operands.push_back(code_ir->Alloc<lir::Const32>(i++));
312*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, index_const_op);
313*f0dffb02SXin Li
314*f0dffb02SXin Li auto aput_op = code_ir->Alloc<lir::Bytecode>();
315*f0dffb02SXin Li aput_op->opcode = dex::OP_APUT_OBJECT;
316*f0dffb02SXin Li aput_op->operands.push_back(code_ir->Alloc<lir::VReg>(src_reg));
317*f0dffb02SXin Li aput_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_reg));
318*f0dffb02SXin Li aput_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_index_reg));
319*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, aput_op);
320*f0dffb02SXin Li
321*f0dffb02SXin Li // if function is static, then jumping over index 1
322*f0dffb02SXin Li // since null should be be passed in this case
323*f0dffb02SXin Li if (i == 1 && is_static) i++;
324*f0dffb02SXin Li }
325*f0dffb02SXin Li
326*f0dffb02SXin Li std::vector<ir::Type*> hook_param_types;
327*f0dffb02SXin Li hook_param_types.push_back(obj_array_type);
328*f0dffb02SXin Li
329*f0dffb02SXin Li auto ir_proto = builder.GetProto(builder.GetType("V"),
330*f0dffb02SXin Li builder.GetTypeList(hook_param_types));
331*f0dffb02SXin Li
332*f0dffb02SXin Li auto ir_method_decl = builder.GetMethodDecl(
333*f0dffb02SXin Li builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
334*f0dffb02SXin Li builder.GetType(hook_method_id_.class_descriptor));
335*f0dffb02SXin Li
336*f0dffb02SXin Li auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
337*f0dffb02SXin Li auto args = code_ir->Alloc<lir::VRegRange>(array_reg, 1);
338*f0dffb02SXin Li auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
339*f0dffb02SXin Li hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
340*f0dffb02SXin Li hook_invoke->operands.push_back(args);
341*f0dffb02SXin Li hook_invoke->operands.push_back(hook_method);
342*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, hook_invoke);
343*f0dffb02SXin Li
344*f0dffb02SXin Li // clean up registries used by us
345*f0dffb02SXin Li // registers are assigned to a marker value 0xFE_FE_FE_FE (decimal
346*f0dffb02SXin Li // value: -16843010) to help identify use of uninitialized registers.
347*f0dffb02SXin Li for (dex::u2 i = 0; i < regs_count; ++i) {
348*f0dffb02SXin Li auto cleanup = code_ir->Alloc<lir::Bytecode>();
349*f0dffb02SXin Li cleanup->opcode = dex::OP_CONST;
350*f0dffb02SXin Li cleanup->operands.push_back(code_ir->Alloc<lir::VReg>(i));
351*f0dffb02SXin Li cleanup->operands.push_back(code_ir->Alloc<lir::Const32>(0xFEFEFEFE));
352*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, cleanup);
353*f0dffb02SXin Li }
354*f0dffb02SXin Li
355*f0dffb02SXin Li // now we have to shift params to their original registers
356*f0dffb02SXin Li if (needsExtraRegs) {
357*f0dffb02SXin Li GenerateShiftParamsCode(code_ir, bytecode, regs_count - non_param_regs);
358*f0dffb02SXin Li }
359*f0dffb02SXin Li return true;
360*f0dffb02SXin Li }
361*f0dffb02SXin Li
Apply(lir::CodeIr * code_ir)362*f0dffb02SXin Li bool ExitHook::Apply(lir::CodeIr* code_ir) {
363*f0dffb02SXin Li ir::Builder builder(code_ir->dex_ir);
364*f0dffb02SXin Li const auto ir_method = code_ir->ir_method;
365*f0dffb02SXin Li const auto declared_return_type = ir_method->decl->prototype->return_type;
366*f0dffb02SXin Li bool return_as_object = (tweak_ & Tweak::ReturnAsObject) != 0;
367*f0dffb02SXin Li // do we have a void-return method?
368*f0dffb02SXin Li bool return_void = (::strcmp(declared_return_type->descriptor->c_str(), "V") == 0);
369*f0dffb02SXin Li // returnAsObject supports only object return type;
370*f0dffb02SXin Li SLICER_CHECK(!return_as_object ||
371*f0dffb02SXin Li (declared_return_type->GetCategory() == ir::Type::Category::Reference));
372*f0dffb02SXin Li const auto return_type = return_as_object ? builder.GetType("Ljava/lang/Object;")
373*f0dffb02SXin Li : declared_return_type;
374*f0dffb02SXin Li
375*f0dffb02SXin Li bool pass_method_signature = (tweak_ & Tweak::PassMethodSignature) != 0;
376*f0dffb02SXin Li // construct the hook method declaration
377*f0dffb02SXin Li std::vector<ir::Type*> param_types;
378*f0dffb02SXin Li if (pass_method_signature) {
379*f0dffb02SXin Li param_types.push_back(builder.GetType("Ljava/lang/String;"));
380*f0dffb02SXin Li }
381*f0dffb02SXin Li if (!return_void) {
382*f0dffb02SXin Li param_types.push_back(return_type);
383*f0dffb02SXin Li }
384*f0dffb02SXin Li
385*f0dffb02SXin Li auto ir_proto = builder.GetProto(return_type, builder.GetTypeList(param_types));
386*f0dffb02SXin Li
387*f0dffb02SXin Li auto ir_method_decl = builder.GetMethodDecl(
388*f0dffb02SXin Li builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
389*f0dffb02SXin Li builder.GetType(hook_method_id_.class_descriptor));
390*f0dffb02SXin Li
391*f0dffb02SXin Li auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
392*f0dffb02SXin Li
393*f0dffb02SXin Li // find and instrument all return instructions
394*f0dffb02SXin Li for (auto instr : code_ir->instructions) {
395*f0dffb02SXin Li BytecodeConvertingVisitor visitor;
396*f0dffb02SXin Li instr->Accept(&visitor);
397*f0dffb02SXin Li auto bytecode = visitor.out;
398*f0dffb02SXin Li if (bytecode == nullptr) {
399*f0dffb02SXin Li continue;
400*f0dffb02SXin Li }
401*f0dffb02SXin Li
402*f0dffb02SXin Li dex::Opcode move_result_opcode = dex::OP_NOP;
403*f0dffb02SXin Li dex::u4 reg = 0;
404*f0dffb02SXin Li int reg_count = 0;
405*f0dffb02SXin Li switch (bytecode->opcode) {
406*f0dffb02SXin Li case dex::OP_RETURN_VOID:
407*f0dffb02SXin Li SLICER_CHECK(return_void);
408*f0dffb02SXin Li break;
409*f0dffb02SXin Li case dex::OP_RETURN:
410*f0dffb02SXin Li SLICER_CHECK(!return_void);
411*f0dffb02SXin Li move_result_opcode = dex::OP_MOVE_RESULT;
412*f0dffb02SXin Li reg = bytecode->CastOperand<lir::VReg>(0)->reg;
413*f0dffb02SXin Li reg_count = 1;
414*f0dffb02SXin Li break;
415*f0dffb02SXin Li case dex::OP_RETURN_OBJECT:
416*f0dffb02SXin Li SLICER_CHECK(!return_void);
417*f0dffb02SXin Li move_result_opcode = dex::OP_MOVE_RESULT_OBJECT;
418*f0dffb02SXin Li reg = bytecode->CastOperand<lir::VReg>(0)->reg;
419*f0dffb02SXin Li reg_count = 1;
420*f0dffb02SXin Li break;
421*f0dffb02SXin Li case dex::OP_RETURN_WIDE:
422*f0dffb02SXin Li SLICER_CHECK(!return_void);
423*f0dffb02SXin Li move_result_opcode = dex::OP_MOVE_RESULT_WIDE;
424*f0dffb02SXin Li reg = bytecode->CastOperand<lir::VRegPair>(0)->base_reg;
425*f0dffb02SXin Li reg_count = 2;
426*f0dffb02SXin Li break;
427*f0dffb02SXin Li default:
428*f0dffb02SXin Li // skip the bytecode...
429*f0dffb02SXin Li continue;
430*f0dffb02SXin Li }
431*f0dffb02SXin Li
432*f0dffb02SXin Li dex::u4 scratch_reg = 0;
433*f0dffb02SXin Li // load method signature into scratch_reg
434*f0dffb02SXin Li if (pass_method_signature) {
435*f0dffb02SXin Li // is there a register that can be overtaken
436*f0dffb02SXin Li bool needsScratchReg = ir_method->code->registers < reg_count + 1;
437*f0dffb02SXin Li if (needsScratchReg) {
438*f0dffb02SXin Li // don't renumber registers underneath us
439*f0dffb02SXin Li slicer::AllocateScratchRegs alloc_regs(1, false);
440*f0dffb02SXin Li alloc_regs.Apply(code_ir);
441*f0dffb02SXin Li }
442*f0dffb02SXin Li
443*f0dffb02SXin Li // we need use one register before results to put signature there
444*f0dffb02SXin Li // however result starts in register 0, thefore it is shifted
445*f0dffb02SXin Li // to register 1
446*f0dffb02SXin Li if (reg == 0 && bytecode->opcode != dex::OP_RETURN_VOID) {
447*f0dffb02SXin Li auto move_op = code_ir->Alloc<lir::Bytecode>();
448*f0dffb02SXin Li switch (bytecode->opcode) {
449*f0dffb02SXin Li case dex::OP_RETURN_OBJECT:
450*f0dffb02SXin Li move_op->opcode = dex::OP_MOVE_OBJECT_16;
451*f0dffb02SXin Li move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg + 1));
452*f0dffb02SXin Li move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
453*f0dffb02SXin Li break;
454*f0dffb02SXin Li case dex::OP_RETURN:
455*f0dffb02SXin Li move_op->opcode = dex::OP_MOVE_16;
456*f0dffb02SXin Li move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg + 1));
457*f0dffb02SXin Li move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
458*f0dffb02SXin Li break;
459*f0dffb02SXin Li case dex::OP_RETURN_WIDE:
460*f0dffb02SXin Li move_op->opcode = dex::OP_MOVE_WIDE_16;
461*f0dffb02SXin Li move_op->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg + 1));
462*f0dffb02SXin Li move_op->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg));
463*f0dffb02SXin Li break;
464*f0dffb02SXin Li default: {
465*f0dffb02SXin Li std::stringstream ss;
466*f0dffb02SXin Li ss <<"Unexpected bytecode opcode: " << bytecode->opcode;
467*f0dffb02SXin Li SLICER_FATAL(ss.str());
468*f0dffb02SXin Li }
469*f0dffb02SXin Li }
470*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, move_op);
471*f0dffb02SXin Li // return is the last call, return is shifted to one, so taking over 0 registry
472*f0dffb02SXin Li scratch_reg = 0;
473*f0dffb02SXin Li } else {
474*f0dffb02SXin Li // return is the last call, so we're taking over previous registry
475*f0dffb02SXin Li scratch_reg = bytecode->opcode == dex::OP_RETURN_VOID ? 0 : reg - 1;
476*f0dffb02SXin Li }
477*f0dffb02SXin Li
478*f0dffb02SXin Li
479*f0dffb02SXin Li // return is the last call, so we're taking over previous registry
480*f0dffb02SXin Li auto method_label = builder.GetAsciiString(MethodLabel(ir_method).c_str());
481*f0dffb02SXin Li auto const_str_op = code_ir->Alloc<lir::Bytecode>();
482*f0dffb02SXin Li const_str_op->opcode = dex::OP_CONST_STRING;
483*f0dffb02SXin Li const_str_op->operands.push_back(code_ir->Alloc<lir::VReg>(scratch_reg)); // dst
484*f0dffb02SXin Li const_str_op->operands.push_back(code_ir->Alloc<lir::String>(method_label, method_label->orig_index)); // src
485*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, const_str_op);
486*f0dffb02SXin Li }
487*f0dffb02SXin Li
488*f0dffb02SXin Li auto args = pass_method_signature
489*f0dffb02SXin Li ? code_ir->Alloc<lir::VRegRange>(scratch_reg, reg_count + 1)
490*f0dffb02SXin Li : code_ir->Alloc<lir::VRegRange>(reg, reg_count);
491*f0dffb02SXin Li auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
492*f0dffb02SXin Li hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
493*f0dffb02SXin Li hook_invoke->operands.push_back(args);
494*f0dffb02SXin Li hook_invoke->operands.push_back(hook_method);
495*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, hook_invoke);
496*f0dffb02SXin Li
497*f0dffb02SXin Li // move result back to the right register
498*f0dffb02SXin Li //
499*f0dffb02SXin Li // NOTE: we're reusing the original return's operand,
500*f0dffb02SXin Li // which is valid and more efficient than allocating
501*f0dffb02SXin Li // a new LIR node, but it's also fragile: we need to be
502*f0dffb02SXin Li // very careful about mutating shared nodes.
503*f0dffb02SXin Li //
504*f0dffb02SXin Li if (move_result_opcode != dex::OP_NOP) {
505*f0dffb02SXin Li auto move_result = code_ir->Alloc<lir::Bytecode>();
506*f0dffb02SXin Li move_result->opcode = move_result_opcode;
507*f0dffb02SXin Li move_result->operands.push_back(bytecode->operands[0]);
508*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, move_result);
509*f0dffb02SXin Li
510*f0dffb02SXin Li if ((tweak_ & Tweak::ReturnAsObject) != 0) {
511*f0dffb02SXin Li auto check_cast = code_ir->Alloc<lir::Bytecode>();
512*f0dffb02SXin Li check_cast->opcode = dex::OP_CHECK_CAST;
513*f0dffb02SXin Li check_cast->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
514*f0dffb02SXin Li check_cast->operands.push_back(
515*f0dffb02SXin Li code_ir->Alloc<lir::Type>(declared_return_type, declared_return_type->orig_index));
516*f0dffb02SXin Li code_ir->instructions.InsertBefore(bytecode, check_cast);
517*f0dffb02SXin Li }
518*f0dffb02SXin Li }
519*f0dffb02SXin Li }
520*f0dffb02SXin Li
521*f0dffb02SXin Li return true;
522*f0dffb02SXin Li }
523*f0dffb02SXin Li
Apply(lir::CodeIr * code_ir)524*f0dffb02SXin Li bool DetourHook::Apply(lir::CodeIr* code_ir) {
525*f0dffb02SXin Li ir::Builder builder(code_ir->dex_ir);
526*f0dffb02SXin Li
527*f0dffb02SXin Li // search for matching invoke-virtual[/range] bytecodes
528*f0dffb02SXin Li for (auto instr : code_ir->instructions) {
529*f0dffb02SXin Li BytecodeConvertingVisitor visitor;
530*f0dffb02SXin Li instr->Accept(&visitor);
531*f0dffb02SXin Li auto bytecode = visitor.out;
532*f0dffb02SXin Li if (bytecode == nullptr) {
533*f0dffb02SXin Li continue;
534*f0dffb02SXin Li }
535*f0dffb02SXin Li
536*f0dffb02SXin Li dex::Opcode new_call_opcode = GetNewOpcode(bytecode->opcode);
537*f0dffb02SXin Li if (new_call_opcode == dex::OP_NOP) {
538*f0dffb02SXin Li continue;
539*f0dffb02SXin Li }
540*f0dffb02SXin Li
541*f0dffb02SXin Li auto orig_method = bytecode->CastOperand<lir::Method>(1)->ir_method;
542*f0dffb02SXin Li if (!orig_method_id_.Match(orig_method)) {
543*f0dffb02SXin Li // this is not the method you're looking for...
544*f0dffb02SXin Li continue;
545*f0dffb02SXin Li }
546*f0dffb02SXin Li
547*f0dffb02SXin Li // construct the detour method declaration
548*f0dffb02SXin Li // (matching the original method, plus an explicit "this" argument)
549*f0dffb02SXin Li std::vector<ir::Type*> param_types;
550*f0dffb02SXin Li param_types.push_back(orig_method->parent);
551*f0dffb02SXin Li if (orig_method->prototype->param_types != nullptr) {
552*f0dffb02SXin Li const auto& orig_param_types = orig_method->prototype->param_types->types;
553*f0dffb02SXin Li param_types.insert(param_types.end(), orig_param_types.begin(),
554*f0dffb02SXin Li orig_param_types.end());
555*f0dffb02SXin Li }
556*f0dffb02SXin Li
557*f0dffb02SXin Li auto ir_proto = builder.GetProto(orig_method->prototype->return_type,
558*f0dffb02SXin Li builder.GetTypeList(param_types));
559*f0dffb02SXin Li
560*f0dffb02SXin Li auto ir_method_decl = builder.GetMethodDecl(
561*f0dffb02SXin Li builder.GetAsciiString(detour_method_id_.method_name), ir_proto,
562*f0dffb02SXin Li builder.GetType(detour_method_id_.class_descriptor));
563*f0dffb02SXin Li
564*f0dffb02SXin Li auto detour_method =
565*f0dffb02SXin Li code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
566*f0dffb02SXin Li
567*f0dffb02SXin Li // We mutate the original invoke bytecode in-place: this is ok
568*f0dffb02SXin Li // because lir::Instructions can't be shared (referenced multiple times)
569*f0dffb02SXin Li // in the code IR. It's also simpler and more efficient than allocating a
570*f0dffb02SXin Li // new IR invoke bytecode.
571*f0dffb02SXin Li bytecode->opcode = new_call_opcode;
572*f0dffb02SXin Li bytecode->operands[1] = detour_method;
573*f0dffb02SXin Li }
574*f0dffb02SXin Li
575*f0dffb02SXin Li return true;
576*f0dffb02SXin Li }
577*f0dffb02SXin Li
GetNewOpcode(dex::Opcode opcode)578*f0dffb02SXin Li dex::Opcode DetourVirtualInvoke::GetNewOpcode(dex::Opcode opcode) {
579*f0dffb02SXin Li switch (opcode) {
580*f0dffb02SXin Li case dex::OP_INVOKE_VIRTUAL:
581*f0dffb02SXin Li return dex::OP_INVOKE_STATIC;
582*f0dffb02SXin Li case dex::OP_INVOKE_VIRTUAL_RANGE:
583*f0dffb02SXin Li return dex::OP_INVOKE_STATIC_RANGE;
584*f0dffb02SXin Li default:
585*f0dffb02SXin Li // skip instruction ...
586*f0dffb02SXin Li return dex::OP_NOP;
587*f0dffb02SXin Li }
588*f0dffb02SXin Li }
589*f0dffb02SXin Li
GetNewOpcode(dex::Opcode opcode)590*f0dffb02SXin Li dex::Opcode DetourInterfaceInvoke::GetNewOpcode(dex::Opcode opcode) {
591*f0dffb02SXin Li switch (opcode) {
592*f0dffb02SXin Li case dex::OP_INVOKE_INTERFACE:
593*f0dffb02SXin Li return dex::OP_INVOKE_STATIC;
594*f0dffb02SXin Li case dex::OP_INVOKE_INTERFACE_RANGE:
595*f0dffb02SXin Li return dex::OP_INVOKE_STATIC_RANGE;
596*f0dffb02SXin Li default:
597*f0dffb02SXin Li // skip instruction ...
598*f0dffb02SXin Li return dex::OP_NOP;
599*f0dffb02SXin Li }
600*f0dffb02SXin Li }
601*f0dffb02SXin Li
602*f0dffb02SXin Li // Register re-numbering visitor
603*f0dffb02SXin Li // (renumbers vN to vN+shift)
604*f0dffb02SXin Li class RegsRenumberVisitor : public lir::Visitor {
605*f0dffb02SXin Li public:
RegsRenumberVisitor(int shift)606*f0dffb02SXin Li explicit RegsRenumberVisitor(int shift) : shift_(shift) {
607*f0dffb02SXin Li SLICER_CHECK_GT(shift, 0);
608*f0dffb02SXin Li }
609*f0dffb02SXin Li
610*f0dffb02SXin Li private:
Visit(lir::Bytecode * bytecode)611*f0dffb02SXin Li virtual bool Visit(lir::Bytecode* bytecode) override {
612*f0dffb02SXin Li for (auto operand : bytecode->operands) {
613*f0dffb02SXin Li operand->Accept(this);
614*f0dffb02SXin Li }
615*f0dffb02SXin Li return true;
616*f0dffb02SXin Li }
617*f0dffb02SXin Li
Visit(lir::DbgInfoAnnotation * dbg_annotation)618*f0dffb02SXin Li virtual bool Visit(lir::DbgInfoAnnotation* dbg_annotation) override {
619*f0dffb02SXin Li for (auto operand : dbg_annotation->operands) {
620*f0dffb02SXin Li operand->Accept(this);
621*f0dffb02SXin Li }
622*f0dffb02SXin Li return true;
623*f0dffb02SXin Li }
624*f0dffb02SXin Li
Visit(lir::VReg * vreg)625*f0dffb02SXin Li virtual bool Visit(lir::VReg* vreg) override {
626*f0dffb02SXin Li vreg->reg += shift_;
627*f0dffb02SXin Li return true;
628*f0dffb02SXin Li }
629*f0dffb02SXin Li
Visit(lir::VRegPair * vreg_pair)630*f0dffb02SXin Li virtual bool Visit(lir::VRegPair* vreg_pair) override {
631*f0dffb02SXin Li vreg_pair->base_reg += shift_;
632*f0dffb02SXin Li return true;
633*f0dffb02SXin Li }
634*f0dffb02SXin Li
Visit(lir::VRegList * vreg_list)635*f0dffb02SXin Li virtual bool Visit(lir::VRegList* vreg_list) override {
636*f0dffb02SXin Li for (auto& reg : vreg_list->registers) {
637*f0dffb02SXin Li reg += shift_;
638*f0dffb02SXin Li }
639*f0dffb02SXin Li return true;
640*f0dffb02SXin Li }
641*f0dffb02SXin Li
Visit(lir::VRegRange * vreg_range)642*f0dffb02SXin Li virtual bool Visit(lir::VRegRange* vreg_range) override {
643*f0dffb02SXin Li vreg_range->base_reg += shift_;
644*f0dffb02SXin Li return true;
645*f0dffb02SXin Li }
646*f0dffb02SXin Li
647*f0dffb02SXin Li private:
648*f0dffb02SXin Li int shift_ = 0;
649*f0dffb02SXin Li };
650*f0dffb02SXin Li
651*f0dffb02SXin Li // Try to allocate registers by renumbering the existing allocation
652*f0dffb02SXin Li //
653*f0dffb02SXin Li // NOTE: we can't bump the register count over 16 since it may
654*f0dffb02SXin Li // make existing bytecodes "unencodable" (if they have 4 bit reg fields)
655*f0dffb02SXin Li //
RegsRenumbering(lir::CodeIr * code_ir)656*f0dffb02SXin Li void AllocateScratchRegs::RegsRenumbering(lir::CodeIr* code_ir) {
657*f0dffb02SXin Li SLICER_CHECK_GT(left_to_allocate_, 0);
658*f0dffb02SXin Li int delta = std::min(left_to_allocate_,
659*f0dffb02SXin Li 16 - static_cast<int>(code_ir->ir_method->code->registers));
660*f0dffb02SXin Li if (delta < 1) {
661*f0dffb02SXin Li // can't allocate any registers through renumbering
662*f0dffb02SXin Li return;
663*f0dffb02SXin Li }
664*f0dffb02SXin Li assert(delta <= 16);
665*f0dffb02SXin Li
666*f0dffb02SXin Li // renumber existing registers
667*f0dffb02SXin Li RegsRenumberVisitor visitor(delta);
668*f0dffb02SXin Li for (auto instr : code_ir->instructions) {
669*f0dffb02SXin Li instr->Accept(&visitor);
670*f0dffb02SXin Li }
671*f0dffb02SXin Li
672*f0dffb02SXin Li // we just allocated "delta" registers (v0..vX)
673*f0dffb02SXin Li Allocate(code_ir, 0, delta);
674*f0dffb02SXin Li }
675*f0dffb02SXin Li
676*f0dffb02SXin Li // Allocates registers by generating prologue code to relocate params
677*f0dffb02SXin Li // into their original registers (parameters are allocated in the last IN registers)
678*f0dffb02SXin Li //
679*f0dffb02SXin Li // There are three types of register moves depending on the value type:
680*f0dffb02SXin Li // 1. vreg -> vreg
681*f0dffb02SXin Li // 2. vreg/wide -> vreg/wide
682*f0dffb02SXin Li // 3. vreg/obj -> vreg/obj
683*f0dffb02SXin Li //
ShiftParams(lir::CodeIr * code_ir)684*f0dffb02SXin Li void AllocateScratchRegs::ShiftParams(lir::CodeIr* code_ir) {
685*f0dffb02SXin Li const auto ir_method = code_ir->ir_method;
686*f0dffb02SXin Li SLICER_CHECK_GT(left_to_allocate_, 0);
687*f0dffb02SXin Li
688*f0dffb02SXin Li const dex::u4 shift = left_to_allocate_;
689*f0dffb02SXin Li Allocate(code_ir, ir_method->code->registers, left_to_allocate_);
690*f0dffb02SXin Li assert(left_to_allocate_ == 0);
691*f0dffb02SXin Li
692*f0dffb02SXin Li // generate the args "relocation" instructions
693*f0dffb02SXin Li auto first_instr = *(code_ir->instructions.begin());
694*f0dffb02SXin Li GenerateShiftParamsCode(code_ir, first_instr, shift);
695*f0dffb02SXin Li }
696*f0dffb02SXin Li
697*f0dffb02SXin Li // Mark [first_reg, first_reg + count) as scratch registers
Allocate(lir::CodeIr * code_ir,dex::u4 first_reg,int count)698*f0dffb02SXin Li void AllocateScratchRegs::Allocate(lir::CodeIr* code_ir, dex::u4 first_reg, int count) {
699*f0dffb02SXin Li SLICER_CHECK(count > 0 && count <= left_to_allocate_);
700*f0dffb02SXin Li code_ir->ir_method->code->registers += count;
701*f0dffb02SXin Li left_to_allocate_ -= count;
702*f0dffb02SXin Li for (int i = 0; i < count; ++i) {
703*f0dffb02SXin Li SLICER_CHECK(scratch_regs_.insert(first_reg + i).second);
704*f0dffb02SXin Li }
705*f0dffb02SXin Li }
706*f0dffb02SXin Li
707*f0dffb02SXin Li // Allocate scratch registers without doing a full register allocation:
708*f0dffb02SXin Li //
709*f0dffb02SXin Li // 1. if there are not params, increase the method regs count and we're done
710*f0dffb02SXin Li // 2. if the method uses less than 16 registers, we can renumber the existing registers
711*f0dffb02SXin Li // 3. if we still have registers to allocate, increase the method registers count,
712*f0dffb02SXin Li // and generate prologue code to shift the param regs into their original registers
713*f0dffb02SXin Li //
Apply(lir::CodeIr * code_ir)714*f0dffb02SXin Li bool AllocateScratchRegs::Apply(lir::CodeIr* code_ir) {
715*f0dffb02SXin Li const auto code = code_ir->ir_method->code;
716*f0dffb02SXin Li // .dex bytecode allows up to 64k vregs
717*f0dffb02SXin Li SLICER_CHECK_LE(code->registers + allocate_count_, (1 << 16));
718*f0dffb02SXin Li
719*f0dffb02SXin Li scratch_regs_.clear();
720*f0dffb02SXin Li left_to_allocate_ = allocate_count_;
721*f0dffb02SXin Li
722*f0dffb02SXin Li // can we allocate by simply incrementing the method regs count?
723*f0dffb02SXin Li if (code->ins_count == 0) {
724*f0dffb02SXin Li Allocate(code_ir, code->registers, left_to_allocate_);
725*f0dffb02SXin Li return true;
726*f0dffb02SXin Li }
727*f0dffb02SXin Li
728*f0dffb02SXin Li // allocate as many registers as possible using renumbering
729*f0dffb02SXin Li if (allow_renumbering_) {
730*f0dffb02SXin Li RegsRenumbering(code_ir);
731*f0dffb02SXin Li }
732*f0dffb02SXin Li
733*f0dffb02SXin Li // if we still have registers to allocate, generate prologue
734*f0dffb02SXin Li // code to shift the params into their original registers
735*f0dffb02SXin Li if (left_to_allocate_ > 0) {
736*f0dffb02SXin Li ShiftParams(code_ir);
737*f0dffb02SXin Li }
738*f0dffb02SXin Li
739*f0dffb02SXin Li assert(left_to_allocate_ == 0);
740*f0dffb02SXin Li assert(scratch_regs_.size() == size_t(allocate_count_));
741*f0dffb02SXin Li return true;
742*f0dffb02SXin Li }
743*f0dffb02SXin Li
InstrumentMethod(ir::EncodedMethod * ir_method)744*f0dffb02SXin Li bool MethodInstrumenter::InstrumentMethod(ir::EncodedMethod* ir_method) {
745*f0dffb02SXin Li SLICER_CHECK_NE(ir_method, nullptr);
746*f0dffb02SXin Li if (ir_method->code == nullptr) {
747*f0dffb02SXin Li // can't instrument abstract methods
748*f0dffb02SXin Li return false;
749*f0dffb02SXin Li }
750*f0dffb02SXin Li
751*f0dffb02SXin Li // apply all the queued transformations
752*f0dffb02SXin Li lir::CodeIr code_ir(ir_method, dex_ir_);
753*f0dffb02SXin Li for (const auto& transformation : transformations_) {
754*f0dffb02SXin Li if (!transformation->Apply(&code_ir)) {
755*f0dffb02SXin Li // the transformation failed, bail out...
756*f0dffb02SXin Li return false;
757*f0dffb02SXin Li }
758*f0dffb02SXin Li }
759*f0dffb02SXin Li code_ir.Assemble();
760*f0dffb02SXin Li return true;
761*f0dffb02SXin Li }
762*f0dffb02SXin Li
InstrumentMethod(const ir::MethodId & method_id)763*f0dffb02SXin Li bool MethodInstrumenter::InstrumentMethod(const ir::MethodId& method_id) {
764*f0dffb02SXin Li // locate the method to be instrumented
765*f0dffb02SXin Li ir::Builder builder(dex_ir_);
766*f0dffb02SXin Li auto ir_method = builder.FindMethod(method_id);
767*f0dffb02SXin Li if (ir_method == nullptr) {
768*f0dffb02SXin Li // we couldn't find the specified method
769*f0dffb02SXin Li return false;
770*f0dffb02SXin Li }
771*f0dffb02SXin Li return InstrumentMethod(ir_method);
772*f0dffb02SXin Li }
773*f0dffb02SXin Li
774*f0dffb02SXin Li } // namespace slicer
775