diff options
| -rw-r--r-- | slicer/instrumentation.cc | 144 | ||||
| -rw-r--r-- | testdata/expected/mi.array_entry_hook | 52 |
2 files changed, 115 insertions, 81 deletions
diff --git a/slicer/instrumentation.cc b/slicer/instrumentation.cc index 7c93791..f0dfbdf 100644 --- a/slicer/instrumentation.cc +++ b/slicer/instrumentation.cc @@ -158,6 +158,54 @@ bool EntryHook::Apply(lir::CodeIr* code_ir) { return true; } +void GenerateShiftParamsCode(lir::CodeIr* code_ir, lir::Instruction* position, dex::u4 shift) { + const auto ir_method = code_ir->ir_method; + SLICER_CHECK(ir_method->code->ins_count > 0); + + // build a param list with the explicit "this" argument for non-static methods + std::vector<ir::Type*> param_types; + if ((ir_method->access_flags & dex::kAccStatic) == 0) { + param_types.push_back(ir_method->decl->parent); + } + if (ir_method->decl->prototype->param_types != nullptr) { + const auto& orig_param_types = ir_method->decl->prototype->param_types->types; + param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end()); + } + + const dex::u4 regs = ir_method->code->registers; + const dex::u4 ins_count = ir_method->code->ins_count; + SLICER_CHECK(regs >= ins_count); + + // generate the args "relocation" instructions + dex::u4 reg = regs - ins_count; + for (const auto& type : param_types) { + auto move = code_ir->Alloc<lir::Bytecode>(); + switch (type->GetCategory()) { + case ir::Type::Category::Reference: + move->opcode = dex::OP_MOVE_OBJECT_16; + move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift)); + move->operands.push_back(code_ir->Alloc<lir::VReg>(reg)); + reg += 1; + break; + case ir::Type::Category::Scalar: + move->opcode = dex::OP_MOVE_16; + move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift)); + move->operands.push_back(code_ir->Alloc<lir::VReg>(reg)); + reg += 1; + break; + case ir::Type::Category::WideScalar: + move->opcode = dex::OP_MOVE_WIDE_16; + move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg - shift)); + move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg)); + reg += 2; + break; + case ir::Type::Category::Void: + SLICER_FATAL("void parameter type"); + } + code_ir->instructions.InsertBefore(position, move); + } +} + bool EntryHook::InjectArrayParamsHook(lir::CodeIr* code_ir, lir::Bytecode* bytecode) { ir::Builder builder(code_ir->dex_ir); const auto ir_method = code_ir->ir_method; @@ -170,28 +218,28 @@ bool EntryHook::InjectArrayParamsHook(lir::CodeIr* code_ir, lir::Bytecode* bytec needsBoxingReg |= type->GetCategory() != ir::Type::Category::Reference; } - // allocate scract registers - slicer::AllocateScratchRegs alloc_regs(2 + needsBoxingReg); - alloc_regs.Apply(code_ir); - auto reg_iterator = alloc_regs.ScratchRegs().begin(); + // number of registers that we need to operate + dex::u2 regs_count = 2 + needsBoxingReg; + auto non_param_regs = ir_method->code->registers - ir_method->code->ins_count; + + // do we have enough registers to operate? + bool needsExtraRegs = non_param_regs < regs_count; + if (needsExtraRegs) { + // we don't have enough registers, so we allocate more, we will shift + // params to their original registers later. + code_ir->ir_method->code->registers += regs_count - non_param_regs; + } + + // simply use three first registry now + // register that will store size of during allocation // later will be reused to store index when do "aput" - dex::u4 array_size_reg = *(reg_iterator); + dex::u4 array_size_reg = 0; // register that will store an array that will be passed // as a parameter in entry hook - dex::u4 array_reg = *(++reg_iterator); + dex::u4 array_reg = 1; // if we need to boxing, this register stores result of boxing - dex::u4 boxing_reg = needsBoxingReg ? *(++reg_iterator) : 0; - - // TODO: handle very "high" registers - if (boxing_reg > 0xff) { - printf("WARNING: can't instrument method %s.%s%s\n", - ir_method->decl->parent->Decl().c_str(), - ir_method->decl->name->c_str(), - ir_method->decl->prototype->Signature().c_str()); - return false; - } - + dex::u4 boxing_reg = needsBoxingReg ? 2 : 0; // array size bytecode auto const_size_op = code_ir->Alloc<lir::Bytecode>(); const_size_op->opcode = dex::OP_CONST; @@ -265,6 +313,22 @@ bool EntryHook::InjectArrayParamsHook(lir::CodeIr* code_ir, lir::Bytecode* bytec hook_invoke->operands.push_back(args); hook_invoke->operands.push_back(hook_method); code_ir->instructions.InsertBefore(bytecode, hook_invoke); + + // clean up registries used by us + // registers are assigned to a marker value 0xFE_FE_FE_FE (decimal + // value: -16843010) to help identify use of uninitialized registers. + for (dex::u2 i = 0; i < regs_count; ++i) { + auto cleanup = code_ir->Alloc<lir::Bytecode>(); + cleanup->opcode = dex::OP_CONST; + cleanup->operands.push_back(code_ir->Alloc<lir::VReg>(i)); + cleanup->operands.push_back(code_ir->Alloc<lir::Const32>(0xFEFEFEFE)); + code_ir->instructions.InsertBefore(bytecode, cleanup); + } + + // now we have to shift params to their original registers + if (needsExtraRegs) { + GenerateShiftParamsCode(code_ir, bytecode, regs_count - non_param_regs); + } return true; } @@ -532,57 +596,15 @@ void AllocateScratchRegs::RegsRenumbering(lir::CodeIr* code_ir) { // void AllocateScratchRegs::ShiftParams(lir::CodeIr* code_ir) { const auto ir_method = code_ir->ir_method; - SLICER_CHECK(ir_method->code->ins_count > 0); SLICER_CHECK(left_to_allocate_ > 0); - // build a param list with the explicit "this" argument for non-static methods - std::vector<ir::Type*> param_types; - if ((ir_method->access_flags & dex::kAccStatic) == 0) { - param_types.push_back(ir_method->decl->parent); - } - if (ir_method->decl->prototype->param_types != nullptr) { - const auto& orig_param_types = ir_method->decl->prototype->param_types->types; - param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end()); - } - const dex::u4 shift = left_to_allocate_; - Allocate(code_ir, ir_method->code->registers, left_to_allocate_); assert(left_to_allocate_ == 0); - const dex::u4 regs = ir_method->code->registers; - const dex::u4 ins_count = ir_method->code->ins_count; - SLICER_CHECK(regs >= ins_count); - // generate the args "relocation" instructions - auto first_instr = code_ir->instructions.begin(); - dex::u4 reg = regs - ins_count; - for (const auto& type : param_types) { - auto move = code_ir->Alloc<lir::Bytecode>(); - switch (type->GetCategory()) { - case ir::Type::Category::Reference: - move->opcode = dex::OP_MOVE_OBJECT_16; - move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift)); - move->operands.push_back(code_ir->Alloc<lir::VReg>(reg)); - reg += 1; - break; - case ir::Type::Category::Scalar: - move->opcode = dex::OP_MOVE_16; - move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift)); - move->operands.push_back(code_ir->Alloc<lir::VReg>(reg)); - reg += 1; - break; - case ir::Type::Category::WideScalar: - move->opcode = dex::OP_MOVE_WIDE_16; - move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg - shift)); - move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg)); - reg += 2; - break; - case ir::Type::Category::Void: - SLICER_FATAL("void parameter type"); - } - code_ir->instructions.insert(first_instr, move); - } + auto first_instr = *(code_ir->instructions.begin()); + GenerateShiftParamsCode(code_ir, first_instr, shift); } // Mark [first_reg, first_reg + count) as scratch registers diff --git a/testdata/expected/mi.array_entry_hook b/testdata/expected/mi.array_entry_hook index 8f2a5e3..48eea67 100644 --- a/testdata/expected/mi.array_entry_hook +++ b/testdata/expected/mi.array_entry_hook @@ -50,22 +50,28 @@ method Target.foo(int, java.lang.String):int 0| const v0, #+3 (0x00000003 | 4.20390e-45) 3| new-array v1, v0, java.lang.Object[] 5| const v0, #+0 (0x00000000 | 0.00000) - 8| aput-object v5, v1, v0 - 10| invoke-static/range {v6..v6}, java.lang.Integer.valueOf(int):java.lang.Integer + 8| aput-object v3, v1, v0 + 10| invoke-static/range {v4..v4}, java.lang.Integer.valueOf(int):java.lang.Integer 13| move-result-object v2 14| const v0, #+1 (0x00000001 | 1.40130e-45) 17| aput-object v2, v1, v0 19| const v0, #+2 (0x00000002 | 2.80260e-45) - 22| aput-object v7, v1, v0 + 22| aput-object v5, v1, v0 24| invoke-static/range {v1..v1}, Tracer.onFooEntry(java.lang.Object[]):void - 27| iget-object v3, v5, Target.base - 29| invoke-virtual {v3,v6,v7}, Base.foo(int, java.lang.String):int - 32| move-result v3 + 27| const v0, #-16843010 (0xfefefefe | -1.69474e+38) + 30| const v1, #-16843010 (0xfefefefe | -1.69474e+38) + 33| const v2, #-16843010 (0xfefefefe | -1.69474e+38) + 36| move-object/16 v2, v3 + 39| move/16 v3, v4 + 42| move-object/16 v4, v5 + 45| iget-object v0, v2, Target.base + 47| invoke-virtual {v0,v3,v4}, Base.foo(int, java.lang.String):int + 50| move-result v0 .line 22 - 33| iget-object v4, v5, Target.iBase - 35| invoke-interface {v4,v7}, IBase.bar(java.lang.String):void + 51| iget-object v1, v2, Target.iBase + 53| invoke-interface {v1,v4}, IBase.bar(java.lang.String):void .line 23 - 38| return v3 + 56| return v0 } method Target.foo(int, java.lang.String[][]):java.lang.Integer @@ -78,22 +84,28 @@ method Target.foo(int, java.lang.String[][]):java.lang.Integer 0| const v0, #+3 (0x00000003 | 4.20390e-45) 3| new-array v1, v0, java.lang.Object[] 5| const v0, #+0 (0x00000000 | 0.00000) - 8| aput-object v5, v1, v0 - 10| invoke-static/range {v6..v6}, java.lang.Integer.valueOf(int):java.lang.Integer + 8| aput-object v3, v1, v0 + 10| invoke-static/range {v4..v4}, java.lang.Integer.valueOf(int):java.lang.Integer 13| move-result-object v2 14| const v0, #+1 (0x00000001 | 1.40130e-45) 17| aput-object v2, v1, v0 19| const v0, #+2 (0x00000002 | 2.80260e-45) - 22| aput-object v7, v1, v0 + 22| aput-object v5, v1, v0 24| invoke-static/range {v1..v1}, Tracer.onFooEntry(java.lang.Object[]):void - 27| iget-object v3, v5, Target.base - 29| const-string v4, "foo" - 31| invoke-virtual {v3,v6,v4}, Base.foo(int, java.lang.String):int + 27| const v0, #-16843010 (0xfefefefe | -1.69474e+38) + 30| const v1, #-16843010 (0xfefefefe | -1.69474e+38) + 33| const v2, #-16843010 (0xfefefefe | -1.69474e+38) + 36| move-object/16 v2, v3 + 39| move/16 v3, v4 + 42| move-object/16 v4, v5 + 45| iget-object v0, v2, Target.base + 47| const-string v1, "foo" + 49| invoke-virtual {v0,v3,v1}, Base.foo(int, java.lang.String):int .line 28 - 34| iget-object v3, v5, Target.iBase - 36| const-string v4, "bar" - 38| invoke-interface {v3,v4}, IBase.bar(java.lang.String):void + 52| iget-object v0, v2, Target.iBase + 54| const-string v1, "bar" + 56| invoke-interface {v0,v1}, IBase.bar(java.lang.String):void .line 29 - 41| const/4 v3, #+0 (0x00000000 | 0.00000) - 42| return-object v3 + 59| const/4 v0, #+0 (0x00000000 | 0.00000) + 60| return-object v0 } |
