linux_emul.c revision 293558
1/*- 2 * Copyright (c) 2006 Roman Divacky 3 * Copyright (c) 2013 Dmitry Chagin 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/10/sys/compat/linux/linux_emul.c 293558 2016-01-09 17:02:35Z dchagin $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/imgact.h> 36#include <sys/kernel.h> 37#include <sys/ktr.h> 38#include <sys/lock.h> 39#include <sys/malloc.h> 40#include <sys/mutex.h> 41#include <sys/sx.h> 42#include <sys/proc.h> 43#include <sys/syscallsubr.h> 44#include <sys/sysent.h> 45 46#include <compat/linux/linux_emul.h> 47#include <compat/linux/linux_misc.h> 48#include <compat/linux/linux_util.h> 49 50 51/* 52 * This returns reference to the thread emuldata entry (if found) 53 * 54 * Hold PROC_LOCK when referencing emuldata from other threads. 55 */ 56struct linux_emuldata * 57em_find(struct thread *td) 58{ 59 struct linux_emuldata *em; 60 61 em = td->td_emuldata; 62 63 return (em); 64} 65 66/* 67 * This returns reference to the proc pemuldata entry (if found) 68 * 69 * Hold PROC_LOCK when referencing proc pemuldata from other threads. 70 * Hold LINUX_PEM_LOCK wher referencing pemuldata members. 71 */ 72struct linux_pemuldata * 73pem_find(struct proc *p) 74{ 75 struct linux_pemuldata *pem; 76 77 pem = p->p_emuldata; 78 79 return (pem); 80} 81 82void 83linux_proc_init(struct thread *td, struct thread *newtd, int flags) 84{ 85 struct linux_emuldata *em; 86 struct linux_pemuldata *pem; 87 struct epoll_emuldata *emd; 88 89 if (newtd != NULL) { 90 /* non-exec call */ 91 em = malloc(sizeof(*em), M_TEMP, M_WAITOK | M_ZERO); 92 if (flags & LINUX_CLONE_THREAD) { 93 LINUX_CTR1(proc_init, "thread newtd(%d)", 94 newtd->td_tid); 95 96 em->em_tid = newtd->td_tid; 97 } else { 98 LINUX_CTR1(proc_init, "fork newtd(%d)", 99 newtd->td_proc->p_pid); 100 101 em->em_tid = newtd->td_proc->p_pid; 102 103 pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO); 104 sx_init(&pem->pem_sx, "lpemlk"); 105 newtd->td_proc->p_emuldata = pem; 106 } 107 newtd->td_emuldata = em; 108 } else { 109 /* exec */ 110 LINUX_CTR1(proc_init, "exec newtd(%d)", 111 td->td_proc->p_pid); 112 113 /* lookup the old one */ 114 em = em_find(td); 115 KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n")); 116 117 em->em_tid = td->td_proc->p_pid; 118 em->flags = 0; 119 em->pdeath_signal = 0; 120 em->robust_futexes = NULL; 121 em->child_clear_tid = NULL; 122 em->child_set_tid = NULL; 123 124 /* epoll should be destroyed in a case of exec. */ 125 pem = pem_find(td->td_proc); 126 KASSERT(pem != NULL, ("proc_exit: proc emuldata not found.\n")); 127 128 if (pem->epoll != NULL) { 129 emd = pem->epoll; 130 pem->epoll = NULL; 131 free(emd, M_EPOLL); 132 } 133 } 134 135} 136 137void 138linux_proc_exit(void *arg __unused, struct proc *p) 139{ 140 struct linux_pemuldata *pem; 141 struct epoll_emuldata *emd; 142 struct thread *td = curthread; 143 144 if (__predict_false(SV_CURPROC_ABI() != SV_ABI_LINUX)) 145 return; 146 147 pem = pem_find(p); 148 if (pem == NULL) 149 return; 150 (p->p_sysent->sv_thread_detach)(td); 151 152 p->p_emuldata = NULL; 153 154 if (pem->epoll != NULL) { 155 emd = pem->epoll; 156 pem->epoll = NULL; 157 free(emd, M_EPOLL); 158 } 159 160 sx_destroy(&pem->pem_sx); 161 free(pem, M_LINUX); 162} 163 164int 165linux_common_execve(struct thread *td, struct image_args *eargs) 166{ 167 struct linux_pemuldata *pem; 168 struct epoll_emuldata *emd; 169 struct linux_emuldata *em; 170 struct proc *p; 171 int error; 172 173 p = td->td_proc; 174 175 /* 176 * Unlike FreeBSD abort all other threads before 177 * proceeding exec. 178 */ 179 PROC_LOCK(p); 180 /* See exit1() comments. */ 181 thread_suspend_check(0); 182 while (p->p_flag & P_HADTHREADS) { 183 if (!thread_single(p, SINGLE_EXIT)) 184 break; 185 thread_suspend_check(0); 186 } 187 PROC_UNLOCK(p); 188 189 error = kern_execve(td, eargs, NULL); 190 if (error != 0) 191 return (error); 192 193 /* 194 * In a case of transition from Linux binary execing to 195 * FreeBSD binary we destroy linux emuldata thread & proc entries. 196 */ 197 if (SV_CURPROC_ABI() != SV_ABI_LINUX) { 198 PROC_LOCK(p); 199 em = em_find(td); 200 KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n")); 201 td->td_emuldata = NULL; 202 203 pem = pem_find(p); 204 KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n")); 205 p->p_emuldata = NULL; 206 PROC_UNLOCK(p); 207 208 if (pem->epoll != NULL) { 209 emd = pem->epoll; 210 pem->epoll = NULL; 211 free(emd, M_EPOLL); 212 } 213 214 free(em, M_TEMP); 215 free(pem, M_LINUX); 216 } 217 return (0); 218} 219 220void 221linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp) 222{ 223 struct thread *td = curthread; 224 225 /* 226 * In a case of execing to linux binary we create linux 227 * emuldata thread entry. 228 */ 229 if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) == 230 SV_ABI_LINUX)) { 231 232 if (SV_PROC_ABI(p) == SV_ABI_LINUX) 233 linux_proc_init(td, NULL, 0); 234 else 235 linux_proc_init(td, td, 0); 236 } 237} 238 239void 240linux_thread_dtor(void *arg __unused, struct thread *td) 241{ 242 struct linux_emuldata *em; 243 244 em = em_find(td); 245 if (em == NULL) 246 return; 247 td->td_emuldata = NULL; 248 249 LINUX_CTR1(exit, "thread dtor(%d)", em->em_tid); 250 251 free(em, M_TEMP); 252} 253 254void 255linux_schedtail(struct thread *td) 256{ 257 struct linux_emuldata *em; 258 struct proc *p; 259 int error = 0; 260 int *child_set_tid; 261 262 p = td->td_proc; 263 264 em = em_find(td); 265 KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n")); 266 child_set_tid = em->child_set_tid; 267 268 if (child_set_tid != NULL) { 269 error = copyout(&em->em_tid, child_set_tid, 270 sizeof(em->em_tid)); 271 LINUX_CTR4(clone, "schedtail(%d) %p stored %d error %d", 272 td->td_tid, child_set_tid, em->em_tid, error); 273 } else 274 LINUX_CTR1(clone, "schedtail(%d)", em->em_tid); 275} 276