mozglue/android/APKOpen.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:6322f7aa2d80
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

mercurial