diff options
| author | Sergey Vasilinets <sergeyv@google.com> | 2019-11-26 06:31:54 -0800 |
|---|---|---|
| committer | android-build-merger <android-build-merger@google.com> | 2019-11-26 06:31:54 -0800 |
| commit | ade947f00d14bf10ea06df1233b27aaaca176e70 (patch) | |
| tree | 1b305933cba011a8932c38d0361010e38ffe844c | |
| parent | 1e4d8b84febef53bf300ded8681bb55e8da46688 (diff) | |
| parent | bb05e0eb559f53d230bbce9be97e39270d8d6895 (diff) | |
| download | platform_tools_dexter-ade947f00d14bf10ea06df1233b27aaaca176e70.tar.gz platform_tools_dexter-ade947f00d14bf10ea06df1233b27aaaca176e70.tar.bz2 platform_tools_dexter-ade947f00d14bf10ea06df1233b27aaaca176e70.zip | |
Create Tweak::ReturnAsObject for ExitHook am: 8940e41223 am: 17c72cc4ec
am: bb05e0eb55
Change-Id: I5aa21eca3a042e06d1d11fcd2030e200f378335e
| -rw-r--r-- | dexter/dexter_tests.py | 1 | ||||
| -rw-r--r-- | dexter/experimental.cc | 13 | ||||
| -rw-r--r-- | slicer/export/slicer/instrumentation.h | 15 | ||||
| -rw-r--r-- | slicer/instrumentation.cc | 20 | ||||
| -rw-r--r-- | testdata/expected/mi.object_exit_hook | 80 |
5 files changed, 125 insertions, 4 deletions
diff --git a/dexter/dexter_tests.py b/dexter/dexter_tests.py index 5e5fe75..5b639ee 100644 --- a/dexter/dexter_tests.py +++ b/dexter/dexter_tests.py @@ -47,6 +47,7 @@ test_cases = { 'regs_usage' : { 'args' : '-x regs_histogram', 'input' : ['*.dex'] }, 'code_coverage' : { 'args' : '-d -x code_coverage', 'input' : ['*.dex'] }, 'array_entry_hook' : { 'args' : '-d -x array_param_entry_hook', 'input' : ['mi.dex'] }, + 'object_exit_hook' : { 'args' : '-d -x return_obj_exit_hook', 'input' : ['mi.dex'] }, } # run a shell command and returns the stdout content diff --git a/dexter/experimental.cc b/dexter/experimental.cc index b721883..47ae29e 100644 --- a/dexter/experimental.cc +++ b/dexter/experimental.cc @@ -504,6 +504,17 @@ void TestArrayParamsEntryHook(std::shared_ptr<ir::DexFile> dex_ir) { SLICER_CHECK(mi.InstrumentMethod(method2)); } +// Test slicer::MethodInstrumenter + Tweak::ReturnAsObject +void TestReturnAsObjectExitHook(std::shared_ptr<ir::DexFile> dex_ir) { + slicer::MethodInstrumenter mi(dex_ir); + mi.AddTransformation<slicer::ExitHook>(ir::MethodId("LTracer;", "onFooExit"), + slicer::ExitHook::Tweak::ReturnAsObject); + + auto method = ir::MethodId("LTarget;", "foo", "(I[[Ljava/lang/String;)Ljava/lang/Integer;"); + SLICER_CHECK(mi.InstrumentMethod(method)); +} + + void ListExperiments(std::shared_ptr<ir::DexFile> dex_ir); using Experiment = void (*)(std::shared_ptr<ir::DexFile>); @@ -521,6 +532,8 @@ std::map<std::string, Experiment> experiments_registry = { { "regs_histogram", &RegsHistogram }, { "code_coverage", &CodeCoverage }, { "array_param_entry_hook", &TestArrayParamsEntryHook }, + { "return_obj_exit_hook", &TestReturnAsObjectExitHook }, + }; // Lists all the registered experiments diff --git a/slicer/export/slicer/instrumentation.h b/slicer/export/slicer/instrumentation.h index bdc04c7..0cb2cc1 100644 --- a/slicer/export/slicer/instrumentation.h +++ b/slicer/export/slicer/instrumentation.h @@ -82,15 +82,28 @@ class EntryHook : public Transformation { // original return value and it may return a new return value. class ExitHook : public Transformation { public: - explicit ExitHook(const ir::MethodId& hook_method_id) : hook_method_id_(hook_method_id) { + enum class Tweak { + None, + // return value will be passed as "Object" type. + // This can be helpful when the code you want to handle the hook doesn't + // have access to the actual type in its classpath or when you want to inject + // the same hook in multiple methods. + ReturnAsObject, + }; + + explicit ExitHook(const ir::MethodId& hook_method_id, Tweak tweak) + : hook_method_id_(hook_method_id), tweak_(tweak) { // hook method signature is generated automatically SLICER_CHECK(hook_method_id_.signature == nullptr); } + explicit ExitHook(const ir::MethodId& hook_method_id) : ExitHook(hook_method_id, Tweak::None) {} + virtual bool Apply(lir::CodeIr* code_ir) override; private: ir::MethodId hook_method_id_; + Tweak tweak_; }; // Base class for detour hooks. Replace every occurrence of specific opcode with diff --git a/slicer/instrumentation.cc b/slicer/instrumentation.cc index 9eaf5da..45895ac 100644 --- a/slicer/instrumentation.cc +++ b/slicer/instrumentation.cc @@ -270,10 +270,15 @@ bool EntryHook::InjectArrayParamsHook(lir::CodeIr* code_ir, lir::Bytecode* bytec bool ExitHook::Apply(lir::CodeIr* code_ir) { ir::Builder builder(code_ir->dex_ir); const auto ir_method = code_ir->ir_method; - const auto return_type = ir_method->decl->prototype->return_type; - + const auto declared_return_type = ir_method->decl->prototype->return_type; + bool return_as_object = tweak_ == Tweak::ReturnAsObject; // do we have a void-return method? - bool return_void = (::strcmp(return_type->descriptor->c_str(), "V") == 0); + bool return_void = (::strcmp(declared_return_type->descriptor->c_str(), "V") == 0); + // returnAsObject supports only object return type; + SLICER_CHECK(!return_as_object || + (declared_return_type->GetCategory() == ir::Type::Category::Reference)); + const auto return_type = return_as_object ? builder.GetType("Ljava/lang/Object;") + : declared_return_type; // construct the hook method declaration std::vector<ir::Type*> param_types; @@ -349,6 +354,15 @@ bool ExitHook::Apply(lir::CodeIr* code_ir) { move_result->opcode = move_result_opcode; move_result->operands.push_back(bytecode->operands[0]); code_ir->instructions.InsertBefore(bytecode, move_result); + + if (tweak_ == Tweak::ReturnAsObject) { + auto check_cast = code_ir->Alloc<lir::Bytecode>(); + check_cast->opcode = dex::OP_CHECK_CAST; + check_cast->operands.push_back(code_ir->Alloc<lir::VReg>(reg)); + check_cast->operands.push_back( + code_ir->Alloc<lir::Type>(declared_return_type, declared_return_type->orig_index)); + code_ir->instructions.InsertBefore(bytecode, check_cast); + } } } diff --git a/testdata/expected/mi.object_exit_hook b/testdata/expected/mi.object_exit_hook new file mode 100644 index 0000000..d49ebae --- /dev/null +++ b/testdata/expected/mi.object_exit_hook @@ -0,0 +1,80 @@ + +method Base.<init>():void +{ + .src "Target.java" + .line 1 + .prologue_end + .line 1 + 0| invoke-direct {v0}, java.lang.Object.<init>():void + 3| return-void +} + +method Base.foo(int, java.lang.String):int +{ + .params "?", "?" + .src "Target.java" + .line 3 + .prologue_end + .line 3 + 0| const/4 v0, #+0 (0x00000000 | 0.00000) + 1| return v0 +} + +method IBase.bar(java.lang.String):void +{ +} + +method Target.<init>(Base, IBase):void +{ + .params "?", "?" + .src "Target.java" + .line 15 + .prologue_end + .line 15 + 0| invoke-direct {v0}, java.lang.Object.<init>():void + .line 16 + 3| iput-object v1, v0, Target.base + .line 17 + 5| iput-object v2, v0, Target.iBase + .line 18 + 7| return-void +} + +method Target.foo(int, java.lang.String):int +{ + .params "?", "?" + .src "Target.java" + .line 21 + .prologue_end + .line 21 + 0| iget-object v0, v2, Target.base + 2| invoke-virtual {v0,v3,v4}, Base.foo(int, java.lang.String):int + 5| move-result v0 + .line 22 + 6| iget-object v1, v2, Target.iBase + 8| invoke-interface {v1,v4}, IBase.bar(java.lang.String):void + .line 23 + 11| return v0 +} + +method Target.foo(int, java.lang.String[][]):java.lang.Integer +{ + .params "?", "?" + .src "Target.java" + .line 27 + .prologue_end + .line 27 + 0| iget-object v0, v2, Target.base + 2| const-string v1, "foo" + 4| invoke-virtual {v0,v3,v1}, Base.foo(int, java.lang.String):int + .line 28 + 7| iget-object v0, v2, Target.iBase + 9| const-string v1, "bar" + 11| invoke-interface {v0,v1}, IBase.bar(java.lang.String):void + .line 29 + 14| const/4 v0, #+0 (0x00000000 | 0.00000) + 15| invoke-static/range {v0..v0}, Tracer.onFooExit(java.lang.Object):java.lang.Object + 18| move-result-object v0 + 19| check-cast v0, java.lang.Integer + 21| return-object v0 +} |
