aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ChangeLog14
-rw-r--r--include/ctf-api.h30
-rw-r--r--include/ctf.h47
-rw-r--r--libctf/ChangeLog8
-rw-r--r--libctf/ctf-archive.c756
-rw-r--r--libctf/ctf-impl.h15
-rw-r--r--libctf/ctf-open.c7
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