1/*
2 * Copyright 2012, 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#define _GNU_SOURCE
18#include <portability.h>
19#include <sched.h>
20#include <stdarg.h>
21#include <stdlib.h>
22#include <signal.h>
23#include <signal_portable.h>
24#include <portability.h>
25#include <stdio.h>
26#include <errno.h>
27#include <filefd_portable.h>
28
29#define PORTABLE_TAG "clone_portable"
30#include <log_portable.h>
31
32
33/*
34 * This function maps the clone function call defined in:
35 *      $TOP/bionic/libc/bionic/bionic_clone.c
36 *
37 * which calls the __bionic_clone() system call which is defined in:
38 *      $TOP/bionic/libc/unistd/arch-mips/bionic/clone.S
39 *
40 * We have to map the low byte of the 'flags' parameter which
41 * contains the number of the termination signal sent to the
42 * parent when the child dies.
43 *
44 * Note that if this signal is specified as anything other than
45 * SIGCHLD, then the parent process must specify the __WALL or
46 * __WCLONE options when waiting for the child with wait(2).
47 *
48 * If no signal is specified, then the parent process is not
49 * signaled when the child terminates.
50 */
51int WRAP(clone)(int (*fn)(void *), void *child_stack, int port_flags, void *arg, ...)
52{
53    va_list     args;
54    int         ret;
55    int         mips_flags;
56    void        *new_tls = NULL;
57    int         *child_tidptr = NULL;
58    int         *parent_tidptr = NULL;
59    int         mips_term_signum;
60    char        *mips_term_signame;
61    int         portable_term_signum;
62    char        *portable_term_signame;
63    int         cloning_vm = ((port_flags & CLONE_VM) == CLONE_VM);
64    int         cloning_files = ((port_flags & CLONE_FILES) == CLONE_FILES);
65    int         cloning_sighand = ((port_flags & CLONE_SIGHAND) == CLONE_SIGHAND);
66
67    ALOGV(" ");
68    ALOGV("%s(fn:%p, child_stack:%p, port_flags:0x%x, arg:%p, ...) {", __func__,
69              fn,    child_stack,    port_flags,      arg);
70
71    /* Shared file descriptor table requires shared memory. */
72    if (cloning_files != cloning_vm) {
73        ALOGE("%s: cloning_files:%d != cloning_vm:%d) ...", __func__,
74                   cloning_files,      cloning_vm);
75
76        ALOGE("%s: ... port_flags:0x%x Not Supported by Lib-Portable!", __func__,
77                       port_flags);
78    }
79
80    /* Shared signal handler table requires shared memory. */
81    if (cloning_sighand != cloning_vm) {
82        ALOGE("%s: cloning_sighand:%d != cloning_vm:%d) ...",  __func__,
83                   cloning_sighand,      cloning_vm);
84
85        ALOGE("%s: ... port_flags:0x%x Not Supported by Lib-Portable!", __func__,
86                       port_flags);
87    }
88
89    /* Extract optional parameters - they are cumulative. */
90    va_start(args, arg);
91    if (port_flags & (CLONE_PARENT_SETTID|CLONE_SETTLS|CLONE_CHILD_SETTID)) {
92        parent_tidptr = va_arg(args, int*);
93    }
94    if (port_flags & (CLONE_SETTLS|CLONE_CHILD_SETTID)) {
95        new_tls = va_arg(args, void*);
96    }
97    if (port_flags & CLONE_CHILD_SETTID) {
98        child_tidptr = va_arg(args, int*);
99    }
100    va_end(args);
101
102    /*
103     * Map the LSB of the flags as explained above.
104     */
105    portable_term_signum = port_flags & 0xFF;
106    if (portable_term_signum == 0) {
107        mips_flags = port_flags;
108    } else {
109        portable_term_signame = map_portable_signum_to_name(portable_term_signum);
110        ALOGV("%s: portable_term_signum:0x%x:'%s'", __func__,
111                   portable_term_signum, portable_term_signame);
112        mips_term_signum = signum_pton(portable_term_signum);
113        mips_term_signame = map_mips_signum_to_name(mips_term_signum);
114        ALOGV("%s: mips_term_signum:0x%x:'%s'", __func__,
115                   mips_term_signum, mips_term_signame);
116        mips_flags = (port_flags & ~0xFF) | (mips_term_signum & 0xFF);
117    }
118    ALOGV("%s: clone(%p, %p, 0x%x, %p, %p, %p, %p);", __func__,
119           fn, child_stack, mips_flags, arg, parent_tidptr, new_tls, child_tidptr);
120
121    ret = REAL(clone)(fn, child_stack, mips_flags, arg, parent_tidptr,
122                new_tls, child_tidptr);
123
124    if (ret > 0) {
125        /*
126         * Disable mapping in the parent if the child could interfere
127         * and make things even worse than skipping the signal and
128         * file read mapping.
129         */
130        if (cloning_files != cloning_vm) {
131            filefd_disable_mapping();
132        }
133        if (cloning_sighand != cloning_vm) {
134            signal_disable_mapping();
135        }
136    }
137
138    ALOGV("%s: return(ret:%d); }", __func__, ret);
139    return ret;
140}
141