dalvik_system_Zygote.cpp revision af02f57404b28590b34fa3777a381c13e3c956eb
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/*
18 * dalvik.system.Zygote
19 */
20#include "Dalvik.h"
21#include "native/InternalNativePriv.h"
22
23#ifdef HAVE_SELINUX
24#include <selinux/android.h>
25#endif
26
27#include <signal.h>
28#include <sys/types.h>
29#include <sys/wait.h>
30#include <grp.h>
31#include <errno.h>
32#include <paths.h>
33#include <sys/personality.h>
34#include <sys/stat.h>
35#include <sys/mount.h>
36#include <linux/fs.h>
37#include <cutils/sched_policy.h>
38#include <cutils/multiuser.h>
39#include <sched.h>
40
41#if defined(HAVE_PRCTL)
42# include <sys/prctl.h>
43#endif
44
45#define ZYGOTE_LOG_TAG "Zygote"
46
47/* must match values in dalvik.system.Zygote */
48enum {
49    DEBUG_ENABLE_DEBUGGER           = 1,
50    DEBUG_ENABLE_CHECKJNI           = 1 << 1,
51    DEBUG_ENABLE_ASSERT             = 1 << 2,
52    DEBUG_ENABLE_SAFEMODE           = 1 << 3,
53    DEBUG_ENABLE_JNI_LOGGING        = 1 << 4,
54};
55
56/* must match values in dalvik.system.Zygote */
57enum {
58    MOUNT_EXTERNAL_NONE = 0,
59    MOUNT_EXTERNAL_SINGLEUSER = 1,
60    MOUNT_EXTERNAL_MULTIUSER = 2,
61};
62
63/*
64 * This signal handler is for zygote mode, since the zygote
65 * must reap its children
66 */
67static void sigchldHandler(int s)
68{
69    pid_t pid;
70    int status;
71
72    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
73        /* Log process-death status that we care about.  In general it is not
74           safe to call ALOG(...) from a signal handler because of possible
75           reentrancy.  However, we know a priori that the current implementation
76           of ALOG() is safe to call from a SIGCHLD handler in the zygote process.
77           If the ALOG() implementation changes its locking strategy or its use
78           of syscalls within the lazy-init critical section, its use here may
79           become unsafe. */
80        if (WIFEXITED(status)) {
81            if (WEXITSTATUS(status)) {
82                ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG, "Process %d exited cleanly (%d)",
83                    (int) pid, WEXITSTATUS(status));
84            } else {
85                IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) {
86                    ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
87                        "Process %d exited cleanly (%d)",
88                        (int) pid, WEXITSTATUS(status));
89                }
90            }
91        } else if (WIFSIGNALED(status)) {
92            if (WTERMSIG(status) != SIGKILL) {
93                ALOG(LOG_DEBUG, ZYGOTE_LOG_TAG,
94                    "Process %d terminated by signal (%d)",
95                    (int) pid, WTERMSIG(status));
96            } else {
97                IF_ALOGV(/*should use ZYGOTE_LOG_TAG*/) {
98                    ALOG(LOG_VERBOSE, ZYGOTE_LOG_TAG,
99                        "Process %d terminated by signal (%d)",
100                        (int) pid, WTERMSIG(status));
101                }
102            }
103#ifdef WCOREDUMP
104            if (WCOREDUMP(status)) {
105                ALOG(LOG_INFO, ZYGOTE_LOG_TAG, "Process %d dumped core",
106                    (int) pid);
107            }
108#endif /* ifdef WCOREDUMP */
109        }
110
111        /*
112         * If the just-crashed process is the system_server, bring down zygote
113         * so that it is restarted by init and system server will be restarted
114         * from there.
115         */
116        if (pid == gDvm.systemServerPid) {
117            ALOG(LOG_INFO, ZYGOTE_LOG_TAG,
118                "Exit zygote because system server (%d) has terminated",
119                (int) pid);
120            kill(getpid(), SIGKILL);
121        }
122    }
123
124    if (pid < 0) {
125        ALOG(LOG_WARN, ZYGOTE_LOG_TAG,
126            "Zygote SIGCHLD error in waitpid: %s",strerror(errno));
127    }
128}
129
130/*
131 * configure sigchld handler for the zygote process
132 * This is configured very late, because earlier in the dalvik lifecycle
133 * we can fork() and exec() for the verifier/optimizer, and we
134 * want to waitpid() for those rather than have them be harvested immediately.
135 *
136 * This ends up being called repeatedly before each fork(), but there's
137 * no real harm in that.
138 */
139static void setSignalHandler()
140{
141    int err;
142    struct sigaction sa;
143
144    memset(&sa, 0, sizeof(sa));
145
146    sa.sa_handler = sigchldHandler;
147
148    err = sigaction (SIGCHLD, &sa, NULL);
149
150    if (err < 0) {
151        ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
152    }
153}
154
155/*
156 * Set the SIGCHLD handler back to default behavior in zygote children
157 */
158static void unsetSignalHandler()
159{
160    int err;
161    struct sigaction sa;
162
163    memset(&sa, 0, sizeof(sa));
164
165    sa.sa_handler = SIG_DFL;
166
167    err = sigaction (SIGCHLD, &sa, NULL);
168
169    if (err < 0) {
170        ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
171    }
172}
173
174/*
175 * Calls POSIX setgroups() using the int[] object as an argument.
176 * A NULL argument is tolerated.
177 */
178
179static int setgroupsIntarray(ArrayObject* gidArray)
180{
181    gid_t *gids;
182    u4 i;
183    s4 *contents;
184
185    if (gidArray == NULL) {
186        return 0;
187    }
188
189    /* just in case gid_t and u4 are different... */
190    gids = (gid_t *)alloca(sizeof(gid_t) * gidArray->length);
191    contents = (s4 *)(void *)gidArray->contents;
192
193    for (i = 0 ; i < gidArray->length ; i++) {
194        gids[i] = (gid_t) contents[i];
195    }
196
197    return setgroups((size_t) gidArray->length, gids);
198}
199
200/*
201 * Sets the resource limits via setrlimit(2) for the values in the
202 * two-dimensional array of integers that's passed in. The second dimension
203 * contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
204 * treated as an empty array.
205 *
206 * -1 is returned on error.
207 */
208static int setrlimitsFromArray(ArrayObject* rlimits)
209{
210    u4 i;
211    struct rlimit rlim;
212
213    if (rlimits == NULL) {
214        return 0;
215    }
216
217    memset (&rlim, 0, sizeof(rlim));
218
219    ArrayObject** tuples = (ArrayObject **)(void *)rlimits->contents;
220
221    for (i = 0; i < rlimits->length; i++) {
222        ArrayObject * rlimit_tuple = tuples[i];
223        s4* contents = (s4 *)(void *)rlimit_tuple->contents;
224        int err;
225
226        if (rlimit_tuple->length != 3) {
227            ALOGE("rlimits array must have a second dimension of size 3");
228            return -1;
229        }
230
231        rlim.rlim_cur = contents[1];
232        rlim.rlim_max = contents[2];
233
234        err = setrlimit(contents[0], &rlim);
235
236        if (err < 0) {
237            return -1;
238        }
239    }
240
241    return 0;
242}
243
244/*
245 * Create private mount space for this process and mount SD card
246 * into it, based on active user.  See storage config details at
247 * http://source.android.com/tech/storage/
248 */
249static int mountExternalStorage(uid_t uid, u4 mountExternal) {
250    userid_t userid = multiuser_getUserId(uid);
251
252    // Create private mount namespace for our process
253    if (unshare(CLONE_NEWNS) == -1) {
254        SLOGE("Failed to unshare(): %s", strerror(errno));
255        return -1;
256    }
257
258    // Mark rootfs as being a slave in our process so that changes
259    // from parent namespace flow into our process.
260    if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) {
261        SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno));
262        return -1;
263    }
264
265    // Create bind mounts to expose external storage
266    if (mountExternal == MOUNT_EXTERNAL_MULTIUSER) {
267        const char* target_base = getenv("ANDROID_STORAGE");
268        const char* target = getenv("EXTERNAL_STORAGE");
269        const char* source_base = getenv("MULTIUSER_EXTERNAL_STORAGE");
270        if (target_base == NULL || target == NULL || source_base == NULL) {
271            SLOGE("Storage environment undefined; unable to provide external storage");
272            return -1;
273        }
274
275        // Give ourselves a tmpfs staging platform to work with, which obscures
276        // any existing shell-specific contents.  Create our mount target, then
277        // remount read-only.
278        if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,
279                "uid=0,gid=1028,mode=0050") == -1) {
280            SLOGE("Failed to mount tmpfs to %s: %s", target_base, strerror(errno));
281            return -1;
282        }
283        if (mkdir(target, 0000) == -1) {
284            SLOGE("Failed to mkdir %s: %s", target, strerror(errno));
285            return -1;
286        }
287        if (mount("tmpfs", target_base, NULL,
288                MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NODEV, NULL)) {
289            SLOGE("Failed to remount ro %s: %s", target_base, strerror(errno));
290            return -1;
291        }
292
293        // Mount our user-specific external storage into place, creating path
294        // if it doesn't already exist.
295        std::string source(StringPrintf("%s/%d", source_base, userid));
296        if (access(source.c_str(), F_OK) == -1) {
297            if (mkdir(source.c_str(), 0000) == -1) {
298                SLOGE("Failed to mkdir %s: %s", source.c_str(), strerror(errno));
299                return -1;
300            }
301        }
302        if (mount(source.c_str(), target, NULL, MS_BIND, NULL) == -1) {
303            SLOGE("Failed to bind mount %s to %s: %s", source.c_str(), target, strerror(errno));
304            return -1;
305        }
306
307    } else {
308        SLOGE("Mount mode %d unsupported", mountExternal);
309        return -1;
310    }
311
312    return 0;
313}
314
315/* native public static int fork(); */
316static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
317{
318    pid_t pid;
319
320    if (!gDvm.zygote) {
321        dvmThrowIllegalStateException(
322            "VM instance not started with -Xzygote");
323
324        RETURN_VOID();
325    }
326
327    if (!dvmGcPreZygoteFork()) {
328        ALOGE("pre-fork heap failed");
329        dvmAbort();
330    }
331
332    setSignalHandler();
333
334    dvmDumpLoaderStats("zygote");
335    pid = fork();
336
337#ifdef HAVE_ANDROID_OS
338    if (pid == 0) {
339        /* child process */
340        extern int gMallocLeakZygoteChild;
341        gMallocLeakZygoteChild = 1;
342    }
343#endif
344
345    RETURN_INT(pid);
346}
347
348/*
349 * Enable/disable debug features requested by the caller.
350 *
351 * debugger
352 *   If set, enable debugging; if not set, disable debugging.  This is
353 *   easy to handle, because the JDWP thread isn't started until we call
354 *   dvmInitAfterZygote().
355 * checkjni
356 *   If set, make sure "check JNI" is enabled.
357 * assert
358 *   If set, make sure assertions are enabled.  This gets fairly weird,
359 *   because it affects the result of a method called by class initializers,
360 *   and hence can't affect pre-loaded/initialized classes.
361 * safemode
362 *   If set, operates the VM in the safe mode. The definition of "safe mode" is
363 *   implementation dependent and currently only the JIT compiler is disabled.
364 *   This is easy to handle because the compiler thread and associated resources
365 *   are not requested until we call dvmInitAfterZygote().
366 */
367static void enableDebugFeatures(u4 debugFlags)
368{
369    ALOGV("debugFlags is 0x%02x", debugFlags);
370
371    gDvm.jdwpAllowed = ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0);
372
373    if ((debugFlags & DEBUG_ENABLE_CHECKJNI) != 0) {
374        /* turn it on if it's not already enabled */
375        dvmLateEnableCheckedJni();
376    }
377
378    if ((debugFlags & DEBUG_ENABLE_JNI_LOGGING) != 0) {
379        gDvmJni.logThirdPartyJni = true;
380    }
381
382    if ((debugFlags & DEBUG_ENABLE_ASSERT) != 0) {
383        /* turn it on if it's not already enabled */
384        dvmLateEnableAssertions();
385    }
386
387    if ((debugFlags & DEBUG_ENABLE_SAFEMODE) != 0) {
388#if defined(WITH_JIT)
389        /* turn off the jit if it is explicitly requested by the app */
390        if (gDvm.executionMode == kExecutionModeJit)
391            gDvm.executionMode = kExecutionModeInterpFast;
392#endif
393    }
394
395#ifdef HAVE_ANDROID_OS
396    if ((debugFlags & DEBUG_ENABLE_DEBUGGER) != 0) {
397        /* To let a non-privileged gdbserver attach to this
398         * process, we must set its dumpable bit flag. However
399         * we are not interested in generating a coredump in
400         * case of a crash, so also set the coredump size to 0
401         * to disable that
402         */
403        if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
404            ALOGE("could not set dumpable bit flag for pid %d: %s",
405                 getpid(), strerror(errno));
406        } else {
407            struct rlimit rl;
408            rl.rlim_cur = 0;
409            rl.rlim_max = RLIM_INFINITY;
410            if (setrlimit(RLIMIT_CORE, &rl) < 0) {
411                ALOGE("could not disable core file generation for pid %d: %s",
412                    getpid(), strerror(errno));
413            }
414        }
415    }
416#endif
417}
418
419/*
420 * Set Linux capability flags.
421 *
422 * Returns 0 on success, errno on failure.
423 */
424static int setCapabilities(int64_t permitted, int64_t effective)
425{
426#ifdef HAVE_ANDROID_OS
427    struct __user_cap_header_struct capheader;
428    struct __user_cap_data_struct capdata;
429
430    memset(&capheader, 0, sizeof(capheader));
431    memset(&capdata, 0, sizeof(capdata));
432
433    capheader.version = _LINUX_CAPABILITY_VERSION;
434    capheader.pid = 0;
435
436    capdata.effective = effective;
437    capdata.permitted = permitted;
438
439    ALOGV("CAPSET perm=%llx eff=%llx", permitted, effective);
440    if (capset(&capheader, &capdata) != 0)
441        return errno;
442#endif /*HAVE_ANDROID_OS*/
443
444    return 0;
445}
446
447#ifdef HAVE_SELINUX
448/*
449 * Set SELinux security context.
450 *
451 * Returns 0 on success, -1 on failure.
452 */
453static int setSELinuxContext(uid_t uid, bool isSystemServer,
454                             const char *seInfo, const char *niceName)
455{
456#ifdef HAVE_ANDROID_OS
457    return selinux_android_setcontext(uid, isSystemServer, seInfo, niceName);
458#else
459    return 0;
460#endif
461}
462#endif
463
464/*
465 * Utility routine to fork zygote and specialize the child process.
466 */
467static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
468{
469    pid_t pid;
470
471    uid_t uid = (uid_t) args[0];
472    gid_t gid = (gid_t) args[1];
473    ArrayObject* gids = (ArrayObject *)args[2];
474    u4 debugFlags = args[3];
475    ArrayObject *rlimits = (ArrayObject *)args[4];
476    u4 mountExternal = MOUNT_EXTERNAL_NONE;
477    int64_t permittedCapabilities, effectiveCapabilities;
478#ifdef HAVE_SELINUX
479    char *seInfo = NULL;
480    char *niceName = NULL;
481#endif
482
483    if (isSystemServer) {
484        /*
485         * Don't use GET_ARG_LONG here for now.  gcc is generating code
486         * that uses register d8 as a temporary, and that's coming out
487         * scrambled in the child process.  b/3138621
488         */
489        //permittedCapabilities = GET_ARG_LONG(args, 5);
490        //effectiveCapabilities = GET_ARG_LONG(args, 7);
491        permittedCapabilities = args[5] | (int64_t) args[6] << 32;
492        effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
493    } else {
494        mountExternal = args[5];
495        permittedCapabilities = effectiveCapabilities = 0;
496#ifdef HAVE_SELINUX
497        StringObject* seInfoObj = (StringObject*)args[6];
498        if (seInfoObj) {
499            seInfo = dvmCreateCstrFromString(seInfoObj);
500            if (!seInfo) {
501                ALOGE("seInfo dvmCreateCstrFromString failed");
502                dvmAbort();
503            }
504        }
505        StringObject* niceNameObj = (StringObject*)args[7];
506        if (niceNameObj) {
507            niceName = dvmCreateCstrFromString(niceNameObj);
508            if (!niceName) {
509                ALOGE("niceName dvmCreateCstrFromString failed");
510                dvmAbort();
511            }
512        }
513#endif
514    }
515
516    if (!gDvm.zygote) {
517        dvmThrowIllegalStateException(
518            "VM instance not started with -Xzygote");
519
520        return -1;
521    }
522
523    if (!dvmGcPreZygoteFork()) {
524        ALOGE("pre-fork heap failed");
525        dvmAbort();
526    }
527
528    setSignalHandler();
529
530    dvmDumpLoaderStats("zygote");
531    pid = fork();
532
533    if (pid == 0) {
534        int err;
535        /* The child process */
536
537#ifdef HAVE_ANDROID_OS
538        extern int gMallocLeakZygoteChild;
539        gMallocLeakZygoteChild = 1;
540
541        /* keep caps across UID change, unless we're staying root */
542        if (uid != 0) {
543            err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
544
545            if (err < 0) {
546                ALOGE("cannot PR_SET_KEEPCAPS: %s", strerror(errno));
547                dvmAbort();
548            }
549        }
550
551#endif /* HAVE_ANDROID_OS */
552
553        if (mountExternal != MOUNT_EXTERNAL_NONE) {
554            err = mountExternalStorage(uid, mountExternal);
555            if (err < 0) {
556                ALOGE("cannot mountExternalStorage(): %s", strerror(errno));
557                if (errno == ENOTCONN || errno == EROFS) {
558                    // Missing FUSE daemon, which is expected when booting encrypted
559                    // devices; let Zygote continue without external storage.
560                } else {
561                    dvmAbort();
562                }
563            }
564        }
565
566        err = setgroupsIntarray(gids);
567        if (err < 0) {
568            ALOGE("cannot setgroups(): %s", strerror(errno));
569            dvmAbort();
570        }
571
572        err = setrlimitsFromArray(rlimits);
573        if (err < 0) {
574            ALOGE("cannot setrlimit(): %s", strerror(errno));
575            dvmAbort();
576        }
577
578        err = setgid(gid);
579        if (err < 0) {
580            ALOGE("cannot setgid(%d): %s", gid, strerror(errno));
581            dvmAbort();
582        }
583
584        err = setuid(uid);
585        if (err < 0) {
586            ALOGE("cannot setuid(%d): %s", uid, strerror(errno));
587            dvmAbort();
588        }
589
590        int current = personality(0xffffFFFF);
591        int success = personality((ADDR_NO_RANDOMIZE | current));
592        if (success == -1) {
593          ALOGW("Personality switch failed. current=%d error=%d\n", current, errno);
594        }
595
596        err = setCapabilities(permittedCapabilities, effectiveCapabilities);
597        if (err != 0) {
598            ALOGE("cannot set capabilities (%llx,%llx): %s",
599                permittedCapabilities, effectiveCapabilities, strerror(err));
600            dvmAbort();
601        }
602
603        err = set_sched_policy(0, SP_DEFAULT);
604        if (err < 0) {
605            ALOGE("cannot set_sched_policy(0, SP_DEFAULT): %s", strerror(-err));
606            dvmAbort();
607        }
608
609#ifdef HAVE_SELINUX
610        err = setSELinuxContext(uid, isSystemServer, seInfo, niceName);
611        if (err < 0) {
612            ALOGE("cannot set SELinux context: %s\n", strerror(errno));
613            dvmAbort();
614        }
615        // These free(3) calls are safe because we know we're only ever forking
616        // a single-threaded process, so we know no other thread held the heap
617        // lock when we forked.
618        free(seInfo);
619        free(niceName);
620#endif
621
622        /*
623         * Our system thread ID has changed.  Get the new one.
624         */
625        Thread* thread = dvmThreadSelf();
626        thread->systemTid = dvmGetSysThreadId();
627
628        /* configure additional debug options */
629        enableDebugFeatures(debugFlags);
630
631        unsetSignalHandler();
632        gDvm.zygote = false;
633        if (!dvmInitAfterZygote()) {
634            ALOGE("error in post-zygote initialization");
635            dvmAbort();
636        }
637    } else if (pid > 0) {
638        /* the parent process */
639#ifdef HAVE_SELINUX
640        free(seInfo);
641        free(niceName);
642#endif
643    }
644
645    return pid;
646}
647
648/*
649 * native public static int nativeForkAndSpecialize(int uid, int gid,
650 *     int[] gids, int debugFlags, int[][] rlimits, int mountExternal,
651 *     String seInfo, String niceName);
652 */
653static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
654    JValue* pResult)
655{
656    pid_t pid;
657
658    pid = forkAndSpecializeCommon(args, false);
659
660    RETURN_INT(pid);
661}
662
663/*
664 * native public static int nativeForkSystemServer(int uid, int gid,
665 *     int[] gids, int debugFlags, int[][] rlimits,
666 *     long permittedCapabilities, long effectiveCapabilities);
667 */
668static void Dalvik_dalvik_system_Zygote_forkSystemServer(
669        const u4* args, JValue* pResult)
670{
671    pid_t pid;
672    pid = forkAndSpecializeCommon(args, true);
673
674    /* The zygote process checks whether the child process has died or not. */
675    if (pid > 0) {
676        int status;
677
678        ALOGI("System server process %d has been created", pid);
679        gDvm.systemServerPid = pid;
680        /* There is a slight window that the system server process has crashed
681         * but it went unnoticed because we haven't published its pid yet. So
682         * we recheck here just to make sure that all is well.
683         */
684        if (waitpid(pid, &status, WNOHANG) == pid) {
685            ALOGE("System server process %d has died. Restarting Zygote!", pid);
686            kill(getpid(), SIGKILL);
687        }
688    }
689    RETURN_INT(pid);
690}
691
692/* native private static void nativeExecShell(String command);
693 */
694static void Dalvik_dalvik_system_Zygote_execShell(
695        const u4* args, JValue* pResult)
696{
697    StringObject* command = (StringObject*)args[0];
698
699    const char *argp[] = {_PATH_BSHELL, "-c", NULL, NULL};
700    argp[2] = dvmCreateCstrFromString(command);
701
702    ALOGI("Exec: %s %s %s", argp[0], argp[1], argp[2]);
703
704    execv(_PATH_BSHELL, (char**)argp);
705    exit(127);
706}
707
708const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
709    { "nativeFork", "()I",
710      Dalvik_dalvik_system_Zygote_fork },
711    { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;)I",
712      Dalvik_dalvik_system_Zygote_forkAndSpecialize },
713    { "nativeForkSystemServer", "(II[II[[IJJ)I",
714      Dalvik_dalvik_system_Zygote_forkSystemServer },
715    { "nativeExecShell", "(Ljava/lang/String;)V",
716      Dalvik_dalvik_system_Zygote_execShell },
717    { NULL, NULL, NULL },
718};
719