shim_clone.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. /* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
  2. /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
  3. /* Copyright (C) 2014 OSCAR lab, Stony Brook University
  4. This file is part of Graphene Library OS.
  5. Graphene Library OS is free software: you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation, either version 3 of the
  8. License, or (at your option) any later version.
  9. Graphene Library OS is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  15. /*
  16. * shim_clone.c
  17. *
  18. * Implementation of system call "clone". (using "clone" as "fork" is not
  19. * implemented yet.)
  20. */
  21. #include <shim_types.h>
  22. #include <shim_internal.h>
  23. #include <shim_table.h>
  24. #include <shim_thread.h>
  25. #include <shim_utils.h>
  26. #include <shim_profile.h>
  27. #include <pal.h>
  28. #include <pal_error.h>
  29. #include <errno.h>
  30. #include <sys/syscall.h>
  31. #include <sys/mman.h>
  32. #include <linux/sched.h>
  33. #include <asm/prctl.h>
  34. /* from **sysdeps/unix/sysv/linux/x86_64/clone.S:
  35. The userland implementation is:
  36. int clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg),
  37. the kernel entry is:
  38. int clone (long flags, void *child_stack).
  39. The parameters are passed in register and on the stack from userland:
  40. rdi: fn
  41. rsi: child_stack
  42. rdx: flags
  43. rcx: arg
  44. r8d: TID field in parent
  45. r9d: thread pointer
  46. %esp+8: TID field in child
  47. The kernel expects:
  48. rax: system call number
  49. rdi: flags
  50. rsi: child_stack
  51. rdx: TID field in parent
  52. r10: TID field in child
  53. r8: thread pointer
  54. */
  55. /*
  56. * This Function is a wrapper around the user provided function.
  57. * Code flow for clone is as follows -
  58. * 1) User application allocates stack for child process and
  59. * calls clone. The clone code sets up the user function
  60. * address and the argument address on the child stack.
  61. * 2)we Hijack the clone call and control flows to shim_clone
  62. * 3)In Shim Clone we just call the DK Api to create a thread by providing a
  63. * wrapper function around the user provided function
  64. * 4)PAL layer allocates a stack and then invokes the clone syscall
  65. * 5)PAL runs thread_init function on PAL allocated Stack
  66. * 6)thread_init calls our wrapper and gives the user provided stack
  67. * address.
  68. * 7.In the wrapper function ,we just do the stack switch to user
  69. * Provided stack and execute the user Provided function.
  70. */
  71. int clone_implementation_wrapper(struct clone_args * arg)
  72. {
  73. //The child thread created by PAL is now running on the
  74. //PAL allocated stack. We need to switch the stack to use
  75. //the user provided stack.
  76. struct clone_args *pcargs = arg;
  77. DkObjectsWaitAny(1, &pcargs->create_event, NO_TIMEOUT);
  78. DkObjectClose(pcargs->create_event);
  79. struct shim_thread * my_thread = pcargs->thread;
  80. assert(my_thread);
  81. get_thread(my_thread);
  82. if (my_thread->set_child_tid) {
  83. *(my_thread->set_child_tid) = my_thread->tid;
  84. debug("clone: tid set at %p\n", my_thread->set_child_tid);
  85. }
  86. void * stack = pcargs->stack;
  87. void * return_pc = pcargs->return_pc;
  88. struct shim_vma * vma = NULL;
  89. lookup_supervma(ALIGN_DOWN(stack), allocsize, &vma);
  90. assert(vma);
  91. my_thread->stack_top = stack;
  92. my_thread->stack_red = my_thread->stack = vma->addr;
  93. __libc_tcb_t tcb;
  94. allocate_tls(&tcb, my_thread);
  95. debug_setbuf(&tcb.shim_tcb, true);
  96. /* Don't signal the initialize event until we are actually init-ed */
  97. DkEventSet(pcargs->initialize_event);
  98. /***** From here down, we are switching to the user-provided stack ****/
  99. //user_stack_addr[0] ==> user provided function address
  100. //user_stack_addr[1] ==> arguments to user provided function.
  101. debug("child swapping stack to %p return %p: %d\n",
  102. stack, return_pc, my_thread->tid);
  103. asm volatile("movq %0, %%rsp\r\n"
  104. "pushq %1\r\n"
  105. "retq\r\n"
  106. : : "r"(stack), "r"(return_pc), "a"(0));
  107. return 0;
  108. }
  109. /* long int __arg0 - flags
  110. * long int __arg1 - 16 bytes ( 2 words ) offset into the child stack allocated
  111. * by the parent */
  112. int shim_do_clone (int flags, void * user_stack_addr, int * parent_tidptr,
  113. void * tls, int * child_tidptr)
  114. {
  115. //The Clone Implementation in glibc has setup the child's stack
  116. //with the function pointer and the argument to the funciton.
  117. INC_PROFILE_OCCURENCE(syscall_use_ipc);
  118. struct shim_thread * self = get_cur_thread();
  119. assert(self);
  120. struct clone_args * new_args = __alloca(sizeof(struct clone_args));
  121. memset(new_args, 0, sizeof(struct clone_args));
  122. int ret = 0;
  123. assert((flags & ~(CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
  124. CLONE_CHILD_CLEARTID|CLONE_SETTLS|
  125. CLONE_VM|CLONE_FILES|
  126. CLONE_FS|CLONE_SIGHAND|CLONE_THREAD|
  127. CLONE_DETACHED| // Unused
  128. #ifdef CLONE_PTRACE
  129. CLONE_PTRACE| // Unused
  130. #endif
  131. CLONE_SYSVSEM|CSIGNAL)) == 0);
  132. new_args->create_event = DkNotificationEventCreate(0);
  133. if (!new_args->create_event) {
  134. ret = -PAL_ERRNO;
  135. goto failed;
  136. }
  137. new_args->initialize_event = DkNotificationEventCreate(0);
  138. if (!new_args->initialize_event) {
  139. ret = -PAL_ERRNO;
  140. goto failed;
  141. }
  142. new_args->thread = get_new_thread(0);
  143. if (!new_args->thread) {
  144. ret = -ENOMEM;
  145. goto failed;
  146. }
  147. new_args->stack = user_stack_addr;
  148. new_args->return_pc = *(void **) user_stack_addr;
  149. if (flags & CLONE_PARENT_SETTID)
  150. new_args->thread->set_child_tid = parent_tidptr;
  151. if ((flags & CLONE_CHILD_SETTID) && (flags & CLONE_CHILD_CLEARTID)) {
  152. ret = -EINVAL;
  153. goto failed;
  154. }
  155. if (flags & CLONE_CHILD_SETTID)
  156. new_args->thread->set_child_tid = child_tidptr;
  157. if (flags & CLONE_CHILD_CLEARTID)
  158. /* Implemented in shim_futex.c: release_clear_child_id */
  159. new_args->thread->clear_child_tid = parent_tidptr;
  160. if (flags & CLONE_SETTLS) {
  161. new_args->thread->tcb = tls;
  162. } else {
  163. new_args->thread->tcb = NULL;
  164. }
  165. if ((flags & CLONE_VM) != CLONE_VM)
  166. debug("Fork-like behavior is not yet implemented in clone\n");
  167. if ((flags & CLONE_FS) != CLONE_FS)
  168. debug("clone without CLONE_FS is not yet implemented\n");
  169. if ((flags & CLONE_SIGHAND) != CLONE_SIGHAND)
  170. debug("clone without CLONE_SIGHAND is not yet implemented\n");
  171. if ((flags & CLONE_SYSVSEM) != CLONE_SYSVSEM)
  172. debug("clone without CLONE_SYSVSEM is not yet implemented\n");
  173. if ((flags & CLONE_THREAD) != CLONE_THREAD)
  174. new_args->thread->tgid = new_args->thread->tid;
  175. struct shim_handle_map * handle_map = get_cur_handle_map(self);
  176. if (flags & CLONE_FILES) {
  177. set_handle_map(new_args->thread, handle_map);
  178. } else {
  179. /* if CLONE_FILES is not given, the new thread should receive
  180. a copy of current descriptor table */
  181. struct shim_handle_map * new_map = NULL;
  182. get_handle_map(handle_map);
  183. dup_handle_map(&new_map, handle_map);
  184. set_handle_map(new_args->thread, new_map);
  185. put_handle_map(handle_map);
  186. }
  187. // Invoke DkThreadCreate to spawn off a child process using the actual
  188. // "clone" system call. DkThreadCreate allocates a stack for the child
  189. // and then runs the given function on that stack However, we want our
  190. // child to run on the Parent allocated stack , so once the DkThreadCreate
  191. // returns .The parent comes back here - however, the child is Happily
  192. // running the function we gave to DkThreadCreate.
  193. PAL_HANDLE pal_handle = thread_create(clone_implementation_wrapper,
  194. new_args, flags);
  195. if (!pal_handle) {
  196. ret = -PAL_ERRNO;
  197. goto failed;
  198. }
  199. new_args->thread->pal_handle = pal_handle;
  200. new_args->thread->in_vm = new_args->thread->is_alive = true;
  201. add_thread(new_args->thread);
  202. set_as_child(NULL, new_args->thread);
  203. put_thread(new_args->thread);
  204. if (new_args->thread->set_child_tid)
  205. *new_args->thread->set_child_tid = new_args->thread->tid;
  206. DkEventSet(new_args->create_event);
  207. DkObjectsWaitAny(1, &new_args->initialize_event, NO_TIMEOUT);
  208. DkObjectClose(new_args->initialize_event);
  209. return new_args->thread->tid;
  210. failed:
  211. if (new_args->create_event)
  212. DkObjectClose(new_args->create_event);
  213. if (new_args->initialize_event)
  214. DkObjectClose(new_args->initialize_event);
  215. if (new_args->thread)
  216. put_thread(new_args->thread);
  217. return ret;
  218. }