/* * rexec * Read the exec structure; ignore any files that don't look * exactly right. Return MID. * return -1 for files that don't look right. * XXX it's hard to be sure when to ignore files, and when to error * out. */ static int rexec(int rfd, int wfd) { RLIB *rp; long nsyms; int nr, symlen; char *strtab = 0; char *sym; struct exec ebuf; struct nlist nl; off_t r_off, w_off; long strsize; int result = -1; /* Get current offsets for original and tmp files. */ r_off = lseek(rfd, (off_t)0, SEEK_CUR); w_off = lseek(wfd, (off_t)0, SEEK_CUR); /* Read in exec structure. */ nr = read(rfd, (char *)&ebuf, sizeof(struct exec)); if (nr != sizeof(struct exec)) goto bad; /* Check magic number and symbol count. */ if (BAD_OBJECT(ebuf) || ebuf.a_syms == 0) goto bad; fix_header_order(&ebuf); /* Seek to string table. */ if (lseek(rfd, N_STROFF(ebuf) + r_off, SEEK_SET) == (off_t)-1) { if (errno == EINVAL) goto bad; else error(archive); } /* Read in size of the string table. */ nr = read(rfd, (char *)&strsize, sizeof(strsize)); if (nr != sizeof(strsize)) goto bad; strsize = fix_32_order(strsize, N_GETMID(ebuf)); /* Read in the string table. */ strsize -= sizeof(strsize); strtab = (char *)emalloc(strsize); nr = read(rfd, strtab, strsize); if (nr != strsize) goto bad; /* Seek to symbol table. */ if (fseek(fp, N_SYMOFF(ebuf) + r_off, SEEK_SET) == (off_t)-1) goto bad; result = N_GETMID(ebuf); /* For each symbol read the nlist entry and save it as necessary. */ nsyms = ebuf.a_syms / sizeof(struct nlist); while (nsyms--) { if (!fread((char *)&nl, sizeof(struct nlist), 1, fp)) { if (feof(fp)) badfmt(); error(archive); } fix_nlist_order(&nl, N_GETMID(ebuf)); /* Ignore if no name or local. */ if (!nl.n_un.n_strx || !(nl.n_type & N_EXT)) continue; /* * If the symbol is an undefined external and the n_value * field is non-zero, keep it. */ if ((nl.n_type & N_TYPE) == N_UNDF && !nl.n_value) continue; /* First four bytes are the table size. */ sym = strtab + nl.n_un.n_strx - sizeof(long); symlen = strlen(sym) + 1; rp = (RLIB *)emalloc(sizeof(RLIB)); rp->sym = (char *)emalloc(symlen); bcopy(sym, rp->sym, symlen); rp->symlen = symlen; rp->pos = w_off; /* Build in forward order for "ar -m" command. */ *pnext = rp; pnext = &rp->next; ++symcnt; tsymlen += symlen; } bad: if (nr < 0) error(archive); free(strtab); (void)lseek(rfd, (off_t)r_off, SEEK_SET); return result; }
/* * read_exec * Read the exec structure; ignore any files that don't look * exactly right. Return MID. * return -1 for files that don't look right. * XXX it's hard to be sure when to ignore files, and when to error * out. */ int read_exec(FILE *rfp, FILE *wfp, long *symcnt, long *tsymlen) { union { struct exec exec; Elf32_Ehdr elf32; Elf64_Ehdr elf64; } eh; struct nlist nl; off_t r_off, w_off; char *strtab = NULL; long strsize, nsyms; int i; /* Get current offsets for original and tmp files. */ r_off = ftello(rfp); w_off = ftello(wfp); /* Read in exec structure. */ if (fread(&eh, sizeof(eh), 1, rfp) != 1) err(1, "fread: %s", archive); if (!elf32_chk_header(&eh.elf32)) { Elf32_Sym sbuf; char *shstr; Elf32_Shdr *shdr; size_t stabsize; elf32_fix_header(&eh.elf32); if (eh.elf32.e_ehsize < sizeof eh.elf32) { warnx("%s: ELF header is too short", archive); goto bad; } if (!(shdr = elf32_load_shdrs(archive, rfp, r_off, &eh.elf32))) goto bad; elf32_fix_shdrs(&eh.elf32, shdr); if (!(shstr = elf32_shstrload(archive, rfp, r_off, &eh.elf32, shdr))) { free(shdr); goto bad; } if (!(strtab = elf32_strload(archive, rfp, r_off, &eh.elf32, shdr, shstr, ELF_STRTAB, &stabsize))) { free(shstr); free(shdr); goto bad; } /* find the symtab section */ for (i = 0; i < eh.elf32.e_shnum; i++) if (!strcmp(shstr + shdr[i].sh_name, ELF_SYMTAB)) { nsyms = shdr[i].sh_size / sizeof(Elf32_Sym); break; } if (i == eh.elf32.e_shnum) { free(shstr); free(shdr); goto bad; } if (fseeko(rfp, r_off + shdr[i].sh_offset, SEEK_SET)) err(1, "fseeko: %s", archive); for (i = 0; i < nsyms; i++) { if (fread(&sbuf, sizeof(sbuf), 1, rfp) != 1) err(1, "fread: %s", archive); elf32_fix_sym(&eh.elf32, &sbuf); if (!sbuf.st_name || sbuf.st_name > stabsize) continue; if (elf32_2nlist(&sbuf, &eh.elf32, shdr, shstr, &nl)) continue; addsym(&nl, strtab, r_off - r_fuzz - sizeof(struct ar_hdr), symcnt, tsymlen, archive); } free(strtab); free(shstr); free(shdr); (void)fseeko(rfp, r_off, SEEK_SET); return MID_ELFFL | eh.elf32.e_machine; } else if (!elf64_chk_header(&eh.elf64)) { Elf64_Sym sbuf; char *shstr; Elf64_Shdr *shdr; size_t stabsize; elf64_fix_header(&eh.elf64); if (eh.elf64.e_ehsize < sizeof eh.elf64) { warnx("%s: ELF header is too short", archive); goto bad; } if (!(shdr = elf64_load_shdrs(archive, rfp, r_off, &eh.elf64))) goto bad; elf64_fix_shdrs(&eh.elf64, shdr); if (!(shstr = elf64_shstrload(archive, rfp, r_off, &eh.elf64, shdr))) { free(shdr); goto bad; } if (!(strtab = elf64_strload(archive, rfp, r_off, &eh.elf64, shdr, shstr, ELF_STRTAB, &stabsize))) { free(shstr); free(shdr); goto bad; } /* find the symtab section */ for (i = 0; i < eh.elf64.e_shnum; i++) if (!strcmp(shstr + shdr[i].sh_name, ELF_SYMTAB)) { nsyms = shdr[i].sh_size / sizeof(Elf64_Sym); break; } if (i == eh.elf64.e_shnum) { free(shstr); free(shdr); goto bad; } if (fseeko(rfp, r_off + shdr[i].sh_offset, SEEK_SET)) err(1, "fseeko: %s", archive); for (i = 0; i < nsyms; i++) { if (fread(&sbuf, sizeof(sbuf), 1, rfp) != 1) err(1, "fread: %s", archive); elf64_fix_sym(&eh.elf64, &sbuf); if (!sbuf.st_name || sbuf.st_name > stabsize) continue; if (elf64_2nlist(&sbuf, &eh.elf64, shdr, shstr, &nl)) continue; addsym(&nl, strtab, r_off - r_fuzz - sizeof(struct ar_hdr), symcnt, tsymlen, archive); } free(strtab); free(shstr); free(shdr); (void)fseeko(rfp, r_off, SEEK_SET); return MID_ELFFL | eh.elf64.e_machine; } else if (BAD_OBJECT(eh.exec) || eh.exec.a_syms == 0) goto bad; fix_header_order(&eh.exec); /* Seek to string table. */ if (fseeko(rfp, N_STROFF(eh.exec) + r_off, SEEK_SET) == -1) { if (errno == EINVAL) goto bad; else err(1, "lseek: %s", archive); } /* Read in size of the string table. */ if (fread((char *)&strsize, sizeof(strsize), 1, rfp) != 1) err(1, "fread: %s", archive); strsize = fix_32_order(strsize, N_GETMID(eh.exec)); /* Read in the string table. */ strsize -= sizeof(strsize); strtab = malloc(strsize); if (!strtab) err(1, "malloc: %s", archive); if (fread(strtab, strsize, 1, rfp) != 1) err(1, "fread: %s", archive); /* Seek to symbol table. */ if (fseek(rfp, N_SYMOFF(eh.exec) + r_off, SEEK_SET) == (off_t)-1) err(1, "fseeko: %s", archive); /* For each symbol read the nlist entry and save it as necessary. */ nsyms = eh.exec.a_syms / sizeof(struct nlist); while (nsyms--) { if (!fread((char *)&nl, sizeof(struct nlist), 1, rfp)) { if (feof(rfp)) badfmt(); err(1, "fread: %s", archive); } fix_nlist_order(&nl, N_GETMID(eh.exec)); addsym(&nl, strtab - sizeof(long), r_off - r_fuzz - sizeof(struct ar_hdr), symcnt, tsymlen, archive); } bad: free(strtab); (void)fseeko(rfp, r_off, SEEK_SET); return N_GETMID(eh.exec); }
/* * show_file() * show symbols from the object file pointed to by fp. The current * file pointer for fp is expected to be at the beginning of an object * file header. */ int show_file(int count, int warn_fmt, const char *name, FILE *fp, off_t foff, union hdr *head) { u_long text, data, bss, total; struct nlist *np, *names, **snames; char *stab; int i, aout, nnames; size_t stabsize; off_t staboff; aout = 0; if (!elf32_chk_header(&head->elf32)) { struct elf_symtab es; es.name = name; es.ehdr = &head->elf32; if (head->elf32.e_ehsize < sizeof head->elf32) { warnx("%s: ELF header is too short", name); return 1; } if (!(es.shdr = elf32_load_shdrs(name, fp, foff, es.ehdr))) return (1); elf32_fix_shdrs(es.ehdr, es.shdr); es.shstr = NULL; if (issize) i = elf32_size(es.ehdr, es.shdr, &text, &data, &bss); else { nrawnames = 0; names = NULL; i = elf32_symload(&es, fp, foff, elf_symadd, &names); stab = es.stab; stabsize = es.stabsz; } free(es.shstr); free(es.shdr); if (i) return (i); } else if (!elf64_chk_header(&head->elf64)) { struct elf_symtab es; es.name = name; es.ehdr = &head->elf64; if (head->elf64.e_ehsize < sizeof head->elf64) { warnx("%s: ELF header is too short", name); return 1; } if (!(es.shdr = elf64_load_shdrs(name, fp, foff, es.ehdr))) return (1); elf64_fix_shdrs(es.ehdr, es.shdr); es.shstr = NULL; if (issize) i = elf64_size(es.ehdr, es.shdr, &text, &data, &bss); else { nrawnames = 0; names = NULL; i = elf64_symload(&es, fp, foff, elf_symadd, &names); stab = es.stab; stabsize = es.stabsz; } free(es.shstr); free(es.shdr); if (i) return (i); } else if (BAD_OBJECT(head->aout)) { if (warn_fmt) warnx("%s: bad format", name); return (1); } else do { u_int32_t w; aout++; fix_header_order(&head->aout); if (issize) { text = head->aout.a_text; data = head->aout.a_data; bss = head->aout.a_bss; break; } /* stop if the object file contains no symbol table */ if (!head->aout.a_syms) { warnx("%s: no name list", name); return(1); } if (fseeko(fp, foff + N_SYMOFF(head->aout), SEEK_SET)) { warn("%s", name); return(1); } #ifdef __LP64__ nrawnames = head->aout.a_syms / sizeof(struct nlist32); #else nrawnames = head->aout.a_syms / sizeof(*names); #endif /* get memory for the symbol table */ if ((names = calloc(nrawnames, sizeof(struct nlist))) == NULL) { warn("%s: malloc names", name); return (1); } #ifdef __LP64__ for (np = names, i = nrawnames; i--; np++) { struct nlist32 nl32; if (fread(&nl32, sizeof(nl32), 1, fp) != 1) { warnx("%s: cannot read symbol table", name); free(names); return (1); } np->n_type = nl32.type; np->n_other = nl32.other; if (byte_sex(N_GETMID(head->aout)) != BYTE_ORDER) { np->n_un.n_strx = swap32(nl32.strx); np->n_desc = swap16(nl32.desc); np->n_value = swap32(nl32.value); } else { np->n_un.n_strx = nl32.strx; np->n_desc = nl32.desc; np->n_value = nl32.value; } } #else if (fread(names, head->aout.a_syms, 1, fp) != 1) { warnx("%s: cannot read symbol table", name); free(names); return (1); } fix_nlists_order(names, nrawnames, N_GETMID(head->aout)); #endif staboff = ftello(fp); /* * Following the symbol table comes the string table. * The first 4-byte-integer gives the total size of the * string table _including_ the size specification itself. */ if (fread(&w, sizeof(w), (size_t)1, fp) != 1) { warnx("%s: cannot read stab size", name); free(names); return(1); } stabsize = fix_32_order(w, N_GETMID(head->aout)); if ((stab = malloc(stabsize)) == NULL) { warn("%s: malloc", name); return 1; } if (pread(fileno(fp), stab, stabsize, staboff) != stabsize) { free(stab); warn("%s: pread", name); return 1; } stabsize -= 4; /* we already have the size */ for (np = names, i = nnames = 0; i < nrawnames; np++, i++) { /* * make n_un.n_name a character pointer by adding * the string table's base to n_un.n_strx * * don't mess with zero offsets */ if (np->n_un.n_strx) np->n_un.n_name = stab + np->n_un.n_strx; else np->n_un.n_name = ""; } } while (0); if (issize) { static int first = 1; if (first) { first = 0; printf("text\tdata\tbss\tdec\thex\n"); } total = text + data + bss; printf("%lu\t%lu\t%lu\t%lu\t%lx", text, data, bss, total, total); if (count > 1) (void)printf("\t%s", name); total_text += text; total_data += data; total_bss += bss; total_total += total; printf("\n"); return (0); } /* else we are nm */ /* * it seems that string table is sequential * relative to the symbol table order */ if ((snames = calloc(nrawnames, sizeof *snames)) == NULL) { warn("%s: malloc snames", name); free(names); return (1); } /* * fix up the symbol table and filter out unwanted entries * * common symbols are characterized by a n_type of N_UNDF and a * non-zero n_value -- change n_type to N_COMM for all such * symbols to make life easier later. * * filter out all entries which we don't want to print anyway */ for (np = names, i = nnames = 0; i < nrawnames; np++, i++) { if (aout && SYMBOL_TYPE(np->n_type) == N_UNDF && np->n_value) np->n_type = N_COMM | (np->n_type & N_EXT); if (!print_all_symbols && IS_DEBUGGER_SYMBOL(np->n_type)) continue; if (print_only_external_symbols && !IS_EXTERNAL(np->n_type)) continue; if (print_only_undefined_symbols && SYMBOL_TYPE(np->n_type) != N_UNDF) continue; snames[nnames++] = np; } /* sort the symbol table if applicable */ if (sfunc) qsort(snames, (size_t)nnames, sizeof(*snames), sfunc); if (count > 1) (void)printf("\n%s:\n", name); /* print out symbols */ for (i = 0; i < nnames; i++) { if (show_extensions && snames[i] != names && SYMBOL_TYPE((snames[i] -1)->n_type) == N_INDR) continue; print_symbol(name, snames[i], aout); } free(snames); free(names); free(stab); return(0); }