diff options
| author | Sergey Vasilinets <sergeyv@google.com> | 2020-04-08 16:49:58 +0100 |
|---|---|---|
| committer | Sergey Vasilinets <sergeyv@google.com> | 2020-04-12 23:48:07 +0100 |
| commit | c7647ba8018b84c399ce5d282d1ac1b9a44b5cd2 (patch) | |
| tree | fd7ae37e7f04d875b68c2488ef306ce043c7a55a | |
| parent | fb3ac2a5b6efef22c621b973378be559f2d32196 (diff) | |
| download | platform_tools_dexter-c7647ba8018b84c399ce5d282d1ac1b9a44b5cd2.tar.gz platform_tools_dexter-c7647ba8018b84c399ce5d282d1ac1b9a44b5cd2.tar.bz2 platform_tools_dexter-c7647ba8018b84c399ce5d282d1ac1b9a44b5cd2.zip | |
Support high registries in ArrayEntryHook.
Previous implementation was failing when scratch registries
were high, because new array instruction requires low registries
as params. This new implementation instead of allocating new
registries simply reuses available ones if there are enough of them.
If a menthod doesn't have enough registries, we increase registry
count, perform entry hook and then shift parameters to original
registries.
Test: AppInspectionTest in ag/10983351 and mi.array_entry_hook
bug: 153006865
Change-Id: I2ec18d479b83e96a6f15b7e93b1a3814e8c8e7e7
| -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 } |
