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