aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey Vasilinets <sergeyv@google.com>2019-11-26 06:31:54 -0800
committerandroid-build-merger <android-build-merger@google.com>2019-11-26 06:31:54 -0800
commitade947f00d14bf10ea06df1233b27aaaca176e70 (patch)
tree1b305933cba011a8932c38d0361010e38ffe844c
parent1e4d8b84febef53bf300ded8681bb55e8da46688 (diff)
parentbb05e0eb559f53d230bbce9be97e39270d8d6895 (diff)
downloadplatform_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.py1
-rw-r--r--dexter/experimental.cc13
-rw-r--r--slicer/export/slicer/instrumentation.h15
-rw-r--r--slicer/instrumentation.cc20
-rw-r--r--testdata/expected/mi.object_exit_hook80
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
+}