xref: /netbsd-src/external/gpl3/gdb.old/dist/libctf/ctf-archive.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
17d62b00eSchristos /* CTF archive files.
2*6881a400Schristos    Copyright (C) 2019-2022 Free Software Foundation, Inc.
37d62b00eSchristos 
47d62b00eSchristos    This file is part of libctf.
57d62b00eSchristos 
67d62b00eSchristos    libctf is free software; you can redistribute it and/or modify it under
77d62b00eSchristos    the terms of the GNU General Public License as published by the Free
87d62b00eSchristos    Software Foundation; either version 3, or (at your option) any later
97d62b00eSchristos    version.
107d62b00eSchristos 
117d62b00eSchristos    This program is distributed in the hope that it will be useful, but
127d62b00eSchristos    WITHOUT ANY WARRANTY; without even the implied warranty of
137d62b00eSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
147d62b00eSchristos    See the GNU General Public License for more details.
157d62b00eSchristos 
167d62b00eSchristos    You should have received a copy of the GNU General Public License
177d62b00eSchristos    along with this program; see the file COPYING.  If not see
187d62b00eSchristos    <http://www.gnu.org/licenses/>.  */
197d62b00eSchristos 
207d62b00eSchristos #include <ctf-impl.h>
217d62b00eSchristos #include <sys/types.h>
227d62b00eSchristos #include <sys/stat.h>
237d62b00eSchristos #include <elf.h>
247d62b00eSchristos #include "ctf-endian.h"
257d62b00eSchristos #include <errno.h>
267d62b00eSchristos #include <fcntl.h>
277d62b00eSchristos #include <stdio.h>
287d62b00eSchristos #include <string.h>
297d62b00eSchristos #include <unistd.h>
307d62b00eSchristos 
317d62b00eSchristos #ifdef HAVE_MMAP
327d62b00eSchristos #include <sys/mman.h>
337d62b00eSchristos #endif
347d62b00eSchristos 
35*6881a400Schristos static off_t arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold);
36*6881a400Schristos static ctf_dict_t *ctf_dict_open_by_offset (const struct ctf_archive *arc,
377d62b00eSchristos 					    const ctf_sect_t *symsect,
387d62b00eSchristos 					    const ctf_sect_t *strsect,
39*6881a400Schristos 					    size_t offset, int little_endian,
40*6881a400Schristos 					    int *errp);
417d62b00eSchristos static int sort_modent_by_name (const void *one, const void *two, void *n);
427d62b00eSchristos static void *arc_mmap_header (int fd, size_t headersz);
437d62b00eSchristos static void *arc_mmap_file (int fd, size_t size);
447d62b00eSchristos static int arc_mmap_writeout (int fd, void *header, size_t headersz,
457d62b00eSchristos 			      const char **errmsg);
467d62b00eSchristos static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg);
47*6881a400Schristos static void ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp);
48*6881a400Schristos 
49*6881a400Schristos /* Flag to indicate "symbol not present" in ctf_archive_internal.ctfi_symdicts
50*6881a400Schristos    and ctfi_symnamedicts.  Never initialized.  */
51*6881a400Schristos static ctf_dict_t enosym;
527d62b00eSchristos 
537d62b00eSchristos /* Write out a CTF archive to the start of the file referenced by the passed-in
54*6881a400Schristos    fd.  The entries in CTF_DICTS are referenced by name: the names are passed in
55*6881a400Schristos    the names array, which must have CTF_DICTS entries.
567d62b00eSchristos 
577d62b00eSchristos    Returns 0 on success, or an errno, or an ECTF_* value.  */
587d62b00eSchristos int
59*6881a400Schristos ctf_arc_write_fd (int fd, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
607d62b00eSchristos 		  const char **names, size_t threshold)
617d62b00eSchristos {
627d62b00eSchristos   const char *errmsg;
637d62b00eSchristos   struct ctf_archive *archdr;
647d62b00eSchristos   size_t i;
657d62b00eSchristos   char dummy = 0;
667d62b00eSchristos   size_t headersz;
677d62b00eSchristos   ssize_t namesz;
687d62b00eSchristos   size_t ctf_startoffs;		/* Start of the section we are working over.  */
697d62b00eSchristos   char *nametbl = NULL;		/* The name table.  */
707d62b00eSchristos   char *np;
717d62b00eSchristos   off_t nameoffs;
727d62b00eSchristos   struct ctf_archive_modent *modent;
737d62b00eSchristos 
747d62b00eSchristos   ctf_dprintf ("Writing CTF archive with %lu files\n",
75*6881a400Schristos 	       (unsigned long) ctf_dict_cnt);
767d62b00eSchristos 
777d62b00eSchristos   /* Figure out the size of the mmap()ed header, including the
787d62b00eSchristos      ctf_archive_modent array.  We assume that all of this needs no
797d62b00eSchristos      padding: a likely assumption, given that it's all made up of
807d62b00eSchristos      uint64_t's.  */
817d62b00eSchristos   headersz = sizeof (struct ctf_archive)
82*6881a400Schristos     + (ctf_dict_cnt * sizeof (uint64_t) * 2);
837d62b00eSchristos   ctf_dprintf ("headersz is %lu\n", (unsigned long) headersz);
847d62b00eSchristos 
857d62b00eSchristos   /* From now on we work in two pieces: an mmap()ed region from zero up to the
867d62b00eSchristos      headersz, and a region updated via write() starting after that, containing
877d62b00eSchristos      all the tables.  Platforms that do not support mmap() just use write().  */
887d62b00eSchristos   ctf_startoffs = headersz;
897d62b00eSchristos   if (lseek (fd, ctf_startoffs - 1, SEEK_SET) < 0)
907d62b00eSchristos     {
917d62b00eSchristos       errmsg = N_("ctf_arc_write(): cannot extend file while writing");
927d62b00eSchristos       goto err;
937d62b00eSchristos     }
947d62b00eSchristos 
957d62b00eSchristos   if (write (fd, &dummy, 1) < 0)
967d62b00eSchristos     {
977d62b00eSchristos       errmsg = N_("ctf_arc_write(): cannot extend file while writing");
987d62b00eSchristos       goto err;
997d62b00eSchristos     }
1007d62b00eSchristos 
1017d62b00eSchristos   if ((archdr = arc_mmap_header (fd, headersz)) == NULL)
1027d62b00eSchristos     {
1037d62b00eSchristos       errmsg = N_("ctf_arc_write(): cannot mmap");
1047d62b00eSchristos       goto err;
1057d62b00eSchristos     }
1067d62b00eSchristos 
1077d62b00eSchristos   /* Fill in everything we can, which is everything other than the name
1087d62b00eSchristos      table offset.  */
1097d62b00eSchristos   archdr->ctfa_magic = htole64 (CTFA_MAGIC);
110*6881a400Schristos   archdr->ctfa_ndicts = htole64 (ctf_dict_cnt);
1117d62b00eSchristos   archdr->ctfa_ctfs = htole64 (ctf_startoffs);
1127d62b00eSchristos 
1137d62b00eSchristos   /* We could validate that all CTF files have the same data model, but
1147d62b00eSchristos      since any reasonable construction process will be building things of
1157d62b00eSchristos      only one bitness anyway, this is pretty pointless, so just use the
1167d62b00eSchristos      model of the first CTF file for all of them.  (It *is* valid to
1177d62b00eSchristos      create an empty archive: the value of ctfa_model is irrelevant in
1187d62b00eSchristos      this case, but we must be sure not to dereference uninitialized
1197d62b00eSchristos      memory.)  */
1207d62b00eSchristos 
121*6881a400Schristos   if (ctf_dict_cnt > 0)
122*6881a400Schristos     archdr->ctfa_model = htole64 (ctf_getmodel (ctf_dicts[0]));
1237d62b00eSchristos 
1247d62b00eSchristos   /* Now write out the CTFs: ctf_archive_modent array via the mapping,
1257d62b00eSchristos      ctfs via write().  The names themselves have not been written yet: we
1267d62b00eSchristos      track them in a local strtab until the time is right, and sort the
1277d62b00eSchristos      modents array after construction.
1287d62b00eSchristos 
1297d62b00eSchristos     The name table is not sorted.  */
1307d62b00eSchristos 
131*6881a400Schristos   for (i = 0, namesz = 0; i < le64toh (archdr->ctfa_ndicts); i++)
1327d62b00eSchristos     namesz += strlen (names[i]) + 1;
1337d62b00eSchristos 
1347d62b00eSchristos   nametbl = malloc (namesz);
1357d62b00eSchristos   if (nametbl == NULL)
1367d62b00eSchristos     {
1377d62b00eSchristos       errmsg = N_("ctf_arc_write(): error writing named CTF to archive");
1387d62b00eSchristos       goto err_unmap;
1397d62b00eSchristos     }
1407d62b00eSchristos 
1417d62b00eSchristos   for (i = 0, namesz = 0,
1427d62b00eSchristos        modent = (ctf_archive_modent_t *) ((char *) archdr
1437d62b00eSchristos 					  + sizeof (struct ctf_archive));
144*6881a400Schristos        i < le64toh (archdr->ctfa_ndicts); i++)
1457d62b00eSchristos     {
1467d62b00eSchristos       off_t off;
1477d62b00eSchristos 
1487d62b00eSchristos       strcpy (&nametbl[namesz], names[i]);
1497d62b00eSchristos 
150*6881a400Schristos       off = arc_write_one_ctf (ctf_dicts[i], fd, threshold);
1517d62b00eSchristos       if ((off < 0) && (off > -ECTF_BASE))
1527d62b00eSchristos 	{
1537d62b00eSchristos 	  errmsg = N_("ctf_arc_write(): cannot determine file "
1547d62b00eSchristos 		      "position while writing to archive");
1557d62b00eSchristos 	  goto err_free;
1567d62b00eSchristos 	}
1577d62b00eSchristos       if (off < 0)
1587d62b00eSchristos 	{
1597d62b00eSchristos 	  errmsg = N_("ctf_arc_write(): cannot write CTF file to archive");
1607d62b00eSchristos 	  errno = off * -1;
1617d62b00eSchristos 	  goto err_free;
1627d62b00eSchristos 	}
1637d62b00eSchristos 
1647d62b00eSchristos       modent->name_offset = htole64 (namesz);
1657d62b00eSchristos       modent->ctf_offset = htole64 (off - ctf_startoffs);
1667d62b00eSchristos       namesz += strlen (names[i]) + 1;
1677d62b00eSchristos       modent++;
1687d62b00eSchristos     }
1697d62b00eSchristos 
1707d62b00eSchristos   ctf_qsort_r ((ctf_archive_modent_t *) ((char *) archdr
1717d62b00eSchristos 					 + sizeof (struct ctf_archive)),
172*6881a400Schristos 	       le64toh (archdr->ctfa_ndicts),
1737d62b00eSchristos 	       sizeof (struct ctf_archive_modent), sort_modent_by_name,
1747d62b00eSchristos 	       nametbl);
1757d62b00eSchristos 
1767d62b00eSchristos    /* Now the name table.  */
1777d62b00eSchristos 
1787d62b00eSchristos   if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0)
1797d62b00eSchristos     {
1807d62b00eSchristos       errmsg = N_("ctf_arc_write(): cannot get current file position "
1817d62b00eSchristos 		  "in archive");
1827d62b00eSchristos       goto err_free;
1837d62b00eSchristos     }
1847d62b00eSchristos   archdr->ctfa_names = htole64 (nameoffs);
1857d62b00eSchristos   np = nametbl;
1867d62b00eSchristos   while (namesz > 0)
1877d62b00eSchristos     {
1887d62b00eSchristos       ssize_t len;
1897d62b00eSchristos       if ((len = write (fd, np, namesz)) < 0)
1907d62b00eSchristos 	{
1917d62b00eSchristos 	  errmsg = N_("ctf_arc_write(): cannot write name table to archive");
1927d62b00eSchristos 	  goto err_free;
1937d62b00eSchristos 	}
1947d62b00eSchristos       namesz -= len;
1957d62b00eSchristos       np += len;
1967d62b00eSchristos     }
1977d62b00eSchristos   free (nametbl);
1987d62b00eSchristos 
1997d62b00eSchristos   if (arc_mmap_writeout (fd, archdr, headersz, &errmsg) < 0)
2007d62b00eSchristos     goto err_unmap;
2017d62b00eSchristos   if (arc_mmap_unmap (archdr, headersz, &errmsg) < 0)
2027d62b00eSchristos     goto err;
2037d62b00eSchristos   return 0;
2047d62b00eSchristos 
2057d62b00eSchristos err_free:
2067d62b00eSchristos   free (nametbl);
2077d62b00eSchristos err_unmap:
2087d62b00eSchristos   arc_mmap_unmap (archdr, headersz, NULL);
2097d62b00eSchristos err:
2107d62b00eSchristos   /* We report errors into the first file in the archive, if any: if this is a
2117d62b00eSchristos      zero-file archive, put it in the open-errors stream for lack of anywhere
2127d62b00eSchristos      else for it to go.  */
213*6881a400Schristos   ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno, "%s",
2147d62b00eSchristos 		gettext (errmsg));
2157d62b00eSchristos   return errno;
2167d62b00eSchristos }
2177d62b00eSchristos 
218*6881a400Schristos /* Write out a CTF archive.  The entries in CTF_DICTS are referenced by name:
219*6881a400Schristos    the names are passed in the names array, which must have CTF_DICTS entries.
2207d62b00eSchristos 
2217d62b00eSchristos    If the filename is NULL, create a temporary file and return a pointer to it.
2227d62b00eSchristos 
2237d62b00eSchristos    Returns 0 on success, or an errno, or an ECTF_* value.  */
2247d62b00eSchristos int
225*6881a400Schristos ctf_arc_write (const char *file, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
2267d62b00eSchristos 	       const char **names, size_t threshold)
2277d62b00eSchristos {
2287d62b00eSchristos   int err;
2297d62b00eSchristos   int fd;
2307d62b00eSchristos 
2317d62b00eSchristos   if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0)
2327d62b00eSchristos     {
233*6881a400Schristos       ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
2347d62b00eSchristos 		    _("ctf_arc_write(): cannot create %s"), file);
2357d62b00eSchristos       return errno;
2367d62b00eSchristos     }
2377d62b00eSchristos 
238*6881a400Schristos   err = ctf_arc_write_fd (fd, ctf_dicts, ctf_dict_cnt, names, threshold);
2397d62b00eSchristos   if (err)
2407d62b00eSchristos     goto err_close;
2417d62b00eSchristos 
2427d62b00eSchristos   if ((err = close (fd)) < 0)
243*6881a400Schristos     ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
2447d62b00eSchristos 		  _("ctf_arc_write(): cannot close after writing to archive"));
2457d62b00eSchristos   goto err;
2467d62b00eSchristos 
2477d62b00eSchristos  err_close:
2487d62b00eSchristos   (void) close (fd);
2497d62b00eSchristos  err:
2507d62b00eSchristos   if (err < 0)
2517d62b00eSchristos     unlink (file);
2527d62b00eSchristos 
2537d62b00eSchristos   return err;
2547d62b00eSchristos }
2557d62b00eSchristos 
2567d62b00eSchristos /* Write one CTF file out.  Return the file position of the written file (or
2577d62b00eSchristos    rather, of the file-size uint64_t that precedes it): negative return is a
2587d62b00eSchristos    negative errno or ctf_errno value.  On error, the file position may no longer
2597d62b00eSchristos    be at the end of the file.  */
2607d62b00eSchristos static off_t
261*6881a400Schristos arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold)
2627d62b00eSchristos {
2637d62b00eSchristos   off_t off, end_off;
2647d62b00eSchristos   uint64_t ctfsz = 0;
2657d62b00eSchristos   char *ctfszp;
2667d62b00eSchristos   size_t ctfsz_len;
267*6881a400Schristos   int (*writefn) (ctf_dict_t * fp, int fd);
2687d62b00eSchristos 
2697d62b00eSchristos   if (ctf_serialize (f) < 0)
2707d62b00eSchristos     return f->ctf_errno * -1;
2717d62b00eSchristos 
2727d62b00eSchristos   if ((off = lseek (fd, 0, SEEK_CUR)) < 0)
2737d62b00eSchristos     return errno * -1;
2747d62b00eSchristos 
2757d62b00eSchristos   if (f->ctf_size > threshold)
2767d62b00eSchristos     writefn = ctf_compress_write;
2777d62b00eSchristos   else
2787d62b00eSchristos     writefn = ctf_write;
2797d62b00eSchristos 
2807d62b00eSchristos   /* This zero-write turns into the size in a moment. */
2817d62b00eSchristos   ctfsz_len = sizeof (ctfsz);
2827d62b00eSchristos   ctfszp = (char *) &ctfsz;
2837d62b00eSchristos   while (ctfsz_len > 0)
2847d62b00eSchristos     {
2857d62b00eSchristos       ssize_t writelen = write (fd, ctfszp, ctfsz_len);
2867d62b00eSchristos       if (writelen < 0)
2877d62b00eSchristos 	return errno * -1;
2887d62b00eSchristos       ctfsz_len -= writelen;
2897d62b00eSchristos       ctfszp += writelen;
2907d62b00eSchristos     }
2917d62b00eSchristos 
2927d62b00eSchristos   if (writefn (f, fd) != 0)
2937d62b00eSchristos     return f->ctf_errno * -1;
2947d62b00eSchristos 
2957d62b00eSchristos   if ((end_off = lseek (fd, 0, SEEK_CUR)) < 0)
2967d62b00eSchristos     return errno * -1;
2977d62b00eSchristos   ctfsz = htole64 (end_off - off);
2987d62b00eSchristos 
2997d62b00eSchristos   if ((lseek (fd, off, SEEK_SET)) < 0)
3007d62b00eSchristos     return errno * -1;
3017d62b00eSchristos 
3027d62b00eSchristos   /* ... here.  */
3037d62b00eSchristos   ctfsz_len = sizeof (ctfsz);
3047d62b00eSchristos   ctfszp = (char *) &ctfsz;
3057d62b00eSchristos   while (ctfsz_len > 0)
3067d62b00eSchristos     {
3077d62b00eSchristos       ssize_t writelen = write (fd, ctfszp, ctfsz_len);
3087d62b00eSchristos       if (writelen < 0)
3097d62b00eSchristos 	return errno * -1;
3107d62b00eSchristos       ctfsz_len -= writelen;
3117d62b00eSchristos       ctfszp += writelen;
3127d62b00eSchristos     }
3137d62b00eSchristos 
3147d62b00eSchristos   end_off = LCTF_ALIGN_OFFS (end_off, 8);
3157d62b00eSchristos   if ((lseek (fd, end_off, SEEK_SET)) < 0)
3167d62b00eSchristos     return errno * -1;
3177d62b00eSchristos 
3187d62b00eSchristos   return off;
3197d62b00eSchristos }
3207d62b00eSchristos 
3217d62b00eSchristos /* qsort() function to sort the array of struct ctf_archive_modents into
3227d62b00eSchristos    ascending name order.  */
3237d62b00eSchristos static int
3247d62b00eSchristos sort_modent_by_name (const void *one, const void *two, void *n)
3257d62b00eSchristos {
3267d62b00eSchristos   const struct ctf_archive_modent *a = one;
3277d62b00eSchristos   const struct ctf_archive_modent *b = two;
3287d62b00eSchristos   char *nametbl = n;
3297d62b00eSchristos 
3307d62b00eSchristos   return strcmp (&nametbl[le64toh (a->name_offset)],
3317d62b00eSchristos 		 &nametbl[le64toh (b->name_offset)]);
3327d62b00eSchristos }
3337d62b00eSchristos 
3347d62b00eSchristos /* bsearch_r() function to search for a given name in the sorted array of struct
3357d62b00eSchristos    ctf_archive_modents.  */
3367d62b00eSchristos static int
3377d62b00eSchristos search_modent_by_name (const void *key, const void *ent, void *arg)
3387d62b00eSchristos {
3397d62b00eSchristos   const char *k = key;
3407d62b00eSchristos   const struct ctf_archive_modent *v = ent;
3417d62b00eSchristos   const char *search_nametbl = arg;
3427d62b00eSchristos 
3437d62b00eSchristos   return strcmp (k, &search_nametbl[le64toh (v->name_offset)]);
3447d62b00eSchristos }
3457d62b00eSchristos 
3467d62b00eSchristos /* Make a new struct ctf_archive_internal wrapper for a ctf_archive or a
347*6881a400Schristos    ctf_dict.  Closes ARC and/or FP on error.  Arrange to free the SYMSECT or
3487d62b00eSchristos    STRSECT, as needed, on close.  Possibly do not unmap on close.  */
3497d62b00eSchristos 
3507d62b00eSchristos struct ctf_archive_internal *
3517d62b00eSchristos ctf_new_archive_internal (int is_archive, int unmap_on_close,
3527d62b00eSchristos 			  struct ctf_archive *arc,
353*6881a400Schristos 			  ctf_dict_t *fp, const ctf_sect_t *symsect,
3547d62b00eSchristos 			  const ctf_sect_t *strsect,
3557d62b00eSchristos 			  int *errp)
3567d62b00eSchristos {
3577d62b00eSchristos   struct ctf_archive_internal *arci;
3587d62b00eSchristos 
3597d62b00eSchristos   if ((arci = calloc (1, sizeof (struct ctf_archive_internal))) == NULL)
3607d62b00eSchristos     {
3617d62b00eSchristos       if (is_archive)
3627d62b00eSchristos 	{
3637d62b00eSchristos 	  if (unmap_on_close)
3647d62b00eSchristos 	    ctf_arc_close_internal (arc);
3657d62b00eSchristos 	}
3667d62b00eSchristos       else
367*6881a400Schristos 	ctf_dict_close (fp);
3687d62b00eSchristos       return (ctf_set_open_errno (errp, errno));
3697d62b00eSchristos     }
3707d62b00eSchristos   arci->ctfi_is_archive = is_archive;
3717d62b00eSchristos   if (is_archive)
3727d62b00eSchristos     arci->ctfi_archive = arc;
3737d62b00eSchristos   else
374*6881a400Schristos     arci->ctfi_dict = fp;
3757d62b00eSchristos   if (symsect)
3767d62b00eSchristos      memcpy (&arci->ctfi_symsect, symsect, sizeof (struct ctf_sect));
3777d62b00eSchristos   if (strsect)
3787d62b00eSchristos      memcpy (&arci->ctfi_strsect, strsect, sizeof (struct ctf_sect));
3797d62b00eSchristos   arci->ctfi_free_symsect = 0;
3807d62b00eSchristos   arci->ctfi_free_strsect = 0;
3817d62b00eSchristos   arci->ctfi_unmap_on_close = unmap_on_close;
382*6881a400Schristos   arci->ctfi_symsect_little_endian = -1;
3837d62b00eSchristos 
3847d62b00eSchristos   return arci;
3857d62b00eSchristos }
3867d62b00eSchristos 
387*6881a400Schristos /* Set the symbol-table endianness of an archive (defaulting the symtab
388*6881a400Schristos    endianness of all ctf_file_t's opened from that archive).  */
389*6881a400Schristos void
390*6881a400Schristos ctf_arc_symsect_endianness (ctf_archive_t *arc, int little_endian)
391*6881a400Schristos {
392*6881a400Schristos   arc->ctfi_symsect_little_endian = !!little_endian;
393*6881a400Schristos   if (!arc->ctfi_is_archive)
394*6881a400Schristos     ctf_symsect_endianness (arc->ctfi_dict, arc->ctfi_symsect_little_endian);
395*6881a400Schristos }
396*6881a400Schristos 
397*6881a400Schristos /* Get the CTF preamble from data in a buffer, which may be either an archive or
398*6881a400Schristos    a CTF dict.  If multiple dicts are present in an archive, the preamble comes
399*6881a400Schristos    from an arbitrary dict.  The preamble is a pointer into the ctfsect passed
400*6881a400Schristos    in.  */
401*6881a400Schristos 
402*6881a400Schristos const ctf_preamble_t *
403*6881a400Schristos ctf_arc_bufpreamble (const ctf_sect_t *ctfsect)
404*6881a400Schristos {
405*6881a400Schristos   if (ctfsect->cts_size > sizeof (uint64_t) &&
406*6881a400Schristos       (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
407*6881a400Schristos     {
408*6881a400Schristos       struct ctf_archive *arc = (struct ctf_archive *) ctfsect->cts_data;
409*6881a400Schristos       return (const ctf_preamble_t *) ((char *) arc + le64toh (arc->ctfa_ctfs)
410*6881a400Schristos 				       + sizeof (uint64_t));
411*6881a400Schristos     }
412*6881a400Schristos   else
413*6881a400Schristos     return (const ctf_preamble_t *) ctfsect->cts_data;
414*6881a400Schristos }
415*6881a400Schristos 
4167d62b00eSchristos /* Open a CTF archive or dictionary from data in a buffer (which the caller must
4177d62b00eSchristos    preserve until ctf_arc_close() time).  Returns the archive, or NULL and an
4187d62b00eSchristos    error in *err (if not NULL).  */
4197d62b00eSchristos ctf_archive_t *
4207d62b00eSchristos ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
4217d62b00eSchristos 		 const ctf_sect_t *strsect, int *errp)
4227d62b00eSchristos {
4237d62b00eSchristos   struct ctf_archive *arc = NULL;
4247d62b00eSchristos   int is_archive;
425*6881a400Schristos   ctf_dict_t *fp = NULL;
4267d62b00eSchristos 
4277d62b00eSchristos   if (ctfsect->cts_size > sizeof (uint64_t) &&
4287d62b00eSchristos       (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
4297d62b00eSchristos     {
4307d62b00eSchristos       /* The archive is mmappable, so this operation is trivial.
4317d62b00eSchristos 
4327d62b00eSchristos 	 This buffer is nonmodifiable, so the trick involving mmapping only part
4337d62b00eSchristos 	 of it and storing the length in the magic number is not applicable: so
4347d62b00eSchristos 	 record this fact in the archive-wrapper header.  (We cannot record it
4357d62b00eSchristos 	 in the archive, because the archive may very well be a read-only
4367d62b00eSchristos 	 mapping.)  */
4377d62b00eSchristos 
4387d62b00eSchristos       is_archive = 1;
4397d62b00eSchristos       arc = (struct ctf_archive *) ctfsect->cts_data;
4407d62b00eSchristos     }
4417d62b00eSchristos   else
4427d62b00eSchristos     {
4437d62b00eSchristos       is_archive = 0;
4447d62b00eSchristos       if ((fp = ctf_bufopen (ctfsect, symsect, strsect, errp)) == NULL)
4457d62b00eSchristos 	{
4467d62b00eSchristos 	  ctf_err_warn (NULL, 0, *errp, _("ctf_arc_bufopen(): cannot open CTF"));
4477d62b00eSchristos 	  return NULL;
4487d62b00eSchristos 	}
4497d62b00eSchristos     }
4507d62b00eSchristos   return ctf_new_archive_internal (is_archive, 0, arc, fp, symsect, strsect,
4517d62b00eSchristos 				   errp);
4527d62b00eSchristos }
4537d62b00eSchristos 
4547d62b00eSchristos /* Open a CTF archive.  Returns the archive, or NULL and an error in *err (if
4557d62b00eSchristos    not NULL).  */
4567d62b00eSchristos struct ctf_archive *
4577d62b00eSchristos ctf_arc_open_internal (const char *filename, int *errp)
4587d62b00eSchristos {
4597d62b00eSchristos   const char *errmsg;
4607d62b00eSchristos   int fd;
4617d62b00eSchristos   struct stat s;
4627d62b00eSchristos   struct ctf_archive *arc;		/* (Actually the whole file.)  */
4637d62b00eSchristos 
4647d62b00eSchristos   libctf_init_debug();
4657d62b00eSchristos   if ((fd = open (filename, O_RDONLY)) < 0)
4667d62b00eSchristos     {
4677d62b00eSchristos       errmsg = N_("ctf_arc_open(): cannot open %s");
4687d62b00eSchristos       goto err;
4697d62b00eSchristos     }
4707d62b00eSchristos   if (fstat (fd, &s) < 0)
4717d62b00eSchristos     {
4727d62b00eSchristos       errmsg = N_("ctf_arc_open(): cannot stat %s");
4737d62b00eSchristos       goto err_close;
4747d62b00eSchristos     }
4757d62b00eSchristos 
4767d62b00eSchristos   if ((arc = arc_mmap_file (fd, s.st_size)) == NULL)
4777d62b00eSchristos     {
4787d62b00eSchristos       errmsg = N_("ctf_arc_open(): cannot read in %s");
4797d62b00eSchristos       goto err_close;
4807d62b00eSchristos     }
4817d62b00eSchristos 
4827d62b00eSchristos   if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
4837d62b00eSchristos     {
4847d62b00eSchristos       errmsg = N_("ctf_arc_open(): %s: invalid magic number");
4857d62b00eSchristos       errno = ECTF_FMT;
4867d62b00eSchristos       goto err_unmap;
4877d62b00eSchristos     }
4887d62b00eSchristos 
4897d62b00eSchristos   /* This horrible hack lets us know how much to unmap when the file is
4907d62b00eSchristos      closed.  (We no longer need the magic number, and the mapping
4917d62b00eSchristos      is private.)  */
4927d62b00eSchristos   arc->ctfa_magic = s.st_size;
4937d62b00eSchristos   close (fd);
4947d62b00eSchristos   return arc;
4957d62b00eSchristos 
4967d62b00eSchristos err_unmap:
4977d62b00eSchristos   arc_mmap_unmap (arc, s.st_size, NULL);
4987d62b00eSchristos err_close:
4997d62b00eSchristos   close (fd);
5007d62b00eSchristos err:
5017d62b00eSchristos   if (errp)
5027d62b00eSchristos     *errp = errno;
5037d62b00eSchristos   ctf_err_warn (NULL, 0, errno, gettext (errmsg), filename);
5047d62b00eSchristos   return NULL;
5057d62b00eSchristos }
5067d62b00eSchristos 
5077d62b00eSchristos /* Close an archive.  */
5087d62b00eSchristos void
5097d62b00eSchristos ctf_arc_close_internal (struct ctf_archive *arc)
5107d62b00eSchristos {
5117d62b00eSchristos   if (arc == NULL)
5127d62b00eSchristos     return;
5137d62b00eSchristos 
5147d62b00eSchristos   /* See the comment in ctf_arc_open().  */
5157d62b00eSchristos   arc_mmap_unmap (arc, arc->ctfa_magic, NULL);
5167d62b00eSchristos }
5177d62b00eSchristos 
5187d62b00eSchristos /* Public entry point: close an archive, or CTF file.  */
5197d62b00eSchristos void
5207d62b00eSchristos ctf_arc_close (ctf_archive_t *arc)
5217d62b00eSchristos {
5227d62b00eSchristos   if (arc == NULL)
5237d62b00eSchristos     return;
5247d62b00eSchristos 
5257d62b00eSchristos   if (arc->ctfi_is_archive)
5267d62b00eSchristos     {
5277d62b00eSchristos       if (arc->ctfi_unmap_on_close)
5287d62b00eSchristos 	ctf_arc_close_internal (arc->ctfi_archive);
5297d62b00eSchristos     }
5307d62b00eSchristos   else
531*6881a400Schristos     ctf_dict_close (arc->ctfi_dict);
532*6881a400Schristos   free (arc->ctfi_symdicts);
533*6881a400Schristos   free (arc->ctfi_symnamedicts);
534*6881a400Schristos   ctf_dynhash_destroy (arc->ctfi_dicts);
5357d62b00eSchristos   if (arc->ctfi_free_symsect)
5367d62b00eSchristos     free ((void *) arc->ctfi_symsect.cts_data);
5377d62b00eSchristos   if (arc->ctfi_free_strsect)
5387d62b00eSchristos     free ((void *) arc->ctfi_strsect.cts_data);
5397d62b00eSchristos   free (arc->ctfi_data);
5407d62b00eSchristos   if (arc->ctfi_bfd_close)
5417d62b00eSchristos     arc->ctfi_bfd_close (arc);
5427d62b00eSchristos   free (arc);
5437d62b00eSchristos }
5447d62b00eSchristos 
545*6881a400Schristos /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
5467d62b00eSchristos    non-NULL.  A name of NULL means to open the default file.  */
547*6881a400Schristos static ctf_dict_t *
548*6881a400Schristos ctf_dict_open_internal (const struct ctf_archive *arc,
5497d62b00eSchristos 			const ctf_sect_t *symsect,
5507d62b00eSchristos 			const ctf_sect_t *strsect,
551*6881a400Schristos 			const char *name, int little_endian,
552*6881a400Schristos 			int *errp)
5537d62b00eSchristos {
5547d62b00eSchristos   struct ctf_archive_modent *modent;
5557d62b00eSchristos   const char *search_nametbl;
5567d62b00eSchristos 
5577d62b00eSchristos   if (name == NULL)
5587d62b00eSchristos     name = _CTF_SECTION;		 /* The default name.  */
5597d62b00eSchristos 
560*6881a400Schristos   ctf_dprintf ("ctf_dict_open_internal(%s): opening\n", name);
5617d62b00eSchristos 
5627d62b00eSchristos   modent = (ctf_archive_modent_t *) ((char *) arc
5637d62b00eSchristos 				     + sizeof (struct ctf_archive));
5647d62b00eSchristos 
5657d62b00eSchristos   search_nametbl = (const char *) arc + le64toh (arc->ctfa_names);
566*6881a400Schristos   modent = bsearch_r (name, modent, le64toh (arc->ctfa_ndicts),
5677d62b00eSchristos 		      sizeof (struct ctf_archive_modent),
5687d62b00eSchristos 		      search_modent_by_name, (void *) search_nametbl);
5697d62b00eSchristos 
5707d62b00eSchristos   /* This is actually a common case and normal operation: no error
5717d62b00eSchristos      debug output.  */
5727d62b00eSchristos   if (modent == NULL)
5737d62b00eSchristos     {
5747d62b00eSchristos       if (errp)
5757d62b00eSchristos 	*errp = ECTF_ARNNAME;
5767d62b00eSchristos       return NULL;
5777d62b00eSchristos     }
5787d62b00eSchristos 
579*6881a400Schristos   return ctf_dict_open_by_offset (arc, symsect, strsect,
580*6881a400Schristos 				  le64toh (modent->ctf_offset),
581*6881a400Schristos 				  little_endian, errp);
5827d62b00eSchristos }
5837d62b00eSchristos 
584*6881a400Schristos /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
5857d62b00eSchristos    non-NULL.  A name of NULL means to open the default file.
5867d62b00eSchristos 
5877d62b00eSchristos    Use the specified string and symbol table sections.
5887d62b00eSchristos 
5897d62b00eSchristos    Public entry point.  */
590*6881a400Schristos ctf_dict_t *
591*6881a400Schristos ctf_dict_open_sections (const ctf_archive_t *arc,
5927d62b00eSchristos 			const ctf_sect_t *symsect,
5937d62b00eSchristos 			const ctf_sect_t *strsect,
5947d62b00eSchristos 			const char *name,
5957d62b00eSchristos 			int *errp)
5967d62b00eSchristos {
5977d62b00eSchristos   if (arc->ctfi_is_archive)
5987d62b00eSchristos     {
599*6881a400Schristos       ctf_dict_t *ret;
600*6881a400Schristos       ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect,
601*6881a400Schristos 				    name, arc->ctfi_symsect_little_endian,
602*6881a400Schristos 				    errp);
6037d62b00eSchristos       if (ret)
604*6881a400Schristos 	{
6057d62b00eSchristos 	  ret->ctf_archive = (ctf_archive_t *) arc;
606*6881a400Schristos 	  ctf_arc_import_parent (arc, ret);
607*6881a400Schristos 	}
6087d62b00eSchristos       return ret;
6097d62b00eSchristos     }
6107d62b00eSchristos 
6117d62b00eSchristos   if ((name != NULL) && (strcmp (name, _CTF_SECTION) != 0))
6127d62b00eSchristos     {
6137d62b00eSchristos       if (errp)
6147d62b00eSchristos 	*errp = ECTF_ARNNAME;
6157d62b00eSchristos       return NULL;
6167d62b00eSchristos     }
617*6881a400Schristos   arc->ctfi_dict->ctf_archive = (ctf_archive_t *) arc;
6187d62b00eSchristos 
619*6881a400Schristos   /* Bump the refcount so that the user can ctf_dict_close() it.  */
620*6881a400Schristos   arc->ctfi_dict->ctf_refcnt++;
621*6881a400Schristos   return arc->ctfi_dict;
6227d62b00eSchristos }
6237d62b00eSchristos 
624*6881a400Schristos /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
6257d62b00eSchristos    non-NULL.  A name of NULL means to open the default file.
6267d62b00eSchristos 
6277d62b00eSchristos    Public entry point.  */
628*6881a400Schristos ctf_dict_t *
629*6881a400Schristos ctf_dict_open (const ctf_archive_t *arc, const char *name, int *errp)
6307d62b00eSchristos {
6317d62b00eSchristos   const ctf_sect_t *symsect = &arc->ctfi_symsect;
6327d62b00eSchristos   const ctf_sect_t *strsect = &arc->ctfi_strsect;
6337d62b00eSchristos 
6347d62b00eSchristos   if (symsect->cts_name == NULL)
6357d62b00eSchristos     symsect = NULL;
6367d62b00eSchristos   if (strsect->cts_name == NULL)
6377d62b00eSchristos     strsect = NULL;
6387d62b00eSchristos 
639*6881a400Schristos   return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
6407d62b00eSchristos }
6417d62b00eSchristos 
642*6881a400Schristos static void
643*6881a400Schristos ctf_cached_dict_close (void *fp)
644*6881a400Schristos {
645*6881a400Schristos   ctf_dict_close ((ctf_dict_t *) fp);
646*6881a400Schristos }
647*6881a400Schristos 
648*6881a400Schristos /* Return the ctf_dict_t with the given name and cache it in the archive's
649*6881a400Schristos    ctfi_dicts.  If this is the first cached dict, designate it the
650*6881a400Schristos    crossdict_cache.  */
651*6881a400Schristos static ctf_dict_t *
652*6881a400Schristos ctf_dict_open_cached (ctf_archive_t *arc, const char *name, int *errp)
653*6881a400Schristos {
654*6881a400Schristos   ctf_dict_t *fp;
655*6881a400Schristos   char *dupname;
656*6881a400Schristos 
657*6881a400Schristos   /* Just return from the cache if possible.  */
658*6881a400Schristos   if (arc->ctfi_dicts
659*6881a400Schristos       && ((fp = ctf_dynhash_lookup (arc->ctfi_dicts, name)) != NULL))
660*6881a400Schristos     {
661*6881a400Schristos       fp->ctf_refcnt++;
662*6881a400Schristos       return fp;
663*6881a400Schristos     }
664*6881a400Schristos 
665*6881a400Schristos   /* Not yet cached: open it.  */
666*6881a400Schristos   fp = ctf_dict_open (arc, name, errp);
667*6881a400Schristos   dupname = strdup (name);
668*6881a400Schristos 
669*6881a400Schristos   if (!fp || !dupname)
670*6881a400Schristos     goto oom;
671*6881a400Schristos 
672*6881a400Schristos   if (arc->ctfi_dicts == NULL)
673*6881a400Schristos     if ((arc->ctfi_dicts
674*6881a400Schristos 	 = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
675*6881a400Schristos 			       free, ctf_cached_dict_close)) == NULL)
676*6881a400Schristos       goto oom;
677*6881a400Schristos 
678*6881a400Schristos   if (ctf_dynhash_insert (arc->ctfi_dicts, dupname, fp) < 0)
679*6881a400Schristos     goto oom;
680*6881a400Schristos   fp->ctf_refcnt++;
681*6881a400Schristos 
682*6881a400Schristos   if (arc->ctfi_crossdict_cache == NULL)
683*6881a400Schristos     arc->ctfi_crossdict_cache = fp;
684*6881a400Schristos 
685*6881a400Schristos   return fp;
686*6881a400Schristos 
687*6881a400Schristos  oom:
688*6881a400Schristos   ctf_dict_close (fp);
689*6881a400Schristos   free (dupname);
690*6881a400Schristos   if (errp)
691*6881a400Schristos     *errp = ENOMEM;
692*6881a400Schristos   return NULL;
693*6881a400Schristos }
694*6881a400Schristos 
695*6881a400Schristos /* Flush any caches the CTF archive may have open.  */
696*6881a400Schristos void
697*6881a400Schristos ctf_arc_flush_caches (ctf_archive_t *wrapper)
698*6881a400Schristos {
699*6881a400Schristos   free (wrapper->ctfi_symdicts);
700*6881a400Schristos   free (wrapper->ctfi_symnamedicts);
701*6881a400Schristos   ctf_dynhash_destroy (wrapper->ctfi_dicts);
702*6881a400Schristos   wrapper->ctfi_symdicts = NULL;
703*6881a400Schristos   wrapper->ctfi_symnamedicts = NULL;
704*6881a400Schristos   wrapper->ctfi_dicts = NULL;
705*6881a400Schristos   wrapper->ctfi_crossdict_cache = NULL;
706*6881a400Schristos }
707*6881a400Schristos 
708*6881a400Schristos /* Return the ctf_dict_t at the given ctfa_ctfs-relative offset, or NULL if
7097d62b00eSchristos    none, setting 'err' if non-NULL.  */
710*6881a400Schristos static ctf_dict_t *
711*6881a400Schristos ctf_dict_open_by_offset (const struct ctf_archive *arc,
7127d62b00eSchristos 			 const ctf_sect_t *symsect,
7137d62b00eSchristos 			 const ctf_sect_t *strsect, size_t offset,
714*6881a400Schristos 			 int little_endian, int *errp)
7157d62b00eSchristos {
7167d62b00eSchristos   ctf_sect_t ctfsect;
717*6881a400Schristos   ctf_dict_t *fp;
7187d62b00eSchristos 
719*6881a400Schristos   ctf_dprintf ("ctf_dict_open_by_offset(%lu): opening\n", (unsigned long) offset);
7207d62b00eSchristos 
7217d62b00eSchristos   memset (&ctfsect, 0, sizeof (ctf_sect_t));
7227d62b00eSchristos 
7237d62b00eSchristos   offset += le64toh (arc->ctfa_ctfs);
7247d62b00eSchristos 
7257d62b00eSchristos   ctfsect.cts_name = _CTF_SECTION;
7267d62b00eSchristos   ctfsect.cts_size = le64toh (*((uint64_t *) ((char *) arc + offset)));
7277d62b00eSchristos   ctfsect.cts_entsize = 1;
7287d62b00eSchristos   ctfsect.cts_data = (void *) ((char *) arc + offset + sizeof (uint64_t));
7297d62b00eSchristos   fp = ctf_bufopen (&ctfsect, symsect, strsect, errp);
7307d62b00eSchristos   if (fp)
731*6881a400Schristos     {
7327d62b00eSchristos       ctf_setmodel (fp, le64toh (arc->ctfa_model));
733*6881a400Schristos       if (little_endian >= 0)
734*6881a400Schristos 	ctf_symsect_endianness (fp, little_endian);
735*6881a400Schristos     }
7367d62b00eSchristos   return fp;
7377d62b00eSchristos }
7387d62b00eSchristos 
739*6881a400Schristos /* Backward compatibility.  */
740*6881a400Schristos ctf_dict_t *
741*6881a400Schristos ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name,
742*6881a400Schristos 		      int *errp)
743*6881a400Schristos {
744*6881a400Schristos   return ctf_dict_open (arc, name, errp);
745*6881a400Schristos }
746*6881a400Schristos 
747*6881a400Schristos ctf_dict_t *
748*6881a400Schristos ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
749*6881a400Schristos 			       const ctf_sect_t *symsect,
750*6881a400Schristos 			       const ctf_sect_t *strsect,
751*6881a400Schristos 			       const char *name,
752*6881a400Schristos 			       int *errp)
753*6881a400Schristos {
754*6881a400Schristos   return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
755*6881a400Schristos }
756*6881a400Schristos 
757*6881a400Schristos /* Import the parent into a ctf archive, if this is a child, the parent is not
758*6881a400Schristos    already set, and a suitable archive member exists.  No error is raised if
759*6881a400Schristos    this is not possible: this is just a best-effort helper operation to give
760*6881a400Schristos    people useful dicts to start with.  */
761*6881a400Schristos static void
762*6881a400Schristos ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp)
763*6881a400Schristos {
764*6881a400Schristos   if ((fp->ctf_flags & LCTF_CHILD) && fp->ctf_parname && !fp->ctf_parent)
765*6881a400Schristos     {
766*6881a400Schristos       ctf_dict_t *parent = ctf_dict_open_cached ((ctf_archive_t *) arc,
767*6881a400Schristos 						 fp->ctf_parname, NULL);
768*6881a400Schristos       if (parent)
769*6881a400Schristos 	{
770*6881a400Schristos 	  ctf_import (fp, parent);
771*6881a400Schristos 	  ctf_dict_close (parent);
772*6881a400Schristos 	}
773*6881a400Schristos     }
774*6881a400Schristos }
775*6881a400Schristos 
7767d62b00eSchristos /* Return the number of members in an archive.  */
7777d62b00eSchristos size_t
7787d62b00eSchristos ctf_archive_count (const ctf_archive_t *wrapper)
7797d62b00eSchristos {
7807d62b00eSchristos   if (!wrapper->ctfi_is_archive)
7817d62b00eSchristos     return 1;
7827d62b00eSchristos 
783*6881a400Schristos   return wrapper->ctfi_archive->ctfa_ndicts;
784*6881a400Schristos }
785*6881a400Schristos 
786*6881a400Schristos /* Look up a symbol in an archive by name or index (if the name is set, a lookup
787*6881a400Schristos    by name is done).  Return the dict in the archive that the symbol is found
788*6881a400Schristos    in, and (optionally) the ctf_id_t of the symbol in that dict (so you don't
789*6881a400Schristos    have to look it up yourself).  The dict is cached, so repeated lookups are
790*6881a400Schristos    nearly free.
791*6881a400Schristos 
792*6881a400Schristos    As usual, you should ctf_dict_close() the returned dict once you are done
793*6881a400Schristos    with it.
794*6881a400Schristos 
795*6881a400Schristos    Returns NULL on error, and an error in errp (if set).  */
796*6881a400Schristos 
797*6881a400Schristos static ctf_dict_t *
798*6881a400Schristos ctf_arc_lookup_sym_or_name (ctf_archive_t *wrapper, unsigned long symidx,
799*6881a400Schristos 			    const char *symname, ctf_id_t *typep, int *errp)
800*6881a400Schristos {
801*6881a400Schristos   ctf_dict_t *fp;
802*6881a400Schristos   void *fpkey;
803*6881a400Schristos   ctf_id_t type;
804*6881a400Schristos 
805*6881a400Schristos   /* The usual non-archive-transparent-wrapper special case.  */
806*6881a400Schristos   if (!wrapper->ctfi_is_archive)
807*6881a400Schristos     {
808*6881a400Schristos       if (!symname)
809*6881a400Schristos 	{
810*6881a400Schristos 	  if ((type = ctf_lookup_by_symbol (wrapper->ctfi_dict, symidx)) == CTF_ERR)
811*6881a400Schristos 	    {
812*6881a400Schristos 	      if (errp)
813*6881a400Schristos 		*errp = ctf_errno (wrapper->ctfi_dict);
814*6881a400Schristos 	      return NULL;
815*6881a400Schristos 	    }
816*6881a400Schristos 	}
817*6881a400Schristos       else
818*6881a400Schristos 	{
819*6881a400Schristos 	  if ((type = ctf_lookup_by_symbol_name (wrapper->ctfi_dict,
820*6881a400Schristos 						 symname)) == CTF_ERR)
821*6881a400Schristos 	    {
822*6881a400Schristos 	      if (errp)
823*6881a400Schristos 		*errp = ctf_errno (wrapper->ctfi_dict);
824*6881a400Schristos 	      return NULL;
825*6881a400Schristos 	    }
826*6881a400Schristos 	}
827*6881a400Schristos       if (typep)
828*6881a400Schristos 	*typep = type;
829*6881a400Schristos       wrapper->ctfi_dict->ctf_refcnt++;
830*6881a400Schristos       return wrapper->ctfi_dict;
831*6881a400Schristos     }
832*6881a400Schristos 
833*6881a400Schristos   if (wrapper->ctfi_symsect.cts_name == NULL
834*6881a400Schristos       || wrapper->ctfi_symsect.cts_data == NULL
835*6881a400Schristos       || wrapper->ctfi_symsect.cts_size == 0
836*6881a400Schristos       || wrapper->ctfi_symsect.cts_entsize == 0)
837*6881a400Schristos     {
838*6881a400Schristos       if (errp)
839*6881a400Schristos 	*errp = ECTF_NOSYMTAB;
840*6881a400Schristos       return NULL;
841*6881a400Schristos     }
842*6881a400Schristos 
843*6881a400Schristos   /* Make enough space for all possible symbol indexes, if not already done.  We
844*6881a400Schristos      cache the originating dictionary of all symbols.  The dict links are weak,
845*6881a400Schristos      to the dictionaries cached in ctfi_dicts: their refcnts are *not* bumped.
846*6881a400Schristos      We also cache similar mappings for symbol names: these are ordinary
847*6881a400Schristos      dynhashes, with weak links to dicts.  */
848*6881a400Schristos 
849*6881a400Schristos   if (!wrapper->ctfi_symdicts)
850*6881a400Schristos     {
851*6881a400Schristos       if ((wrapper->ctfi_symdicts = calloc (wrapper->ctfi_symsect.cts_size
852*6881a400Schristos 					    / wrapper->ctfi_symsect.cts_entsize,
853*6881a400Schristos 					    sizeof (ctf_dict_t *))) == NULL)
854*6881a400Schristos 	{
855*6881a400Schristos 	  if (errp)
856*6881a400Schristos 	    *errp = ENOMEM;
857*6881a400Schristos 	  return NULL;
858*6881a400Schristos 	}
859*6881a400Schristos     }
860*6881a400Schristos   if (!wrapper->ctfi_symnamedicts)
861*6881a400Schristos     {
862*6881a400Schristos       if ((wrapper->ctfi_symnamedicts = ctf_dynhash_create (ctf_hash_string,
863*6881a400Schristos 							    ctf_hash_eq_string,
864*6881a400Schristos 							    free, NULL)) == NULL)
865*6881a400Schristos 	{
866*6881a400Schristos 	  if (errp)
867*6881a400Schristos 	    *errp = ENOMEM;
868*6881a400Schristos 	  return NULL;
869*6881a400Schristos 	}
870*6881a400Schristos     }
871*6881a400Schristos 
872*6881a400Schristos   /* Perhaps the dict in which we found a previous lookup is cached.  If it's
873*6881a400Schristos      supposed to be cached but we don't find it, pretend it was always not
874*6881a400Schristos      found: this should never happen, but shouldn't be allowed to cause trouble
875*6881a400Schristos      if it does.  */
876*6881a400Schristos 
877*6881a400Schristos   if ((symname && ctf_dynhash_lookup_kv (wrapper->ctfi_symnamedicts,
878*6881a400Schristos 					 symname, NULL, &fpkey))
879*6881a400Schristos       || (!symname && wrapper->ctfi_symdicts[symidx] != NULL))
880*6881a400Schristos     {
881*6881a400Schristos       if (symname)
882*6881a400Schristos 	fp = (ctf_dict_t *) fpkey;
883*6881a400Schristos       else
884*6881a400Schristos 	fp = wrapper->ctfi_symdicts[symidx];
885*6881a400Schristos 
886*6881a400Schristos       if (fp == &enosym)
887*6881a400Schristos 	goto no_sym;
888*6881a400Schristos 
889*6881a400Schristos       if (symname)
890*6881a400Schristos 	{
891*6881a400Schristos 	  if ((type = ctf_lookup_by_symbol_name (fp, symname)) == CTF_ERR)
892*6881a400Schristos 	    goto cache_no_sym;
893*6881a400Schristos 	}
894*6881a400Schristos       else
895*6881a400Schristos 	{
896*6881a400Schristos 	  if ((type = ctf_lookup_by_symbol (fp, symidx)) == CTF_ERR)
897*6881a400Schristos 	    goto cache_no_sym;
898*6881a400Schristos 	}
899*6881a400Schristos 
900*6881a400Schristos       if (typep)
901*6881a400Schristos 	*typep = type;
902*6881a400Schristos       fp->ctf_refcnt++;
903*6881a400Schristos       return fp;
904*6881a400Schristos     }
905*6881a400Schristos 
906*6881a400Schristos   /* Not cached: find it and cache it.  We must track open errors ourselves even
907*6881a400Schristos      if our caller doesn't, to be able to distinguish no-error end-of-iteration
908*6881a400Schristos      from open errors.  */
909*6881a400Schristos 
910*6881a400Schristos   int local_err;
911*6881a400Schristos   int *local_errp;
912*6881a400Schristos   ctf_next_t *i = NULL;
913*6881a400Schristos   const char *name;
914*6881a400Schristos 
915*6881a400Schristos   if (errp)
916*6881a400Schristos     local_errp = errp;
917*6881a400Schristos   else
918*6881a400Schristos     local_errp = &local_err;
919*6881a400Schristos 
920*6881a400Schristos   while ((fp = ctf_archive_next (wrapper, &i, &name, 0, local_errp)) != NULL)
921*6881a400Schristos     {
922*6881a400Schristos       if (!symname)
923*6881a400Schristos 	{
924*6881a400Schristos 	  if ((type = ctf_lookup_by_symbol (fp, symidx)) != CTF_ERR)
925*6881a400Schristos 	    wrapper->ctfi_symdicts[symidx] = fp;
926*6881a400Schristos 	}
927*6881a400Schristos       else
928*6881a400Schristos 	{
929*6881a400Schristos 	  if ((type = ctf_lookup_by_symbol_name (fp, symname)) != CTF_ERR)
930*6881a400Schristos 	    {
931*6881a400Schristos 	      char *tmp;
932*6881a400Schristos 	      /* No error checking, as above.  */
933*6881a400Schristos 	      if ((tmp = strdup (symname)) != NULL)
934*6881a400Schristos 		ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, fp);
935*6881a400Schristos 	    }
936*6881a400Schristos 	}
937*6881a400Schristos 
938*6881a400Schristos       if (type != CTF_ERR)
939*6881a400Schristos 	{
940*6881a400Schristos 	  if (typep)
941*6881a400Schristos 	    *typep = type;
942*6881a400Schristos 	  ctf_next_destroy (i);
943*6881a400Schristos 	  return fp;
944*6881a400Schristos 	}
945*6881a400Schristos       if (ctf_errno (fp) != ECTF_NOTYPEDAT)
946*6881a400Schristos 	{
947*6881a400Schristos 	  if (errp)
948*6881a400Schristos 	    *errp = ctf_errno (fp);
949*6881a400Schristos 	  ctf_next_destroy (i);
950*6881a400Schristos 	  return NULL;				/* errno is set for us.  */
951*6881a400Schristos 	}
952*6881a400Schristos       ctf_dict_close (fp);
953*6881a400Schristos     }
954*6881a400Schristos   if (*local_errp != ECTF_NEXT_END)
955*6881a400Schristos     {
956*6881a400Schristos       ctf_next_destroy (i);
957*6881a400Schristos       return NULL;
958*6881a400Schristos     }
959*6881a400Schristos 
960*6881a400Schristos   /* Don't leak end-of-iteration to the caller.  */
961*6881a400Schristos   *local_errp = 0;
962*6881a400Schristos 
963*6881a400Schristos  cache_no_sym:
964*6881a400Schristos   if (!symname)
965*6881a400Schristos     wrapper->ctfi_symdicts[symidx] = &enosym;
966*6881a400Schristos   else
967*6881a400Schristos     {
968*6881a400Schristos       char *tmp;
969*6881a400Schristos 
970*6881a400Schristos       /* No error checking: if caching fails, there is only a slight performance
971*6881a400Schristos 	 impact.  */
972*6881a400Schristos       if ((tmp = strdup (symname)) != NULL)
973*6881a400Schristos 	if (ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, &enosym) < 0)
974*6881a400Schristos 	  free (tmp);
975*6881a400Schristos     }
976*6881a400Schristos 
977*6881a400Schristos  no_sym:
978*6881a400Schristos   if (errp)
979*6881a400Schristos     *errp = ECTF_NOTYPEDAT;
980*6881a400Schristos   if (typep)
981*6881a400Schristos     *typep = CTF_ERR;
982*6881a400Schristos   return NULL;
983*6881a400Schristos }
984*6881a400Schristos 
985*6881a400Schristos /* The public API for looking up a symbol by index.  */
986*6881a400Schristos ctf_dict_t *
987*6881a400Schristos ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx,
988*6881a400Schristos 		       ctf_id_t *typep, int *errp)
989*6881a400Schristos {
990*6881a400Schristos   return ctf_arc_lookup_sym_or_name (wrapper, symidx, NULL, typep, errp);
991*6881a400Schristos }
992*6881a400Schristos 
993*6881a400Schristos /* The public API for looking up a symbol by name. */
994*6881a400Schristos 
995*6881a400Schristos ctf_dict_t *
996*6881a400Schristos ctf_arc_lookup_symbol_name (ctf_archive_t *wrapper, const char *symname,
997*6881a400Schristos 			    ctf_id_t *typep, int *errp)
998*6881a400Schristos {
999*6881a400Schristos   return ctf_arc_lookup_sym_or_name (wrapper, 0, symname, typep, errp);
10007d62b00eSchristos }
10017d62b00eSchristos 
10027d62b00eSchristos /* Raw iteration over all CTF files in an archive.  We pass the raw data for all
10037d62b00eSchristos    CTF files in turn to the specified callback function.  */
10047d62b00eSchristos static int
10057d62b00eSchristos ctf_archive_raw_iter_internal (const struct ctf_archive *arc,
10067d62b00eSchristos 			       ctf_archive_raw_member_f *func, void *data)
10077d62b00eSchristos {
10087d62b00eSchristos   int rc;
10097d62b00eSchristos   size_t i;
10107d62b00eSchristos   struct ctf_archive_modent *modent;
10117d62b00eSchristos   const char *nametbl;
10127d62b00eSchristos 
10137d62b00eSchristos   modent = (ctf_archive_modent_t *) ((char *) arc
10147d62b00eSchristos 				     + sizeof (struct ctf_archive));
10157d62b00eSchristos   nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
10167d62b00eSchristos 
1017*6881a400Schristos   for (i = 0; i < le64toh (arc->ctfa_ndicts); i++)
10187d62b00eSchristos     {
10197d62b00eSchristos       const char *name;
10207d62b00eSchristos       char *fp;
10217d62b00eSchristos 
10227d62b00eSchristos       name = &nametbl[le64toh (modent[i].name_offset)];
10237d62b00eSchristos       fp = ((char *) arc + le64toh (arc->ctfa_ctfs)
10247d62b00eSchristos 	    + le64toh (modent[i].ctf_offset));
10257d62b00eSchristos 
10267d62b00eSchristos       if ((rc = func (name, (void *) (fp + sizeof (uint64_t)),
10277d62b00eSchristos 		      le64toh (*((uint64_t *) fp)), data)) != 0)
10287d62b00eSchristos 	return rc;
10297d62b00eSchristos     }
10307d62b00eSchristos   return 0;
10317d62b00eSchristos }
10327d62b00eSchristos 
10337d62b00eSchristos /* Raw iteration over all CTF files in an archive: public entry point.
10347d62b00eSchristos 
10357d62b00eSchristos    Returns -EINVAL if not supported for this sort of archive.  */
10367d62b00eSchristos int
10377d62b00eSchristos ctf_archive_raw_iter (const ctf_archive_t *arc,
10387d62b00eSchristos 		      ctf_archive_raw_member_f * func, void *data)
10397d62b00eSchristos {
10407d62b00eSchristos   if (arc->ctfi_is_archive)
10417d62b00eSchristos     return ctf_archive_raw_iter_internal (arc->ctfi_archive, func, data);
10427d62b00eSchristos 
10437d62b00eSchristos   return -EINVAL;			 /* Not supported. */
10447d62b00eSchristos }
10457d62b00eSchristos 
10467d62b00eSchristos /* Iterate over all CTF files in an archive: public entry point.  We pass all
10477d62b00eSchristos    CTF files in turn to the specified callback function.  */
10487d62b00eSchristos int
10497d62b00eSchristos ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
10507d62b00eSchristos 		  void *data)
10517d62b00eSchristos {
1052*6881a400Schristos   ctf_next_t *i = NULL;
1053*6881a400Schristos   ctf_dict_t *fp;
1054*6881a400Schristos   const char *name;
1055*6881a400Schristos   int err;
10567d62b00eSchristos 
1057*6881a400Schristos   while ((fp = ctf_archive_next (arc, &i, &name, 0, &err)) != NULL)
1058*6881a400Schristos     {
1059*6881a400Schristos       int rc;
10607d62b00eSchristos 
1061*6881a400Schristos       if ((rc = func (fp, name, data)) != 0)
1062*6881a400Schristos 	{
1063*6881a400Schristos 	  ctf_dict_close (fp);
1064*6881a400Schristos 	  ctf_next_destroy (i);
1065*6881a400Schristos 	  return rc;
1066*6881a400Schristos 	}
1067*6881a400Schristos       ctf_dict_close (fp);
1068*6881a400Schristos     }
1069*6881a400Schristos   return 0;
10707d62b00eSchristos }
10717d62b00eSchristos 
10727d62b00eSchristos /* Iterate over all CTF files in an archive, returning each dict in turn as a
1073*6881a400Schristos    ctf_dict_t, and NULL on error or end of iteration.  It is the caller's
1074*6881a400Schristos    responsibility to close it.  Parent dicts may be skipped.
1075*6881a400Schristos 
1076*6881a400Schristos    The archive member is cached for rapid return on future calls.
10777d62b00eSchristos 
10787d62b00eSchristos    We identify parents by name rather than by flag value: for now, with the
10797d62b00eSchristos    linker only emitting parents named _CTF_SECTION, this works well enough.  */
10807d62b00eSchristos 
1081*6881a400Schristos ctf_dict_t *
10827d62b00eSchristos ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **name,
10837d62b00eSchristos 		  int skip_parent, int *errp)
10847d62b00eSchristos {
1085*6881a400Schristos   ctf_dict_t *f;
10867d62b00eSchristos   ctf_next_t *i = *it;
10877d62b00eSchristos   struct ctf_archive *arc;
10887d62b00eSchristos   struct ctf_archive_modent *modent;
10897d62b00eSchristos   const char *nametbl;
10907d62b00eSchristos   const char *name_;
10917d62b00eSchristos 
10927d62b00eSchristos   if (!i)
10937d62b00eSchristos     {
10947d62b00eSchristos       if ((i = ctf_next_create()) == NULL)
10957d62b00eSchristos 	{
10967d62b00eSchristos 	  if (errp)
10977d62b00eSchristos 	    *errp = ENOMEM;
10987d62b00eSchristos 	  return NULL;
10997d62b00eSchristos 	}
11007d62b00eSchristos       i->cu.ctn_arc = wrapper;
11017d62b00eSchristos       i->ctn_iter_fun = (void (*) (void)) ctf_archive_next;
11027d62b00eSchristos       *it = i;
11037d62b00eSchristos     }
11047d62b00eSchristos 
11057d62b00eSchristos   if ((void (*) (void)) ctf_archive_next != i->ctn_iter_fun)
11067d62b00eSchristos     {
11077d62b00eSchristos       if (errp)
11087d62b00eSchristos 	*errp = ECTF_NEXT_WRONGFUN;
11097d62b00eSchristos       return NULL;
11107d62b00eSchristos     }
11117d62b00eSchristos 
11127d62b00eSchristos   if (wrapper != i->cu.ctn_arc)
11137d62b00eSchristos     {
11147d62b00eSchristos       if (errp)
11157d62b00eSchristos 	*errp = ECTF_NEXT_WRONGFP;
11167d62b00eSchristos       return NULL;
11177d62b00eSchristos     }
11187d62b00eSchristos 
1119*6881a400Schristos   /* Iteration is made a bit more complex by the need to handle ctf_dict_t's
11207d62b00eSchristos      transparently wrapped in a single-member archive.  These are parents: if
11217d62b00eSchristos      skip_parent is on, they are skipped and the iterator terminates
11227d62b00eSchristos      immediately.  */
11237d62b00eSchristos 
11247d62b00eSchristos   if (!wrapper->ctfi_is_archive && i->ctn_n == 0)
11257d62b00eSchristos     {
11267d62b00eSchristos       i->ctn_n++;
11277d62b00eSchristos       if (!skip_parent)
11287d62b00eSchristos 	{
1129*6881a400Schristos 	  wrapper->ctfi_dict->ctf_refcnt++;
1130*6881a400Schristos 	  if (name)
1131*6881a400Schristos 	    *name = _CTF_SECTION;
1132*6881a400Schristos 	  return wrapper->ctfi_dict;
11337d62b00eSchristos 	}
11347d62b00eSchristos     }
11357d62b00eSchristos 
11367d62b00eSchristos   arc = wrapper->ctfi_archive;
11377d62b00eSchristos 
11387d62b00eSchristos   /* The loop keeps going when skip_parent is on as long as the member we find
11397d62b00eSchristos      is the parent (i.e. at most two iterations, but possibly an early return if
11407d62b00eSchristos      *all* we have is a parent).  */
11417d62b00eSchristos 
11427d62b00eSchristos   do
11437d62b00eSchristos     {
1144*6881a400Schristos       if ((!wrapper->ctfi_is_archive) || (i->ctn_n >= le64toh (arc->ctfa_ndicts)))
11457d62b00eSchristos 	{
11467d62b00eSchristos 	  ctf_next_destroy (i);
11477d62b00eSchristos 	  *it = NULL;
11487d62b00eSchristos 	  if (errp)
11497d62b00eSchristos 	    *errp = ECTF_NEXT_END;
11507d62b00eSchristos 	  return NULL;
11517d62b00eSchristos 	}
11527d62b00eSchristos 
11537d62b00eSchristos       modent = (ctf_archive_modent_t *) ((char *) arc
11547d62b00eSchristos 					 + sizeof (struct ctf_archive));
11557d62b00eSchristos       nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
11567d62b00eSchristos 
11577d62b00eSchristos       name_ = &nametbl[le64toh (modent[i->ctn_n].name_offset)];
11587d62b00eSchristos       i->ctn_n++;
1159*6881a400Schristos     }
1160*6881a400Schristos   while (skip_parent && strcmp (name_, _CTF_SECTION) == 0);
11617d62b00eSchristos 
11627d62b00eSchristos   if (name)
11637d62b00eSchristos     *name = name_;
11647d62b00eSchristos 
1165*6881a400Schristos   f = ctf_dict_open_cached ((ctf_archive_t *) wrapper, name_, errp);
11667d62b00eSchristos   return f;
11677d62b00eSchristos }
11687d62b00eSchristos 
11697d62b00eSchristos #ifdef HAVE_MMAP
11707d62b00eSchristos /* Map the header in.  Only used on new, empty files.  */
11717d62b00eSchristos static void *arc_mmap_header (int fd, size_t headersz)
11727d62b00eSchristos {
11737d62b00eSchristos   void *hdr;
11747d62b00eSchristos   if ((hdr = mmap (NULL, headersz, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
11757d62b00eSchristos 		   0)) == MAP_FAILED)
11767d62b00eSchristos     return NULL;
11777d62b00eSchristos   return hdr;
11787d62b00eSchristos }
11797d62b00eSchristos 
11807d62b00eSchristos /* mmap() the whole file, for reading only.  (Map it writably, but privately: we
11817d62b00eSchristos    need to modify the region, but don't need anyone else to see the
11827d62b00eSchristos    modifications.)  */
11837d62b00eSchristos static void *arc_mmap_file (int fd, size_t size)
11847d62b00eSchristos {
11857d62b00eSchristos   void *arc;
11867d62b00eSchristos   if ((arc = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
11877d62b00eSchristos 		   fd, 0)) == MAP_FAILED)
11887d62b00eSchristos     return NULL;
11897d62b00eSchristos   return arc;
11907d62b00eSchristos }
11917d62b00eSchristos 
11927d62b00eSchristos /* Persist the header to disk.  */
11937d62b00eSchristos static int arc_mmap_writeout (int fd _libctf_unused_, void *header,
11947d62b00eSchristos 			      size_t headersz, const char **errmsg)
11957d62b00eSchristos {
11967d62b00eSchristos     if (msync (header, headersz, MS_ASYNC) < 0)
11977d62b00eSchristos     {
11987d62b00eSchristos       if (errmsg)
11997d62b00eSchristos 	*errmsg = N_("arc_mmap_writeout(): cannot sync after writing "
12007d62b00eSchristos 		     "to %s: %s");
12017d62b00eSchristos       return -1;
12027d62b00eSchristos     }
12037d62b00eSchristos     return 0;
12047d62b00eSchristos }
12057d62b00eSchristos 
12067d62b00eSchristos /* Unmap the region.  */
12077d62b00eSchristos static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg)
12087d62b00eSchristos {
12097d62b00eSchristos   if (munmap (header, headersz) < 0)
12107d62b00eSchristos     {
12117d62b00eSchristos       if (errmsg)
12127d62b00eSchristos 	*errmsg = N_("arc_mmap_munmap(): cannot unmap after writing "
12137d62b00eSchristos 		     "to %s: %s");
12147d62b00eSchristos       return -1;
12157d62b00eSchristos     }
12167d62b00eSchristos     return 0;
12177d62b00eSchristos }
12187d62b00eSchristos #else
12197d62b00eSchristos /* Map the header in.  Only used on new, empty files.  */
12207d62b00eSchristos static void *arc_mmap_header (int fd _libctf_unused_, size_t headersz)
12217d62b00eSchristos {
12227d62b00eSchristos   void *hdr;
12237d62b00eSchristos   if ((hdr = malloc (headersz)) == NULL)
12247d62b00eSchristos     return NULL;
12257d62b00eSchristos   return hdr;
12267d62b00eSchristos }
12277d62b00eSchristos 
12287d62b00eSchristos /* Pull in the whole file, for reading only.  We assume the current file
12297d62b00eSchristos    position is at the start of the file.  */
12307d62b00eSchristos static void *arc_mmap_file (int fd, size_t size)
12317d62b00eSchristos {
12327d62b00eSchristos   char *data;
12337d62b00eSchristos 
12347d62b00eSchristos   if ((data = malloc (size)) == NULL)
12357d62b00eSchristos     return NULL;
12367d62b00eSchristos 
12377d62b00eSchristos   if (ctf_pread (fd, data, size, 0) < 0)
12387d62b00eSchristos     {
12397d62b00eSchristos       free (data);
12407d62b00eSchristos       return NULL;
12417d62b00eSchristos     }
12427d62b00eSchristos   return data;
12437d62b00eSchristos }
12447d62b00eSchristos 
12457d62b00eSchristos /* Persist the header to disk.  */
12467d62b00eSchristos static int arc_mmap_writeout (int fd, void *header, size_t headersz,
12477d62b00eSchristos 			      const char **errmsg)
12487d62b00eSchristos {
12497d62b00eSchristos   ssize_t len;
12507d62b00eSchristos   size_t acc = 0;
12517d62b00eSchristos   char *data = (char *) header;
12527d62b00eSchristos   ssize_t count = headersz;
12537d62b00eSchristos 
12547d62b00eSchristos   if ((lseek (fd, 0, SEEK_SET)) < 0)
12557d62b00eSchristos     {
12567d62b00eSchristos       if (errmsg)
12577d62b00eSchristos 	*errmsg = N_("arc_mmap_writeout(): cannot seek while writing header to "
12587d62b00eSchristos 		     "%s: %s");
12597d62b00eSchristos       return -1;
12607d62b00eSchristos     }
12617d62b00eSchristos 
12627d62b00eSchristos   while (headersz > 0)
12637d62b00eSchristos     {
12647d62b00eSchristos       if ((len = write (fd, data, count)) < 0)
12657d62b00eSchristos 	{
12667d62b00eSchristos 	  if (errmsg)
12677d62b00eSchristos 	    *errmsg = N_("arc_mmap_writeout(): cannot write header to %s: %s");
12687d62b00eSchristos 	  return len;
12697d62b00eSchristos 	}
12707d62b00eSchristos       if (len == EINTR)
12717d62b00eSchristos 	continue;
12727d62b00eSchristos 
12737d62b00eSchristos       acc += len;
12747d62b00eSchristos       if (len == 0)				/* EOF.  */
12757d62b00eSchristos 	break;
12767d62b00eSchristos 
12777d62b00eSchristos       count -= len;
12787d62b00eSchristos       data += len;
12797d62b00eSchristos     }
12807d62b00eSchristos   return 0;
12817d62b00eSchristos }
12827d62b00eSchristos 
12837d62b00eSchristos /* Unmap the region.  */
12847d62b00eSchristos static int arc_mmap_unmap (void *header, size_t headersz _libctf_unused_,
12857d62b00eSchristos 			   const char **errmsg _libctf_unused_)
12867d62b00eSchristos {
12877d62b00eSchristos   free (header);
12887d62b00eSchristos   return 0;
12897d62b00eSchristos }
12907d62b00eSchristos #endif
1291