1 /* $NetBSD: coff_exec.c,v 1.17 2002/10/05 22:34:03 chs Exp $ */ 2 3 /* 4 * Copyright (c) 1994, 1995 Scott Bartram 5 * Copyright (c) 1994 Adam Glass 6 * Copyright (c) 1993, 1994 Christopher G. Demetriou 7 * All rights reserved. 8 * 9 * originally from kern/exec_ecoff.c 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by Scott Bartram. 22 * 4. The name of the author may not be used to endorse or promote products 23 * derived from this software without specific prior written permission 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/proc.h> 40 #include <sys/exec.h> 41 #include <sys/malloc.h> 42 #include <sys/vnode.h> 43 #include <sys/resourcevar.h> 44 #include <sys/namei.h> 45 #include <uvm/uvm_extern.h> 46 47 #include <sys/exec_coff.h> 48 49 static int coff_find_section(struct proc *, struct vnode *, 50 struct coff_filehdr *, struct coff_scnhdr *, int); 51 52 /* 53 * exec_coff_makecmds(): Check if it's an coff-format executable. 54 * 55 * Given a proc pointer and an exec package pointer, see if the referent 56 * of the epp is in coff format. Check 'standard' magic numbers for 57 * this architecture. If that fails, return failure. 58 * 59 * This function is responsible for creating a set of vmcmds which can be 60 * used to build the process's vm space and inserting them into the exec 61 * package. 62 */ 63 64 int 65 exec_coff_makecmds(struct proc *p, struct exec_package *epp) 66 { 67 int error; 68 struct coff_filehdr *fp = epp->ep_hdr; 69 struct coff_aouthdr *ap; 70 71 if (epp->ep_hdrvalid < COFF_HDR_SIZE) 72 return ENOEXEC; 73 74 if (COFF_BADMAG(fp)) 75 return ENOEXEC; 76 77 ap = (void *)((char *)epp->ep_hdr + sizeof(struct coff_filehdr)); 78 switch (ap->a_magic) { 79 case COFF_OMAGIC: 80 error = exec_coff_prep_omagic(p, epp, fp, ap); 81 break; 82 case COFF_NMAGIC: 83 error = exec_coff_prep_nmagic(p, epp, fp, ap); 84 break; 85 case COFF_ZMAGIC: 86 error = exec_coff_prep_zmagic(p, epp, fp, ap); 87 break; 88 default: 89 return ENOEXEC; 90 } 91 92 #ifdef TODO 93 if (error == 0) 94 error = cpu_exec_coff_hook(p, epp); 95 #endif 96 97 if (error) 98 kill_vmcmds(&epp->ep_vmcmds); 99 100 return error; 101 } 102 103 /* 104 * exec_coff_setup_stack(): Set up the stack segment for a coff 105 * executable. 106 * 107 * Note that the ep_ssize parameter must be set to be the current stack 108 * limit; this is adjusted in the body of execve() to yield the 109 * appropriate stack segment usage once the argument length is 110 * calculated. 111 * 112 * This function returns an int for uniformity with other (future) formats' 113 * stack setup functions. They might have errors to return. 114 */ 115 116 int 117 exec_coff_setup_stack(struct proc *p, struct exec_package *epp) 118 { 119 DPRINTF(("enter exec_coff_setup_stack\n")); 120 121 epp->ep_maxsaddr = USRSTACK - MAXSSIZ; 122 epp->ep_minsaddr = USRSTACK; 123 epp->ep_ssize = p->p_rlimit[RLIMIT_STACK].rlim_cur; 124 125 /* 126 * set up commands for stack. note that this takes *two*, one to 127 * map the part of the stack which we can access, and one to map 128 * the part which we can't. 129 * 130 * arguably, it could be made into one, but that would require the 131 * addition of another mapping proc, which is unnecessary 132 * 133 * note that in memory, things assumed to be: 0 ....... ep_maxsaddr 134 * <stack> ep_minsaddr 135 */ 136 DPRINTF(("VMCMD: addr %lx size %lx\n", epp->ep_maxsaddr, 137 (epp->ep_minsaddr - epp->ep_ssize) - epp->ep_maxsaddr)); 138 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, 139 ((epp->ep_minsaddr - epp->ep_ssize) - epp->ep_maxsaddr), 140 epp->ep_maxsaddr, NULLVP, 0, VM_PROT_NONE); 141 DPRINTF(("VMCMD: addr %lx size %lx\n", 142 epp->ep_minsaddr - epp->ep_ssize, 143 epp->ep_ssize)); 144 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, epp->ep_ssize, 145 (epp->ep_minsaddr - epp->ep_ssize), NULLVP, 0, 146 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 147 148 return 0; 149 } 150 151 152 /* 153 * exec_coff_prep_omagic(): Prepare a COFF OMAGIC binary's exec package 154 */ 155 156 int 157 exec_coff_prep_omagic(struct proc *p, struct exec_package *epp, 158 struct coff_filehdr *fp, struct coff_aouthdr *ap) 159 { 160 epp->ep_taddr = COFF_SEGMENT_ALIGN(fp, ap, ap->a_tstart); 161 epp->ep_tsize = ap->a_tsize; 162 epp->ep_daddr = COFF_SEGMENT_ALIGN(fp, ap, ap->a_dstart); 163 epp->ep_dsize = ap->a_dsize; 164 epp->ep_entry = ap->a_entry; 165 166 /* set up command for text and data segments */ 167 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, 168 ap->a_tsize + ap->a_dsize, epp->ep_taddr, epp->ep_vp, 169 COFF_TXTOFF(fp, ap), 170 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 171 172 /* set up command for bss segment */ 173 #ifdef __sh__ 174 if (ap->a_bsize > 0) 175 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, ap->a_bsize, 176 COFF_ROUND(ap->a_dstart + ap->a_dsize, COFF_LDPGSZ), 177 NULLVP, 0, 178 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 179 #else 180 if (ap->a_bsize > 0) 181 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, ap->a_bsize, 182 COFF_SEGMENT_ALIGN(fp, ap, 183 ap->a_dstart + ap->a_dsize), 184 NULLVP, 0, 185 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 186 #endif 187 188 return exec_coff_setup_stack(p, epp); 189 } 190 191 /* 192 * exec_coff_prep_nmagic(): Prepare a 'native' NMAGIC COFF binary's exec 193 * package. 194 */ 195 196 int 197 exec_coff_prep_nmagic(p, epp, fp, ap) 198 struct proc *p; 199 struct exec_package *epp; 200 struct coff_filehdr *fp; 201 struct coff_aouthdr *ap; 202 { 203 epp->ep_taddr = COFF_SEGMENT_ALIGN(fp, ap, ap->a_tstart); 204 epp->ep_tsize = ap->a_tsize; 205 epp->ep_daddr = COFF_ROUND(ap->a_dstart, COFF_LDPGSZ); 206 epp->ep_dsize = ap->a_dsize; 207 epp->ep_entry = ap->a_entry; 208 209 /* set up command for text segment */ 210 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, epp->ep_tsize, 211 epp->ep_taddr, epp->ep_vp, COFF_TXTOFF(fp, ap), 212 VM_PROT_READ|VM_PROT_EXECUTE); 213 214 /* set up command for data segment */ 215 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, epp->ep_dsize, 216 epp->ep_daddr, epp->ep_vp, COFF_DATOFF(fp, ap), 217 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 218 219 /* set up command for bss segment */ 220 if (ap->a_bsize > 0) 221 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, ap->a_bsize, 222 COFF_SEGMENT_ALIGN(fp, ap, 223 ap->a_dstart + ap->a_dsize), 224 NULLVP, 0, 225 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 226 227 return exec_coff_setup_stack(p, epp); 228 } 229 230 /* 231 * coff_find_section - load specified section header 232 * 233 * TODO - optimize by reading all section headers in at once 234 */ 235 236 static int 237 coff_find_section(struct proc *p, struct vnode *vp, struct coff_filehdr *fp, 238 struct coff_scnhdr *sh, int s_type) 239 { 240 int i, pos, resid, siz, error; 241 242 pos = COFF_HDR_SIZE; 243 for (i = 0; i < fp->f_nscns; i++, pos += sizeof(struct coff_scnhdr)) { 244 siz = sizeof(struct coff_scnhdr); 245 error = vn_rdwr(UIO_READ, vp, (caddr_t) sh, 246 siz, pos, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, 247 &resid, p); 248 if (error) { 249 DPRINTF(("section hdr %d read error %d\n", i, error)); 250 return error; 251 } 252 siz -= resid; 253 if (siz != sizeof(struct coff_scnhdr)) { 254 DPRINTF(("incomplete read: hdr %d ask=%d, rem=%d got %d\n", 255 s_type, sizeof(struct coff_scnhdr), 256 resid, siz)); 257 return ENOEXEC; 258 } 259 DPRINTF(("found section: %lu\n", sh->s_flags)); 260 if (sh->s_flags == s_type) 261 return 0; 262 } 263 return ENOEXEC; 264 } 265 266 /* 267 * exec_coff_prep_zmagic(): Prepare a COFF ZMAGIC binary's exec package 268 * 269 * First, set the various offsets/lengths in the exec package. 270 * 271 * Then, mark the text image busy (so it can be demand paged) or error 272 * out if this is not possible. Finally, set up vmcmds for the 273 * text, data, bss, and stack segments. 274 */ 275 276 int 277 exec_coff_prep_zmagic(struct proc *p, struct exec_package *epp, 278 struct coff_filehdr *fp, struct coff_aouthdr *ap) 279 { 280 int error; 281 u_long offset; 282 long dsize; 283 #ifndef __sh__ 284 long baddr, bsize; 285 #endif 286 struct coff_scnhdr sh; 287 288 DPRINTF(("enter exec_coff_prep_zmagic\n")); 289 290 /* set up command for text segment */ 291 error = coff_find_section(p, epp->ep_vp, fp, &sh, COFF_STYP_TEXT); 292 if (error) { 293 DPRINTF(("can't find text section: %d\n", error)); 294 return error; 295 } 296 DPRINTF(("COFF text addr %lu size %ld offset %ld\n", sh.s_vaddr, 297 sh.s_size, sh.s_scnptr)); 298 epp->ep_taddr = COFF_ALIGN(sh.s_vaddr); 299 offset = sh.s_scnptr - (sh.s_vaddr - epp->ep_taddr); 300 epp->ep_tsize = sh.s_size + (sh.s_vaddr - epp->ep_taddr); 301 302 error = vn_marktext(epp->ep_vp); 303 if (error) 304 return (error); 305 306 DPRINTF(("VMCMD: addr %lx size %lx offset %lx\n", epp->ep_taddr, 307 epp->ep_tsize, offset)); 308 if (!(offset & PAGE_MASK) && !(epp->ep_taddr & PAGE_MASK)) { 309 epp->ep_tsize = round_page(epp->ep_tsize); 310 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_pagedvn, epp->ep_tsize, 311 epp->ep_taddr, epp->ep_vp, offset, 312 VM_PROT_READ|VM_PROT_EXECUTE); 313 } else { 314 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, epp->ep_tsize, 315 epp->ep_taddr, epp->ep_vp, offset, 316 VM_PROT_READ|VM_PROT_EXECUTE); 317 } 318 319 /* set up command for data segment */ 320 error = coff_find_section(p, epp->ep_vp, fp, &sh, COFF_STYP_DATA); 321 if (error) { 322 DPRINTF(("can't find data section: %d\n", error)); 323 return error; 324 } 325 DPRINTF(("COFF data addr %lx size %ld offset %ld\n", sh.s_vaddr, 326 sh.s_size, sh.s_scnptr)); 327 epp->ep_daddr = COFF_ALIGN(sh.s_vaddr); 328 offset = sh.s_scnptr - (sh.s_vaddr - epp->ep_daddr); 329 dsize = sh.s_size + (sh.s_vaddr - epp->ep_daddr); 330 #ifdef __sh__ 331 epp->ep_dsize = round_page(dsize) + ap->a_bsize; 332 #else 333 epp->ep_dsize = dsize + ap->a_bsize; 334 #endif 335 336 DPRINTF(("VMCMD: addr %lx size %lx offset %lx\n", epp->ep_daddr, 337 dsize, offset)); 338 if (!(offset & PAGE_MASK) && !(epp->ep_daddr & PAGE_MASK)) { 339 dsize = round_page(dsize); 340 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_pagedvn, dsize, 341 epp->ep_daddr, epp->ep_vp, offset, 342 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 343 } else { 344 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, 345 dsize, epp->ep_daddr, epp->ep_vp, offset, 346 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 347 } 348 349 #ifdef __sh__ 350 if (ap->a_bsize > 0) { 351 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, ap->a_bsize, 352 COFF_ROUND(ap->a_dstart + ap->a_dsize, COFF_LDPGSZ), 353 NULLVP, 0, 354 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 355 } 356 #else 357 /* set up command for bss segment */ 358 baddr = round_page(epp->ep_daddr + dsize); 359 bsize = epp->ep_daddr + epp->ep_dsize - baddr; 360 if (bsize > 0) { 361 DPRINTF(("VMCMD: addr %x size %x offset %x\n", 362 baddr, bsize, 0)); 363 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, 364 bsize, baddr, NULLVP, 0, 365 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 366 } 367 #endif 368 369 #ifdef TODO 370 /* load any shared libraries */ 371 error = coff_find_section(p, epp->ep_vp, fp, &sh, COFF_STYP_SHLIB); 372 if (!error) { 373 int resid; 374 struct coff_slhdr *slhdr; 375 char buf[128], *bufp; /* FIXME */ 376 int len = sh.s_size, path_index, entry_len; 377 378 DPRINTF(("COFF shlib size %d offset %d\n", 379 sh.s_size, sh.s_scnptr)); 380 381 error = vn_rdwr(UIO_READ, epp->ep_vp, (caddr_t) buf, 382 len, sh.s_scnptr, 383 UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, 384 &resid, p); 385 if (error) { 386 DPRINTF(("shlib section read error %d\n", error)); 387 return ENOEXEC; 388 } 389 bufp = buf; 390 while (len) { 391 slhdr = (struct coff_slhdr *)bufp; 392 path_index = slhdr->path_index * sizeof(long); 393 entry_len = slhdr->entry_len * sizeof(long); 394 395 DPRINTF(("path_index: %d entry_len: %d name: %s\n", 396 path_index, entry_len, slhdr->sl_name)); 397 398 error = coff_load_shlib(p, slhdr->sl_name, epp); 399 if (error) 400 return ENOEXEC; 401 bufp += entry_len; 402 len -= entry_len; 403 } 404 } 405 #endif 406 /* set up entry point */ 407 epp->ep_entry = ap->a_entry; 408 409 #if 1 410 DPRINTF(("text addr: %lx size: %ld data addr: %lx size: %ld entry: %lx\n", 411 epp->ep_taddr, epp->ep_tsize, 412 epp->ep_daddr, epp->ep_dsize, 413 epp->ep_entry)); 414 #endif 415 return exec_coff_setup_stack(p, epp); 416 } 417 418 #if 0 419 int 420 coff_load_shlib(struct proc *p, char *path, struct exec_package *epp) 421 { 422 int error, siz, resid; 423 int taddr, tsize, daddr, dsize, offset; 424 struct nameidata nd; 425 struct coff_filehdr fh, *fhp = &fh; 426 struct coff_scnhdr sh, *shp = &sh; 427 caddr_t sg = stackgap_init(p, 0); 428 429 /* 430 * 1. open shlib file 431 * 2. read filehdr 432 * 3. map text, data, and bss out of it using VM_* 433 */ 434 #ifdef TODO 435 IBCS2_CHECK_ALT_EXIST(p, &sg, path); 436 #endif 437 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, p); 438 /* first get the vnode */ 439 if ((error = namei(&nd)) != 0) { 440 DPRINTF(("coff_load_shlib: can't find library %s\n", path)); 441 return error; 442 } 443 444 siz = sizeof(struct coff_filehdr); 445 error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t) fhp, siz, 0, 446 UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p); 447 if (error) { 448 DPRINTF(("filehdr read error %d\n", error)); 449 vrele(nd.ni_vp); 450 return error; 451 } 452 siz -= resid; 453 if (siz != sizeof(struct coff_filehdr)) { 454 DPRINTF(("coff_load_shlib: incomplete read: ask=%d, rem=%d got %d\n", 455 sizeof(struct coff_filehdr), resid, siz)); 456 vrele(nd.ni_vp); 457 return ENOEXEC; 458 } 459 460 /* load text */ 461 error = coff_find_section(p, nd.ni_vp, fhp, shp, COFF_STYP_TEXT); 462 if (error) { 463 DPRINTF(("can't find shlib text section\n")); 464 vrele(nd.ni_vp); 465 return error; 466 } 467 DPRINTF(("COFF text addr %x size %d offset %d\n", sh.s_vaddr, 468 sh.s_size, sh.s_scnptr)); 469 taddr = COFF_ALIGN(shp->s_vaddr); 470 offset = shp->s_scnptr - (shp->s_vaddr - taddr); 471 tsize = shp->s_size + (shp->s_vaddr - taddr); 472 DPRINTF(("VMCMD: addr %x size %x offset %x\n", taddr, tsize, offset)); 473 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, tsize, taddr, 474 nd.ni_vp, offset, 475 VM_PROT_READ|VM_PROT_EXECUTE); 476 477 /* load data */ 478 error = coff_find_section(p, nd.ni_vp, fhp, shp, COFF_STYP_DATA); 479 if (error) { 480 DPRINTF(("can't find shlib data section\n")); 481 vrele(nd.ni_vp); 482 return error; 483 } 484 DPRINTF(("COFF data addr %x size %d offset %d\n", shp->s_vaddr, 485 shp->s_size, shp->s_scnptr)); 486 daddr = COFF_ALIGN(shp->s_vaddr); 487 offset = shp->s_scnptr - (shp->s_vaddr - daddr); 488 dsize = shp->s_size + (shp->s_vaddr - daddr); 489 /* epp->ep_dsize = dsize + ap->a_bsize; */ 490 491 DPRINTF(("VMCMD: addr %x size %x offset %x\n", daddr, dsize, offset)); 492 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_readvn, 493 dsize, daddr, nd.ni_vp, offset, 494 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 495 496 /* load bss */ 497 error = coff_find_section(p, nd.ni_vp, fhp, shp, COFF_STYP_BSS); 498 if (!error) { 499 int baddr = round_page(daddr + dsize); 500 int bsize = daddr + dsize + shp->s_size - baddr; 501 if (bsize > 0) { 502 DPRINTF(("VMCMD: addr %x size %x offset %x\n", 503 baddr, bsize, 0)); 504 NEW_VMCMD(&epp->ep_vmcmds, vmcmd_map_zero, 505 bsize, baddr, NULLVP, 0, 506 VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); 507 } 508 } 509 vrele(nd.ni_vp); 510 511 return 0; 512 } 513 #endif 514 515