1 /*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD$
33 */
34
35 #include <sys/param.h>
36 #include <sys/linker.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <machine/elf.h>
44 #define FREEBSD_ELF
45
46 #include <err.h>
47
48 #include "ef.h"
49
50 #define MAXSEGS 3
51 struct ef_file {
52 char* ef_name;
53 struct elf_file *ef_efile;
54 Elf_Phdr * ef_ph;
55 int ef_fd;
56 int ef_type;
57 Elf_Ehdr ef_hdr;
58 void* ef_fpage; /* First block of the file */
59 int ef_fplen; /* length of first block */
60 Elf_Dyn* ef_dyn; /* Symbol table etc. */
61 Elf_Hashelt ef_nbuckets;
62 Elf_Hashelt ef_nchains;
63 Elf_Hashelt* ef_buckets;
64 Elf_Hashelt* ef_chains;
65 Elf_Hashelt* ef_hashtab;
66 Elf_Off ef_stroff;
67 caddr_t ef_strtab;
68 int ef_strsz;
69 Elf_Off ef_symoff;
70 Elf_Sym* ef_symtab;
71 int ef_nsegs;
72 Elf_Phdr * ef_segs[MAXSEGS];
73 int ef_verbose;
74 Elf_Rel * ef_rel; /* relocation table */
75 int ef_relsz; /* number of entries */
76 Elf_Rela * ef_rela; /* relocation table */
77 int ef_relasz; /* number of entries */
78 };
79
80 static void ef_print_phdr(Elf_Phdr *);
81 static u_long ef_get_offset(elf_file_t, Elf_Off);
82 static int ef_parse_dynamic(elf_file_t);
83
84 static int ef_get_type(elf_file_t ef);
85 static int ef_close(elf_file_t ef);
86 static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
87 static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr);
88 static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
89 static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len,
90 void *dest);
91 static int ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len,
92 char *dest);
93 static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
94 void **ptr);
95 static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
96 void **ptr);
97 static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx);
98 static int ef_lookup_set(elf_file_t ef, const char *name, long *startp,
99 long *stopp, long *countp);
100 static int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
101
102 static struct elf_file_ops ef_file_ops = {
103 ef_get_type,
104 ef_close,
105 ef_read,
106 ef_read_entry,
107 ef_seg_read,
108 ef_seg_read_rel,
109 ef_seg_read_string,
110 ef_seg_read_entry,
111 ef_seg_read_entry_rel,
112 ef_symaddr,
113 ef_lookup_set,
114 ef_lookup_symbol
115 };
116
117 static void
ef_print_phdr(Elf_Phdr * phdr)118 ef_print_phdr(Elf_Phdr *phdr)
119 {
120
121 if ((phdr->p_flags & PF_W) == 0) {
122 printf("text=0x%lx ", (long)phdr->p_filesz);
123 } else {
124 printf("data=0x%lx", (long)phdr->p_filesz);
125 if (phdr->p_filesz < phdr->p_memsz)
126 printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
127 printf(" ");
128 }
129 }
130
131 static u_long
ef_get_offset(elf_file_t ef,Elf_Off off)132 ef_get_offset(elf_file_t ef, Elf_Off off)
133 {
134 Elf_Phdr *ph;
135 int i;
136
137 for (i = 0; i < ef->ef_nsegs; i++) {
138 ph = ef->ef_segs[i];
139 if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
140 return ph->p_offset + (off - ph->p_vaddr);
141 }
142 }
143 return 0;
144 }
145
146 static int
ef_get_type(elf_file_t ef)147 ef_get_type(elf_file_t ef)
148 {
149
150 return (ef->ef_type);
151 }
152
153 /*
154 * next three functions copied from link_elf.c
155 */
156 static unsigned long
elf_hash(const char * name)157 elf_hash(const char *name)
158 {
159 const unsigned char *p = (const unsigned char *) name;
160 unsigned long h = 0;
161 unsigned long g;
162
163 while (*p != '\0') {
164 h = (h << 4) + *p++;
165 if ((g = h & 0xf0000000) != 0)
166 h ^= g >> 24;
167 h &= ~g;
168 }
169 return h;
170 }
171
172 static int
ef_lookup_symbol(elf_file_t ef,const char * name,Elf_Sym ** sym)173 ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
174 {
175 unsigned long symnum;
176 Elf_Sym* symp;
177 char *strp;
178 unsigned long hash;
179
180 /* First, search hashed global symbols */
181 hash = elf_hash(name);
182 symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
183
184 while (symnum != STN_UNDEF) {
185 if (symnum >= ef->ef_nchains) {
186 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
187 ef->ef_name);
188 return ENOENT;
189 }
190
191 symp = ef->ef_symtab + symnum;
192 if (symp->st_name == 0) {
193 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
194 ef->ef_name);
195 return ENOENT;
196 }
197
198 strp = ef->ef_strtab + symp->st_name;
199
200 if (strcmp(name, strp) == 0) {
201 if (symp->st_shndx != SHN_UNDEF ||
202 (symp->st_value != 0 &&
203 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
204 *sym = symp;
205 return 0;
206 } else
207 return ENOENT;
208 }
209
210 symnum = ef->ef_chains[symnum];
211 }
212
213 return ENOENT;
214 }
215
216 static int
ef_lookup_set(elf_file_t ef,const char * name,long * startp,long * stopp,long * countp)217 ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp,
218 long *countp)
219 {
220 Elf_Sym *sym;
221 char *setsym;
222 int error, len;
223
224 len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
225 setsym = malloc(len);
226 if (setsym == NULL)
227 return (ENOMEM);
228
229 /* get address of first entry */
230 snprintf(setsym, len, "%s%s", "__start_set_", name);
231 error = ef_lookup_symbol(ef, setsym, &sym);
232 if (error)
233 goto out;
234 *startp = sym->st_value;
235
236 /* get address of last entry */
237 snprintf(setsym, len, "%s%s", "__stop_set_", name);
238 error = ef_lookup_symbol(ef, setsym, &sym);
239 if (error)
240 goto out;
241 *stopp = sym->st_value;
242
243 /* and the number of entries */
244 *countp = (*stopp - *startp) / sizeof(void *);
245
246 out:
247 free(setsym);
248 return (error);
249 }
250
251 static Elf_Addr
ef_symaddr(elf_file_t ef,Elf_Size symidx)252 ef_symaddr(elf_file_t ef, Elf_Size symidx)
253 {
254 const Elf_Sym *sym;
255
256 if (symidx >= ef->ef_nchains)
257 return (0);
258 sym = ef->ef_symtab + symidx;
259
260 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
261 sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
262 return (sym->st_value);
263 return (0);
264 }
265
266 static int
ef_parse_dynamic(elf_file_t ef)267 ef_parse_dynamic(elf_file_t ef)
268 {
269 Elf_Dyn *dp;
270 Elf_Hashelt hashhdr[2];
271 /* int plttype = DT_REL;*/
272 int error;
273 Elf_Off rel_off;
274 Elf_Off rela_off;
275 int rel_sz;
276 int rela_sz;
277 int rel_entry;
278 int rela_entry;
279
280 rel_off = rela_off = 0;
281 rel_sz = rela_sz = 0;
282 rel_entry = rela_entry = 0;
283 for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
284 switch (dp->d_tag) {
285 case DT_HASH:
286 error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
287 sizeof(hashhdr), hashhdr);
288 if (error) {
289 warnx("can't read hash header (%lx)",
290 ef_get_offset(ef, dp->d_un.d_ptr));
291 return error;
292 }
293 ef->ef_nbuckets = hashhdr[0];
294 ef->ef_nchains = hashhdr[1];
295 error = ef_read_entry(ef, -1,
296 (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt),
297 (void**)&ef->ef_hashtab);
298 if (error) {
299 warnx("can't read hash table");
300 return error;
301 }
302 ef->ef_buckets = ef->ef_hashtab;
303 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
304 break;
305 case DT_STRTAB:
306 ef->ef_stroff = dp->d_un.d_ptr;
307 break;
308 case DT_STRSZ:
309 ef->ef_strsz = dp->d_un.d_val;
310 break;
311 case DT_SYMTAB:
312 ef->ef_symoff = dp->d_un.d_ptr;
313 break;
314 case DT_SYMENT:
315 if (dp->d_un.d_val != sizeof(Elf_Sym))
316 return EFTYPE;
317 break;
318 case DT_REL:
319 if (rel_off != 0)
320 warnx("second DT_REL entry ignored");
321 rel_off = dp->d_un.d_ptr;
322 break;
323 case DT_RELSZ:
324 if (rel_sz != 0)
325 warnx("second DT_RELSZ entry ignored");
326 rel_sz = dp->d_un.d_val;
327 break;
328 case DT_RELENT:
329 if (rel_entry != 0)
330 warnx("second DT_RELENT entry ignored");
331 rel_entry = dp->d_un.d_val;
332 break;
333 case DT_RELA:
334 if (rela_off != 0)
335 warnx("second DT_RELA entry ignored");
336 rela_off = dp->d_un.d_ptr;
337 break;
338 case DT_RELASZ:
339 if (rela_sz != 0)
340 warnx("second DT_RELASZ entry ignored");
341 rela_sz = dp->d_un.d_val;
342 break;
343 case DT_RELAENT:
344 if (rela_entry != 0)
345 warnx("second DT_RELAENT entry ignored");
346 rela_entry = dp->d_un.d_val;
347 break;
348 }
349 }
350 if (ef->ef_symoff == 0) {
351 warnx("%s: no .dynsym section found\n", ef->ef_name);
352 return EFTYPE;
353 }
354 if (ef->ef_stroff == 0) {
355 warnx("%s: no .dynstr section found\n", ef->ef_name);
356 return EFTYPE;
357 }
358 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
359 ef->ef_nchains * sizeof(Elf_Sym),
360 (void**)&ef->ef_symtab) != 0) {
361 if (ef->ef_verbose)
362 warnx("%s: can't load .dynsym section (0x%lx)",
363 ef->ef_name, (long)ef->ef_symoff);
364 return EIO;
365 }
366 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
367 (void**)&ef->ef_strtab) != 0) {
368 warnx("can't load .dynstr section");
369 return EIO;
370 }
371 if (rel_off != 0) {
372 if (rel_entry == 0) {
373 warnx("%s: no DT_RELENT for DT_REL", ef->ef_name);
374 return (EFTYPE);
375 }
376 if (rel_entry != sizeof(Elf_Rel)) {
377 warnx("%s: inconsistent DT_RELENT value",
378 ef->ef_name);
379 return (EFTYPE);
380 }
381 if (rel_sz % rel_entry != 0) {
382 warnx("%s: inconsistent values for DT_RELSZ and "
383 "DT_RELENT", ef->ef_name);
384 return (EFTYPE);
385 }
386 if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz,
387 (void **)&ef->ef_rel) != 0) {
388 warnx("%s: cannot load DT_REL section", ef->ef_name);
389 return (EIO);
390 }
391 ef->ef_relsz = rel_sz / rel_entry;
392 if (ef->ef_verbose)
393 warnx("%s: %d REL entries", ef->ef_name,
394 ef->ef_relsz);
395 }
396 if (rela_off != 0) {
397 if (rela_entry == 0) {
398 warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name);
399 return (EFTYPE);
400 }
401 if (rela_entry != sizeof(Elf_Rela)) {
402 warnx("%s: inconsistent DT_RELAENT value",
403 ef->ef_name);
404 return (EFTYPE);
405 }
406 if (rela_sz % rela_entry != 0) {
407 warnx("%s: inconsistent values for DT_RELASZ and "
408 "DT_RELAENT", ef->ef_name);
409 return (EFTYPE);
410 }
411 if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz,
412 (void **)&ef->ef_rela) != 0) {
413 warnx("%s: cannot load DT_RELA section", ef->ef_name);
414 return (EIO);
415 }
416 ef->ef_relasz = rela_sz / rela_entry;
417 if (ef->ef_verbose)
418 warnx("%s: %d RELA entries", ef->ef_name,
419 ef->ef_relasz);
420 }
421 return 0;
422 }
423
424 static int
ef_read(elf_file_t ef,Elf_Off offset,size_t len,void * dest)425 ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
426 {
427 ssize_t r;
428
429 if (offset != (Elf_Off)-1) {
430 if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
431 return EIO;
432 }
433
434 r = read(ef->ef_fd, dest, len);
435 if (r != -1 && (size_t)r == len)
436 return 0;
437 else
438 return EIO;
439 }
440
441 static int
ef_read_entry(elf_file_t ef,Elf_Off offset,size_t len,void ** ptr)442 ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
443 {
444 int error;
445
446 *ptr = malloc(len);
447 if (*ptr == NULL)
448 return ENOMEM;
449 error = ef_read(ef, offset, len, *ptr);
450 if (error)
451 free(*ptr);
452 return error;
453 }
454
455 static int
ef_seg_read(elf_file_t ef,Elf_Off offset,size_t len,void * dest)456 ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
457 {
458 u_long ofs = ef_get_offset(ef, offset);
459
460 if (ofs == 0) {
461 if (ef->ef_verbose)
462 warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
463 ef->ef_name, (long)offset, ofs);
464 return EFAULT;
465 }
466 return ef_read(ef, ofs, len, dest);
467 }
468
469 static int
ef_seg_read_rel(elf_file_t ef,Elf_Off offset,size_t len,void * dest)470 ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
471 {
472 u_long ofs = ef_get_offset(ef, offset);
473 const Elf_Rela *a;
474 const Elf_Rel *r;
475 int error;
476
477 if (ofs == 0) {
478 if (ef->ef_verbose)
479 warnx("ef_seg_read_rel(%s): zero offset (%lx:%ld)",
480 ef->ef_name, (long)offset, ofs);
481 return EFAULT;
482 }
483 if ((error = ef_read(ef, ofs, len, dest)) != 0)
484 return (error);
485
486 for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
487 error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len,
488 dest);
489 if (error != 0)
490 return (error);
491 }
492 for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
493 error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len,
494 dest);
495 if (error != 0)
496 return (error);
497 }
498 return (0);
499 }
500
501 static int
ef_seg_read_string(elf_file_t ef,Elf_Off offset,size_t len,char * dest)502 ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest)
503 {
504 u_long ofs = ef_get_offset(ef, offset);
505 ssize_t r;
506
507 if (ofs == 0 || ofs == (Elf_Off)-1) {
508 if (ef->ef_verbose)
509 warnx("ef_seg_read_string(%s): bad offset (%lx:%ld)",
510 ef->ef_name, (long)offset, ofs);
511 return (EFAULT);
512 }
513
514 r = pread(ef->ef_fd, dest, len, ofs);
515 if (r < 0)
516 return (errno);
517 if (strnlen(dest, len) == len)
518 return (EFAULT);
519
520 return (0);
521 }
522
523 static int
ef_seg_read_entry(elf_file_t ef,Elf_Off offset,size_t len,void ** ptr)524 ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
525 {
526 int error;
527
528 *ptr = malloc(len);
529 if (*ptr == NULL)
530 return ENOMEM;
531 error = ef_seg_read(ef, offset, len, *ptr);
532 if (error)
533 free(*ptr);
534 return error;
535 }
536
537 static int
ef_seg_read_entry_rel(elf_file_t ef,Elf_Off offset,size_t len,void ** ptr)538 ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
539 {
540 int error;
541
542 *ptr = malloc(len);
543 if (*ptr == NULL)
544 return ENOMEM;
545 error = ef_seg_read_rel(ef, offset, len, *ptr);
546 if (error)
547 free(*ptr);
548 return error;
549 }
550
551 int
ef_open(const char * filename,struct elf_file * efile,int verbose)552 ef_open(const char *filename, struct elf_file *efile, int verbose)
553 {
554 elf_file_t ef;
555 Elf_Ehdr *hdr;
556 int fd;
557 int error;
558 int phlen, res;
559 int nsegs;
560 Elf_Phdr *phdr, *phdyn, *phlimit;
561
562 if (filename == NULL)
563 return EFTYPE;
564 if ((fd = open(filename, O_RDONLY)) == -1)
565 return errno;
566
567 ef = malloc(sizeof(*ef));
568 if (ef == NULL) {
569 close(fd);
570 return (ENOMEM);
571 }
572
573 efile->ef_ef = ef;
574 efile->ef_ops = &ef_file_ops;
575
576 bzero(ef, sizeof(*ef));
577 ef->ef_verbose = verbose;
578 ef->ef_fd = fd;
579 ef->ef_name = strdup(filename);
580 ef->ef_efile = efile;
581 hdr = (Elf_Ehdr *)&ef->ef_hdr;
582 do {
583 res = read(fd, hdr, sizeof(*hdr));
584 error = EFTYPE;
585 if (res != sizeof(*hdr))
586 break;
587 if (!IS_ELF(*hdr))
588 break;
589 if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
590 hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
591 hdr->e_ident[EI_VERSION] != EV_CURRENT ||
592 hdr->e_version != EV_CURRENT ||
593 hdr->e_machine != ELF_TARG_MACH ||
594 hdr->e_phentsize != sizeof(Elf_Phdr))
595 break;
596 phlen = hdr->e_phnum * sizeof(Elf_Phdr);
597 if (ef_read_entry(ef, hdr->e_phoff, phlen,
598 (void**)&ef->ef_ph) != 0)
599 break;
600 phdr = ef->ef_ph;
601 phlimit = phdr + hdr->e_phnum;
602 nsegs = 0;
603 phdyn = NULL;
604 while (phdr < phlimit) {
605 if (verbose > 1)
606 ef_print_phdr(phdr);
607 switch (phdr->p_type) {
608 case PT_LOAD:
609 if (nsegs < MAXSEGS)
610 ef->ef_segs[nsegs] = phdr;
611 nsegs++;
612 break;
613 case PT_PHDR:
614 break;
615 case PT_DYNAMIC:
616 phdyn = phdr;
617 break;
618 }
619 phdr++;
620 }
621 if (verbose > 1)
622 printf("\n");
623 if (phdyn == NULL) {
624 warnx("Skipping %s: not dynamically-linked",
625 filename);
626 break;
627 } else if (nsegs > MAXSEGS) {
628 warnx("%s: too many segments", filename);
629 break;
630 }
631 ef->ef_nsegs = nsegs;
632 if (ef_read_entry(ef, phdyn->p_offset,
633 phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
634 printf("ef_read_entry failed\n");
635 break;
636 }
637 error = ef_parse_dynamic(ef);
638 if (error)
639 break;
640 if (hdr->e_type == ET_DYN) {
641 ef->ef_type = EFT_KLD;
642 /* pad = (u_int)dest & PAGE_MASK;
643 if (pad)
644 dest += PAGE_SIZE - pad;*/
645 error = 0;
646 } else if (hdr->e_type == ET_EXEC) {
647 /* dest = hdr->e_entry;
648 if (dest == 0)
649 break;*/
650 ef->ef_type = EFT_KERNEL;
651 error = 0;
652 } else
653 break;
654 } while(0);
655 if (error)
656 ef_close(ef);
657 return error;
658 }
659
660 static int
ef_close(elf_file_t ef)661 ef_close(elf_file_t ef)
662 {
663 close(ef->ef_fd);
664 /* if (ef->ef_fpage)
665 free(ef->ef_fpage);*/
666 if (ef->ef_name)
667 free(ef->ef_name);
668 ef->ef_efile->ef_ops = NULL;
669 ef->ef_efile->ef_ef = NULL;
670 free(ef);
671 return 0;
672 }
673