load_elf.c revision 93922
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * Copyright (c) 1998 Peter Wemm <peter@freebsd.org> 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 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/boot/common/load_elf.c 93922 2002-04-06 04:09:42Z peter $ 28 */ 29 30#include <sys/param.h> 31#include <sys/exec.h> 32#include <sys/reboot.h> 33#include <sys/linker.h> 34#include <sys/module.h> 35#include <string.h> 36#include <machine/bootinfo.h> 37#include <machine/elf.h> 38#include <stand.h> 39#define FREEBSD_ELF 40#include <link.h> 41 42#include "bootstrap.h" 43 44#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) 45 46 47typedef struct elf_file { 48 Elf_Phdr *ph; 49 Elf_Ehdr *ehdr; 50 Elf_Sym *symtab; 51 Elf_Hashelt *hashtab; 52 Elf_Hashelt nbuckets; 53 Elf_Hashelt nchains; 54 Elf_Hashelt *buckets; 55 Elf_Hashelt *chains; 56 char *strtab; 57 size_t strsz; 58 int fd; 59 caddr_t firstpage; 60 size_t firstlen; 61 int kernel; 62 vm_offset_t off; 63} *elf_file_t; 64 65static int elf_loadimage(struct preloaded_file *mp, elf_file_t ef, vm_offset_t loadaddr); 66static int elf_lookup_symbol(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); 67static int elf_parse_modmetadata(struct preloaded_file *mp, elf_file_t ef); 68static char *fake_modname(const char *name); 69 70const char *elf_kerneltype = "elf kernel"; 71const char *elf_moduletype = "elf module"; 72 73/* 74 * Attempt to load the file (file) as an ELF module. It will be stored at 75 * (dest), and a pointer to a module structure describing the loaded object 76 * will be saved in (result). 77 */ 78int 79elf_loadfile(char *filename, vm_offset_t dest, struct preloaded_file **result) 80{ 81 struct preloaded_file *fp, *kfp; 82 struct elf_file ef; 83 Elf_Ehdr *ehdr; 84 int err; 85 u_int pad; 86 ssize_t bytes_read; 87 88 fp = NULL; 89 bzero(&ef, sizeof(struct elf_file)); 90 91 /* 92 * Open the image, read and validate the ELF header 93 */ 94 if (filename == NULL) /* can't handle nameless */ 95 return(EFTYPE); 96 if ((ef.fd = open(filename, O_RDONLY)) == -1) 97 return(errno); 98 ef.firstpage = malloc(PAGE_SIZE); 99 if (ef.firstpage == NULL) { 100 close(ef.fd); 101 return(ENOMEM); 102 } 103 bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); 104 ef.firstlen = (size_t)bytes_read; 105 if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { 106 err = EFTYPE; /* could be EIO, but may be small file */ 107 goto oerr; 108 } 109 ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; 110 111 /* Is it ELF? */ 112 if (!IS_ELF(*ehdr)) { 113 err = EFTYPE; 114 goto oerr; 115 } 116 if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ 117 ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || 118 ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ 119 ehdr->e_version != EV_CURRENT || 120 ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ 121 err = EFTYPE; 122 goto oerr; 123 } 124 125 126 /* 127 * Check to see what sort of module we are. 128 */ 129 kfp = file_findfile(NULL, NULL); 130 if (ehdr->e_type == ET_DYN) { 131 /* Looks like a kld module */ 132 if (kfp == NULL) { 133 printf("elf_loadfile: can't load module before kernel\n"); 134 err = EPERM; 135 goto oerr; 136 } 137 if (strcmp(elf_kerneltype, kfp->f_type)) { 138 printf("elf_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); 139 err = EPERM; 140 goto oerr; 141 } 142 /* Looks OK, got ahead */ 143 ef.kernel = 0; 144 145 /* Page-align the load address */ 146 pad = (u_int)dest & PAGE_MASK; 147 if (pad != 0) { 148 pad = PAGE_SIZE - pad; 149 dest += pad; 150 } 151 } else if (ehdr->e_type == ET_EXEC) { 152 /* Looks like a kernel */ 153 if (kfp != NULL) { 154 printf("elf_loadfile: kernel already loaded\n"); 155 err = EPERM; 156 goto oerr; 157 } 158 /* 159 * Calculate destination address based on kernel entrypoint 160 */ 161 dest = (vm_offset_t) ehdr->e_entry; 162 if (dest == 0) { 163 printf("elf_loadfile: not a kernel (maybe static binary?)\n"); 164 err = EPERM; 165 goto oerr; 166 } 167 ef.kernel = 1; 168 169 } else { 170 err = EFTYPE; 171 goto oerr; 172 } 173 174 /* 175 * Ok, we think we should handle this. 176 */ 177 fp = file_alloc(); 178 if (fp == NULL) { 179 printf("elf_loadfile: cannot allocate module info\n"); 180 err = EPERM; 181 goto out; 182 } 183 if (ef.kernel) 184 setenv("kernelname", filename, 1); 185 fp->f_name = strdup(filename); 186 fp->f_type = strdup(ef.kernel ? elf_kerneltype : elf_moduletype); 187 188#ifdef ELF_VERBOSE 189 if (ef.kernel) 190 printf("%s entry at %p\n", filename, (void *) dest); 191#else 192 printf("%s ", filename); 193#endif 194 195 fp->f_size = elf_loadimage(fp, &ef, dest); 196 if (fp->f_size == 0 || fp->f_addr == 0) 197 goto ioerr; 198 199 /* save exec header as metadata */ 200 file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); 201 202 /* Load OK, return module pointer */ 203 *result = (struct preloaded_file *)fp; 204 err = 0; 205 goto out; 206 207 ioerr: 208 err = EIO; 209 oerr: 210 file_discard(fp); 211 out: 212 if (ef.firstpage) 213 free(ef.firstpage); 214 close(ef.fd); 215 return(err); 216} 217 218/* 219 * With the file (fd) open on the image, and (ehdr) containing 220 * the Elf header, load the image at (off) 221 */ 222static int 223elf_loadimage(struct preloaded_file *fp, elf_file_t ef, vm_offset_t off) 224{ 225 int i; 226 u_int j; 227 Elf_Ehdr *ehdr; 228 Elf_Phdr *phdr, *php; 229 Elf_Shdr *shdr; 230 int ret; 231 vm_offset_t firstaddr; 232 vm_offset_t lastaddr; 233 void *buf; 234 size_t resid, chunk; 235 ssize_t result; 236 vm_offset_t dest; 237 vm_offset_t ssym, esym; 238 Elf_Dyn *dp; 239 int ndp; 240 int symstrindex; 241 int symtabindex; 242 long size; 243 u_int fpcopy; 244 245 dp = NULL; 246 shdr = NULL; 247 ret = 0; 248 firstaddr = lastaddr = 0; 249 ehdr = ef->ehdr; 250 if (ef->kernel) { 251#ifdef __i386__ 252 off = - (off & 0xff000000u); /* i386 relocates after locore */ 253#else 254 off = 0; /* alpha is direct mapped for kernels */ 255#endif 256 } 257 ef->off = off; 258 259 if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { 260 printf("elf_loadimage: program header not within first page\n"); 261 goto out; 262 } 263 phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); 264 265 for (i = 0; i < ehdr->e_phnum; i++) { 266 /* We want to load PT_LOAD segments only.. */ 267 if (phdr[i].p_type != PT_LOAD) 268 continue; 269 270#ifdef ELF_VERBOSE 271 printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", 272 (long)phdr[i].p_filesz, (long)phdr[i].p_offset, 273 (long)(phdr[i].p_vaddr + off), 274 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 275#else 276 if ((phdr[i].p_flags & PF_W) == 0) { 277 printf("text=0x%lx ", (long)phdr[i].p_filesz); 278 } else { 279 printf("data=0x%lx", (long)phdr[i].p_filesz); 280 if (phdr[i].p_filesz < phdr[i].p_memsz) 281 printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz)); 282 printf(" "); 283 } 284#endif 285 fpcopy = 0; 286 if (ef->firstlen > phdr[i].p_offset) { 287 fpcopy = ef->firstlen - phdr[i].p_offset; 288 archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, 289 phdr[i].p_vaddr + off, fpcopy); 290 } 291 if (phdr[i].p_filesz > fpcopy) { 292 if (lseek(ef->fd, (off_t)(phdr[i].p_offset + fpcopy), 293 SEEK_SET) == -1) { 294 printf("\nelf_loadexec: cannot seek\n"); 295 goto out; 296 } 297 if (archsw.arch_readin(ef->fd, phdr[i].p_vaddr + off + fpcopy, 298 phdr[i].p_filesz - fpcopy) != (ssize_t)(phdr[i].p_filesz - fpcopy)) { 299 printf("\nelf_loadexec: archsw.readin failed\n"); 300 goto out; 301 } 302 } 303 /* clear space from oversized segments; eg: bss */ 304 if (phdr[i].p_filesz < phdr[i].p_memsz) { 305#ifdef ELF_VERBOSE 306 printf(" (bss: 0x%lx-0x%lx)", 307 (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), 308 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 309#endif 310 311 /* no archsw.arch_bzero */ 312 buf = malloc(PAGE_SIZE); 313 if (buf == NULL) { 314 printf("\nelf_loadimage: malloc() failed\n"); 315 goto out; 316 } 317 bzero(buf, PAGE_SIZE); 318 resid = phdr[i].p_memsz - phdr[i].p_filesz; 319 dest = phdr[i].p_vaddr + off + phdr[i].p_filesz; 320 while (resid > 0) { 321 chunk = min(PAGE_SIZE, resid); 322 archsw.arch_copyin(buf, dest, chunk); 323 resid -= chunk; 324 dest += chunk; 325 } 326 free(buf); 327 } 328#ifdef ELF_VERBOSE 329 printf("\n"); 330#endif 331 332 if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) 333 firstaddr = phdr[i].p_vaddr + off; 334 if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) 335 lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; 336 } 337 lastaddr = roundup(lastaddr, sizeof(long)); 338 339 /* 340 * Now grab the symbol tables. This isn't easy if we're reading a 341 * .gz file. I think the rule is going to have to be that you must 342 * strip a file to remove symbols before gzipping it so that we do not 343 * try to lseek() on it. 344 */ 345 chunk = ehdr->e_shnum * ehdr->e_shentsize; 346 if (chunk == 0 || ehdr->e_shoff == 0) 347 goto nosyms; 348 shdr = malloc(chunk); 349 if (shdr == NULL) 350 goto nosyms; 351 if (lseek(ef->fd, (off_t)ehdr->e_shoff, SEEK_SET) == -1) { 352 printf("\nelf_loadimage: cannot lseek() to section headers"); 353 goto nosyms; 354 } 355 result = read(ef->fd, shdr, chunk); 356 if (result < 0 || (size_t)result != chunk) { 357 printf("\nelf_loadimage: read section headers failed"); 358 goto nosyms; 359 } 360 symtabindex = -1; 361 symstrindex = -1; 362 for (i = 0; i < ehdr->e_shnum; i++) { 363 if (shdr[i].sh_type != SHT_SYMTAB) 364 continue; 365 for (j = 0; j < ehdr->e_phnum; j++) { 366 if (phdr[j].p_type != PT_LOAD) 367 continue; 368 if (shdr[i].sh_offset >= phdr[j].p_offset && 369 (shdr[i].sh_offset + shdr[i].sh_size <= 370 phdr[j].p_offset + phdr[j].p_filesz)) { 371 shdr[i].sh_offset = 0; 372 shdr[i].sh_size = 0; 373 break; 374 } 375 } 376 if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) 377 continue; /* alread loaded in a PT_LOAD above */ 378 /* Save it for loading below */ 379 symtabindex = i; 380 symstrindex = shdr[i].sh_link; 381 } 382 if (symtabindex < 0 || symstrindex < 0) 383 goto nosyms; 384 385 /* Ok, committed to a load. */ 386#ifndef ELF_VERBOSE 387 printf("syms=["); 388#endif 389 ssym = lastaddr; 390 for (i = symtabindex; i >= 0; i = symstrindex) { 391#ifdef ELF_VERBOSE 392 char *secname; 393 394 switch(shdr[i].sh_type) { 395 case SHT_SYMTAB: /* Symbol table */ 396 secname = "symtab"; 397 break; 398 case SHT_STRTAB: /* String table */ 399 secname = "strtab"; 400 break; 401 default: 402 secname = "WHOA!!"; 403 break; 404 } 405#endif 406 407 size = shdr[i].sh_size; 408 archsw.arch_copyin(&size, lastaddr, sizeof(size)); 409 lastaddr += sizeof(long); 410 411#ifdef ELF_VERBOSE 412 printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname, 413 shdr[i].sh_size, shdr[i].sh_offset, 414 lastaddr, lastaddr + shdr[i].sh_size); 415#else 416 if (i == symstrindex) 417 printf("+"); 418 printf("0x%lx+0x%lx", (long)sizeof(size), size); 419#endif 420 421 if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { 422 printf("\nelf_loadimage: could not seek for symbols - skipped!"); 423 lastaddr = ssym; 424 ssym = 0; 425 goto nosyms; 426 } 427 result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size); 428 if (result < 0 || (size_t)result != shdr[i].sh_size) { 429 printf("\nelf_loadimage: could not read symbols - skipped!"); 430 lastaddr = ssym; 431 ssym = 0; 432 goto nosyms; 433 } 434 /* Reset offsets relative to ssym */ 435 lastaddr += shdr[i].sh_size; 436 lastaddr = roundup(lastaddr, sizeof(long)); 437 if (i == symtabindex) 438 symtabindex = -1; 439 else if (i == symstrindex) 440 symstrindex = -1; 441 } 442 esym = lastaddr; 443#ifndef ELF_VERBOSE 444 printf("]"); 445#endif 446 447 file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); 448 file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); 449 450nosyms: 451 printf("\n"); 452 453 ret = lastaddr - firstaddr; 454 fp->f_addr = firstaddr; 455 456 php = NULL; 457 for (i = 0; i < ehdr->e_phnum; i++) { 458 if (phdr[i].p_type == PT_DYNAMIC) { 459 php = phdr + i; 460 dp = (Elf_Dyn *)(php->p_vaddr); 461 file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(dp), &dp); 462 dp = NULL; 463 break; 464 } 465 } 466 467 if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ 468 goto out; 469 470 ndp = php->p_filesz / sizeof(Elf_Dyn); 471 if (ndp == 0) 472 goto out; 473 dp = malloc(php->p_filesz); 474 if (dp == NULL) 475 goto out; 476 archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); 477 478 ef->strsz = 0; 479 for (i = 0; i < ndp; i++) { 480 if (dp[i].d_tag == NULL) 481 break; 482 switch (dp[i].d_tag) { 483 case DT_HASH: 484 ef->hashtab = (Elf_Hashelt*)(dp[i].d_un.d_ptr + off); 485 break; 486 case DT_STRTAB: 487 ef->strtab = (char *)(dp[i].d_un.d_ptr + off); 488 break; 489 case DT_STRSZ: 490 ef->strsz = dp[i].d_un.d_val; 491 break; 492 case DT_SYMTAB: 493 ef->symtab = (Elf_Sym*)(dp[i].d_un.d_ptr + off); 494 break; 495 default: 496 break; 497 } 498 } 499 if (ef->hashtab == NULL || ef->symtab == NULL || 500 ef->strtab == NULL || ef->strsz == 0) 501 goto out; 502 COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); 503 COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); 504 ef->buckets = ef->hashtab + 2; 505 ef->chains = ef->buckets + ef->nbuckets; 506 if (elf_parse_modmetadata(fp, ef) == 0) 507 goto out; 508 509 if (ef->kernel) /* kernel must not depend on anything */ 510 goto out; 511 512out: 513 if (dp) 514 free(dp); 515 if (shdr) 516 free(shdr); 517 return ret; 518} 519 520static char invalid_name[] = "bad"; 521 522char * 523fake_modname(const char *name) 524{ 525 const char *sp, *ep; 526 char *fp; 527 size_t len; 528 529 sp = strrchr(name, '/'); 530 if (sp) 531 sp++; 532 else 533 sp = name; 534 ep = strrchr(name, '.'); 535 if (ep) { 536 if (ep == name) { 537 sp = invalid_name; 538 ep = invalid_name + sizeof(invalid_name) - 1; 539 } 540 } else 541 ep = name + strlen(name); 542 len = ep - sp; 543 fp = malloc(len + 1); 544 if (fp == NULL) 545 return NULL; 546 memcpy(fp, sp, len); 547 fp[len] = '\0'; 548 return fp; 549} 550 551int 552elf_parse_modmetadata(struct preloaded_file *fp, elf_file_t ef) 553{ 554 struct mod_metadata md; 555 struct mod_depend *mdepend; 556 struct mod_version mver; 557 Elf_Sym sym; 558 char *s, *v, **p, **p_stop; 559 int modcnt, minfolen; 560 561 if (elf_lookup_symbol(fp, ef, "__start_set_modmetadata_set", &sym) != 0) 562 return ENOENT; 563 p = (char **)(sym.st_value + ef->off); 564 if (elf_lookup_symbol(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) 565 return ENOENT; 566 p_stop = (char **)(sym.st_value + ef->off); 567 568 modcnt = 0; 569 while (p < p_stop) { 570 COPYOUT(p++, &v, sizeof(v)); 571 COPYOUT(v + ef->off, &md, sizeof(md)); 572 switch(md.md_type) { 573 case MDT_DEPEND: 574 if (ef->kernel) /* kernel must not depend on anything */ 575 break; 576 s = strdupout((vm_offset_t)(md.md_cval + ef->off)); 577 minfolen = sizeof(*mdepend) + strlen(s) + 1; 578 mdepend = malloc(minfolen); 579 if (mdepend == NULL) 580 return ENOMEM; 581 COPYOUT((vm_offset_t)(md.md_data + ef->off), mdepend, sizeof(*mdepend)); 582 strcpy((char*)(mdepend + 1), s); 583 free(s); 584 file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); 585 free(mdepend); 586 break; 587 case MDT_VERSION: 588 s = strdupout((vm_offset_t)(md.md_cval + ef->off)); 589 COPYOUT((vm_offset_t)(md.md_data + ef->off), &mver, sizeof(mver)); 590 file_addmodule(fp, s, mver.mv_version, NULL); 591 free(s); 592 modcnt++; 593 break; 594 } 595 } 596 if (modcnt == 0) { 597 s = fake_modname(fp->f_name); 598 file_addmodule(fp, s, 1, NULL); 599 free(s); 600 } 601 return 0; 602} 603 604static unsigned long 605elf_hash(const char *name) 606{ 607 const unsigned char *p = (const unsigned char *) name; 608 unsigned long h = 0; 609 unsigned long g; 610 611 while (*p != '\0') { 612 h = (h << 4) + *p++; 613 if ((g = h & 0xf0000000) != 0) 614 h ^= g >> 24; 615 h &= ~g; 616 } 617 return h; 618} 619 620static const char elf_bad_symtable[] = "elf_lookup_symbol: corrupt symbol table\n"; 621int 622elf_lookup_symbol(struct preloaded_file *fp, elf_file_t ef, const char* name, 623 Elf_Sym *symp) 624{ 625 unsigned long symnum; 626 Elf_Sym sym; 627 char *strp; 628 unsigned long hash; 629 630 hash = elf_hash(name); 631 COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); 632 633 while (symnum != STN_UNDEF) { 634 if (symnum >= ef->nchains) { 635 printf(elf_bad_symtable); 636 return ENOENT; 637 } 638 639 COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); 640 if (sym.st_name == 0) { 641 printf(elf_bad_symtable); 642 return ENOENT; 643 } 644 645 strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); 646 if (strcmp(name, strp) == 0) { 647 free(strp); 648 if (sym.st_shndx != SHN_UNDEF || 649 (sym.st_value != 0 && 650 ELF_ST_TYPE(sym.st_info) == STT_FUNC)) { 651 *symp = sym; 652 return 0; 653 } 654 return ENOENT; 655 } 656 free(strp); 657 COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); 658 } 659 return ENOENT; 660} 661