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