Get a basic modification of dex file working

This allows the modification of a single classes methods through
transformation. One must ensure that the provided dex file only
contains one function and does not add or remove any methods or fields
and does not change the inheritance hierarchy in any way. The provided
dex file must verify and there must be no frames of the old code
present on any thread. These constraints are not checked or verified.
Breaking them might cause undefined behavior in all parts of the
runtime. Code that has been inlined in any way might not be replaced.
This feature is extremely experimental.

Bug: 31455788
Test: ./test/run-test --host 902-hello-transformation

Change-Id: I35133d24f6cdafdd2af9dc9863e15ba8493fc50e
diff --git a/test/902-hello-transformation/build b/test/902-hello-transformation/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/902-hello-transformation/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/902-hello-transformation/expected.txt b/test/902-hello-transformation/expected.txt
new file mode 100644
index 0000000..e86e814
--- /dev/null
+++ b/test/902-hello-transformation/expected.txt
@@ -0,0 +1,3 @@
+Hello
+modifying class 'Transform'
+Goodbye
diff --git a/test/902-hello-transformation/info.txt b/test/902-hello-transformation/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/902-hello-transformation/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/902-hello-transformation/run b/test/902-hello-transformation/run
new file mode 100755
index 0000000..204e4cc
--- /dev/null
+++ b/test/902-hello-transformation/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+  other_args=" -Xcompiler-option --debuggable "
+else
+  other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=902-hello-transformation,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/902-hello-transformation/src/Main.java b/test/902-hello-transformation/src/Main.java
new file mode 100644
index 0000000..204b6e7
--- /dev/null
+++ b/test/902-hello-transformation/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest(new Transform());
+  }
+
+  public static void doTest(Transform t) {
+    t.sayHi();
+    doClassTransformation(Transform.class);
+    t.sayHi();
+  }
+
+  // Transforms the class
+  private static native void doClassTransformation(Class target);
+}
diff --git a/test/902-hello-transformation/src/Transform.java b/test/902-hello-transformation/src/Transform.java
new file mode 100644
index 0000000..dc0a0c4
--- /dev/null
+++ b/test/902-hello-transformation/src/Transform.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Transform {
+  public void sayHi() {
+    System.out.println("Hello");
+  }
+}
diff --git a/test/902-hello-transformation/transform.cc b/test/902-hello-transformation/transform.cc
new file mode 100644
index 0000000..e0d623e
--- /dev/null
+++ b/test/902-hello-transformation/transform.cc
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "art_method-inl.h"
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "utils.h"
+
+namespace art {
+namespace Test902HelloTransformation {
+
+static bool RuntimeIsJvm = false;
+
+jvmtiEnv* jvmti_env;
+bool IsJVM() {
+  return RuntimeIsJvm;
+}
+
+// base64 encoded class/dex file for
+//
+// class Transform {
+//   public void sayHi() {
+//     System.out.println("Goodbye");
+//   }
+// }
+const char* class_file_base64 =
+    "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB"
+    "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA"
+    "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq"
+    "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph"
+    "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG"
+    "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB"
+    "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=";
+
+const char* dex_file_base64 =
+    "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO"
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB"
+    "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA"
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA"
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA"
+    "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA"
+    "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50"
+    "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh"
+    "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291"
+    "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA"
+    "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA"
+    "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA"
+    "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=";
+
+static void JNICALL transformationHook(jvmtiEnv *jvmtienv,
+                                       JNIEnv* jni_env                 ATTRIBUTE_UNUSED,
+                                       jclass class_being_redefined    ATTRIBUTE_UNUSED,
+                                       jobject loader                  ATTRIBUTE_UNUSED,
+                                       const char* name,
+                                       jobject protection_domain       ATTRIBUTE_UNUSED,
+                                       jint class_data_len             ATTRIBUTE_UNUSED,
+                                       const unsigned char* class_data ATTRIBUTE_UNUSED,
+                                       jint* new_class_data_len,
+                                       unsigned char** new_class_data) {
+  if (strcmp("Transform", name)) {
+    return;
+  }
+  printf("modifying class '%s'\n", name);
+  bool is_jvm = IsJVM();
+  size_t decode_len = 0;
+  unsigned char* new_data;
+  std::unique_ptr<uint8_t[]> file_data(
+      DecodeBase64((is_jvm) ? class_file_base64 : dex_file_base64, &decode_len));
+  jvmtiError ret = JVMTI_ERROR_NONE;
+  if ((ret = jvmtienv->Allocate(static_cast<jlong>(decode_len), &new_data)) != JVMTI_ERROR_NONE) {
+    printf("Unable to allocate buffer!\n");
+    return;
+  }
+  memcpy(new_data, file_data.get(), decode_len);
+  *new_class_data_len = static_cast<jint>(decode_len);
+  *new_class_data = new_data;
+  return;
+}
+
+using RetransformWithHookFunction = jvmtiError (*)(jvmtiEnv*, jclass, jvmtiEventClassFileLoadHook);
+static void DoClassTransformation(jvmtiEnv* jvmtienv, JNIEnv* jnienv, jclass target) {
+  if (IsJVM()) {
+    UNUSED(jnienv);
+    jvmtienv->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullptr);
+    jvmtiError ret = jvmtienv->RetransformClasses(1, &target);
+    if (ret != JVMTI_ERROR_NONE) {
+      char* err;
+      jvmtienv->GetErrorName(ret, &err);
+      printf("Error transforming: %s\n", err);
+    }
+  } else {
+    RetransformWithHookFunction f =
+        reinterpret_cast<RetransformWithHookFunction>(jvmtienv->functions->reserved1);
+    if (f(jvmtienv, target, transformationHook) != JVMTI_ERROR_NONE) {
+      printf("Failed to tranform class!");
+      return;
+    }
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doClassTransformation(JNIEnv* env,
+                                                                  jclass,
+                                                                  jclass target) {
+  JavaVM* vm;
+  if (env->GetJavaVM(&vm)) {
+    printf("Unable to get javaVM!\n");
+    return;
+  }
+  DoClassTransformation(jvmti_env, env, target);
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options,
+            void* reserved ATTRIBUTE_UNUSED) {
+  jvmtiCapabilities caps;
+  RuntimeIsJvm = (strcmp("jvm", options) == 0);
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  if (IsJVM()) {
+    jvmti_env->GetPotentialCapabilities(&caps);
+    jvmti_env->AddCapabilities(&caps);
+    jvmtiEventCallbacks cbs;
+    memset(&cbs, 0, sizeof(cbs));
+    cbs.ClassFileLoadHook = transformationHook;
+    jvmti_env->SetEventCallbacks(&cbs, sizeof(jvmtiEventCallbacks));
+  }
+  return 0;
+}
+
+}  // namespace Test902HelloTransformation
+}  // namespace art
+
diff --git a/test/902-hello-transformation/transform.h b/test/902-hello-transformation/transform.h
new file mode 100644
index 0000000..661058d
--- /dev/null
+++ b/test/902-hello-transformation/transform.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_902_HELLO_TRANSFORMATION_TRANSFORM_H_
+#define ART_TEST_902_HELLO_TRANSFORMATION_TRANSFORM_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test902HelloTransformation {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test902HelloTransformation
+}  // namespace art
+
+#endif  // ART_TEST_902_HELLO_TRANSFORMATION_TRANSFORM_H_
diff --git a/test/Android.bp b/test/Android.bp
index ff408f4..84e82da 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -239,6 +239,7 @@
     srcs: [
         "ti-agent/common_load.cc",
         "901-hello-ti-agent/basics.cc",
+        "902-hello-transformation/transform.cc",
     ],
     shared_libs: [
         "libart",
@@ -255,6 +256,7 @@
     srcs: [
         "ti-agent/common_load.cc",
         "901-hello-ti-agent/basics.cc",
+        "902-hello-transformation/transform.cc",
     ],
     shared_libs: [
         "libartd",
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index a445f4d..c51cb0d 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -310,8 +310,9 @@
 fi
 
 if [ "$USE_JVM" = "y" ]; then
+  export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
   # Xmx is necessary since we don't pass down the ART flags to JVM.
-  cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@"
+  cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes ${FLAGS} $MAIN $@ ${ARGS}"
   if [ "$DEV_MODE" = "y" ]; then
     echo $cmdline
   fi
diff --git a/test/run-test b/test/run-test
index ae53f9e..250263a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -743,9 +743,7 @@
   fi
 fi
 
-if [ "$runtime" != "jvm" ]; then
   run_args="${run_args} --testlib ${testlib}"
-fi
 
 # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB.
 build_file_size_limit=2048
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index ed280e4..53bb153 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 
 #include "901-hello-ti-agent/basics.h"
+#include "902-hello-transformation/transform.h"
 
 namespace art {
 
@@ -39,6 +40,7 @@
 // A list of all the agents we have for testing.
 AgentLib agents[] = {
   { "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr },
+  { "902-hello-transformation", Test902HelloTransformation::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {