• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * copyright (C) 2011 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 #include <signal.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <algorithm>
22 #include <memory>
23 
24 #include "base/fast_exit.h"
25 #include "jni.h"
26 #include "nativehelper/JniInvocation.h"
27 #include "nativehelper/ScopedLocalRef.h"
28 #include "nativehelper/toStringArray.h"
29 
30 namespace art {
31 
32 // Determine whether or not the specified method is public.
IsMethodPublic(JNIEnv * env,jclass c,jmethodID method_id)33 static bool IsMethodPublic(JNIEnv* env, jclass c, jmethodID method_id) {
34   ScopedLocalRef<jobject> reflected(env, env->ToReflectedMethod(c, method_id, JNI_FALSE));
35   if (reflected.get() == nullptr) {
36     fprintf(stderr, "Failed to get reflected method\n");
37     return false;
38   }
39   // We now have a Method instance.  We need to call its
40   // getModifiers() method.
41   jclass method_class = env->FindClass("java/lang/reflect/Method");
42   if (method_class == nullptr) {
43     fprintf(stderr, "Failed to find class java.lang.reflect.Method\n");
44     return false;
45   }
46   jmethodID mid = env->GetMethodID(method_class, "getModifiers", "()I");
47   if (mid == nullptr) {
48     fprintf(stderr, "Failed to find java.lang.reflect.Method.getModifiers\n");
49     return false;
50   }
51   int modifiers = env->CallIntMethod(reflected.get(), mid);
52   static const int PUBLIC = 0x0001;  // java.lang.reflect.Modifiers.PUBLIC
53   if ((modifiers & PUBLIC) == 0) {
54     fprintf(stderr, "Modifiers mismatch\n");
55     return false;
56   }
57   return true;
58 }
59 
InvokeMain(JNIEnv * env,char ** argv)60 static int InvokeMain(JNIEnv* env, char** argv) {
61   // We want to call main() with a String array with our arguments in
62   // it.  Create an array and populate it.  Note argv[0] is not
63   // included.
64   ScopedLocalRef<jobjectArray> args(env, toStringArray(env, argv + 1));
65   if (args.get() == nullptr) {
66     env->ExceptionDescribe();
67     return EXIT_FAILURE;
68   }
69 
70   // Find [class].main(String[]).
71 
72   // Convert "com.android.Blah" to "com/android/Blah".
73   std::string class_name(argv[0]);
74   std::replace(class_name.begin(), class_name.end(), '.', '/');
75 
76   ScopedLocalRef<jclass> klass(env, env->FindClass(class_name.c_str()));
77   if (klass.get() == nullptr) {
78     fprintf(stderr, "Unable to locate class '%s'\n", class_name.c_str());
79     env->ExceptionDescribe();
80     return EXIT_FAILURE;
81   }
82 
83   jmethodID method = env->GetStaticMethodID(klass.get(), "main", "([Ljava/lang/String;)V");
84   if (method == nullptr) {
85     fprintf(stderr, "Unable to find static main(String[]) in '%s'\n", class_name.c_str());
86     env->ExceptionDescribe();
87     return EXIT_FAILURE;
88   }
89 
90   // Make sure the method is public.  JNI doesn't prevent us from
91   // calling a private method, so we have to check it explicitly.
92   if (!IsMethodPublic(env, klass.get(), method)) {
93     fprintf(stderr, "Sorry, main() is not public in '%s'\n", class_name.c_str());
94     env->ExceptionDescribe();
95     return EXIT_FAILURE;
96   }
97 
98   // Invoke main().
99   env->CallStaticVoidMethod(klass.get(), method, args.get());
100 
101   // Check whether there was an uncaught exception. We don't log any uncaught exception here;
102   // detaching this thread will do that for us, but it will clear the exception (and invalidate
103   // our JNIEnv), so we need to check here.
104   return env->ExceptionCheck() ? EXIT_FAILURE : EXIT_SUCCESS;
105 }
106 
107 // Parse arguments.  Most of it just gets passed through to the runtime.
108 // The JNI spec defines a handful of standard arguments.
dalvikvm(int argc,char ** argv)109 static int dalvikvm(int argc, char** argv) {
110   setvbuf(stdout, nullptr, _IONBF, 0);
111 
112   // Skip over argv[0].
113   argv++;
114   argc--;
115 
116   // If we're adding any additional stuff, e.g. function hook specifiers,
117   // add them to the count here.
118   //
119   // We're over-allocating, because this includes the options to the runtime
120   // plus the options to the program.
121   int option_count = argc;
122   std::unique_ptr<JavaVMOption[]> options(new JavaVMOption[option_count]());
123 
124   // Copy options over.  Everything up to the name of the class starts
125   // with a '-' (the function hook stuff is strictly internal).
126   //
127   // [Do we need to catch & handle "-jar" here?]
128   bool need_extra = false;
129   const char* lib = nullptr;
130   const char* what = nullptr;
131   int curr_opt, arg_idx;
132   for (curr_opt = arg_idx = 0; arg_idx < argc; arg_idx++) {
133     if (argv[arg_idx][0] != '-' && !need_extra) {
134       break;
135     }
136     if (strncmp(argv[arg_idx], "-XXlib:", strlen("-XXlib:")) == 0) {
137       lib = argv[arg_idx] + strlen("-XXlib:");
138       continue;
139     }
140 
141     options[curr_opt++].optionString = argv[arg_idx];
142 
143     // Some options require an additional argument.
144     need_extra = false;
145     if (strcmp(argv[arg_idx], "-classpath") == 0 || strcmp(argv[arg_idx], "-cp") == 0) {
146       need_extra = true;
147       what = argv[arg_idx];
148     }
149   }
150 
151   if (need_extra) {
152     fprintf(stderr, "%s must be followed by an additional argument giving a value\n", what);
153     return EXIT_FAILURE;
154   }
155 
156   if (curr_opt > option_count) {
157     fprintf(stderr, "curr_opt(%d) > option_count(%d)\n", curr_opt, option_count);
158     abort();
159     return EXIT_FAILURE;
160   }
161 
162   // Find the JNI_CreateJavaVM implementation.
163   JniInvocation jni_invocation;
164   if (!jni_invocation.Init(lib)) {
165     fprintf(stderr, "Failed to initialize JNI invocation API from %s\n", lib);
166     return EXIT_FAILURE;
167   }
168 
169   JavaVMInitArgs init_args;
170   init_args.version = JNI_VERSION_1_6;
171   init_args.options = options.get();
172   init_args.nOptions = curr_opt;
173   init_args.ignoreUnrecognized = JNI_FALSE;
174 
175   // Start the runtime. The current thread becomes the main thread.
176   JavaVM* vm = nullptr;
177   JNIEnv* env = nullptr;
178   if (JNI_CreateJavaVM(&vm, &env, &init_args) != JNI_OK) {
179     fprintf(stderr, "Failed to initialize runtime (check log for details)\n");
180     return EXIT_FAILURE;
181   }
182 
183   // Make sure they provided a class name. We do this after
184   // JNI_CreateJavaVM so that things like "-help" have the opportunity
185   // to emit a usage statement.
186   if (arg_idx == argc) {
187     fprintf(stderr, "Class name required\n");
188     return EXIT_FAILURE;
189   }
190 
191   int rc = InvokeMain(env, &argv[arg_idx]);
192 
193 #if defined(NDEBUG)
194   // The DestroyJavaVM call will detach this thread for us. In debug builds, we don't want to
195   // detach because detaching disables the CheckSafeToLockOrUnlock checking.
196   if (vm->DetachCurrentThread() != JNI_OK) {
197     fprintf(stderr, "Warning: unable to detach main thread\n");
198     rc = EXIT_FAILURE;
199   }
200 #endif
201 
202   if (vm->DestroyJavaVM() != 0) {
203     fprintf(stderr, "Warning: runtime did not shut down cleanly\n");
204     rc = EXIT_FAILURE;
205   }
206 
207   return rc;
208 }
209 
210 }  // namespace art
211 
212 // TODO(b/141622862): stop leaks
__asan_default_options()213 extern "C" const char *__asan_default_options() {
214     return "detect_leaks=0";
215 }
216 
main(int argc,char ** argv)217 int main(int argc, char** argv) {
218   // Do not allow static destructors to be called, since it's conceivable that
219   // daemons may still awaken (literally).
220   art::FastExit(art::dalvikvm(argc, argv));
221 }
222