123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290 |
- /* CTF archive files.
- Copyright (C) 2019-2022 Free Software Foundation, Inc.
- This file is part of libctf.
- libctf is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 3, or (at your option) any later
- version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not see
- <http://www.gnu.org/licenses/>. */
- #include <ctf-impl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <elf.h>
- #include "ctf-endian.h"
- #include <errno.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #ifdef HAVE_MMAP
- #include <sys/mman.h>
- #endif
- static off_t arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold);
- static ctf_dict_t *ctf_dict_open_by_offset (const struct ctf_archive *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- size_t offset, int little_endian,
- int *errp);
- static int sort_modent_by_name (const void *one, const void *two, void *n);
- static void *arc_mmap_header (int fd, size_t headersz);
- static void *arc_mmap_file (int fd, size_t size);
- static int arc_mmap_writeout (int fd, void *header, size_t headersz,
- const char **errmsg);
- static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg);
- static void ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp);
- /* Flag to indicate "symbol not present" in ctf_archive_internal.ctfi_symdicts
- and ctfi_symnamedicts. Never initialized. */
- static ctf_dict_t enosym;
- /* Write out a CTF archive to the start of the file referenced by the passed-in
- fd. The entries in CTF_DICTS are referenced by name: the names are passed in
- the names array, which must have CTF_DICTS entries.
- Returns 0 on success, or an errno, or an ECTF_* value. */
- int
- ctf_arc_write_fd (int fd, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
- const char **names, size_t threshold)
- {
- const char *errmsg;
- struct ctf_archive *archdr;
- size_t i;
- char dummy = 0;
- size_t headersz;
- ssize_t namesz;
- size_t ctf_startoffs; /* Start of the section we are working over. */
- char *nametbl = NULL; /* The name table. */
- char *np;
- off_t nameoffs;
- struct ctf_archive_modent *modent;
- ctf_dprintf ("Writing CTF archive with %lu files\n",
- (unsigned long) ctf_dict_cnt);
- /* Figure out the size of the mmap()ed header, including the
- ctf_archive_modent array. We assume that all of this needs no
- padding: a likely assumption, given that it's all made up of
- uint64_t's. */
- headersz = sizeof (struct ctf_archive)
- + (ctf_dict_cnt * sizeof (uint64_t) * 2);
- ctf_dprintf ("headersz is %lu\n", (unsigned long) headersz);
- /* From now on we work in two pieces: an mmap()ed region from zero up to the
- headersz, and a region updated via write() starting after that, containing
- all the tables. Platforms that do not support mmap() just use write(). */
- ctf_startoffs = headersz;
- if (lseek (fd, ctf_startoffs - 1, SEEK_SET) < 0)
- {
- errmsg = N_("ctf_arc_write(): cannot extend file while writing");
- goto err;
- }
- if (write (fd, &dummy, 1) < 0)
- {
- errmsg = N_("ctf_arc_write(): cannot extend file while writing");
- goto err;
- }
- if ((archdr = arc_mmap_header (fd, headersz)) == NULL)
- {
- errmsg = N_("ctf_arc_write(): cannot mmap");
- goto err;
- }
- /* Fill in everything we can, which is everything other than the name
- table offset. */
- archdr->ctfa_magic = htole64 (CTFA_MAGIC);
- archdr->ctfa_ndicts = htole64 (ctf_dict_cnt);
- archdr->ctfa_ctfs = htole64 (ctf_startoffs);
- /* We could validate that all CTF files have the same data model, but
- since any reasonable construction process will be building things of
- only one bitness anyway, this is pretty pointless, so just use the
- model of the first CTF file for all of them. (It *is* valid to
- create an empty archive: the value of ctfa_model is irrelevant in
- this case, but we must be sure not to dereference uninitialized
- memory.) */
- if (ctf_dict_cnt > 0)
- archdr->ctfa_model = htole64 (ctf_getmodel (ctf_dicts[0]));
- /* Now write out the CTFs: ctf_archive_modent array via the mapping,
- ctfs via write(). The names themselves have not been written yet: we
- track them in a local strtab until the time is right, and sort the
- modents array after construction.
- The name table is not sorted. */
- for (i = 0, namesz = 0; i < le64toh (archdr->ctfa_ndicts); i++)
- namesz += strlen (names[i]) + 1;
- nametbl = malloc (namesz);
- if (nametbl == NULL)
- {
- errmsg = N_("ctf_arc_write(): error writing named CTF to archive");
- goto err_unmap;
- }
- for (i = 0, namesz = 0,
- modent = (ctf_archive_modent_t *) ((char *) archdr
- + sizeof (struct ctf_archive));
- i < le64toh (archdr->ctfa_ndicts); i++)
- {
- off_t off;
- strcpy (&nametbl[namesz], names[i]);
- off = arc_write_one_ctf (ctf_dicts[i], fd, threshold);
- if ((off < 0) && (off > -ECTF_BASE))
- {
- errmsg = N_("ctf_arc_write(): cannot determine file "
- "position while writing to archive");
- goto err_free;
- }
- if (off < 0)
- {
- errmsg = N_("ctf_arc_write(): cannot write CTF file to archive");
- errno = off * -1;
- goto err_free;
- }
- modent->name_offset = htole64 (namesz);
- modent->ctf_offset = htole64 (off - ctf_startoffs);
- namesz += strlen (names[i]) + 1;
- modent++;
- }
- ctf_qsort_r ((ctf_archive_modent_t *) ((char *) archdr
- + sizeof (struct ctf_archive)),
- le64toh (archdr->ctfa_ndicts),
- sizeof (struct ctf_archive_modent), sort_modent_by_name,
- nametbl);
- /* Now the name table. */
- if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0)
- {
- errmsg = N_("ctf_arc_write(): cannot get current file position "
- "in archive");
- goto err_free;
- }
- archdr->ctfa_names = htole64 (nameoffs);
- np = nametbl;
- while (namesz > 0)
- {
- ssize_t len;
- if ((len = write (fd, np, namesz)) < 0)
- {
- errmsg = N_("ctf_arc_write(): cannot write name table to archive");
- goto err_free;
- }
- namesz -= len;
- np += len;
- }
- free (nametbl);
- if (arc_mmap_writeout (fd, archdr, headersz, &errmsg) < 0)
- goto err_unmap;
- if (arc_mmap_unmap (archdr, headersz, &errmsg) < 0)
- goto err;
- return 0;
- err_free:
- free (nametbl);
- err_unmap:
- arc_mmap_unmap (archdr, headersz, NULL);
- err:
- /* We report errors into the first file in the archive, if any: if this is a
- zero-file archive, put it in the open-errors stream for lack of anywhere
- else for it to go. */
- ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno, "%s",
- gettext (errmsg));
- return errno;
- }
- /* Write out a CTF archive. The entries in CTF_DICTS are referenced by name:
- the names are passed in the names array, which must have CTF_DICTS entries.
- If the filename is NULL, create a temporary file and return a pointer to it.
- Returns 0 on success, or an errno, or an ECTF_* value. */
- int
- ctf_arc_write (const char *file, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
- const char **names, size_t threshold)
- {
- int err;
- int fd;
- if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0)
- {
- ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
- _("ctf_arc_write(): cannot create %s"), file);
- return errno;
- }
- err = ctf_arc_write_fd (fd, ctf_dicts, ctf_dict_cnt, names, threshold);
- if (err)
- goto err_close;
- if ((err = close (fd)) < 0)
- ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
- _("ctf_arc_write(): cannot close after writing to archive"));
- goto err;
- err_close:
- (void) close (fd);
- err:
- if (err < 0)
- unlink (file);
- return err;
- }
- /* Write one CTF file out. Return the file position of the written file (or
- rather, of the file-size uint64_t that precedes it): negative return is a
- negative errno or ctf_errno value. On error, the file position may no longer
- be at the end of the file. */
- static off_t
- arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold)
- {
- off_t off, end_off;
- uint64_t ctfsz = 0;
- char *ctfszp;
- size_t ctfsz_len;
- int (*writefn) (ctf_dict_t * fp, int fd);
- if (ctf_serialize (f) < 0)
- return f->ctf_errno * -1;
- if ((off = lseek (fd, 0, SEEK_CUR)) < 0)
- return errno * -1;
- if (f->ctf_size > threshold)
- writefn = ctf_compress_write;
- else
- writefn = ctf_write;
- /* This zero-write turns into the size in a moment. */
- ctfsz_len = sizeof (ctfsz);
- ctfszp = (char *) &ctfsz;
- while (ctfsz_len > 0)
- {
- ssize_t writelen = write (fd, ctfszp, ctfsz_len);
- if (writelen < 0)
- return errno * -1;
- ctfsz_len -= writelen;
- ctfszp += writelen;
- }
- if (writefn (f, fd) != 0)
- return f->ctf_errno * -1;
- if ((end_off = lseek (fd, 0, SEEK_CUR)) < 0)
- return errno * -1;
- ctfsz = htole64 (end_off - off);
- if ((lseek (fd, off, SEEK_SET)) < 0)
- return errno * -1;
- /* ... here. */
- ctfsz_len = sizeof (ctfsz);
- ctfszp = (char *) &ctfsz;
- while (ctfsz_len > 0)
- {
- ssize_t writelen = write (fd, ctfszp, ctfsz_len);
- if (writelen < 0)
- return errno * -1;
- ctfsz_len -= writelen;
- ctfszp += writelen;
- }
- end_off = LCTF_ALIGN_OFFS (end_off, 8);
- if ((lseek (fd, end_off, SEEK_SET)) < 0)
- return errno * -1;
- return off;
- }
- /* qsort() function to sort the array of struct ctf_archive_modents into
- ascending name order. */
- static int
- sort_modent_by_name (const void *one, const void *two, void *n)
- {
- const struct ctf_archive_modent *a = one;
- const struct ctf_archive_modent *b = two;
- char *nametbl = n;
- return strcmp (&nametbl[le64toh (a->name_offset)],
- &nametbl[le64toh (b->name_offset)]);
- }
- /* bsearch_r() function to search for a given name in the sorted array of struct
- ctf_archive_modents. */
- static int
- search_modent_by_name (const void *key, const void *ent, void *arg)
- {
- const char *k = key;
- const struct ctf_archive_modent *v = ent;
- const char *search_nametbl = arg;
- return strcmp (k, &search_nametbl[le64toh (v->name_offset)]);
- }
- /* Make a new struct ctf_archive_internal wrapper for a ctf_archive or a
- ctf_dict. Closes ARC and/or FP on error. Arrange to free the SYMSECT or
- STRSECT, as needed, on close. Possibly do not unmap on close. */
- struct ctf_archive_internal *
- ctf_new_archive_internal (int is_archive, int unmap_on_close,
- struct ctf_archive *arc,
- ctf_dict_t *fp, const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- int *errp)
- {
- struct ctf_archive_internal *arci;
- if ((arci = calloc (1, sizeof (struct ctf_archive_internal))) == NULL)
- {
- if (is_archive)
- {
- if (unmap_on_close)
- ctf_arc_close_internal (arc);
- }
- else
- ctf_dict_close (fp);
- return (ctf_set_open_errno (errp, errno));
- }
- arci->ctfi_is_archive = is_archive;
- if (is_archive)
- arci->ctfi_archive = arc;
- else
- arci->ctfi_dict = fp;
- if (symsect)
- memcpy (&arci->ctfi_symsect, symsect, sizeof (struct ctf_sect));
- if (strsect)
- memcpy (&arci->ctfi_strsect, strsect, sizeof (struct ctf_sect));
- arci->ctfi_free_symsect = 0;
- arci->ctfi_free_strsect = 0;
- arci->ctfi_unmap_on_close = unmap_on_close;
- arci->ctfi_symsect_little_endian = -1;
- return arci;
- }
- /* Set the symbol-table endianness of an archive (defaulting the symtab
- endianness of all ctf_file_t's opened from that archive). */
- void
- ctf_arc_symsect_endianness (ctf_archive_t *arc, int little_endian)
- {
- arc->ctfi_symsect_little_endian = !!little_endian;
- if (!arc->ctfi_is_archive)
- ctf_symsect_endianness (arc->ctfi_dict, arc->ctfi_symsect_little_endian);
- }
- /* Get the CTF preamble from data in a buffer, which may be either an archive or
- a CTF dict. If multiple dicts are present in an archive, the preamble comes
- from an arbitrary dict. The preamble is a pointer into the ctfsect passed
- in. */
- const ctf_preamble_t *
- ctf_arc_bufpreamble (const ctf_sect_t *ctfsect)
- {
- if (ctfsect->cts_size > sizeof (uint64_t) &&
- (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
- {
- struct ctf_archive *arc = (struct ctf_archive *) ctfsect->cts_data;
- return (const ctf_preamble_t *) ((char *) arc + le64toh (arc->ctfa_ctfs)
- + sizeof (uint64_t));
- }
- else
- return (const ctf_preamble_t *) ctfsect->cts_data;
- }
- /* Open a CTF archive or dictionary from data in a buffer (which the caller must
- preserve until ctf_arc_close() time). Returns the archive, or NULL and an
- error in *err (if not NULL). */
- ctf_archive_t *
- ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
- const ctf_sect_t *strsect, int *errp)
- {
- struct ctf_archive *arc = NULL;
- int is_archive;
- ctf_dict_t *fp = NULL;
- if (ctfsect->cts_size > sizeof (uint64_t) &&
- (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
- {
- /* The archive is mmappable, so this operation is trivial.
- This buffer is nonmodifiable, so the trick involving mmapping only part
- of it and storing the length in the magic number is not applicable: so
- record this fact in the archive-wrapper header. (We cannot record it
- in the archive, because the archive may very well be a read-only
- mapping.) */
- is_archive = 1;
- arc = (struct ctf_archive *) ctfsect->cts_data;
- }
- else
- {
- is_archive = 0;
- if ((fp = ctf_bufopen (ctfsect, symsect, strsect, errp)) == NULL)
- {
- ctf_err_warn (NULL, 0, *errp, _("ctf_arc_bufopen(): cannot open CTF"));
- return NULL;
- }
- }
- return ctf_new_archive_internal (is_archive, 0, arc, fp, symsect, strsect,
- errp);
- }
- /* Open a CTF archive. Returns the archive, or NULL and an error in *err (if
- not NULL). */
- struct ctf_archive *
- ctf_arc_open_internal (const char *filename, int *errp)
- {
- const char *errmsg;
- int fd;
- struct stat s;
- struct ctf_archive *arc; /* (Actually the whole file.) */
- libctf_init_debug();
- if ((fd = open (filename, O_RDONLY)) < 0)
- {
- errmsg = N_("ctf_arc_open(): cannot open %s");
- goto err;
- }
- if (fstat (fd, &s) < 0)
- {
- errmsg = N_("ctf_arc_open(): cannot stat %s");
- goto err_close;
- }
- if ((arc = arc_mmap_file (fd, s.st_size)) == NULL)
- {
- errmsg = N_("ctf_arc_open(): cannot read in %s");
- goto err_close;
- }
- if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
- {
- errmsg = N_("ctf_arc_open(): %s: invalid magic number");
- errno = ECTF_FMT;
- goto err_unmap;
- }
- /* This horrible hack lets us know how much to unmap when the file is
- closed. (We no longer need the magic number, and the mapping
- is private.) */
- arc->ctfa_magic = s.st_size;
- close (fd);
- return arc;
- err_unmap:
- arc_mmap_unmap (arc, s.st_size, NULL);
- err_close:
- close (fd);
- err:
- if (errp)
- *errp = errno;
- ctf_err_warn (NULL, 0, errno, gettext (errmsg), filename);
- return NULL;
- }
- /* Close an archive. */
- void
- ctf_arc_close_internal (struct ctf_archive *arc)
- {
- if (arc == NULL)
- return;
- /* See the comment in ctf_arc_open(). */
- arc_mmap_unmap (arc, arc->ctfa_magic, NULL);
- }
- /* Public entry point: close an archive, or CTF file. */
- void
- ctf_arc_close (ctf_archive_t *arc)
- {
- if (arc == NULL)
- return;
- if (arc->ctfi_is_archive)
- {
- if (arc->ctfi_unmap_on_close)
- ctf_arc_close_internal (arc->ctfi_archive);
- }
- else
- ctf_dict_close (arc->ctfi_dict);
- free (arc->ctfi_symdicts);
- free (arc->ctfi_symnamedicts);
- ctf_dynhash_destroy (arc->ctfi_dicts);
- if (arc->ctfi_free_symsect)
- free ((void *) arc->ctfi_symsect.cts_data);
- if (arc->ctfi_free_strsect)
- free ((void *) arc->ctfi_strsect.cts_data);
- free (arc->ctfi_data);
- if (arc->ctfi_bfd_close)
- arc->ctfi_bfd_close (arc);
- free (arc);
- }
- /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
- non-NULL. A name of NULL means to open the default file. */
- static ctf_dict_t *
- ctf_dict_open_internal (const struct ctf_archive *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- const char *name, int little_endian,
- int *errp)
- {
- struct ctf_archive_modent *modent;
- const char *search_nametbl;
- if (name == NULL)
- name = _CTF_SECTION; /* The default name. */
- ctf_dprintf ("ctf_dict_open_internal(%s): opening\n", name);
- modent = (ctf_archive_modent_t *) ((char *) arc
- + sizeof (struct ctf_archive));
- search_nametbl = (const char *) arc + le64toh (arc->ctfa_names);
- modent = bsearch_r (name, modent, le64toh (arc->ctfa_ndicts),
- sizeof (struct ctf_archive_modent),
- search_modent_by_name, (void *) search_nametbl);
- /* This is actually a common case and normal operation: no error
- debug output. */
- if (modent == NULL)
- {
- if (errp)
- *errp = ECTF_ARNNAME;
- return NULL;
- }
- return ctf_dict_open_by_offset (arc, symsect, strsect,
- le64toh (modent->ctf_offset),
- little_endian, errp);
- }
- /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
- non-NULL. A name of NULL means to open the default file.
- Use the specified string and symbol table sections.
- Public entry point. */
- ctf_dict_t *
- ctf_dict_open_sections (const ctf_archive_t *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- const char *name,
- int *errp)
- {
- if (arc->ctfi_is_archive)
- {
- ctf_dict_t *ret;
- ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect,
- name, arc->ctfi_symsect_little_endian,
- errp);
- if (ret)
- {
- ret->ctf_archive = (ctf_archive_t *) arc;
- ctf_arc_import_parent (arc, ret);
- }
- return ret;
- }
- if ((name != NULL) && (strcmp (name, _CTF_SECTION) != 0))
- {
- if (errp)
- *errp = ECTF_ARNNAME;
- return NULL;
- }
- arc->ctfi_dict->ctf_archive = (ctf_archive_t *) arc;
- /* Bump the refcount so that the user can ctf_dict_close() it. */
- arc->ctfi_dict->ctf_refcnt++;
- return arc->ctfi_dict;
- }
- /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
- non-NULL. A name of NULL means to open the default file.
- Public entry point. */
- ctf_dict_t *
- ctf_dict_open (const ctf_archive_t *arc, const char *name, int *errp)
- {
- const ctf_sect_t *symsect = &arc->ctfi_symsect;
- const ctf_sect_t *strsect = &arc->ctfi_strsect;
- if (symsect->cts_name == NULL)
- symsect = NULL;
- if (strsect->cts_name == NULL)
- strsect = NULL;
- return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
- }
- static void
- ctf_cached_dict_close (void *fp)
- {
- ctf_dict_close ((ctf_dict_t *) fp);
- }
- /* Return the ctf_dict_t with the given name and cache it in the archive's
- ctfi_dicts. If this is the first cached dict, designate it the
- crossdict_cache. */
- static ctf_dict_t *
- ctf_dict_open_cached (ctf_archive_t *arc, const char *name, int *errp)
- {
- ctf_dict_t *fp;
- char *dupname;
- /* Just return from the cache if possible. */
- if (arc->ctfi_dicts
- && ((fp = ctf_dynhash_lookup (arc->ctfi_dicts, name)) != NULL))
- {
- fp->ctf_refcnt++;
- return fp;
- }
- /* Not yet cached: open it. */
- fp = ctf_dict_open (arc, name, errp);
- dupname = strdup (name);
- if (!fp || !dupname)
- goto oom;
- if (arc->ctfi_dicts == NULL)
- if ((arc->ctfi_dicts
- = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
- free, ctf_cached_dict_close)) == NULL)
- goto oom;
- if (ctf_dynhash_insert (arc->ctfi_dicts, dupname, fp) < 0)
- goto oom;
- fp->ctf_refcnt++;
- if (arc->ctfi_crossdict_cache == NULL)
- arc->ctfi_crossdict_cache = fp;
- return fp;
- oom:
- ctf_dict_close (fp);
- free (dupname);
- if (errp)
- *errp = ENOMEM;
- return NULL;
- }
- /* Flush any caches the CTF archive may have open. */
- void
- ctf_arc_flush_caches (ctf_archive_t *wrapper)
- {
- free (wrapper->ctfi_symdicts);
- free (wrapper->ctfi_symnamedicts);
- ctf_dynhash_destroy (wrapper->ctfi_dicts);
- wrapper->ctfi_symdicts = NULL;
- wrapper->ctfi_symnamedicts = NULL;
- wrapper->ctfi_dicts = NULL;
- wrapper->ctfi_crossdict_cache = NULL;
- }
- /* Return the ctf_dict_t at the given ctfa_ctfs-relative offset, or NULL if
- none, setting 'err' if non-NULL. */
- static ctf_dict_t *
- ctf_dict_open_by_offset (const struct ctf_archive *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect, size_t offset,
- int little_endian, int *errp)
- {
- ctf_sect_t ctfsect;
- ctf_dict_t *fp;
- ctf_dprintf ("ctf_dict_open_by_offset(%lu): opening\n", (unsigned long) offset);
- memset (&ctfsect, 0, sizeof (ctf_sect_t));
- offset += le64toh (arc->ctfa_ctfs);
- ctfsect.cts_name = _CTF_SECTION;
- ctfsect.cts_size = le64toh (*((uint64_t *) ((char *) arc + offset)));
- ctfsect.cts_entsize = 1;
- ctfsect.cts_data = (void *) ((char *) arc + offset + sizeof (uint64_t));
- fp = ctf_bufopen (&ctfsect, symsect, strsect, errp);
- if (fp)
- {
- ctf_setmodel (fp, le64toh (arc->ctfa_model));
- if (little_endian >= 0)
- ctf_symsect_endianness (fp, little_endian);
- }
- return fp;
- }
- /* Backward compatibility. */
- ctf_dict_t *
- ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name,
- int *errp)
- {
- return ctf_dict_open (arc, name, errp);
- }
- ctf_dict_t *
- ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- const char *name,
- int *errp)
- {
- return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
- }
- /* Import the parent into a ctf archive, if this is a child, the parent is not
- already set, and a suitable archive member exists. No error is raised if
- this is not possible: this is just a best-effort helper operation to give
- people useful dicts to start with. */
- static void
- ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp)
- {
- if ((fp->ctf_flags & LCTF_CHILD) && fp->ctf_parname && !fp->ctf_parent)
- {
- ctf_dict_t *parent = ctf_dict_open_cached ((ctf_archive_t *) arc,
- fp->ctf_parname, NULL);
- if (parent)
- {
- ctf_import (fp, parent);
- ctf_dict_close (parent);
- }
- }
- }
- /* Return the number of members in an archive. */
- size_t
- ctf_archive_count (const ctf_archive_t *wrapper)
- {
- if (!wrapper->ctfi_is_archive)
- return 1;
- return wrapper->ctfi_archive->ctfa_ndicts;
- }
- /* Look up a symbol in an archive by name or index (if the name is set, a lookup
- by name is done). Return the dict in the archive that the symbol is found
- in, and (optionally) the ctf_id_t of the symbol in that dict (so you don't
- have to look it up yourself). The dict is cached, so repeated lookups are
- nearly free.
- As usual, you should ctf_dict_close() the returned dict once you are done
- with it.
- Returns NULL on error, and an error in errp (if set). */
- static ctf_dict_t *
- ctf_arc_lookup_sym_or_name (ctf_archive_t *wrapper, unsigned long symidx,
- const char *symname, ctf_id_t *typep, int *errp)
- {
- ctf_dict_t *fp;
- void *fpkey;
- ctf_id_t type;
- /* The usual non-archive-transparent-wrapper special case. */
- if (!wrapper->ctfi_is_archive)
- {
- if (!symname)
- {
- if ((type = ctf_lookup_by_symbol (wrapper->ctfi_dict, symidx)) == CTF_ERR)
- {
- if (errp)
- *errp = ctf_errno (wrapper->ctfi_dict);
- return NULL;
- }
- }
- else
- {
- if ((type = ctf_lookup_by_symbol_name (wrapper->ctfi_dict,
- symname)) == CTF_ERR)
- {
- if (errp)
- *errp = ctf_errno (wrapper->ctfi_dict);
- return NULL;
- }
- }
- if (typep)
- *typep = type;
- wrapper->ctfi_dict->ctf_refcnt++;
- return wrapper->ctfi_dict;
- }
- if (wrapper->ctfi_symsect.cts_name == NULL
- || wrapper->ctfi_symsect.cts_data == NULL
- || wrapper->ctfi_symsect.cts_size == 0
- || wrapper->ctfi_symsect.cts_entsize == 0)
- {
- if (errp)
- *errp = ECTF_NOSYMTAB;
- return NULL;
- }
- /* Make enough space for all possible symbol indexes, if not already done. We
- cache the originating dictionary of all symbols. The dict links are weak,
- to the dictionaries cached in ctfi_dicts: their refcnts are *not* bumped.
- We also cache similar mappings for symbol names: these are ordinary
- dynhashes, with weak links to dicts. */
- if (!wrapper->ctfi_symdicts)
- {
- if ((wrapper->ctfi_symdicts = calloc (wrapper->ctfi_symsect.cts_size
- / wrapper->ctfi_symsect.cts_entsize,
- sizeof (ctf_dict_t *))) == NULL)
- {
- if (errp)
- *errp = ENOMEM;
- return NULL;
- }
- }
- if (!wrapper->ctfi_symnamedicts)
- {
- if ((wrapper->ctfi_symnamedicts = ctf_dynhash_create (ctf_hash_string,
- ctf_hash_eq_string,
- free, NULL)) == NULL)
- {
- if (errp)
- *errp = ENOMEM;
- return NULL;
- }
- }
- /* Perhaps the dict in which we found a previous lookup is cached. If it's
- supposed to be cached but we don't find it, pretend it was always not
- found: this should never happen, but shouldn't be allowed to cause trouble
- if it does. */
- if ((symname && ctf_dynhash_lookup_kv (wrapper->ctfi_symnamedicts,
- symname, NULL, &fpkey))
- || (!symname && wrapper->ctfi_symdicts[symidx] != NULL))
- {
- if (symname)
- fp = (ctf_dict_t *) fpkey;
- else
- fp = wrapper->ctfi_symdicts[symidx];
- if (fp == &enosym)
- goto no_sym;
- if (symname)
- {
- if ((type = ctf_lookup_by_symbol_name (fp, symname)) == CTF_ERR)
- goto cache_no_sym;
- }
- else
- {
- if ((type = ctf_lookup_by_symbol (fp, symidx)) == CTF_ERR)
- goto cache_no_sym;
- }
- if (typep)
- *typep = type;
- fp->ctf_refcnt++;
- return fp;
- }
- /* Not cached: find it and cache it. We must track open errors ourselves even
- if our caller doesn't, to be able to distinguish no-error end-of-iteration
- from open errors. */
- int local_err;
- int *local_errp;
- ctf_next_t *i = NULL;
- const char *name;
- if (errp)
- local_errp = errp;
- else
- local_errp = &local_err;
- while ((fp = ctf_archive_next (wrapper, &i, &name, 0, local_errp)) != NULL)
- {
- if (!symname)
- {
- if ((type = ctf_lookup_by_symbol (fp, symidx)) != CTF_ERR)
- wrapper->ctfi_symdicts[symidx] = fp;
- }
- else
- {
- if ((type = ctf_lookup_by_symbol_name (fp, symname)) != CTF_ERR)
- {
- char *tmp;
- /* No error checking, as above. */
- if ((tmp = strdup (symname)) != NULL)
- ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, fp);
- }
- }
- if (type != CTF_ERR)
- {
- if (typep)
- *typep = type;
- ctf_next_destroy (i);
- return fp;
- }
- if (ctf_errno (fp) != ECTF_NOTYPEDAT)
- {
- if (errp)
- *errp = ctf_errno (fp);
- ctf_next_destroy (i);
- return NULL; /* errno is set for us. */
- }
- ctf_dict_close (fp);
- }
- if (*local_errp != ECTF_NEXT_END)
- {
- ctf_next_destroy (i);
- return NULL;
- }
- /* Don't leak end-of-iteration to the caller. */
- *local_errp = 0;
- cache_no_sym:
- if (!symname)
- wrapper->ctfi_symdicts[symidx] = &enosym;
- else
- {
- char *tmp;
- /* No error checking: if caching fails, there is only a slight performance
- impact. */
- if ((tmp = strdup (symname)) != NULL)
- if (ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, &enosym) < 0)
- free (tmp);
- }
- no_sym:
- if (errp)
- *errp = ECTF_NOTYPEDAT;
- if (typep)
- *typep = CTF_ERR;
- return NULL;
- }
- /* The public API for looking up a symbol by index. */
- ctf_dict_t *
- ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx,
- ctf_id_t *typep, int *errp)
- {
- return ctf_arc_lookup_sym_or_name (wrapper, symidx, NULL, typep, errp);
- }
- /* The public API for looking up a symbol by name. */
- ctf_dict_t *
- ctf_arc_lookup_symbol_name (ctf_archive_t *wrapper, const char *symname,
- ctf_id_t *typep, int *errp)
- {
- return ctf_arc_lookup_sym_or_name (wrapper, 0, symname, typep, errp);
- }
- /* Raw iteration over all CTF files in an archive. We pass the raw data for all
- CTF files in turn to the specified callback function. */
- static int
- ctf_archive_raw_iter_internal (const struct ctf_archive *arc,
- ctf_archive_raw_member_f *func, void *data)
- {
- int rc;
- size_t i;
- struct ctf_archive_modent *modent;
- const char *nametbl;
- modent = (ctf_archive_modent_t *) ((char *) arc
- + sizeof (struct ctf_archive));
- nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
- for (i = 0; i < le64toh (arc->ctfa_ndicts); i++)
- {
- const char *name;
- char *fp;
- name = &nametbl[le64toh (modent[i].name_offset)];
- fp = ((char *) arc + le64toh (arc->ctfa_ctfs)
- + le64toh (modent[i].ctf_offset));
- if ((rc = func (name, (void *) (fp + sizeof (uint64_t)),
- le64toh (*((uint64_t *) fp)), data)) != 0)
- return rc;
- }
- return 0;
- }
- /* Raw iteration over all CTF files in an archive: public entry point.
- Returns -EINVAL if not supported for this sort of archive. */
- int
- ctf_archive_raw_iter (const ctf_archive_t *arc,
- ctf_archive_raw_member_f * func, void *data)
- {
- if (arc->ctfi_is_archive)
- return ctf_archive_raw_iter_internal (arc->ctfi_archive, func, data);
- return -EINVAL; /* Not supported. */
- }
- /* Iterate over all CTF files in an archive: public entry point. We pass all
- CTF files in turn to the specified callback function. */
- int
- ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
- void *data)
- {
- ctf_next_t *i = NULL;
- ctf_dict_t *fp;
- const char *name;
- int err;
- while ((fp = ctf_archive_next (arc, &i, &name, 0, &err)) != NULL)
- {
- int rc;
- if ((rc = func (fp, name, data)) != 0)
- {
- ctf_dict_close (fp);
- ctf_next_destroy (i);
- return rc;
- }
- ctf_dict_close (fp);
- }
- return 0;
- }
- /* Iterate over all CTF files in an archive, returning each dict in turn as a
- ctf_dict_t, and NULL on error or end of iteration. It is the caller's
- responsibility to close it. Parent dicts may be skipped.
- The archive member is cached for rapid return on future calls.
- We identify parents by name rather than by flag value: for now, with the
- linker only emitting parents named _CTF_SECTION, this works well enough. */
- ctf_dict_t *
- ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **name,
- int skip_parent, int *errp)
- {
- ctf_dict_t *f;
- ctf_next_t *i = *it;
- struct ctf_archive *arc;
- struct ctf_archive_modent *modent;
- const char *nametbl;
- const char *name_;
- if (!i)
- {
- if ((i = ctf_next_create()) == NULL)
- {
- if (errp)
- *errp = ENOMEM;
- return NULL;
- }
- i->cu.ctn_arc = wrapper;
- i->ctn_iter_fun = (void (*) (void)) ctf_archive_next;
- *it = i;
- }
- if ((void (*) (void)) ctf_archive_next != i->ctn_iter_fun)
- {
- if (errp)
- *errp = ECTF_NEXT_WRONGFUN;
- return NULL;
- }
- if (wrapper != i->cu.ctn_arc)
- {
- if (errp)
- *errp = ECTF_NEXT_WRONGFP;
- return NULL;
- }
- /* Iteration is made a bit more complex by the need to handle ctf_dict_t's
- transparently wrapped in a single-member archive. These are parents: if
- skip_parent is on, they are skipped and the iterator terminates
- immediately. */
- if (!wrapper->ctfi_is_archive && i->ctn_n == 0)
- {
- i->ctn_n++;
- if (!skip_parent)
- {
- wrapper->ctfi_dict->ctf_refcnt++;
- if (name)
- *name = _CTF_SECTION;
- return wrapper->ctfi_dict;
- }
- }
- arc = wrapper->ctfi_archive;
- /* The loop keeps going when skip_parent is on as long as the member we find
- is the parent (i.e. at most two iterations, but possibly an early return if
- *all* we have is a parent). */
- do
- {
- if ((!wrapper->ctfi_is_archive) || (i->ctn_n >= le64toh (arc->ctfa_ndicts)))
- {
- ctf_next_destroy (i);
- *it = NULL;
- if (errp)
- *errp = ECTF_NEXT_END;
- return NULL;
- }
- modent = (ctf_archive_modent_t *) ((char *) arc
- + sizeof (struct ctf_archive));
- nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
- name_ = &nametbl[le64toh (modent[i->ctn_n].name_offset)];
- i->ctn_n++;
- }
- while (skip_parent && strcmp (name_, _CTF_SECTION) == 0);
- if (name)
- *name = name_;
- f = ctf_dict_open_cached ((ctf_archive_t *) wrapper, name_, errp);
- return f;
- }
- #ifdef HAVE_MMAP
- /* Map the header in. Only used on new, empty files. */
- static void *arc_mmap_header (int fd, size_t headersz)
- {
- void *hdr;
- if ((hdr = mmap (NULL, headersz, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
- 0)) == MAP_FAILED)
- return NULL;
- return hdr;
- }
- /* mmap() the whole file, for reading only. (Map it writably, but privately: we
- need to modify the region, but don't need anyone else to see the
- modifications.) */
- static void *arc_mmap_file (int fd, size_t size)
- {
- void *arc;
- if ((arc = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
- fd, 0)) == MAP_FAILED)
- return NULL;
- return arc;
- }
- /* Persist the header to disk. */
- static int arc_mmap_writeout (int fd _libctf_unused_, void *header,
- size_t headersz, const char **errmsg)
- {
- if (msync (header, headersz, MS_ASYNC) < 0)
- {
- if (errmsg)
- *errmsg = N_("arc_mmap_writeout(): cannot sync after writing "
- "to %s: %s");
- return -1;
- }
- return 0;
- }
- /* Unmap the region. */
- static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg)
- {
- if (munmap (header, headersz) < 0)
- {
- if (errmsg)
- *errmsg = N_("arc_mmap_munmap(): cannot unmap after writing "
- "to %s: %s");
- return -1;
- }
- return 0;
- }
- #else
- /* Map the header in. Only used on new, empty files. */
- static void *arc_mmap_header (int fd _libctf_unused_, size_t headersz)
- {
- void *hdr;
- if ((hdr = malloc (headersz)) == NULL)
- return NULL;
- return hdr;
- }
- /* Pull in the whole file, for reading only. We assume the current file
- position is at the start of the file. */
- static void *arc_mmap_file (int fd, size_t size)
- {
- char *data;
- if ((data = malloc (size)) == NULL)
- return NULL;
- if (ctf_pread (fd, data, size, 0) < 0)
- {
- free (data);
- return NULL;
- }
- return data;
- }
- /* Persist the header to disk. */
- static int arc_mmap_writeout (int fd, void *header, size_t headersz,
- const char **errmsg)
- {
- ssize_t len;
- size_t acc = 0;
- char *data = (char *) header;
- ssize_t count = headersz;
- if ((lseek (fd, 0, SEEK_SET)) < 0)
- {
- if (errmsg)
- *errmsg = N_("arc_mmap_writeout(): cannot seek while writing header to "
- "%s: %s");
- return -1;
- }
- while (headersz > 0)
- {
- if ((len = write (fd, data, count)) < 0)
- {
- if (errmsg)
- *errmsg = N_("arc_mmap_writeout(): cannot write header to %s: %s");
- return len;
- }
- if (len == EINTR)
- continue;
- acc += len;
- if (len == 0) /* EOF. */
- break;
- count -= len;
- data += len;
- }
- return 0;
- }
- /* Unmap the region. */
- static int arc_mmap_unmap (void *header, size_t headersz _libctf_unused_,
- const char **errmsg _libctf_unused_)
- {
- free (header);
- return 0;
- }
- #endif
|