|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 /* |
|
6 * This custom library loading code is only meant to be called |
|
7 * during initialization. As a result, it takes no special |
|
8 * precautions to be threadsafe. Any of the library loading functions |
|
9 * like mozload should not be available to other code. |
|
10 */ |
|
11 |
|
12 #include <jni.h> |
|
13 #include <android/log.h> |
|
14 #include <sys/types.h> |
|
15 #include <sys/stat.h> |
|
16 #include <sys/mman.h> |
|
17 #include <sys/limits.h> |
|
18 #include <errno.h> |
|
19 #include <string.h> |
|
20 #include <stdio.h> |
|
21 #include <stdlib.h> |
|
22 #include <time.h> |
|
23 #include <fcntl.h> |
|
24 #include <unistd.h> |
|
25 #include <zlib.h> |
|
26 #include "dlfcn.h" |
|
27 #include "APKOpen.h" |
|
28 #include <sys/time.h> |
|
29 #include <sys/resource.h> |
|
30 #include <sys/prctl.h> |
|
31 #include "Zip.h" |
|
32 #include "sqlite3.h" |
|
33 #include "SQLiteBridge.h" |
|
34 #include "NSSBridge.h" |
|
35 #include "ElfLoader.h" |
|
36 #include "application.ini.h" |
|
37 |
|
38 /* Android headers don't define RUSAGE_THREAD */ |
|
39 #ifndef RUSAGE_THREAD |
|
40 #define RUSAGE_THREAD 1 |
|
41 #endif |
|
42 |
|
43 #ifndef RELEASE_BUILD |
|
44 /* Official builds have the debuggable flag set to false, which disables |
|
45 * the backtrace dumper from bionic. However, as it is useful for native |
|
46 * crashes happening before the crash reporter is registered, re-enable |
|
47 * it on non release builds (i.e. nightly and aurora). |
|
48 * Using a constructor so that it is re-enabled as soon as libmozglue.so |
|
49 * is loaded. |
|
50 */ |
|
51 __attribute__((constructor)) |
|
52 void make_dumpable() { |
|
53 prctl(PR_SET_DUMPABLE, 1); |
|
54 } |
|
55 #endif |
|
56 |
|
57 extern "C" { |
|
58 /* |
|
59 * To work around http://code.google.com/p/android/issues/detail?id=23203 |
|
60 * we don't link with the crt objects. In some configurations, this means |
|
61 * a lack of the __dso_handle symbol because it is defined there, and |
|
62 * depending on the android platform and ndk versions used, it may or may |
|
63 * not be defined in libc.so. In the latter case, we fail to link. Defining |
|
64 * it here as weak makes us provide the symbol when it's not provided by |
|
65 * the crt objects, making the change transparent for future NDKs that |
|
66 * would fix the original problem. On older NDKs, it is not a problem |
|
67 * either because the way __dso_handle was used was already broken (and |
|
68 * the custom linker works around it). |
|
69 */ |
|
70 NS_EXPORT __attribute__((weak)) void *__dso_handle; |
|
71 } |
|
72 |
|
73 typedef int mozglueresult; |
|
74 |
|
75 enum StartupEvent { |
|
76 #define mozilla_StartupTimeline_Event(ev, z) ev, |
|
77 #include "StartupTimeline.h" |
|
78 #undef mozilla_StartupTimeline_Event |
|
79 MAX_STARTUP_EVENT_ID |
|
80 }; |
|
81 |
|
82 using namespace mozilla; |
|
83 |
|
84 /** |
|
85 * Local TimeStamp::Now()-compatible implementation used to record timestamps |
|
86 * which will be passed to XRE_StartupTimelineRecord(). |
|
87 */ |
|
88 |
|
89 static uint64_t TimeStamp_Now() |
|
90 { |
|
91 struct timespec ts; |
|
92 int rv = clock_gettime(CLOCK_MONOTONIC, &ts); |
|
93 |
|
94 if (rv != 0) { |
|
95 return 0; |
|
96 } |
|
97 |
|
98 uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000; |
|
99 return baseNs + (uint64_t)ts.tv_nsec; |
|
100 } |
|
101 |
|
102 static struct mapping_info * lib_mapping = nullptr; |
|
103 |
|
104 NS_EXPORT const struct mapping_info * |
|
105 getLibraryMapping() |
|
106 { |
|
107 return lib_mapping; |
|
108 } |
|
109 |
|
110 void |
|
111 JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg) |
|
112 { |
|
113 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Throw\n"); |
|
114 jclass cls = jenv->FindClass(classname); |
|
115 if (cls == nullptr) { |
|
116 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find exception class (or exception pending) %s\n", classname); |
|
117 exit(FAILURE); |
|
118 } |
|
119 int rc = jenv->ThrowNew(cls, msg); |
|
120 if (rc < 0) { |
|
121 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg); |
|
122 exit(FAILURE); |
|
123 } |
|
124 jenv->DeleteLocalRef(cls); |
|
125 } |
|
126 |
|
127 #define JNI_STUBS |
|
128 #include "jni-stubs.inc" |
|
129 #undef JNI_STUBS |
|
130 |
|
131 static void * xul_handle = nullptr; |
|
132 #ifndef MOZ_FOLD_LIBS |
|
133 static void * sqlite_handle = nullptr; |
|
134 static void * nspr_handle = nullptr; |
|
135 static void * plc_handle = nullptr; |
|
136 #else |
|
137 #define sqlite_handle nss_handle |
|
138 #define nspr_handle nss_handle |
|
139 #define plc_handle nss_handle |
|
140 #endif |
|
141 static void * nss_handle = nullptr; |
|
142 |
|
143 template <typename T> inline void |
|
144 xul_dlsym(const char *symbolName, T *value) |
|
145 { |
|
146 *value = (T) (uintptr_t) __wrap_dlsym(xul_handle, symbolName); |
|
147 } |
|
148 |
|
149 static int mapping_count = 0; |
|
150 |
|
151 #define MAX_MAPPING_INFO 32 |
|
152 |
|
153 extern "C" void |
|
154 report_mapping(char *name, void *base, uint32_t len, uint32_t offset) |
|
155 { |
|
156 if (mapping_count >= MAX_MAPPING_INFO) |
|
157 return; |
|
158 |
|
159 struct mapping_info *info = &lib_mapping[mapping_count++]; |
|
160 info->name = strdup(name); |
|
161 info->base = (uintptr_t)base; |
|
162 info->len = len; |
|
163 info->offset = offset; |
|
164 } |
|
165 |
|
166 static mozglueresult |
|
167 loadGeckoLibs(const char *apkName) |
|
168 { |
|
169 chdir(getenv("GRE_HOME")); |
|
170 |
|
171 uint64_t t0 = TimeStamp_Now(); |
|
172 struct rusage usage1_thread, usage1; |
|
173 getrusage(RUSAGE_THREAD, &usage1_thread); |
|
174 getrusage(RUSAGE_SELF, &usage1); |
|
175 |
|
176 RefPtr<Zip> zip = ZipCollection::GetZip(apkName); |
|
177 |
|
178 char *file = new char[strlen(apkName) + sizeof("!/assets/libxul.so")]; |
|
179 sprintf(file, "%s!/assets/libxul.so", apkName); |
|
180 xul_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); |
|
181 delete[] file; |
|
182 |
|
183 if (!xul_handle) { |
|
184 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!"); |
|
185 return FAILURE; |
|
186 } |
|
187 |
|
188 #define JNI_BINDINGS |
|
189 #include "jni-stubs.inc" |
|
190 #undef JNI_BINDINGS |
|
191 |
|
192 void (*XRE_StartupTimelineRecord)(int, uint64_t); |
|
193 xul_dlsym("XRE_StartupTimelineRecord", &XRE_StartupTimelineRecord); |
|
194 |
|
195 uint64_t t1 = TimeStamp_Now(); |
|
196 struct rusage usage2_thread, usage2; |
|
197 getrusage(RUSAGE_THREAD, &usage2_thread); |
|
198 getrusage(RUSAGE_SELF, &usage2); |
|
199 |
|
200 #define RUSAGE_TIMEDIFF(u1, u2, field) \ |
|
201 ((u2.ru_ ## field.tv_sec - u1.ru_ ## field.tv_sec) * 1000 + \ |
|
202 (u2.ru_ ## field.tv_usec - u1.ru_ ## field.tv_usec) / 1000) |
|
203 |
|
204 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %lldms total, %ldms(%ldms) user, %ldms(%ldms) system, %ld(%ld) faults", |
|
205 (t1 - t0) / 1000000, |
|
206 RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, utime), |
|
207 RUSAGE_TIMEDIFF(usage1, usage2, utime), |
|
208 RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, stime), |
|
209 RUSAGE_TIMEDIFF(usage1, usage2, stime), |
|
210 usage2_thread.ru_majflt - usage1_thread.ru_majflt, |
|
211 usage2.ru_majflt - usage1.ru_majflt); |
|
212 |
|
213 XRE_StartupTimelineRecord(LINKER_INITIALIZED, t0); |
|
214 XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1); |
|
215 return SUCCESS; |
|
216 } |
|
217 |
|
218 static mozglueresult loadNSSLibs(const char *apkName); |
|
219 |
|
220 static mozglueresult |
|
221 loadSQLiteLibs(const char *apkName) |
|
222 { |
|
223 if (sqlite_handle) |
|
224 return SUCCESS; |
|
225 |
|
226 #ifdef MOZ_FOLD_LIBS |
|
227 if (loadNSSLibs(apkName) != SUCCESS) |
|
228 return FAILURE; |
|
229 #else |
|
230 chdir(getenv("GRE_HOME")); |
|
231 |
|
232 RefPtr<Zip> zip = ZipCollection::GetZip(apkName); |
|
233 if (!lib_mapping) { |
|
234 lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping)); |
|
235 } |
|
236 |
|
237 char *file = new char[strlen(apkName) + sizeof("!/assets/libmozsqlite3.so")]; |
|
238 sprintf(file, "%s!/assets/libmozsqlite3.so", apkName); |
|
239 sqlite_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); |
|
240 delete [] file; |
|
241 |
|
242 if (!sqlite_handle) { |
|
243 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!"); |
|
244 return FAILURE; |
|
245 } |
|
246 #endif |
|
247 |
|
248 setup_sqlite_functions(sqlite_handle); |
|
249 return SUCCESS; |
|
250 } |
|
251 |
|
252 static mozglueresult |
|
253 loadNSSLibs(const char *apkName) |
|
254 { |
|
255 if (nss_handle && nspr_handle && plc_handle) |
|
256 return SUCCESS; |
|
257 |
|
258 chdir(getenv("GRE_HOME")); |
|
259 |
|
260 RefPtr<Zip> zip = ZipCollection::GetZip(apkName); |
|
261 if (!lib_mapping) { |
|
262 lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping)); |
|
263 } |
|
264 |
|
265 char *file = new char[strlen(apkName) + sizeof("!/assets/libnss3.so")]; |
|
266 sprintf(file, "%s!/assets/libnss3.so", apkName); |
|
267 nss_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); |
|
268 delete [] file; |
|
269 |
|
270 #ifndef MOZ_FOLD_LIBS |
|
271 file = new char[strlen(apkName) + sizeof("!/assets/libnspr4.so")]; |
|
272 sprintf(file, "%s!/assets/libnspr4.so", apkName); |
|
273 nspr_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); |
|
274 delete [] file; |
|
275 |
|
276 file = new char[strlen(apkName) + sizeof("!/assets/libplc4.so")]; |
|
277 sprintf(file, "%s!/assets/libplc4.so", apkName); |
|
278 plc_handle = __wrap_dlopen(file, RTLD_GLOBAL | RTLD_LAZY); |
|
279 delete [] file; |
|
280 #endif |
|
281 |
|
282 if (!nss_handle) { |
|
283 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnss3!"); |
|
284 return FAILURE; |
|
285 } |
|
286 |
|
287 #ifndef MOZ_FOLD_LIBS |
|
288 if (!nspr_handle) { |
|
289 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnspr4!"); |
|
290 return FAILURE; |
|
291 } |
|
292 |
|
293 if (!plc_handle) { |
|
294 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libplc4!"); |
|
295 return FAILURE; |
|
296 } |
|
297 #endif |
|
298 |
|
299 return setup_nss_functions(nss_handle, nspr_handle, plc_handle); |
|
300 } |
|
301 |
|
302 extern "C" NS_EXPORT void JNICALL |
|
303 Java_org_mozilla_gecko_mozglue_GeckoLoader_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) |
|
304 { |
|
305 const char* str; |
|
306 // XXX: java doesn't give us true UTF8, we should figure out something |
|
307 // better to do here |
|
308 str = jenv->GetStringUTFChars(jApkName, nullptr); |
|
309 if (str == nullptr) |
|
310 return; |
|
311 |
|
312 int res = loadGeckoLibs(str); |
|
313 if (res != SUCCESS) { |
|
314 JNI_Throw(jenv, "java/lang/Exception", "Error loading gecko libraries"); |
|
315 } |
|
316 jenv->ReleaseStringUTFChars(jApkName, str); |
|
317 } |
|
318 |
|
319 extern "C" NS_EXPORT void JNICALL |
|
320 Java_org_mozilla_gecko_mozglue_GeckoLoader_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) { |
|
321 if (jShouldExtract) { |
|
322 putenv("MOZ_LINKER_EXTRACT=1"); |
|
323 } |
|
324 |
|
325 const char* str; |
|
326 // XXX: java doesn't give us true UTF8, we should figure out something |
|
327 // better to do here |
|
328 str = jenv->GetStringUTFChars(jApkName, nullptr); |
|
329 if (str == nullptr) |
|
330 return; |
|
331 |
|
332 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n"); |
|
333 mozglueresult rv = loadSQLiteLibs(str); |
|
334 if (rv != SUCCESS) { |
|
335 JNI_Throw(jenv, "java/lang/Exception", "Error loading sqlite libraries"); |
|
336 } |
|
337 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite done\n"); |
|
338 jenv->ReleaseStringUTFChars(jApkName, str); |
|
339 } |
|
340 |
|
341 extern "C" NS_EXPORT void JNICALL |
|
342 Java_org_mozilla_gecko_mozglue_GeckoLoader_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) { |
|
343 if (jShouldExtract) { |
|
344 putenv("MOZ_LINKER_EXTRACT=1"); |
|
345 } |
|
346 |
|
347 const char* str; |
|
348 // XXX: java doesn't give us true UTF8, we should figure out something |
|
349 // better to do here |
|
350 str = jenv->GetStringUTFChars(jApkName, nullptr); |
|
351 if (str == nullptr) |
|
352 return; |
|
353 |
|
354 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n"); |
|
355 mozglueresult rv = loadNSSLibs(str); |
|
356 if (rv != SUCCESS) { |
|
357 JNI_Throw(jenv, "java/lang/Exception", "Error loading nss libraries"); |
|
358 } |
|
359 __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n"); |
|
360 jenv->ReleaseStringUTFChars(jApkName, str); |
|
361 } |
|
362 |
|
363 typedef void (*GeckoStart_t)(void *, const nsXREAppData *); |
|
364 |
|
365 extern "C" NS_EXPORT void JNICALL |
|
366 Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jstring jargs) |
|
367 { |
|
368 GeckoStart_t GeckoStart; |
|
369 xul_dlsym("GeckoStart", &GeckoStart); |
|
370 if (GeckoStart == nullptr) |
|
371 return; |
|
372 // XXX: java doesn't give us true UTF8, we should figure out something |
|
373 // better to do here |
|
374 int len = jenv->GetStringUTFLength(jargs); |
|
375 // GeckoStart needs to write in the args buffer, so we need a copy. |
|
376 char *args = (char *) malloc(len + 1); |
|
377 jenv->GetStringUTFRegion(jargs, 0, len, args); |
|
378 args[len] = '\0'; |
|
379 GeckoStart(args, &sAppData); |
|
380 free(args); |
|
381 } |
|
382 |
|
383 typedef int GeckoProcessType; |
|
384 |
|
385 extern "C" NS_EXPORT mozglueresult |
|
386 ChildProcessInit(int argc, char* argv[]) |
|
387 { |
|
388 int i; |
|
389 for (i = 0; i < (argc - 1); i++) { |
|
390 if (strcmp(argv[i], "-greomni")) |
|
391 continue; |
|
392 |
|
393 i = i + 1; |
|
394 break; |
|
395 } |
|
396 |
|
397 if (loadNSSLibs(argv[i]) != SUCCESS) { |
|
398 return FAILURE; |
|
399 } |
|
400 if (loadSQLiteLibs(argv[i]) != SUCCESS) { |
|
401 return FAILURE; |
|
402 } |
|
403 if (loadGeckoLibs(argv[i]) != SUCCESS) { |
|
404 return FAILURE; |
|
405 } |
|
406 |
|
407 GeckoProcessType (*fXRE_StringToChildProcessType)(char*); |
|
408 xul_dlsym("XRE_StringToChildProcessType", &fXRE_StringToChildProcessType); |
|
409 |
|
410 mozglueresult (*fXRE_InitChildProcess)(int, char**, GeckoProcessType); |
|
411 xul_dlsym("XRE_InitChildProcess", &fXRE_InitChildProcess); |
|
412 |
|
413 GeckoProcessType proctype = fXRE_StringToChildProcessType(argv[--argc]); |
|
414 |
|
415 return fXRE_InitChildProcess(argc, argv, proctype); |
|
416 } |
|
417 |