diff options
author | Wez Furlong <wez@netevil.org> | 2011-04-03 14:37:41 -0400 |
---|---|---|
committer | Wez Furlong <wez@netevil.org> | 2011-04-03 14:37:41 -0400 |
commit | 253acb864d82ab0406d6a6bcecd09e502c64b140 (patch) | |
tree | 115e5562e80acc02e7329053f258cf1c0e3e1e38 /cvt/input.c | |
download | ctf-253acb864d82ab0406d6a6bcecd09e502c64b140.tar.gz |
Pull in the CTF sources from the FreeBSD 8.2 tree; arrange for them
to build under autoconf.
Diffstat (limited to 'cvt/input.c')
-rw-r--r-- | cvt/input.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/cvt/input.c b/cvt/input.c new file mode 100644 index 0000000..900cf85 --- /dev/null +++ b/cvt/input.c @@ -0,0 +1,412 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Routines for retrieving CTF data from a .SUNW_ctf ELF section + */ + +#include "libctf/ctf_impl.h" +#include "ctftools.h" +#include "memory.h" +#include "common/symbol.h" + +typedef int read_cb_f(tdata_t *, char *, void *); + +/* + * Return the source types that the object was generated from. + */ +source_types_t +built_source_types(Elf *elf, char const *file) +{ + source_types_t types = SOURCE_NONE; + symit_data_t *si; + + if ((si = symit_new(elf, file)) == NULL) + return (SOURCE_NONE); + + while (symit_next(si, STT_FILE) != NULL) { + char *name = symit_name(si); + size_t len = strlen(name); + if (len < 2 || name[len - 2] != '.') { + types |= SOURCE_UNKNOWN; + continue; + } + + switch (name[len - 1]) { + case 'c': + types |= SOURCE_C; + break; + case 'h': + /* ignore */ + break; + case 's': + case 'S': + types |= SOURCE_S; + break; + default: + types |= SOURCE_UNKNOWN; + } + } + + symit_free(si); + return (types); +} + +static int +read_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg, + int require_ctf) +{ + Elf_Scn *ctfscn; + Elf_Data *ctfdata = NULL; + symit_data_t *si = NULL; + int ctfscnidx; + tdata_t *td; + + if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) { + if (require_ctf && + (built_source_types(elf, file) & SOURCE_C)) { + terminate("Input file %s was partially built from " + "C sources, but no CTF data was present\n", file); + } + return (0); + } + + if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL || + (ctfdata = elf_getdata(ctfscn, NULL)) == NULL) + elfterminate(file, "Cannot read CTF section"); + + /* Reconstruction of type tree */ + if ((si = symit_new(elf, file)) == NULL) { + warning("%s has no symbol table - skipping", file); + return (0); + } + + td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label); + tdata_build_hashes(td); + + symit_free(si); + + if (td != NULL) { + if (func(td, file, arg) < 0) + return (-1); + else + return (1); + } + return (0); +} + +static int +read_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func, + void *arg, int require_ctf) +{ + Elf *melf; + Elf_Cmd cmd = ELF_C_READ; + Elf_Arhdr *arh; + int secnum = 1, found = 0; + + while ((melf = elf_begin(fd, cmd, elf)) != NULL) { + int rc = 0; + + if ((arh = elf_getarhdr(melf)) == NULL) { + elfterminate(file, "Can't get archive header for " + "member %d", secnum); + } + + /* skip special sections - their names begin with "/" */ + if (*arh->ar_name != '/') { + size_t memlen = strlen(file) + 1 + + strlen(arh->ar_name) + 1 + 1; + char *memname = xmalloc(memlen); + + snprintf(memname, memlen, "%s(%s)", file, arh->ar_name); + + switch (elf_kind(melf)) { + case ELF_K_AR: + rc = read_archive(fd, melf, memname, label, + func, arg, require_ctf); + break; + case ELF_K_ELF: + rc = read_file(melf, memname, label, + func, arg, require_ctf); + break; + default: + terminate("%s: Unknown elf kind %d\n", + memname, elf_kind(melf)); + } + + free(memname); + } + + cmd = elf_next(melf); + (void) elf_end(melf); + secnum++; + + if (rc < 0) + return (rc); + else + found += rc; + } + + return (found); +} + +static int +read_ctf_common(char *file, char *label, read_cb_f *func, void *arg, + int require_ctf) +{ + Elf *elf; + int found = 0; + int fd; + + debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE")); + + (void) elf_version(EV_CURRENT); + + if ((fd = open(file, O_RDONLY)) < 0) + terminate("%s: Cannot open for reading", file); + if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) + elfterminate(file, "Cannot read"); + + switch (elf_kind(elf)) { + case ELF_K_AR: + found = read_archive(fd, elf, file, label, + func, arg, require_ctf); + break; + + case ELF_K_ELF: + found = read_file(elf, file, label, + func, arg, require_ctf); + break; + + default: + terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf)); + } + + (void) elf_end(elf); + (void) close(fd); + + return (found); +} + +/*ARGSUSED*/ +int +read_ctf_save_cb(tdata_t *td, char *name __unused, void *retp) +{ + tdata_t **tdp = retp; + + *tdp = td; + + return (1); +} + +int +read_ctf(char **files, int n, char *label, read_cb_f *func, void *private, + int require_ctf) +{ + int found; + int i, rc; + + for (i = 0, found = 0; i < n; i++) { + if ((rc = read_ctf_common(files[i], label, func, + private, require_ctf)) < 0) + return (rc); + found += rc; + } + + return (found); +} + +static int +count_archive(int fd, Elf *elf, char *file) +{ + Elf *melf; + Elf_Cmd cmd = ELF_C_READ; + Elf_Arhdr *arh; + int nfiles = 0, err = 0; + + while ((melf = elf_begin(fd, cmd, elf)) != NULL) { + if ((arh = elf_getarhdr(melf)) == NULL) { + warning("Can't process input archive %s\n", + file); + err++; + } + + if (*arh->ar_name != '/') + nfiles++; + + cmd = elf_next(melf); + (void) elf_end(melf); + } + + if (err > 0) + return (-1); + + return (nfiles); +} + +int +count_files(char **files, int n) +{ + int nfiles = 0, err = 0; + Elf *elf; + int fd, rc, i; + + (void) elf_version(EV_CURRENT); + + for (i = 0; i < n; i++) { + char *file = files[i]; + + if ((fd = open(file, O_RDONLY)) < 0) { + warning("Can't read input file %s", file); + err++; + continue; + } + + if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warning("Can't open input file %s: %s\n", file, + elf_errmsg(-1)); + err++; + (void) close(fd); + continue; + } + + switch (elf_kind(elf)) { + case ELF_K_AR: + if ((rc = count_archive(fd, elf, file)) < 0) + err++; + else + nfiles += rc; + break; + case ELF_K_ELF: + nfiles++; + break; + default: + warning("Input file %s is corrupt\n", file); + err++; + } + + (void) elf_end(elf); + (void) close(fd); + } + + if (err > 0) + return (-1); + + debug(2, "Found %d files in %d input files\n", nfiles, n); + + return (nfiles); +} + +struct symit_data { + GElf_Shdr si_shdr; + Elf_Data *si_symd; + Elf_Data *si_strd; + GElf_Sym si_cursym; + char *si_curname; + char *si_curfile; + int si_nument; + int si_next; +}; + +symit_data_t * +symit_new(Elf *elf, const char *file) +{ + symit_data_t *si; + Elf_Scn *scn; + int symtabidx; + + if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0) + return (NULL); + + si = xcalloc(sizeof (symit_data_t)); + + if ((scn = elf_getscn(elf, symtabidx)) == NULL || + gelf_getshdr(scn, &si->si_shdr) == NULL || + (si->si_symd = elf_getdata(scn, NULL)) == NULL) + elfterminate(file, "Cannot read .symtab"); + + if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL || + (si->si_strd = elf_getdata(scn, NULL)) == NULL) + elfterminate(file, "Cannot read strings for .symtab"); + + si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize; + + return (si); +} + +void +symit_free(symit_data_t *si) +{ + free(si); +} + +void +symit_reset(symit_data_t *si) +{ + si->si_next = 0; +} + +char * +symit_curfile(symit_data_t *si) +{ + return (si->si_curfile); +} + +GElf_Sym * +symit_next(symit_data_t *si, int type) +{ + GElf_Sym sym; + int check_sym = (type == STT_OBJECT || type == STT_FUNC); + + for (; si->si_next < si->si_nument; si->si_next++) { + gelf_getsym(si->si_symd, si->si_next, &si->si_cursym); + gelf_getsym(si->si_symd, si->si_next, &sym); + si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name; + + if (GELF_ST_TYPE(sym.st_info) == STT_FILE) + si->si_curfile = si->si_curname; + + if (GELF_ST_TYPE(sym.st_info) != type || + sym.st_shndx == SHN_UNDEF) + continue; + + if (check_sym && ignore_symbol(&sym, si->si_curname)) + continue; + + si->si_next++; + + return (&si->si_cursym); + } + + return (NULL); +} + +char * +symit_name(symit_data_t *si) +{ + return (si->si_curname); +} |