Home | History | Annotate | Download | only in dalvikvm
      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