imgact_elf.c revision 36765
1/*- 2 * Copyright (c) 1995-1996 S�ren Schmidt 3 * Copyright (c) 1996 Peter Wemm 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 withough 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 * $Id: imgact_elf.c,v 1.25 1998/06/07 17:11:31 dfr Exp $ 30 */ 31 32#include "opt_rlimit.h" 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/exec.h> 37#include <sys/mman.h> 38#include <sys/imgact.h> 39#include <sys/imgact_elf.h> 40#include <sys/kernel.h> 41#include <sys/sysent.h> 42#include <sys/malloc.h> 43#include <sys/namei.h> 44#include <sys/proc.h> 45#include <sys/syscall.h> 46#include <sys/signalvar.h> 47#include <sys/sysctl.h> 48#include <sys/vnode.h> 49 50#include <vm/vm.h> 51#include <vm/vm_kern.h> 52#include <vm/vm_param.h> 53#include <vm/pmap.h> 54#include <sys/lock.h> 55#include <vm/vm_map.h> 56#include <vm/vm_prot.h> 57#include <vm/vm_extern.h> 58 59#include <machine/md_var.h> 60 61#define MAX_PHDR 32 /* XXX enough ? */ 62 63#if ELF_TARG_CLASS == ELFCLASS32 64 65#define Elf_Ehdr Elf32_Ehdr 66#define Elf_Phdr Elf32_Phdr 67#define Elf_Auxargs Elf32_Auxargs 68#define Elf_Brandinfo Elf32_Brandinfo 69 70#else 71 72#define Elf_Ehdr Elf64_Ehdr 73#define Elf_Phdr Elf64_Phdr 74#define Elf_Auxargs Elf64_Auxargs 75#define Elf_Brandinfo Elf64_Brandinfo 76 77#endif 78 79 80static int elf_check_header __P((const Elf_Ehdr *hdr, int type)); 81static int elf_load_section __P((struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot)); 82static int elf_load_file __P((struct proc *p, char *file, u_long *addr, u_long *entry)); 83static int elf_freebsd_fixup __P((long **stack_base, struct image_params *imgp)); 84static int exec_elf_imgact __P((struct image_params *imgp)); 85 86static int elf_trace = 0; 87SYSCTL_INT(_debug, OID_AUTO, elf_trace, CTLFLAG_RW, &elf_trace, 0, ""); 88#define UPRINTF if (elf_trace) uprintf 89 90static struct sysentvec elf_freebsd_sysvec = { 91 SYS_MAXSYSCALL, 92 sysent, 93 0, 94 0, 95 0, 96 0, 97 0, 98 0, 99 elf_freebsd_fixup, 100 sendsig, 101 sigcode, 102 &szsigcode, 103 0, 104 "FreeBSD ELF" 105}; 106 107static Elf_Brandinfo freebsd_brand_info = { 108 "FreeBSD", 109 "", 110 "/usr/libexec/ld-elf.so.1", 111 &elf_freebsd_sysvec 112 }; 113static Elf_Brandinfo *elf_brand_list[MAX_BRANDS] = { 114 &freebsd_brand_info, 115 NULL, NULL, NULL, 116 NULL, NULL, NULL, NULL 117 }; 118 119int 120elf_insert_brand_entry(Elf_Brandinfo *entry) 121{ 122 int i; 123 124 for (i=1; i<MAX_BRANDS; i++) { 125 if (elf_brand_list[i] == NULL) { 126 elf_brand_list[i] = entry; 127 break; 128 } 129 } 130 if (i == MAX_BRANDS) 131 return -1; 132 return 0; 133} 134 135int 136elf_remove_brand_entry(Elf_Brandinfo *entry) 137{ 138 int i; 139 140 for (i=1; i<MAX_BRANDS; i++) { 141 if (elf_brand_list[i] == entry) { 142 elf_brand_list[i] = NULL; 143 break; 144 } 145 } 146 if (i == MAX_BRANDS) 147 return -1; 148 return 0; 149} 150 151static int 152elf_check_header(const Elf_Ehdr *hdr, int type) 153{ 154 if (!(hdr->e_ident[EI_MAG0] == ELFMAG0 && 155 hdr->e_ident[EI_MAG1] == ELFMAG1 && 156 hdr->e_ident[EI_MAG2] == ELFMAG2 && 157 hdr->e_ident[EI_MAG3] == ELFMAG3)) 158 return ENOEXEC; 159 160#ifdef __i386__ 161 if (hdr->e_machine != EM_386 && hdr->e_machine != EM_486) 162#endif 163#ifdef __alpha__ 164 if (hdr->e_machine != EM_ALPHA) 165#endif 166 return ENOEXEC; 167 168 169 if (hdr->e_type != type) 170 return ENOEXEC; 171 172 return 0; 173} 174 175static int 176elf_load_section(struct vmspace *vmspace, struct vnode *vp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot) 177{ 178 size_t map_len; 179 vm_offset_t map_addr; 180 int error; 181 unsigned char *data_buf = 0; 182 size_t copy_len; 183 184 map_addr = trunc_page(vmaddr); 185 186 if (memsz > filsz) 187 map_len = trunc_page(offset+filsz) - trunc_page(offset); 188 else 189 map_len = round_page(offset+filsz) - trunc_page(offset); 190 191 if (error = vm_mmap (&vmspace->vm_map, 192 &map_addr, 193 map_len, 194 prot, 195 VM_PROT_ALL, 196 MAP_PRIVATE | MAP_FIXED, 197 (caddr_t)vp, 198 trunc_page(offset))) 199 return error; 200 201 if (memsz == filsz) 202 return 0; 203 204 /* 205 * We have to map the remaining bit of the file into the kernel's 206 * memory map, allocate some anonymous memory, and copy that last 207 * bit into it. The remaining space should be .bss... 208 */ 209 copy_len = (offset + filsz) - trunc_page(offset + filsz); 210 map_addr = trunc_page(vmaddr + filsz); 211 map_len = round_page(vmaddr + memsz) - map_addr; 212 213 if (map_len != 0) { 214 if (error = vm_map_find(&vmspace->vm_map, NULL, 0, 215 &map_addr, map_len, FALSE, 216 VM_PROT_ALL, VM_PROT_ALL,0)) 217 return error; 218 } 219 220 if (error = vm_mmap(exec_map, 221 (vm_offset_t *)&data_buf, 222 PAGE_SIZE, 223 VM_PROT_READ, 224 VM_PROT_READ, 225 0, 226 (caddr_t)vp, 227 trunc_page(offset + filsz))) 228 return error; 229 230 error = copyout(data_buf, (caddr_t)map_addr, copy_len); 231 232 vm_map_remove(exec_map, (vm_offset_t)data_buf, 233 (vm_offset_t)data_buf + PAGE_SIZE); 234 235 /* 236 * set it to the specified protection 237 */ 238 vm_map_protect(&vmspace->vm_map, map_addr, map_addr + map_len, prot, 239 FALSE); 240 241 UPRINTF("bss size %d (%x)\n", map_len-copy_len, map_len-copy_len); 242 return error; 243} 244 245static int 246elf_load_file(struct proc *p, char *file, u_long *addr, u_long *entry) 247{ 248 Elf_Ehdr *hdr = NULL; 249 Elf_Phdr *phdr = NULL; 250 struct nameidata nd; 251 struct vmspace *vmspace = p->p_vmspace; 252 struct vattr attr; 253 struct image_params image_params, *imgp; 254 vm_prot_t prot = 0; 255 unsigned long text_size = 0, data_size = 0; 256 unsigned long text_addr = 0, data_addr = 0; 257 int header_size = 0; 258 int error, i; 259 260 imgp = &image_params; 261 /* 262 * Initialize part of the common data 263 */ 264 imgp->proc = p; 265 imgp->uap = NULL; 266 imgp->attr = &attr; 267 imgp->firstpage = NULL; 268 imgp->image_header = (char *)kmem_alloc_wait(exec_map, PAGE_SIZE); 269 270 if (imgp->image_header == NULL) { 271 nd.ni_vp = NULL; 272 error = ENOMEM; 273 goto fail; 274 } 275 276 NDINIT(&nd, LOOKUP, LOCKLEAF|FOLLOW, UIO_SYSSPACE, file, p); 277 278 if (error = namei(&nd)) { 279 nd.ni_vp = NULL; 280 goto fail; 281 } 282 283 imgp->vp = nd.ni_vp; 284 285 /* 286 * Check permissions, modes, uid, etc on the file, and "open" it. 287 */ 288 error = exec_check_permissions(imgp); 289 if (error) { 290 VOP_UNLOCK(nd.ni_vp, 0, p); 291 goto fail; 292 } 293 294 error = exec_map_first_page(imgp); 295 VOP_UNLOCK(nd.ni_vp, 0, p); 296 if (error) 297 goto fail; 298 299 hdr = (Elf_Ehdr *)imgp->image_header; 300 if (error = elf_check_header(hdr, ET_DYN)) 301 goto fail; 302 303 /* 304 * ouch, need to bounds check in case user gives us a corrupted 305 * file with an insane header size 306 */ 307 if (hdr->e_phnum > MAX_PHDR) { /* XXX: ever more than this? */ 308 error = ENOEXEC; 309 goto fail; 310 } 311 312 header_size = hdr->e_phentsize * hdr->e_phnum; 313 314 /* Only support headers that fit within first page for now */ 315 if (header_size + hdr->e_phoff > PAGE_SIZE) { 316 error = ENOEXEC; 317 goto fail; 318 } 319 320 phdr = (Elf_Phdr *)(imgp->image_header + hdr->e_phoff); 321 322 for (i = 0; i < hdr->e_phnum; i++) { 323 switch(phdr[i].p_type) { 324 325 case PT_NULL: /* NULL section */ 326 UPRINTF ("ELF(file) PT_NULL section\n"); 327 break; 328 case PT_LOAD: /* Loadable segment */ 329 { 330 UPRINTF ("ELF(file) PT_LOAD section "); 331 if (phdr[i].p_flags & PF_X) 332 prot |= VM_PROT_EXECUTE; 333 if (phdr[i].p_flags & PF_W) 334 prot |= VM_PROT_WRITE; 335 if (phdr[i].p_flags & PF_R) 336 prot |= VM_PROT_READ; 337 338 if (error = elf_load_section(vmspace, nd.ni_vp, 339 phdr[i].p_offset, 340 (caddr_t)phdr[i].p_vaddr + 341 (*addr), 342 phdr[i].p_memsz, 343 phdr[i].p_filesz, prot)) 344 goto fail; 345 346 /* 347 * Is this .text or .data ?? 348 * 349 * We only handle one each of those yet XXX 350 */ 351 if (hdr->e_entry >= phdr[i].p_vaddr && 352 hdr->e_entry <(phdr[i].p_vaddr+phdr[i].p_memsz)) { 353 text_addr = trunc_page(phdr[i].p_vaddr+(*addr)); 354 text_size = round_page(phdr[i].p_memsz + 355 phdr[i].p_vaddr - 356 trunc_page(phdr[i].p_vaddr)); 357 *entry=(unsigned long)hdr->e_entry+(*addr); 358 UPRINTF(".text <%08x,%08x> entry=%08x\n", 359 text_addr, text_size, *entry); 360 } else { 361 data_addr = trunc_page(phdr[i].p_vaddr+(*addr)); 362 data_size = round_page(phdr[i].p_memsz + 363 phdr[i].p_vaddr - 364 trunc_page(phdr[i].p_vaddr)); 365 UPRINTF(".data <%08x,%08x>\n", 366 data_addr, data_size); 367 } 368 } 369 break; 370 371 case PT_DYNAMIC:/* Dynamic link information */ 372 UPRINTF ("ELF(file) PT_DYNAMIC section\n"); 373 break; 374 case PT_INTERP: /* Path to interpreter */ 375 UPRINTF ("ELF(file) PT_INTERP section\n"); 376 break; 377 case PT_NOTE: /* Note section */ 378 UPRINTF ("ELF(file) PT_NOTE section\n"); 379 break; 380 case PT_SHLIB: /* Shared lib section */ 381 UPRINTF ("ELF(file) PT_SHLIB section\n"); 382 break; 383 case PT_PHDR: /* Program header table info */ 384 UPRINTF ("ELF(file) PT_PHDR section\n"); 385 break; 386 default: 387 UPRINTF ("ELF(file) %d section ??\n", phdr[i].p_type ); 388 } 389 } 390 391fail: 392 if (imgp->firstpage) 393 exec_unmap_first_page(imgp); 394 if (imgp->image_header) 395 kmem_free_wakeup(exec_map, (vm_offset_t)imgp->image_header, 396 PAGE_SIZE); 397 if (nd.ni_vp) 398 vrele(nd.ni_vp); 399 400 return error; 401} 402 403static int 404exec_elf_imgact(struct image_params *imgp) 405{ 406 const Elf_Ehdr *hdr = (const Elf_Ehdr *) imgp->image_header; 407 const Elf_Phdr *phdr, *mapped_phdr = NULL; 408 Elf_Auxargs *elf_auxargs = NULL; 409 struct vmspace *vmspace; 410 vm_prot_t prot = 0; 411 u_long text_size = 0, data_size = 0; 412 u_long text_addr = 0, data_addr = 0; 413 u_long addr, entry = 0, proghdr = 0; 414 int error, i, header_size = 0; 415 const char *interp = NULL; 416 char *brand = NULL; 417 char path[MAXPATHLEN]; 418 419 /* 420 * Do we have a valid ELF header ? 421 */ 422 if (elf_check_header(hdr, ET_EXEC)) 423 return -1; 424 425 /* 426 * From here on down, we return an errno, not -1, as we've 427 * detected an ELF file. 428 */ 429 430 /* 431 * ouch, need to bounds check in case user gives us a corrupted 432 * file with an insane header size 433 */ 434 if (hdr->e_phnum > MAX_PHDR) { /* XXX: ever more than this? */ 435 return ENOEXEC; 436 } 437 438 header_size = hdr->e_phentsize * hdr->e_phnum; 439 440 if ((hdr->e_phoff > PAGE_SIZE) || 441 (hdr->e_phoff + header_size) > PAGE_SIZE) { 442 /* Only support headers in first page for now */ 443 return ENOEXEC; 444 } else { 445 phdr = (const Elf_Phdr*) 446 ((const char *)imgp->image_header + hdr->e_phoff); 447 } 448 449 /* 450 * From this point on, we may have resources that need to be freed. 451 */ 452 if (error = exec_extract_strings(imgp)) 453 goto fail; 454 455 exec_new_vmspace(imgp); 456 457 vmspace = imgp->proc->p_vmspace; 458 459 for (i = 0; i < hdr->e_phnum; i++) { 460 switch(phdr[i].p_type) { 461 462 case PT_NULL: /* NULL section */ 463 UPRINTF ("ELF PT_NULL section\n"); 464 break; 465 case PT_LOAD: /* Loadable segment */ 466 { 467 UPRINTF ("ELF PT_LOAD section "); 468 if (phdr[i].p_flags & PF_X) 469 prot |= VM_PROT_EXECUTE; 470 if (phdr[i].p_flags & PF_W) 471 prot |= VM_PROT_WRITE; 472 if (phdr[i].p_flags & PF_R) 473 prot |= VM_PROT_READ; 474 475 if (error = elf_load_section(vmspace, imgp->vp, 476 phdr[i].p_offset, 477 (caddr_t)phdr[i].p_vaddr, 478 phdr[i].p_memsz, 479 phdr[i].p_filesz, prot)) 480 goto fail; 481 482 /* 483 * Is this .text or .data ?? 484 * 485 * We only handle one each of those yet XXX 486 */ 487 if (hdr->e_entry >= phdr[i].p_vaddr && 488 hdr->e_entry <(phdr[i].p_vaddr+phdr[i].p_memsz)) { 489 text_addr = trunc_page(phdr[i].p_vaddr); 490 text_size = round_page(phdr[i].p_memsz + 491 phdr[i].p_vaddr - 492 text_addr); 493 entry = (u_long)hdr->e_entry; 494 UPRINTF(".text <%08x,%08x> entry=%08x\n", 495 text_addr, text_size, entry); 496 } else { 497 data_addr = trunc_page(phdr[i].p_vaddr); 498 data_size = round_page(phdr[i].p_memsz + 499 phdr[i].p_vaddr - 500 data_addr); 501 UPRINTF(".data <%08x,%08x>\n", 502 data_addr, data_size); 503 } 504 } 505 break; 506 507 case PT_DYNAMIC:/* Dynamic link information */ 508 UPRINTF ("ELF PT_DYNAMIC section ??\n"); 509 break; 510 case PT_INTERP: /* Path to interpreter */ 511 UPRINTF ("ELF PT_INTERP section "); 512 if (phdr[i].p_filesz > MAXPATHLEN || 513 phdr[i].p_offset + phdr[i].p_filesz > PAGE_SIZE) { 514 error = ENOEXEC; 515 goto fail; 516 } 517 interp = imgp->image_header + phdr[i].p_offset; 518 UPRINTF("<%s>\n", interp); 519 break; 520 case PT_NOTE: /* Note section */ 521 UPRINTF ("ELF PT_NOTE section\n"); 522 break; 523 case PT_SHLIB: /* Shared lib section */ 524 UPRINTF ("ELF PT_SHLIB section\n"); 525 break; 526 case PT_PHDR: /* Program header table info */ 527 UPRINTF ("ELF PT_PHDR section <%x>\n", phdr[i].p_vaddr); 528 proghdr = phdr[i].p_vaddr; 529 break; 530 default: 531 UPRINTF ("ELF %d section ??\n", phdr[i].p_type); 532 } 533 } 534 535 vmspace->vm_tsize = text_size >> PAGE_SHIFT; 536 vmspace->vm_taddr = (caddr_t)text_addr; 537 vmspace->vm_dsize = data_size >> PAGE_SHIFT; 538 vmspace->vm_daddr = (caddr_t)data_addr; 539 540 addr = 2L*MAXDSIZ; /* May depend on OS type XXX */ 541 542 imgp->entry_addr = entry; 543 544 /* 545 * So which kind (brand) of ELF binary do we have at hand 546 * FreeBSD, Linux, SVR4 or something else ?? 547 * If its has a interpreter section try that first 548 */ 549 if (interp) { 550 for (i=0; i<MAX_BRANDS; i++) { 551 if (elf_brand_list[i] != NULL) { 552 if (!strcmp(interp, elf_brand_list[i]->interp_path)) { 553 imgp->proc->p_sysent = 554 elf_brand_list[i]->sysvec; 555 strcpy(path, elf_brand_list[i]->emul_path); 556 strcat(path, elf_brand_list[i]->interp_path); 557 UPRINTF("interpreter=<%s> %s\n", 558 elf_brand_list[i]->interp_path, 559 elf_brand_list[i]->emul_path); 560 break; 561 } 562 } 563 } 564 } 565 566 /* 567 * If there is no interpreter, or recognition of it 568 * failed, se if the binary is branded. 569 */ 570 if (!interp || i == MAX_BRANDS) { 571 brand = (char *)&(hdr->e_ident[EI_BRAND]); 572 for (i=0; i<MAX_BRANDS; i++) { 573 if (elf_brand_list[i] != NULL) { 574 if (!strcmp(brand, elf_brand_list[i]->brand)) { 575 imgp->proc->p_sysent = elf_brand_list[i]->sysvec; 576 if (interp) { 577 strcpy(path, elf_brand_list[i]->emul_path); 578 strcat(path, elf_brand_list[i]->interp_path); 579 UPRINTF("interpreter=<%s> %s\n", 580 elf_brand_list[i]->interp_path, 581 elf_brand_list[i]->emul_path); 582 } 583 break; 584 } 585 } 586 } 587 } 588 if (i == MAX_BRANDS) { 589 uprintf("ELF binary type not known\n"); 590#ifdef __alpha__ 591 uprintf("assuming FreeBSD\n"); 592 i = 0; 593#else 594 error = ENOEXEC; 595 goto fail; 596#endif 597 } 598 if (interp) { 599 if (error = elf_load_file(imgp->proc, 600 path, 601 &addr, /* XXX */ 602 &imgp->entry_addr)) { 603 uprintf("ELF interpreter %s not found\n", path); 604 goto fail; 605 } 606 } 607 608 UPRINTF("Executing %s binary\n", elf_brand_list[i]->brand); 609 610 /* 611 * Construct auxargs table (used by the fixup routine) 612 */ 613 elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK); 614 elf_auxargs->execfd = -1; 615 elf_auxargs->phdr = proghdr; 616 elf_auxargs->phent = hdr->e_phentsize; 617 elf_auxargs->phnum = hdr->e_phnum; 618 elf_auxargs->pagesz = PAGE_SIZE; 619 elf_auxargs->base = addr; 620 elf_auxargs->flags = 0; 621 elf_auxargs->entry = entry; 622 elf_auxargs->trace = elf_trace; 623 624 imgp->auxargs = elf_auxargs; 625 imgp->interpreted = 0; 626 627 /* don't allow modifying the file while we run it */ 628 imgp->vp->v_flag |= VTEXT; 629 630fail: 631 return error; 632} 633 634static int 635elf_freebsd_fixup(long **stack_base, struct image_params *imgp) 636{ 637 Elf_Auxargs *args = (Elf_Auxargs *)imgp->auxargs; 638 long *pos; 639 640 pos = *stack_base + (imgp->argc + imgp->envc + 2); 641 642 if (args->trace) { 643 AUXARGS_ENTRY(pos, AT_DEBUG, 1); 644 } 645 if (args->execfd != -1) { 646 AUXARGS_ENTRY(pos, AT_EXECFD, args->execfd); 647 } 648 AUXARGS_ENTRY(pos, AT_PHDR, args->phdr); 649 AUXARGS_ENTRY(pos, AT_PHENT, args->phent); 650 AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum); 651 AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz); 652 AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); 653 AUXARGS_ENTRY(pos, AT_ENTRY, args->entry); 654 AUXARGS_ENTRY(pos, AT_BASE, args->base); 655 AUXARGS_ENTRY(pos, AT_NULL, 0); 656 657 free(imgp->auxargs, M_TEMP); 658 imgp->auxargs = NULL; 659 660 (*stack_base)--; 661 **stack_base = (long)imgp->argc; 662 return 0; 663} 664 665/* 666 * Tell kern_execve.c about it, with a little help from the linker. 667 * Since `const' objects end up in the text segment, TEXT_SET is the 668 * correct directive to use. 669 */ 670static const struct execsw elf_execsw = {exec_elf_imgact, "ELF"}; 671TEXT_SET(execsw_set, elf_execsw); 672 673