12faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes/* 2fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom * copyright (C) 2011 The Android Open Source Project 32faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * 42faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * Licensed under the Apache License, Version 2.0 (the "License"); 52faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * you may not use this file except in compliance with the License. 62faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * You may obtain a copy of the License at 72faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * 82faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * http://www.apache.org/licenses/LICENSE-2.0 92faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * 102faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * Unless required by applicable law or agreed to in writing, software 112faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * distributed under the License is distributed on an "AS IS" BASIS, 122faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * See the License for the specific language governing permissions and 142faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes * limitations under the License. 152faa5f1271587cda765f26bcf2951065300a01ffElliott Hughes */ 166b6b5f0e67ce03f38223a525612955663bc1799bCarl Shapiro 172ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro#include <signal.h> 1890a3369d3b6238f1a4c9b19ca68978dab1c39bc4Elliott Hughes 1990a3369d3b6238f1a4c9b19ca68978dab1c39bc4Elliott Hughes#include <cstdio> 2090a3369d3b6238f1a4c9b19ca68978dab1c39bc4Elliott Hughes#include <cstring> 212ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro#include <string> 227b21670581d13db32f1384a3b2692bcfc8f57320Carl Shapiro 232ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro#include "jni.h" 240eba633357c6c01b298434c7fd8a5705b5b2e18fBrian Carlstrom#include "JniInvocation.h" 25eac766769e3114a078c188ea26776a81f0edb3cfElliott Hughes#include "ScopedLocalRef.h" 26eac766769e3114a078c188ea26776a81f0edb3cfElliott Hughes#include "toStringArray.h" 27eac766769e3114a078c188ea26776a81f0edb3cfElliott Hughes#include "UniquePtr.h" 282ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 2911d1b0c31ddd710d26068da8e0e4621002205b4bElliott Hughesnamespace art { 3011d1b0c31ddd710d26068da8e0e4621002205b4bElliott Hughes 312ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro// Determine whether or not the specified method is public. 32e84278b035c8a48f6032ef98fae008d875fba7a4Elliott Hughesstatic bool IsMethodPublic(JNIEnv* env, jclass c, jmethodID method_id) { 33e84278b035c8a48f6032ef98fae008d875fba7a4Elliott Hughes ScopedLocalRef<jobject> reflected(env, env->ToReflectedMethod(c, method_id, JNI_FALSE)); 342ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (reflected.get() == NULL) { 353320cf46afd082398aa401b246e6f301cebdf64dBrian Carlstrom fprintf(stderr, "Failed to get reflected method\n"); 362ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro return false; 372ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 382ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // We now have a Method instance. We need to call its 392ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // getModifiers() method. 40fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom jclass method_class = env->FindClass("java/lang/reflect/Method"); 41fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom if (method_class == NULL) { 42fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom fprintf(stderr, "Failed to find class java.lang.reflect.Method\n"); 43fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom return false; 44fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom } 45fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom jmethodID mid = env->GetMethodID(method_class, "getModifiers", "()I"); 46eac766769e3114a078c188ea26776a81f0edb3cfElliott Hughes if (mid == NULL) { 47eac766769e3114a078c188ea26776a81f0edb3cfElliott Hughes fprintf(stderr, "Failed to find java.lang.reflect.Method.getModifiers\n"); 482ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro return false; 492ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 50eac766769e3114a078c188ea26776a81f0edb3cfElliott Hughes int modifiers = env->CallIntMethod(reflected.get(), mid); 51fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC 52fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom if ((modifiers & PUBLIC) == 0) { 532ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro return false; 542ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 552ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro return true; 562ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro} 572ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 581bac54ffa933fbe9b92b62437577f2f4583eff1aElliott Hughesstatic int InvokeMain(JNIEnv* env, char** argv) { 592ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // We want to call main() with a String array with our arguments in 602ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // it. Create an array and populate it. Note argv[0] is not 612ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // included. 620a96fe07175b960050a606fadbf76d85a060e4faElliott Hughes ScopedLocalRef<jobjectArray> args(env, toStringArray(env, argv + 1)); 632ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (args.get() == NULL) { 64d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes env->ExceptionDescribe(); 65d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes return EXIT_FAILURE; 662ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 672ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 682ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // Find [class].main(String[]). 692ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 702ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // Convert "com.android.Blah" to "com/android/Blah". 71e5b0dc83537bf915c6abe4efeae6e501daf75a27Elliott Hughes std::string class_name(argv[0]); 722ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro std::replace(class_name.begin(), class_name.end(), '.', '/'); 732ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 740dab4ecc01c5db0d0846bf5b4f608634d7404f74Elliott Hughes ScopedLocalRef<jclass> klass(env, env->FindClass(class_name.c_str())); 752ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (klass.get() == NULL) { 762ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro fprintf(stderr, "Unable to locate class '%s'\n", class_name.c_str()); 77d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes env->ExceptionDescribe(); 78d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes return EXIT_FAILURE; 792ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 802ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 81d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes jmethodID method = env->GetStaticMethodID(klass.get(), "main", "([Ljava/lang/String;)V"); 822ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (method == NULL) { 83d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes fprintf(stderr, "Unable to find static main(String[]) in '%s'\n", class_name.c_str()); 84d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes env->ExceptionDescribe(); 85d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes return EXIT_FAILURE; 862ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 872ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 882ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // Make sure the method is public. JNI doesn't prevent us from 892ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // calling a private method, so we have to check it explicitly. 902ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (!IsMethodPublic(env, klass.get(), method)) { 91d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes fprintf(stderr, "Sorry, main() is not public in '%s'\n", class_name.c_str()); 92d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes env->ExceptionDescribe(); 93d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes return EXIT_FAILURE; 942ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 952ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 962ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // Invoke main(). 972ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro env->CallStaticVoidMethod(klass.get(), method, args.get()); 980c8c6732ee872d6f475db6cd6c8e205e152b113cElliott Hughes 990c8c6732ee872d6f475db6cd6c8e205e152b113cElliott Hughes // Check whether there was an uncaught exception. We don't log any uncaught exception here; 1000c8c6732ee872d6f475db6cd6c8e205e152b113cElliott Hughes // detaching this thread will do that for us, but it will clear the exception (and invalidate 1010c8c6732ee872d6f475db6cd6c8e205e152b113cElliott Hughes // our JNIEnv), so we need to check here. 1020c8c6732ee872d6f475db6cd6c8e205e152b113cElliott Hughes return env->ExceptionCheck() ? EXIT_FAILURE : EXIT_SUCCESS; 1032ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro} 1042ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 10581ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes// Parse arguments. Most of it just gets passed through to the runtime. 1062ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro// The JNI spec defines a handful of standard arguments. 107fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstromstatic int dalvikvm(int argc, char** argv) { 1082ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro setvbuf(stdout, NULL, _IONBF, 0); 1092ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1102ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // Skip over argv[0]. 1112ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro argv++; 1122ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro argc--; 1132ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1142ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // If we're adding any additional stuff, e.g. function hook specifiers, 1152ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // add them to the count here. 1162ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // 11781ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes // We're over-allocating, because this includes the options to the runtime 1182ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // plus the options to the program. 1192ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro int option_count = argc; 12090a3369d3b6238f1a4c9b19ca68978dab1c39bc4Elliott Hughes UniquePtr<JavaVMOption[]> options(new JavaVMOption[option_count]()); 1212ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1222ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // Copy options over. Everything up to the name of the class starts 1232ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // with a '-' (the function hook stuff is strictly internal). 1242ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // 1252ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // [Do we need to catch & handle "-jar" here?] 1262ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro bool need_extra = false; 127934313b5b14474fee3b84bbda0f0d3fe4805c63fBrian Carlstrom const char* lib = NULL; 12881ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes const char* what = NULL; 1292ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro int curr_opt, arg_idx; 1302ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro for (curr_opt = arg_idx = 0; arg_idx < argc; arg_idx++) { 1312ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (argv[arg_idx][0] != '-' && !need_extra) { 1322ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro break; 1332ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 134fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom if (strncmp(argv[arg_idx], "-XXlib:", strlen("-XXlib:")) == 0) { 135fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom lib = argv[arg_idx] + strlen("-XXlib:"); 136fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom continue; 137fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom } 138fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom 1392ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro options[curr_opt++].optionString = argv[arg_idx]; 1402ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1412ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // Some options require an additional argument. 1422ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro need_extra = false; 143d6fe38d96b6116bd53cf2cb14734af8d69e08661Elliott Hughes if (strcmp(argv[arg_idx], "-classpath") == 0 || strcmp(argv[arg_idx], "-cp") == 0) { 1442ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro need_extra = true; 14581ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes what = argv[arg_idx]; 1462ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 1472ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 1482ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1492ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (need_extra) { 15081ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes fprintf(stderr, "%s must be followed by an additional argument giving a value\n", what); 1512ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro return EXIT_FAILURE; 1522ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 1532ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 15481ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes // Make sure they provided a class name. 1552ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (arg_idx == argc) { 1562ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro fprintf(stderr, "Class name required\n"); 1572ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro return EXIT_FAILURE; 1582ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 1592ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1602ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro // insert additional internal options here 1612ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 162fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom if (curr_opt >= option_count) { 163fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom fprintf(stderr, "curr_opt(%d) >= option_count(%d)\n", curr_opt, option_count); 164fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom abort(); 165fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom return EXIT_FAILURE; 166fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom } 167fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom 168fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom // Find the JNI_CreateJavaVM implementation. 1690eba633357c6c01b298434c7fd8a5705b5b2e18fBrian Carlstrom JniInvocation jni_invocation; 1700eba633357c6c01b298434c7fd8a5705b5b2e18fBrian Carlstrom if (!jni_invocation.Init(lib)) { 1710eba633357c6c01b298434c7fd8a5705b5b2e18fBrian Carlstrom fprintf(stderr, "Failed to initialize JNI invocation API from %s\n", lib); 172fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom return EXIT_FAILURE; 173fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom } 1742ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1752ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro JavaVMInitArgs init_args; 176f2682d5a6ce0f7de58da8fd4ec8aec200c43b92eElliott Hughes init_args.version = JNI_VERSION_1_6; 1772ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro init_args.options = options.get(); 1782ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro init_args.nOptions = curr_opt; 1792ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro init_args.ignoreUnrecognized = JNI_FALSE; 1802ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 18181ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes // Start the runtime. The current thread becomes the main thread. 1822ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro JavaVM* vm = NULL; 1832ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro JNIEnv* env = NULL; 1842ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro if (JNI_CreateJavaVM(&vm, &env, &init_args) != JNI_OK) { 185fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom fprintf(stderr, "Failed to initialize runtime (check log for details)\n"); 1862ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro return EXIT_FAILURE; 1872ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 1882ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 1891bac54ffa933fbe9b92b62437577f2f4583eff1aElliott Hughes int rc = InvokeMain(env, &argv[arg_idx]); 1902ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 19117057b15cb8d8da97b2bc28fd38bdcc7a34e846eElliott Hughes#if defined(NDEBUG) 19217057b15cb8d8da97b2bc28fd38bdcc7a34e846eElliott Hughes // The DestroyJavaVM call will detach this thread for us. In debug builds, we don't want to 19317057b15cb8d8da97b2bc28fd38bdcc7a34e846eElliott Hughes // detach because detaching disables the CheckSafeToLockOrUnlock checking. 194f2682d5a6ce0f7de58da8fd4ec8aec200c43b92eElliott Hughes if (vm->DetachCurrentThread() != JNI_OK) { 1952ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro fprintf(stderr, "Warning: unable to detach main thread\n"); 196c1674ed06662420213441ff2b818f2f71f9098dcElliott Hughes rc = EXIT_FAILURE; 1972ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 19817057b15cb8d8da97b2bc28fd38bdcc7a34e846eElliott Hughes#endif 1992ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 200f2682d5a6ce0f7de58da8fd4ec8aec200c43b92eElliott Hughes if (vm->DestroyJavaVM() != 0) { 20181ff3184e7eb8de4605c7646674ea4f9fa29b5f3Elliott Hughes fprintf(stderr, "Warning: runtime did not shut down cleanly\n"); 202c1674ed06662420213441ff2b818f2f71f9098dcElliott Hughes rc = EXIT_FAILURE; 2032ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro } 2042ed144c2b49ae1da6c464d7a1be0062870530802Carl Shapiro 205c1674ed06662420213441ff2b818f2f71f9098dcElliott Hughes return rc; 2067b21670581d13db32f1384a3b2692bcfc8f57320Carl Shapiro} 20711d1b0c31ddd710d26068da8e0e4621002205b4bElliott Hughes 2087934ac288acfb2552bb0b06ec1f61e5820d924a4Brian Carlstrom} // namespace art 20911d1b0c31ddd710d26068da8e0e4621002205b4bElliott Hughes 21011d1b0c31ddd710d26068da8e0e4621002205b4bElliott Hughesint main(int argc, char** argv) { 211fa42b4410d49134a8e63dc2196be4013d286f2d6Brian Carlstrom return art::dalvikvm(argc, argv); 21211d1b0c31ddd710d26068da8e0e4621002205b4bElliott Hughes} 213