diff options
-rw-r--r-- | include/ChangeLog | 14 | ||||
-rw-r--r-- | include/ctf-api.h | 30 | ||||
-rw-r--r-- | include/ctf.h | 47 | ||||
-rw-r--r-- | libctf/ChangeLog | 8 | ||||
-rw-r--r-- | libctf/ctf-archive.c | 756 | ||||
-rw-r--r-- | libctf/ctf-impl.h | 15 | ||||
-rw-r--r-- | libctf/ctf-open.c | 7 |
7 files changed, 877 insertions, 0 deletions
diff --git a/include/ChangeLog b/include/ChangeLog index 8b82efe..fb7bd88 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,5 +1,19 @@ 2019-05-28 Nick Alcock <nick.alcock@oracle.com> + * ctf.h (CTFA_MAGIC): New. + (struct ctf_archive): New. + (struct ctf_archive_modent): Likewise. + * ctf-api.h (ctf_archive_member_f): New. + (ctf_archive_raw_member_f): Likewise. + (ctf_arc_write): Likewise. + (ctf_arc_close): Likewise. + (ctf_arc_open_by_name): Likewise. + (ctf_archive_iter): Likewise. + (ctf_archive_raw_iter): Likewise. + (ctf_get_arc): Likewise. + +2019-05-28 Nick Alcock <nick.alcock@oracle.com> + * ctf-api.h (ctf_file_close): New declaration. (ctf_getdatasect): Likewise. (ctf_parent_file): Likewise. diff --git a/include/ctf-api.h b/include/ctf-api.h index d9eff0b..4cac635 100644 --- a/include/ctf-api.h +++ b/include/ctf-api.h @@ -198,13 +198,35 @@ enum #define CTF_ADD_NONROOT 0 /* Type only visible in nested scope. */ #define CTF_ADD_ROOT 1 /* Type visible at top-level scope. */ +/* These typedefs are used to define the signature for callback functions + that can be used with the iteration and visit functions below. */ + +typedef int ctf_archive_member_f (ctf_file_t *fp, const char *name, void *arg); +typedef int ctf_archive_raw_member_f (const char *name, const void *content, + size_t len, void *arg); + extern ctf_sect_t ctf_getdatasect (const ctf_file_t *); +extern ctf_archive_t *ctf_get_arc (const ctf_file_t *); +extern void ctf_arc_close (ctf_archive_t *); +extern ctf_file_t *ctf_arc_open_by_name (const ctf_archive_t *, + const char *, int *); +extern ctf_file_t *ctf_arc_open_by_name_sections (const ctf_archive_t *, + const ctf_sect_t *, + const ctf_sect_t *, + const char *, int *); + +/* The next functions return or close real CTF files, or write out CTF archives, + not opaque containers around either. */ + extern ctf_file_t *ctf_simple_open (const char *, size_t, const char *, size_t, size_t, const char *, size_t, int *); extern ctf_file_t *ctf_bufopen (const ctf_sect_t *, const ctf_sect_t *, const ctf_sect_t *, int *); extern void ctf_file_close (ctf_file_t *); +extern int ctf_arc_write (const char *, ctf_file_t **, size_t, + const char **, size_t); + extern ctf_file_t *ctf_parent_file (ctf_file_t *); extern const char *ctf_parent_name (ctf_file_t *); extern void ctf_parent_name_set (ctf_file_t *, const char *); @@ -218,6 +240,14 @@ extern void *ctf_getspecific (ctf_file_t *); extern int ctf_errno (ctf_file_t *); extern const char *ctf_errmsg (int); +extern int ctf_archive_iter (const ctf_archive_t *, ctf_archive_member_f *, + void *); +/* This function alone does not currently operate on CTF files masquerading + as archives, and returns -EINVAL: the raw data is no longer available. It is + expected to be used only by archiving tools, in any case, which have no need + to deal with non-archives at all. */ +extern int ctf_archive_raw_iter (const ctf_archive_t *, + ctf_archive_raw_member_f *, void *); extern ctf_id_t ctf_add_array (ctf_file_t *, uint32_t, const ctf_arinfo_t *); extern ctf_id_t ctf_add_const (ctf_file_t *, uint32_t, ctf_id_t); diff --git a/include/ctf.h b/include/ctf.h index 6580a21..2c3384f 100644 --- a/include/ctf.h +++ b/include/ctf.h @@ -510,6 +510,53 @@ typedef struct ctf_enum int cte_value; /* Value associated with this name. */ } ctf_enum_t; +/* The ctf_archive is a collection of ctf_file_t's stored together. The format + is suitable for mmap()ing: this control structure merely describes the + mmap()ed archive (and overlaps the first few bytes of it), hence the + greater care taken with integral types. All CTF files in an archive + must have the same data model. (This is not validated.) + + All integers in this structure are stored in little-endian byte order. + + The code relies on the fact that everything in this header is a uint64_t + and thus the header needs no padding (in particular, that no padding is + needed between ctfa_ctfs and the unnamed ctfa_archive_modent array + that follows it). + + This is *not* the same as the data structure returned by the ctf_arc_*() + functions: this is the low-level on-disk representation. */ + +#define CTFA_MAGIC 0x8b47f2a4d7623eeb /* Random. */ +struct ctf_archive +{ + /* Magic number. (In loaded files, overwritten with the file size + so ctf_arc_close() knows how much to munmap()). */ + uint64_t ctfa_magic; + + /* CTF data model. */ + uint64_t ctfa_model; + + /* Number of CTF files in the archive. */ + uint64_t ctfa_nfiles; + + /* Offset of the name table. */ + uint64_t ctfa_names; + + /* Offset of the CTF table. Each element starts with a size (a uint64_t + in network byte order) then a ctf_file_t of that size. */ + uint64_t ctfa_ctfs; +}; + +/* An array of ctfa_nnamed of this structure lies at + ctf_archive[ctf_archive->ctfa_modents] and gives the ctfa_ctfs or + ctfa_names-relative offsets of each name or ctf_file_t. */ + +typedef struct ctf_archive_modent +{ + uint64_t name_offset; + uint64_t ctf_offset; +} ctf_archive_modent_t; + #ifdef __cplusplus } #endif diff --git a/libctf/ChangeLog b/libctf/ChangeLog index 4a69c62..aa3fb3b 100644 --- a/libctf/ChangeLog +++ b/libctf/ChangeLog @@ -1,5 +1,13 @@ 2019-05-28 Nick Alcock <nick.alcock@oracle.com> + * ctf-archive.c: New. + * ctf-impl.h (ctf_archive_internal): New type. + (ctf_arc_open_internal): New declaration. + (ctf_arc_bufopen): Likewise. + (ctf_arc_close_internal): Likewise. + +2019-05-28 Nick Alcock <nick.alcock@oracle.com> + * ctf-open.c: New file. * swap.h: Likewise. diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c new file mode 100644 index 0000000..ab658fd --- /dev/null +++ b/libctf/ctf-archive.c @@ -0,0 +1,756 @@ +/* CTF archive files. + Copyright (C) 2019 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 <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_file_t * f, int fd, size_t threshold); +static ctf_file_t *ctf_arc_open_by_offset (const struct ctf_archive *arc, + const ctf_sect_t *symsect, + const ctf_sect_t *strsect, + size_t offset, 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); + +/* bsearch() internal state. */ +static __thread char *search_nametbl; + +/* Write out a CTF archive. The entries in CTF_FILES are referenced by name: + the names are passed in the names array, which must have CTF_FILES entries. + + Returns 0 on success, or an errno, or an ECTF_* value. */ +int +ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt, + const char **names, size_t threshold) +{ + const char *errmsg; + struct ctf_archive *archdr; + int fd; + 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 archive %s with %zi files\n", file, ctf_file_cnt); + + if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0) + { + errmsg = "ctf_arc_write(): cannot create %s: %s\n"; + goto err; + } + + /* 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_file_cnt * sizeof (uint64_t) * 2); + ctf_dprintf ("headersz is %zi\n", 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 = "ctf_arc_write(): cannot extend file while writing %s: %s\n"; + goto err_close; + } + + if (write (fd, &dummy, 1) < 0) + { + errmsg = "ctf_arc_write(): cannot extend file while writing %s: %s\n"; + goto err_close; + } + + if ((archdr = arc_mmap_header (fd, headersz)) == NULL) + { + errmsg = "ctf_arc_write(): Cannot mmap() %s: %s\n"; + goto err_close; + } + + /* Fill in everything we can, which is everything other than the name + table offset. */ + archdr->ctfa_magic = htole64 (CTFA_MAGIC); + archdr->ctfa_nfiles = htole64 (ctf_file_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_file_cnt > 0) + archdr->ctfa_model = htole64 (ctf_getmodel (ctf_files[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_nfiles); i++) + namesz += strlen (names[i]) + 1; + + nametbl = malloc (namesz); + if (nametbl == NULL) + { + errmsg = "Error writing named CTF to %s: %s\n"; + goto err_unmap; + } + + for (i = 0, namesz = 0, + modent = (ctf_archive_modent_t *) ((char *) archdr + + sizeof (struct ctf_archive)); + i < le64toh (archdr->ctfa_nfiles); i++) + { + off_t off; + + strcpy (&nametbl[namesz], names[i]); + + off = arc_write_one_ctf (ctf_files[i], fd, threshold); + ctf_dprintf ("Written %s, offset now %zi\n", names[i], off); + if ((off < 0) && (off > -ECTF_BASE)) + { + errmsg = "ctf_arc_write(): Cannot determine file " + "position while writing %s: %s"; + goto err_free; + } + if (off < 0) + { + errmsg = "ctf_arc_write(): Cannot write CTF file to %s: %s\n"; + errno = off * -1; + goto err_free; + } + + modent->name_offset = htole64 (namesz); + modent->ctf_offset = htole64 (off - ctf_startoffs); + namesz += strlen (names[i]) + 1; + modent++; + } + + qsort_r ((ctf_archive_modent_t *) ((char *) archdr + + sizeof (struct ctf_archive)), + le64toh (archdr->ctfa_nfiles), + sizeof (struct ctf_archive_modent), sort_modent_by_name, nametbl); + + /* Now the name table. */ + + if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0) + { + errmsg = "ctf_arc_write(): Cannot get current file position " + "in %s: %s\n"; + goto err_free; + } + archdr->ctfa_names = htole64 (nameoffs); + np = nametbl; + while (namesz > 0) + { + ssize_t len; + if ((len = write (fd, np, namesz)) < 0) + { + errmsg = "ctf_arc_write(): Cannot write name table in %s: %s\n"; + 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_unlink; + if (close (fd) < 0) + { + errmsg = "ctf_arc_write(): Cannot close after writing to %s: %s\n"; + goto err_unlink; + } + + return 0; + +err_free: + free (nametbl); +err_unmap: + arc_mmap_unmap (archdr, headersz, NULL); +err_close: + close (fd); +err_unlink: + unlink (file); +err: + ctf_dprintf (errmsg, file, errno < ECTF_BASE ? strerror (errno) : + ctf_errmsg (errno)); + return errno; +} + +/* 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_file_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_file_t * fp, int fd); + + 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() 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) +{ + const char *k = key; + const struct ctf_archive_modent *v = ent; + + return strcmp (k, &search_nametbl[le64toh (v->name_offset)]); +} + +/* A trivial wrapper: open a CTF archive, 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). */ +struct ctf_archive * +ctf_arc_bufopen (const void *buf, size_t size _libctf_unused_, int *errp) +{ + struct ctf_archive *arc = (struct ctf_archive *) buf; + + if (le64toh (arc->ctfa_magic) != CTFA_MAGIC) + { + if (errp) + *errp = ECTF_FMT; + return NULL; + } + return arc; +} + +/* 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 = "ctf_arc_open(): cannot open %s: %s\n"; + goto err; + } + if (fstat (fd, &s) < 0) + { + errmsg = "ctf_arc_open(): cannot stat %s: %s\n"; + goto err_close; + } + + if ((arc = arc_mmap_file (fd, s.st_size)) == NULL) + { + errmsg = "ctf_arc_open(): Cannot read in %s: %s\n"; + goto err_close; + } + + if (le64toh (arc->ctfa_magic) != CTFA_MAGIC) + { + errmsg = "ctf_arc_open(): 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_dprintf (errmsg, filename, errno < ECTF_BASE ? strerror (errno) : + ctf_errmsg (errno)); + 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) + ctf_arc_close_internal (arc->ctfi_archive); + else + ctf_file_close (arc->ctfi_file); + free ((void *) arc->ctfi_symsect.cts_data); + free ((void *) arc->ctfi_strsect.cts_data); + free (arc->ctfi_data); + free (arc); +} + +/* Return the ctf_file_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_file_t * +ctf_arc_open_by_name_internal (const struct ctf_archive *arc, + const ctf_sect_t *symsect, + const ctf_sect_t *strsect, + const char *name, int *errp) +{ + struct ctf_archive_modent *modent; + + if (name == NULL) + name = _CTF_SECTION; /* The default name. */ + + ctf_dprintf ("ctf_arc_open_by_name(%s): opening\n", name); + + modent = (ctf_archive_modent_t *) ((char *) arc + + sizeof (struct ctf_archive)); + + search_nametbl = (char *) arc + le64toh (arc->ctfa_names); + modent = bsearch (name, modent, le64toh (arc->ctfa_nfiles), + sizeof (struct ctf_archive_modent), + search_modent_by_name); + + /* 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_arc_open_by_offset (arc, symsect, strsect, + le64toh (modent->ctf_offset), errp); +} + +/* Return the ctf_file_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_file_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) +{ + if (arc->ctfi_is_archive) + { + ctf_file_t *ret; + ret = ctf_arc_open_by_name_internal (arc->ctfi_archive, symsect, strsect, + name, errp); + if (ret) + ret->ctf_archive = (ctf_archive_t *) arc; + return ret; + } + + if ((name != NULL) && (strcmp (name, _CTF_SECTION) != 0)) + { + if (errp) + *errp = ECTF_ARNNAME; + return NULL; + } + arc->ctfi_file->ctf_archive = (ctf_archive_t *) arc; + + /* Bump the refcount so that the user can ctf_file_close() it. */ + arc->ctfi_file->ctf_refcnt++; + return arc->ctfi_file; +} + +/* Return the ctf_file_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_file_t * +ctf_arc_open_by_name (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_arc_open_by_name_sections (arc, symsect, strsect, name, errp); +} + +/* Return the ctf_file_t at the given ctfa_ctfs-relative offset, or NULL if + none, setting 'err' if non-NULL. */ +static ctf_file_t * +ctf_arc_open_by_offset (const struct ctf_archive *arc, + const ctf_sect_t *symsect, + const ctf_sect_t *strsect, size_t offset, + int *errp) +{ + ctf_sect_t ctfsect; + ctf_file_t *fp; + + ctf_dprintf ("ctf_arc_open_by_offset(%zi): opening\n", offset); + + bzero (&ctfsect, sizeof (ctf_sect_t)); + + offset += le64toh (arc->ctfa_ctfs); + + ctfsect.cts_name = _CTF_SECTION; + ctfsect.cts_type = SHT_PROGBITS; + ctfsect.cts_flags = SHF_ALLOC; + ctfsect.cts_size = le64toh (*((uint64_t *) ((char *) arc + offset))); + ctfsect.cts_entsize = 1; + ctfsect.cts_offset = 0; + 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)); + return fp; +} + +/* 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_nfiles); 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. We pass all CTF files in turn to + the specified callback function. */ +static int +ctf_archive_iter_internal (const ctf_archive_t *wrapper, + const struct ctf_archive *arc, + const ctf_sect_t *symsect, + const ctf_sect_t *strsect, + ctf_archive_member_f *func, void *data) +{ + int rc; + size_t i; + ctf_file_t *f; + 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_nfiles); i++) + { + const char *name; + + name = &nametbl[le64toh (modent[i].name_offset)]; + if ((f = ctf_arc_open_by_name_internal (arc, symsect, strsect, + name, &rc)) == NULL) + return rc; + + f->ctf_archive = (ctf_archive_t *) wrapper; + if ((rc = func (f, name, data)) != 0) + { + ctf_file_close (f); + return rc; + } + + ctf_file_close (f); + } + return 0; +} + +/* 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) +{ + 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; + + if (arc->ctfi_is_archive) + return ctf_archive_iter_internal (arc, arc->ctfi_archive, symsect, strsect, + func, data); + + return func (arc->ctfi_file, _CTF_SECTION, data); +} + +#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 = "arc_mmap_writeout(): Cannot sync after writing to %s: %s\n"; + 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 = "arc_mmap_munmap(): Cannot unmap after writing to %s: %s\n"; + return -1; + } + return 0; +} +#else +/* Map the header in. Only used on new, empty files. */ +static void *arc_mmap_header (int fd, 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 = "arc_mmap_writeout(): Cannot seek while writing header to " + "%s: %s\n"; + return -1; + } + + while (headersz > 0) + { + if ((len = write (fd, data, count)) < 0) + { + if (errmsg) + *errmsg = "arc_mmap_writeout(): Cannot write header to %s: %s\n"; + 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 diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index ef4221a..898be8c 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -230,6 +230,18 @@ struct ctf_file void *ctf_specific; /* Data for ctf_get/setspecific(). */ }; +/* An abstraction over both a ctf_file_t and a ctf_archive_t. */ + +struct ctf_archive_internal +{ + int ctfi_is_archive; + ctf_file_t *ctfi_file; + struct ctf_archive *ctfi_archive; + ctf_sect_t ctfi_symsect; + ctf_sect_t ctfi_strsect; + void *ctfi_data; +}; + /* Return x rounded up to an alignment boundary. eg, P2ROUNDUP(0x1234, 0x100) == 0x1300 (0x13*align) eg, P2ROUNDUP(0x5600, 0x100) == 0x5600 (0x56*align) */ @@ -310,6 +322,9 @@ extern ctf_dvdef_t *ctf_dvd_lookup (const ctf_file_t *, const char *); extern const char *ctf_strraw (ctf_file_t *, uint32_t); extern const char *ctf_strptr (ctf_file_t *, uint32_t); +extern struct ctf_archive *ctf_arc_open_internal (const char *, int *); +extern struct ctf_archive *ctf_arc_bufopen (const void *, size_t, int *); +extern void ctf_arc_close_internal (struct ctf_archive *); extern void *ctf_set_open_errno (int *, int); extern long ctf_set_errno (ctf_file_t *, int); diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index fcc0c9d..8c6294a 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -1572,6 +1572,13 @@ ctf_file_close (ctf_file_t *fp) ctf_free (fp); } +/* Get the CTF archive from which this ctf_file_t is derived. */ +ctf_archive_t * +ctf_get_arc (const ctf_file_t *fp) +{ + return fp->ctf_archive; +} + /* Return the ctfsect out of the core ctf_impl. Useful for freeing the ctfsect's data * after ctf_file_close(), which is why we return the actual structure, not a pointer to it, since that is likely to become a pointer to |