• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
blockSigpipe()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  */
createStringArray(JNIEnv * env,char * const argv[],int argc)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  */
methodIsPublic(JNIEnv * env,jclass clazz,jmethodID methodId)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  */
main(int argc,char * const argv[])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 
303