1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 /* 17 * Command-line invocation of the Dalvik VM. 18 */ 19 #include "jni.h" 20 21 #include <stdlib.h> 22 #include <stdio.h> 23 #include <string.h> 24 #include <signal.h> 25 #include <assert.h> 26 27 28 /* 29 * We want failed write() calls to just return with an error. 30 */ 31 static void blockSigpipe() 32 { 33 sigset_t mask; 34 35 sigemptyset(&mask); 36 sigaddset(&mask, SIGPIPE); 37 if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0) 38 fprintf(stderr, "WARNING: SIGPIPE not blocked\n"); 39 } 40 41 /* 42 * Create a String[] and populate it with the contents of argv. 43 */ 44 static jobjectArray createStringArray(JNIEnv* env, char* const argv[], int argc) 45 { 46 jclass stringClass = NULL; 47 jobjectArray strArray = NULL; 48 jobjectArray result = NULL; 49 int i; 50 51 stringClass = (*env)->FindClass(env, "java/lang/String"); 52 if ((*env)->ExceptionCheck(env)) { 53 fprintf(stderr, "Got exception while finding class String\n"); 54 goto bail; 55 } 56 assert(stringClass != NULL); 57 strArray = (*env)->NewObjectArray(env, argc, stringClass, NULL); 58 if ((*env)->ExceptionCheck(env)) { 59 fprintf(stderr, "Got exception while creating String array\n"); 60 goto bail; 61 } 62 assert(strArray != NULL); 63 64 for (i = 0; i < argc; i++) { 65 jstring argStr; 66 67 argStr = (*env)->NewStringUTF(env, argv[i]); 68 if ((*env)->ExceptionCheck(env)) { 69 fprintf(stderr, "Got exception while allocating Strings\n"); 70 goto bail; 71 } 72 assert(argStr != NULL); 73 (*env)->SetObjectArrayElement(env, strArray, i, argStr); 74 (*env)->DeleteLocalRef(env, argStr); 75 } 76 77 /* return the array, and ensure we don't delete the local ref to it */ 78 result = strArray; 79 strArray = NULL; 80 81 bail: 82 (*env)->DeleteLocalRef(env, stringClass); 83 (*env)->DeleteLocalRef(env, strArray); 84 return result; 85 } 86 87 /* 88 * Determine whether or not the specified method is public. 89 * 90 * Returns JNI_TRUE on success, JNI_FALSE on failure. 91 */ 92 static int methodIsPublic(JNIEnv* env, jclass clazz, jmethodID methodId) 93 { 94 static const int PUBLIC = 0x0001; // java.lang.reflect.Modifiers.PUBLIC 95 jobject refMethod = NULL; 96 jclass methodClass = NULL; 97 jmethodID getModifiersId; 98 int modifiers; 99 int result = JNI_FALSE; 100 101 refMethod = (*env)->ToReflectedMethod(env, clazz, methodId, JNI_FALSE); 102 if (refMethod == NULL) { 103 fprintf(stderr, "Dalvik VM unable to get reflected method\n"); 104 goto bail; 105 } 106 107 /* 108 * We now have a Method instance. We need to call 109 * its getModifiers() method. 110 */ 111 methodClass = (*env)->FindClass(env, "java/lang/reflect/Method"); 112 if (methodClass == NULL) { 113 fprintf(stderr, "Dalvik VM unable to find class Method\n"); 114 goto bail; 115 } 116 getModifiersId = (*env)->GetMethodID(env, methodClass, 117 "getModifiers", "()I"); 118 if (getModifiersId == NULL) { 119 fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n"); 120 goto bail; 121 } 122 123 modifiers = (*env)->CallIntMethod(env, refMethod, getModifiersId); 124 if ((modifiers & PUBLIC) == 0) { 125 fprintf(stderr, "Dalvik VM: main() is not public\n"); 126 goto bail; 127 } 128 129 result = JNI_TRUE; 130 131 bail: 132 (*env)->DeleteLocalRef(env, refMethod); 133 (*env)->DeleteLocalRef(env, methodClass); 134 return result; 135 } 136 137 /* 138 * Parse arguments. Most of it just gets passed through to the VM. The 139 * JNI spec defines a handful of standard arguments. 140 */ 141 int main(int argc, char* const argv[]) 142 { 143 JavaVM* vm = NULL; 144 JNIEnv* env = NULL; 145 JavaVMInitArgs initArgs; 146 JavaVMOption* options = NULL; 147 char* slashClass = NULL; 148 int optionCount, curOpt, i, argIdx; 149 int needExtra = JNI_FALSE; 150 int result = 1; 151 152 setvbuf(stdout, NULL, _IONBF, 0); 153 154 /* ignore argv[0] */ 155 argv++; 156 argc--; 157 158 /* 159 * If we're adding any additional stuff, e.g. function hook specifiers, 160 * add them to the count here. 161 * 162 * We're over-allocating, because this includes the options to the VM 163 * plus the options to the program. 164 */ 165 optionCount = argc; 166 167 options = (JavaVMOption*) malloc(sizeof(JavaVMOption) * optionCount); 168 memset(options, 0, sizeof(JavaVMOption) * optionCount); 169 170 /* 171 * Copy options over. Everything up to the name of the class starts 172 * with a '-' (the function hook stuff is strictly internal). 173 * 174 * [Do we need to catch & handle "-jar" here?] 175 */ 176 for (curOpt = argIdx = 0; argIdx < argc; argIdx++) { 177 if (argv[argIdx][0] != '-' && !needExtra) 178 break; 179 options[curOpt++].optionString = strdup(argv[argIdx]); 180 181 /* some options require an additional arg */ 182 needExtra = JNI_FALSE; 183 if (strcmp(argv[argIdx], "-classpath") == 0 || 184 strcmp(argv[argIdx], "-cp") == 0) 185 /* others? */ 186 { 187 needExtra = JNI_TRUE; 188 } 189 } 190 191 if (needExtra) { 192 fprintf(stderr, "Dalvik VM requires value after last option flag\n"); 193 goto bail; 194 } 195 196 /* insert additional internal options here */ 197 198 assert(curOpt <= optionCount); 199 200 initArgs.version = JNI_VERSION_1_4; 201 initArgs.options = options; 202 initArgs.nOptions = curOpt; 203 initArgs.ignoreUnrecognized = JNI_FALSE; 204 205 //printf("nOptions = %d\n", initArgs.nOptions); 206 207 blockSigpipe(); 208 209 /* 210 * Start VM. The current thread becomes the main thread of the VM. 211 */ 212 if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) { 213 fprintf(stderr, "Dalvik VM init failed (check log file)\n"); 214 goto bail; 215 } 216 217 /* 218 * Make sure they provided a class name. We do this after VM init 219 * so that things like "-Xrunjdwp:help" have the opportunity to emit 220 * a usage statement. 221 */ 222 if (argIdx == argc) { 223 fprintf(stderr, "Dalvik VM requires a class name\n"); 224 goto bail; 225 } 226 227 /* 228 * We want to call main() with a String array with our arguments in it. 229 * Create an array and populate it. Note argv[0] is not included. 230 */ 231 jobjectArray strArray; 232 strArray = createStringArray(env, &argv[argIdx+1], argc-argIdx-1); 233 if (strArray == NULL) 234 goto bail; 235 236 /* 237 * Find [class].main(String[]). 238 */ 239 jclass startClass; 240 jmethodID startMeth; 241 char* cp; 242 243 /* convert "com.android.Blah" to "com/android/Blah" */ 244 slashClass = strdup(argv[argIdx]); 245 for (cp = slashClass; *cp != '\0'; cp++) 246 if (*cp == '.') 247 *cp = '/'; 248 249 startClass = (*env)->FindClass(env, slashClass); 250 if (startClass == NULL) { 251 fprintf(stderr, "Dalvik VM unable to locate class '%s'\n", slashClass); 252 goto bail; 253 } 254 255 startMeth = (*env)->GetStaticMethodID(env, startClass, 256 "main", "([Ljava/lang/String;)V"); 257 if (startMeth == NULL) { 258 fprintf(stderr, "Dalvik VM unable to find static main(String[]) in '%s'\n", 259 slashClass); 260 goto bail; 261 } 262 263 /* 264 * Make sure the method is public. JNI doesn't prevent us from calling 265 * a private method, so we have to check it explicitly. 266 */ 267 if (!methodIsPublic(env, startClass, startMeth)) 268 goto bail; 269 270 /* 271 * Invoke main(). 272 */ 273 (*env)->CallStaticVoidMethod(env, startClass, startMeth, strArray); 274 275 if (!(*env)->ExceptionCheck(env)) 276 result = 0; 277 278 bail: 279 /*printf("Shutting down Dalvik VM\n");*/ 280 if (vm != NULL) { 281 /* 282 * This allows join() and isAlive() on the main thread to work 283 * correctly, and also provides uncaught exception handling. 284 */ 285 if ((*vm)->DetachCurrentThread(vm) != JNI_OK) { 286 fprintf(stderr, "Warning: unable to detach main thread\n"); 287 result = 1; 288 } 289 290 if ((*vm)->DestroyJavaVM(vm) != 0) 291 fprintf(stderr, "Warning: Dalvik VM did not shut down cleanly\n"); 292 /*printf("\nDalvik VM has exited\n");*/ 293 } 294 295 for (i = 0; i < optionCount; i++) 296 free((char*) options[i].optionString); 297 free(options); 298 free(slashClass); 299 /*printf("--- VM is down, process exiting\n");*/ 300 return result; 301 } 302