linux_emul.c revision 293532
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 293532 2016-01-09 16:20:29Z 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#include <sys/sysproto.h> 46#include <sys/unistd.h> 47 48#include <compat/linux/linux_emul.h> 49#include <compat/linux/linux_misc.h> 50#include <compat/linux/linux_util.h> 51 52 53/* 54 * This returns reference to the thread emuldata entry (if found) 55 * 56 * Hold PROC_LOCK when referencing emuldata from other threads. 57 */ 58struct linux_emuldata * 59em_find(struct thread *td) 60{ 61 struct linux_emuldata *em; 62 63 em = td->td_emuldata; 64 65 return (em); 66} 67 68/* 69 * This returns reference to the proc pemuldata entry (if found) 70 * 71 * Hold PROC_LOCK when referencing proc pemuldata from other threads. 72 * Hold LINUX_PEM_LOCK wher referencing pemuldata members. 73 */ 74struct linux_pemuldata * 75pem_find(struct proc *p) 76{ 77 struct linux_pemuldata *pem; 78 79 pem = p->p_emuldata; 80 81 return (pem); 82} 83 84void 85linux_proc_init(struct thread *td, struct thread *newtd, int flags) 86{ 87 struct linux_emuldata *em; 88 struct linux_pemuldata *pem; 89 90 if (newtd != NULL) { 91 /* non-exec call */ 92 em = malloc(sizeof(*em), M_TEMP, M_WAITOK | M_ZERO); 93 em->pdeath_signal = 0; 94 em->robust_futexes = NULL; 95 if (flags & LINUX_CLONE_THREAD) { 96 em->em_tid = newtd->td_tid; 97 } else { 98 99 em->em_tid = newtd->td_proc->p_pid; 100 101 pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO); 102 sx_init(&pem->pem_sx, "lpemlk"); 103 newtd->td_proc->p_emuldata = pem; 104 } 105 newtd->td_emuldata = em; 106 } else { 107 /* exec */ 108 109 /* lookup the old one */ 110 em = em_find(td); 111 KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n")); 112 113 em->em_tid = td->td_proc->p_pid; 114 } 115 116 em->child_clear_tid = NULL; 117 em->child_set_tid = NULL; 118} 119 120void 121linux_proc_exit(void *arg __unused, struct proc *p) 122{ 123 struct linux_pemuldata *pem; 124 struct thread *td = curthread; 125 126 if (__predict_false(SV_CURPROC_ABI() != SV_ABI_LINUX)) 127 return; 128 129 pem = pem_find(p); 130 if (pem == NULL) 131 return; 132 (p->p_sysent->sv_thread_detach)(td); 133 134 p->p_emuldata = NULL; 135 136 sx_destroy(&pem->pem_sx); 137 free(pem, M_LINUX); 138} 139 140int 141linux_common_execve(struct thread *td, struct image_args *eargs) 142{ 143 struct linux_pemuldata *pem; 144 struct linux_emuldata *em; 145 struct proc *p; 146 int error; 147 148 p = td->td_proc; 149 150 /* 151 * Unlike FreeBSD abort all other threads before 152 * proceeding exec. 153 */ 154 PROC_LOCK(p); 155 /* See exit1() comments. */ 156 thread_suspend_check(0); 157 while (p->p_flag & P_HADTHREADS) { 158 if (!thread_single(p, SINGLE_EXIT)) 159 break; 160 thread_suspend_check(0); 161 } 162 PROC_UNLOCK(p); 163 164 error = kern_execve(td, eargs, NULL); 165 if (error != 0) 166 return (error); 167 168 /* 169 * In a case of transition from Linux binary execing to 170 * FreeBSD binary we destroy linux emuldata thread & proc entries. 171 */ 172 if (SV_CURPROC_ABI() != SV_ABI_LINUX) { 173 PROC_LOCK(p); 174 em = em_find(td); 175 KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n")); 176 td->td_emuldata = NULL; 177 178 pem = pem_find(p); 179 KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n")); 180 p->p_emuldata = NULL; 181 PROC_UNLOCK(p); 182 183 free(em, M_TEMP); 184 free(pem, M_LINUX); 185 } 186 return (0); 187} 188 189void 190linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp) 191{ 192 struct thread *td = curthread; 193 194 /* 195 * In a case of execing to linux binary we create linux 196 * emuldata thread entry. 197 */ 198 if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) == 199 SV_ABI_LINUX)) { 200 if (SV_PROC_ABI(p) == SV_ABI_LINUX) 201 linux_proc_init(td, NULL, 0); 202 else 203 linux_proc_init(td, td, 0); 204 } 205} 206 207void 208linux_thread_dtor(void *arg __unused, struct thread *td) 209{ 210 struct linux_emuldata *em; 211 212 em = em_find(td); 213 if (em == NULL) 214 return; 215 td->td_emuldata = NULL; 216 217 LINUX_CTR1(exit, "thread dtor(%d)", em->em_tid); 218 219 free(em, M_TEMP); 220} 221 222void 223linux_schedtail(struct thread *td) 224{ 225 struct linux_emuldata *em; 226 struct proc *p; 227 int error = 0; 228 int *child_set_tid; 229 230 p = td->td_proc; 231 232 em = em_find(td); 233 KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n")); 234 child_set_tid = em->child_set_tid; 235 236 if (child_set_tid != NULL) { 237 error = copyout(&em->em_tid, (int *)child_set_tid, 238 sizeof(em->em_tid)); 239 LINUX_CTR4(clone, "schedtail(%d) %p stored %d error %d", 240 td->td_tid, child_set_tid, em->em_tid, error); 241 } else 242 LINUX_CTR1(clone, "schedtail(%d)", em->em_tid); 243} 244