aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey Vasilinets <sergeyv@google.com>2020-04-08 16:49:58 +0100
committerSergey Vasilinets <sergeyv@google.com>2020-04-12 23:48:07 +0100
commitc7647ba8018b84c399ce5d282d1ac1b9a44b5cd2 (patch)
treefd7ae37e7f04d875b68c2488ef306ce043c7a55a
parentfb3ac2a5b6efef22c621b973378be559f2d32196 (diff)
downloadplatform_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.cc144
-rw-r--r--testdata/expected/mi.array_entry_hook52
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
}