• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "slicer/instrumentation.h"
18 #include "slicer/dex_ir_builder.h"
19 
20 namespace slicer {
21 
22 namespace {
23 
24 struct BytecodeConvertingVisitor : public lir::Visitor {
25   lir::Bytecode* out = nullptr;
Visitslicer::__anon63a0acdc0111::BytecodeConvertingVisitor26   bool Visit(lir::Bytecode* bytecode) {
27     out = bytecode;
28     return true;
29   }
30 };
31 
32 }  // namespace
33 
Apply(lir::CodeIr * code_ir)34 bool EntryHook::Apply(lir::CodeIr* code_ir) {
35   ir::Builder builder(code_ir->dex_ir);
36   const auto ir_method = code_ir->ir_method;
37 
38   // construct the hook method declaration
39   std::vector<ir::Type*> param_types;
40   if ((ir_method->access_flags & dex::kAccStatic) == 0) {
41     ir::Type* this_argument_type;
42     if (use_object_type_for_this_argument_) {
43       this_argument_type = builder.GetType("Ljava/lang/Object;");
44     } else {
45       this_argument_type = ir_method->decl->parent;
46     }
47     param_types.push_back(this_argument_type);
48   }
49   if (ir_method->decl->prototype->param_types != nullptr) {
50     const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
51     param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
52   }
53 
54   auto ir_proto = builder.GetProto(builder.GetType("V"),
55                                    builder.GetTypeList(param_types));
56 
57   auto ir_method_decl = builder.GetMethodDecl(
58       builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
59       builder.GetType(hook_method_id_.class_descriptor));
60 
61   auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
62 
63   // argument registers
64   auto regs = ir_method->code->registers;
65   auto args_count = ir_method->code->ins_count;
66   auto args = code_ir->Alloc<lir::VRegRange>(regs - args_count, args_count);
67 
68   // invoke hook bytecode
69   auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
70   hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
71   hook_invoke->operands.push_back(args);
72   hook_invoke->operands.push_back(hook_method);
73 
74   // insert the hook before the first bytecode in the method body
75   for (auto instr : code_ir->instructions) {
76     BytecodeConvertingVisitor visitor;
77     instr->Accept(&visitor);
78     auto bytecode = visitor.out;
79     if (bytecode == nullptr) {
80       continue;
81     }
82     code_ir->instructions.InsertBefore(bytecode, hook_invoke);
83     break;
84   }
85 
86   return true;
87 }
88 
Apply(lir::CodeIr * code_ir)89 bool ExitHook::Apply(lir::CodeIr* code_ir) {
90   ir::Builder builder(code_ir->dex_ir);
91   const auto ir_method = code_ir->ir_method;
92   const auto return_type = ir_method->decl->prototype->return_type;
93 
94   // do we have a void-return method?
95   bool return_void = (::strcmp(return_type->descriptor->c_str(), "V") == 0);
96 
97   // construct the hook method declaration
98   std::vector<ir::Type*> param_types;
99   if (!return_void) {
100     param_types.push_back(return_type);
101   }
102 
103   auto ir_proto = builder.GetProto(return_type, builder.GetTypeList(param_types));
104 
105   auto ir_method_decl = builder.GetMethodDecl(
106       builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
107       builder.GetType(hook_method_id_.class_descriptor));
108 
109   auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
110 
111   // find and instrument all return instructions
112   for (auto instr : code_ir->instructions) {
113     BytecodeConvertingVisitor visitor;
114     instr->Accept(&visitor);
115     auto bytecode = visitor.out;
116     if (bytecode == nullptr) {
117       continue;
118     }
119 
120     dex::Opcode move_result_opcode = dex::OP_NOP;
121     dex::u4 reg = 0;
122     int reg_count = 0;
123 
124     switch (bytecode->opcode) {
125       case dex::OP_RETURN_VOID:
126         SLICER_CHECK(return_void);
127         break;
128       case dex::OP_RETURN:
129         SLICER_CHECK(!return_void);
130         move_result_opcode = dex::OP_MOVE_RESULT;
131         reg = bytecode->CastOperand<lir::VReg>(0)->reg;
132         reg_count = 1;
133         break;
134       case dex::OP_RETURN_OBJECT:
135         SLICER_CHECK(!return_void);
136         move_result_opcode = dex::OP_MOVE_RESULT_OBJECT;
137         reg = bytecode->CastOperand<lir::VReg>(0)->reg;
138         reg_count = 1;
139         break;
140       case dex::OP_RETURN_WIDE:
141         SLICER_CHECK(!return_void);
142         move_result_opcode = dex::OP_MOVE_RESULT_WIDE;
143         reg = bytecode->CastOperand<lir::VRegPair>(0)->base_reg;
144         reg_count = 2;
145         break;
146       default:
147         // skip the bytecode...
148         continue;
149     }
150 
151     // invoke hook bytecode
152     auto args = code_ir->Alloc<lir::VRegRange>(reg, reg_count);
153     auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
154     hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
155     hook_invoke->operands.push_back(args);
156     hook_invoke->operands.push_back(hook_method);
157     code_ir->instructions.InsertBefore(bytecode, hook_invoke);
158 
159     // move result back to the right register
160     //
161     // NOTE: we're reusing the original return's operand,
162     //   which is valid and more efficient than allocating
163     //   a new LIR node, but it's also fragile: we need to be
164     //   very careful about mutating shared nodes.
165     //
166     if (move_result_opcode != dex::OP_NOP) {
167       auto move_result = code_ir->Alloc<lir::Bytecode>();
168       move_result->opcode = move_result_opcode;
169       move_result->operands.push_back(bytecode->operands[0]);
170       code_ir->instructions.InsertBefore(bytecode, move_result);
171     }
172   }
173 
174   return true;
175 }
176 
Apply(lir::CodeIr * code_ir)177 bool DetourHook::Apply(lir::CodeIr* code_ir) {
178   ir::Builder builder(code_ir->dex_ir);
179 
180   // search for matching invoke-virtual[/range] bytecodes
181   for (auto instr : code_ir->instructions) {
182     BytecodeConvertingVisitor visitor;
183     instr->Accept(&visitor);
184     auto bytecode = visitor.out;
185     if (bytecode == nullptr) {
186       continue;
187     }
188 
189     dex::Opcode new_call_opcode = GetNewOpcode(bytecode->opcode);
190     if (new_call_opcode == dex::OP_NOP) {
191       continue;
192     }
193 
194     auto orig_method = bytecode->CastOperand<lir::Method>(1)->ir_method;
195     if (!orig_method_id_.Match(orig_method)) {
196       // this is not the method you're looking for...
197       continue;
198     }
199 
200     // construct the detour method declaration
201     // (matching the original method, plus an explicit "this" argument)
202     std::vector<ir::Type*> param_types;
203     param_types.push_back(orig_method->parent);
204     if (orig_method->prototype->param_types != nullptr) {
205       const auto& orig_param_types = orig_method->prototype->param_types->types;
206       param_types.insert(param_types.end(), orig_param_types.begin(),
207                          orig_param_types.end());
208     }
209 
210     auto ir_proto = builder.GetProto(orig_method->prototype->return_type,
211                                      builder.GetTypeList(param_types));
212 
213     auto ir_method_decl = builder.GetMethodDecl(
214         builder.GetAsciiString(detour_method_id_.method_name), ir_proto,
215         builder.GetType(detour_method_id_.class_descriptor));
216 
217     auto detour_method =
218         code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
219 
220     // We mutate the original invoke bytecode in-place: this is ok
221     // because lir::Instructions can't be shared (referenced multiple times)
222     // in the code IR. It's also simpler and more efficient than allocating a
223     // new IR invoke bytecode.
224     bytecode->opcode = new_call_opcode;
225     bytecode->operands[1] = detour_method;
226   }
227 
228   return true;
229 }
230 
GetNewOpcode(dex::Opcode opcode)231 dex::Opcode DetourVirtualInvoke::GetNewOpcode(dex::Opcode opcode) {
232   switch (opcode) {
233     case dex::OP_INVOKE_VIRTUAL:
234       return dex::OP_INVOKE_STATIC;
235     case dex::OP_INVOKE_VIRTUAL_RANGE:
236       return dex::OP_INVOKE_STATIC_RANGE;
237     default:
238       // skip instruction ...
239       return dex::OP_NOP;
240   }
241 }
242 
GetNewOpcode(dex::Opcode opcode)243 dex::Opcode DetourInterfaceInvoke::GetNewOpcode(dex::Opcode opcode) {
244   switch (opcode) {
245     case dex::OP_INVOKE_INTERFACE:
246       return dex::OP_INVOKE_STATIC;
247     case dex::OP_INVOKE_INTERFACE_RANGE:
248       return dex::OP_INVOKE_STATIC_RANGE;
249     default:
250       // skip instruction ...
251       return dex::OP_NOP;
252   }
253 }
254 
255 // Register re-numbering visitor
256 // (renumbers vN to vN+shift)
257 class RegsRenumberVisitor : public lir::Visitor {
258  public:
RegsRenumberVisitor(int shift)259   explicit RegsRenumberVisitor(int shift) : shift_(shift) {
260     SLICER_CHECK(shift > 0);
261   }
262 
263  private:
Visit(lir::Bytecode * bytecode)264   virtual bool Visit(lir::Bytecode* bytecode) override {
265     for (auto operand : bytecode->operands) {
266       operand->Accept(this);
267     }
268     return true;
269   }
270 
Visit(lir::DbgInfoAnnotation * dbg_annotation)271   virtual bool Visit(lir::DbgInfoAnnotation* dbg_annotation) override {
272     for (auto operand : dbg_annotation->operands) {
273       operand->Accept(this);
274     }
275     return true;
276   }
277 
Visit(lir::VReg * vreg)278   virtual bool Visit(lir::VReg* vreg) override {
279     vreg->reg += shift_;
280     return true;
281   }
282 
Visit(lir::VRegPair * vreg_pair)283   virtual bool Visit(lir::VRegPair* vreg_pair) override {
284     vreg_pair->base_reg += shift_;
285     return true;
286   }
287 
Visit(lir::VRegList * vreg_list)288   virtual bool Visit(lir::VRegList* vreg_list) override {
289     for (auto& reg : vreg_list->registers) {
290       reg += shift_;
291     }
292     return true;
293   }
294 
Visit(lir::VRegRange * vreg_range)295   virtual bool Visit(lir::VRegRange* vreg_range) override {
296     vreg_range->base_reg += shift_;
297     return true;
298   }
299 
300  private:
301   int shift_ = 0;
302 };
303 
304 // Try to allocate registers by renumbering the existing allocation
305 //
306 // NOTE: we can't bump the register count over 16 since it may
307 //  make existing bytecodes "unencodable" (if they have 4 bit reg fields)
308 //
RegsRenumbering(lir::CodeIr * code_ir)309 void AllocateScratchRegs::RegsRenumbering(lir::CodeIr* code_ir) {
310   SLICER_CHECK(left_to_allocate_ > 0);
311   int delta = std::min(left_to_allocate_,
312                        16 - static_cast<int>(code_ir->ir_method->code->registers));
313   if (delta < 1) {
314     // can't allocate any registers through renumbering
315     return;
316   }
317   assert(delta <= 16);
318 
319   // renumber existing registers
320   RegsRenumberVisitor visitor(delta);
321   for (auto instr : code_ir->instructions) {
322     instr->Accept(&visitor);
323   }
324 
325   // we just allocated "delta" registers (v0..vX)
326   Allocate(code_ir, 0, delta);
327 }
328 
329 // Allocates registers by generating prologue code to relocate params
330 // into their original registers (parameters are allocated in the last IN registers)
331 //
332 // There are three types of register moves depending on the value type:
333 // 1. vreg -> vreg
334 // 2. vreg/wide -> vreg/wide
335 // 3. vreg/obj -> vreg/obj
336 //
ShiftParams(lir::CodeIr * code_ir)337 void AllocateScratchRegs::ShiftParams(lir::CodeIr* code_ir) {
338   const auto ir_method = code_ir->ir_method;
339   SLICER_CHECK(ir_method->code->ins_count > 0);
340   SLICER_CHECK(left_to_allocate_ > 0);
341 
342   // build a param list with the explicit "this" argument for non-static methods
343   std::vector<ir::Type*> param_types;
344   if ((ir_method->access_flags & dex::kAccStatic) == 0) {
345     param_types.push_back(ir_method->decl->parent);
346   }
347   if (ir_method->decl->prototype->param_types != nullptr) {
348     const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
349     param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
350   }
351 
352   const dex::u4 shift = left_to_allocate_;
353 
354   Allocate(code_ir, ir_method->code->registers, left_to_allocate_);
355   assert(left_to_allocate_ == 0);
356 
357   const dex::u4 regs = ir_method->code->registers;
358   const dex::u4 ins_count = ir_method->code->ins_count;
359   SLICER_CHECK(regs >= ins_count);
360 
361   // generate the args "relocation" instructions
362   auto first_instr = code_ir->instructions.begin();
363   dex::u4 reg = regs - ins_count;
364   for (const auto& type : param_types) {
365     auto move = code_ir->Alloc<lir::Bytecode>();
366     switch (type->GetCategory()) {
367       case ir::Type::Category::Reference:
368         move->opcode = dex::OP_MOVE_OBJECT_16;
369         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
370         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
371         reg += 1;
372         break;
373       case ir::Type::Category::Scalar:
374         move->opcode = dex::OP_MOVE_16;
375         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
376         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
377         reg += 1;
378         break;
379       case ir::Type::Category::WideScalar:
380         move->opcode = dex::OP_MOVE_WIDE_16;
381         move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg - shift));
382         move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg));
383         reg += 2;
384         break;
385       case ir::Type::Category::Void:
386         SLICER_FATAL("void parameter type");
387     }
388     code_ir->instructions.insert(first_instr, move);
389   }
390 }
391 
392 // Mark [first_reg, first_reg + count) as scratch registers
Allocate(lir::CodeIr * code_ir,dex::u4 first_reg,int count)393 void AllocateScratchRegs::Allocate(lir::CodeIr* code_ir, dex::u4 first_reg, int count) {
394   SLICER_CHECK(count > 0 && count <= left_to_allocate_);
395   code_ir->ir_method->code->registers += count;
396   left_to_allocate_ -= count;
397   for (int i = 0; i < count; ++i) {
398     SLICER_CHECK(scratch_regs_.insert(first_reg + i).second);
399   }
400 }
401 
402 // Allocate scratch registers without doing a full register allocation:
403 //
404 // 1. if there are not params, increase the method regs count and we're done
405 // 2. if the method uses less than 16 registers, we can renumber the existing registers
406 // 3. if we still have registers to allocate, increase the method registers count,
407 //     and generate prologue code to shift the param regs into their original registers
408 //
Apply(lir::CodeIr * code_ir)409 bool AllocateScratchRegs::Apply(lir::CodeIr* code_ir) {
410   const auto code = code_ir->ir_method->code;
411   // .dex bytecode allows up to 64k vregs
412   SLICER_CHECK(code->registers + allocate_count_ <= (1 << 16));
413 
414   scratch_regs_.clear();
415   left_to_allocate_ = allocate_count_;
416 
417   // can we allocate by simply incrementing the method regs count?
418   if (code->ins_count == 0) {
419     Allocate(code_ir, code->registers, left_to_allocate_);
420     return true;
421   }
422 
423   // allocate as many registers as possible using renumbering
424   if (allow_renumbering_) {
425     RegsRenumbering(code_ir);
426   }
427 
428   // if we still have registers to allocate, generate prologue
429   // code to shift the params into their original registers
430   if (left_to_allocate_ > 0) {
431     ShiftParams(code_ir);
432   }
433 
434   assert(left_to_allocate_ == 0);
435   assert(scratch_regs_.size() == size_t(allocate_count_));
436   return true;
437 }
438 
InstrumentMethod(ir::EncodedMethod * ir_method)439 bool MethodInstrumenter::InstrumentMethod(ir::EncodedMethod* ir_method) {
440   SLICER_CHECK(ir_method != nullptr);
441   if (ir_method->code == nullptr) {
442     // can't instrument abstract methods
443     return false;
444   }
445 
446   // apply all the queued transformations
447   lir::CodeIr code_ir(ir_method, dex_ir_);
448   for (const auto& transformation : transformations_) {
449     if (!transformation->Apply(&code_ir)) {
450       // the transformation failed, bail out...
451       return false;
452     }
453   }
454   code_ir.Assemble();
455   return true;
456 }
457 
InstrumentMethod(const ir::MethodId & method_id)458 bool MethodInstrumenter::InstrumentMethod(const ir::MethodId& method_id) {
459   // locate the method to be instrumented
460   ir::Builder builder(dex_ir_);
461   auto ir_method = builder.FindMethod(method_id);
462   if (ir_method == nullptr) {
463     // we couldn't find the specified method
464     return false;
465   }
466   return InstrumentMethod(ir_method);
467 }
468 
469 }  // namespace slicer
470