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