xref: /netbsd-src/external/gpl3/gdb/dist/libctf/ctf-dump.c (revision 12989c96ee862c63521a9ead8c44629b7a2ba9b1)
18dffb485Schristos /* Textual dumping of CTF data.
2*12989c96Schristos    Copyright (C) 2019-2024 Free Software Foundation, Inc.
38dffb485Schristos 
48dffb485Schristos    This file is part of libctf.
58dffb485Schristos 
68dffb485Schristos    libctf is free software; you can redistribute it and/or modify it under
78dffb485Schristos    the terms of the GNU General Public License as published by the Free
88dffb485Schristos    Software Foundation; either version 3, or (at your option) any later
98dffb485Schristos    version.
108dffb485Schristos 
118dffb485Schristos    This program is distributed in the hope that it will be useful, but
128dffb485Schristos    WITHOUT ANY WARRANTY; without even the implied warranty of
138dffb485Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
148dffb485Schristos    See the GNU General Public License for more details.
158dffb485Schristos 
168dffb485Schristos    You should have received a copy of the GNU General Public License
178dffb485Schristos    along with this program; see the file COPYING.  If not see
188dffb485Schristos    <http://www.gnu.org/licenses/>.  */
198dffb485Schristos 
208dffb485Schristos #include <ctf-impl.h>
218dffb485Schristos #include <string.h>
228dffb485Schristos 
238dffb485Schristos #define str_append(s, a) ctf_str_append_noerr (s, a)
248dffb485Schristos 
258dffb485Schristos /* One item to be dumped, in string form.  */
268dffb485Schristos 
278dffb485Schristos typedef struct ctf_dump_item
288dffb485Schristos {
298dffb485Schristos   ctf_list_t cdi_list;
308dffb485Schristos   char *cdi_item;
318dffb485Schristos } ctf_dump_item_t;
328dffb485Schristos 
338dffb485Schristos /* Cross-call state for dumping.  Basically just enough to track the section in
348dffb485Schristos    use and a list of return strings.  */
358dffb485Schristos 
368dffb485Schristos struct ctf_dump_state
378dffb485Schristos {
388dffb485Schristos   ctf_sect_names_t cds_sect;
394b169a6bSchristos   ctf_dict_t *cds_fp;
408dffb485Schristos   ctf_dump_item_t *cds_current;
418dffb485Schristos   ctf_list_t cds_items;
428dffb485Schristos };
438dffb485Schristos 
448dffb485Schristos /* Cross-call state for ctf_dump_member. */
458dffb485Schristos 
468dffb485Schristos typedef struct ctf_dump_membstate
478dffb485Schristos {
488dffb485Schristos   char **cdm_str;
494b169a6bSchristos   ctf_dict_t *cdm_fp;
504b169a6bSchristos   const char *cdm_toplevel_indent;
518dffb485Schristos } ctf_dump_membstate_t;
528dffb485Schristos 
538dffb485Schristos static int
548dffb485Schristos ctf_dump_append (ctf_dump_state_t *state, char *str)
558dffb485Schristos {
568dffb485Schristos   ctf_dump_item_t *cdi;
578dffb485Schristos 
588dffb485Schristos   if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
598dffb485Schristos     return (ctf_set_errno (state->cds_fp, ENOMEM));
608dffb485Schristos 
618dffb485Schristos   cdi->cdi_item = str;
628dffb485Schristos   ctf_list_append (&state->cds_items, cdi);
638dffb485Schristos   return 0;
648dffb485Schristos }
658dffb485Schristos 
668dffb485Schristos static void
678dffb485Schristos ctf_dump_free (ctf_dump_state_t *state)
688dffb485Schristos {
698dffb485Schristos   ctf_dump_item_t *cdi, *next_cdi;
708dffb485Schristos 
718dffb485Schristos   if (state == NULL)
728dffb485Schristos     return;
738dffb485Schristos 
748dffb485Schristos   for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
758dffb485Schristos        cdi = next_cdi)
768dffb485Schristos     {
778dffb485Schristos       free (cdi->cdi_item);
788dffb485Schristos       next_cdi = ctf_list_next (cdi);
798dffb485Schristos       free (cdi);
808dffb485Schristos     }
818dffb485Schristos }
828dffb485Schristos 
834b169a6bSchristos /* Return a dump for a single type, without member info: but do optionally show
844b169a6bSchristos    the type's references.  */
854b169a6bSchristos 
864b169a6bSchristos #define CTF_FT_REFS     0x2 	/* Print referenced types.  */
874b169a6bSchristos #define CTF_FT_BITFIELD 0x4	/* Print :BITS if a bitfield.  */
884b169a6bSchristos #define CTF_FT_ID       0x8	/* Print "ID: " in front of type IDs.  */
898dffb485Schristos 
908dffb485Schristos static char *
914b169a6bSchristos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
928dffb485Schristos {
938dffb485Schristos   ctf_id_t new_id;
948dffb485Schristos   char *str = NULL, *bit = NULL, *buf = NULL;
958dffb485Schristos 
964b169a6bSchristos   ctf_set_errno (fp, 0);
978dffb485Schristos   new_id = id;
988dffb485Schristos   do
998dffb485Schristos     {
1004b169a6bSchristos       ctf_encoding_t ep;
1014b169a6bSchristos       ctf_arinfo_t ar;
1024b169a6bSchristos       int kind, unsliced_kind;
1034b169a6bSchristos       ssize_t size, align;
1048dffb485Schristos       const char *nonroot_leader = "";
1058dffb485Schristos       const char *nonroot_trailer = "";
1064b169a6bSchristos       const char *idstr = "";
1078dffb485Schristos 
1088dffb485Schristos       id = new_id;
1098dffb485Schristos       if (flag == CTF_ADD_NONROOT)
1108dffb485Schristos 	{
1118dffb485Schristos 	  nonroot_leader = "{";
1128dffb485Schristos 	  nonroot_trailer = "}";
1138dffb485Schristos 	}
1148dffb485Schristos 
1158dffb485Schristos       buf = ctf_type_aname (fp, id);
1168dffb485Schristos       if (!buf)
1178dffb485Schristos 	{
1188dffb485Schristos 	  if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
1198dffb485Schristos 	    {
1204b169a6bSchristos 	      ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
1218dffb485Schristos 	      str = str_append (str, " (type not represented in CTF)");
1224b169a6bSchristos 	      return str;
1238dffb485Schristos 	    }
1248dffb485Schristos 
1258dffb485Schristos 	  goto err;
1268dffb485Schristos 	}
1278dffb485Schristos 
1284b169a6bSchristos       if (flag & CTF_FT_ID)
1294b169a6bSchristos 	idstr = "ID ";
1304b169a6bSchristos       if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
1314b169a6bSchristos 		    id, ctf_type_kind (fp, id)) < 0)
1328dffb485Schristos 	goto oom;
1338dffb485Schristos       str = str_append (str, bit);
1348dffb485Schristos       free (bit);
1358dffb485Schristos       bit = NULL;
1368dffb485Schristos 
1378dffb485Schristos       if (buf[0] != '\0')
1388dffb485Schristos 	str = str_append (str, buf);
1398dffb485Schristos 
1408dffb485Schristos       free (buf);
1418dffb485Schristos       buf = NULL;
1428dffb485Schristos 
1434b169a6bSchristos       unsliced_kind = ctf_type_kind_unsliced (fp, id);
1444b169a6bSchristos       kind = ctf_type_kind (fp, id);
1454b169a6bSchristos 
1464b169a6bSchristos       /* Report encodings of everything with an encoding other than enums:
1474b169a6bSchristos 	 base-type enums cannot have a nonzero cte_offset or cte_bits value.
1484b169a6bSchristos 	 (Slices of them can, but they are of kind CTF_K_SLICE.)  */
1494b169a6bSchristos       if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
1508dffb485Schristos 	{
1514b169a6bSchristos 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
1524b169a6bSchristos 	      && flag & CTF_FT_BITFIELD)
1538dffb485Schristos 	    {
1544b169a6bSchristos 	      if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
1558dffb485Schristos 		goto oom;
1568dffb485Schristos 	      str = str_append (str, bit);
1578dffb485Schristos 	      free (bit);
1588dffb485Schristos 	      bit = NULL;
1594b169a6bSchristos 	    }
1608dffb485Schristos 
1614b169a6bSchristos 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
1624b169a6bSchristos 	      || ep.cte_offset != 0)
1634b169a6bSchristos 	    {
1644b169a6bSchristos 	      const char *slice = "";
1654b169a6bSchristos 
1664b169a6bSchristos 	      if (unsliced_kind == CTF_K_SLICE)
1674b169a6bSchristos 		slice = "slice ";
1684b169a6bSchristos 
1694b169a6bSchristos 	      if (asprintf (&bit, " [%s0x%x:0x%x]",
1704b169a6bSchristos 			    slice, ep.cte_offset, ep.cte_bits) < 0)
1714b169a6bSchristos 		goto oom;
1724b169a6bSchristos 	      str = str_append (str, bit);
1734b169a6bSchristos 	      free (bit);
1744b169a6bSchristos 	      bit = NULL;
1754b169a6bSchristos 	    }
1764b169a6bSchristos 
1774b169a6bSchristos 	  if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
1784b169a6bSchristos 	    goto oom;
1794b169a6bSchristos 	  str = str_append (str, bit);
1804b169a6bSchristos 	  free (bit);
1814b169a6bSchristos 	  bit = NULL;
1824b169a6bSchristos 	}
1834b169a6bSchristos 
1844b169a6bSchristos       size = ctf_type_size (fp, id);
1854b169a6bSchristos       if (kind != CTF_K_FUNCTION && size >= 0)
1864b169a6bSchristos 	{
1874b169a6bSchristos 	  if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
1888dffb485Schristos 	    goto oom;
1898dffb485Schristos 
1908dffb485Schristos 	  str = str_append (str, bit);
1918dffb485Schristos 	  free (bit);
1928dffb485Schristos 	  bit = NULL;
1934b169a6bSchristos 	}
1948dffb485Schristos 
1954b169a6bSchristos       align = ctf_type_align (fp, id);
1964b169a6bSchristos       if (align >= 0)
1974b169a6bSchristos 	{
1984b169a6bSchristos 	  if (asprintf (&bit, " (aligned at 0x%lx)",
1994b169a6bSchristos 			(unsigned long int) align) < 0)
2004b169a6bSchristos 	    goto oom;
2014b169a6bSchristos 
2024b169a6bSchristos 	  str = str_append (str, bit);
2034b169a6bSchristos 	  free (bit);
2044b169a6bSchristos 	  bit = NULL;
2054b169a6bSchristos 	}
2064b169a6bSchristos 
2074b169a6bSchristos       if (nonroot_trailer[0] != 0)
2084b169a6bSchristos 	str = str_append (str, nonroot_trailer);
2094b169a6bSchristos 
2104b169a6bSchristos       /* Just exit after one iteration if we are not showing the types this type
2114b169a6bSchristos 	 references.  */
2124b169a6bSchristos       if (!(flag & CTF_FT_REFS))
2134b169a6bSchristos 	return str;
2144b169a6bSchristos 
2154b169a6bSchristos       /* Keep going as long as this type references another.  We consider arrays
2164b169a6bSchristos 	 to "reference" their element type. */
2174b169a6bSchristos 
2184b169a6bSchristos       if (kind == CTF_K_ARRAY)
2194b169a6bSchristos 	{
2204b169a6bSchristos 	  if (ctf_array_info (fp, id, &ar) < 0)
2214b169a6bSchristos 	    goto err;
2224b169a6bSchristos 	  new_id = ar.ctr_contents;
2234b169a6bSchristos 	}
2244b169a6bSchristos       else
2258dffb485Schristos 	new_id = ctf_type_reference (fp, id);
2268dffb485Schristos       if (new_id != CTF_ERR)
2278dffb485Schristos 	str = str_append (str, " -> ");
2284b169a6bSchristos     }
2294b169a6bSchristos   while (new_id != CTF_ERR);
2308dffb485Schristos 
2318dffb485Schristos   if (ctf_errno (fp) != ECTF_NOTREF)
2328dffb485Schristos     {
2338dffb485Schristos       free (str);
2348dffb485Schristos       return NULL;
2358dffb485Schristos     }
2368dffb485Schristos 
2378dffb485Schristos   return str;
2388dffb485Schristos 
2398dffb485Schristos  oom:
2408dffb485Schristos   ctf_set_errno (fp, errno);
2418dffb485Schristos  err:
242*12989c96Schristos   ctf_err_warn (fp, 1, ctf_errno (fp), _("cannot format name dumping type 0x%lx"),
243*12989c96Schristos 		id);
2448dffb485Schristos   free (buf);
2458dffb485Schristos   free (str);
2468dffb485Schristos   free (bit);
2478dffb485Schristos   return NULL;
2488dffb485Schristos }
2498dffb485Schristos 
2508dffb485Schristos /* Dump one string field from the file header into the cds_items.  */
2518dffb485Schristos static int
2524b169a6bSchristos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
2538dffb485Schristos 			  const char *name, uint32_t value)
2548dffb485Schristos {
2558dffb485Schristos   char *str;
2568dffb485Schristos   if (value)
2578dffb485Schristos     {
2588dffb485Schristos       if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
2598dffb485Schristos 	goto err;
2608dffb485Schristos       ctf_dump_append (state, str);
2618dffb485Schristos     }
2628dffb485Schristos   return 0;
2638dffb485Schristos 
2648dffb485Schristos  err:
2658dffb485Schristos   return (ctf_set_errno (fp, errno));
2668dffb485Schristos }
2678dffb485Schristos 
2688dffb485Schristos /* Dump one section-offset field from the file header into the cds_items.  */
2698dffb485Schristos static int
2704b169a6bSchristos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
2718dffb485Schristos 			   const char *sect, uint32_t off, uint32_t nextoff)
2728dffb485Schristos {
2738dffb485Schristos   char *str;
2748dffb485Schristos   if (nextoff - off)
2758dffb485Schristos     {
2768dffb485Schristos       if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
2778dffb485Schristos 		    (unsigned long) off, (unsigned long) (nextoff - 1),
2788dffb485Schristos 		    (unsigned long) (nextoff - off)) < 0)
2798dffb485Schristos 	goto err;
2808dffb485Schristos       ctf_dump_append (state, str);
2818dffb485Schristos     }
2828dffb485Schristos   return 0;
2838dffb485Schristos 
2848dffb485Schristos  err:
2858dffb485Schristos   return (ctf_set_errno (fp, errno));
2868dffb485Schristos }
2878dffb485Schristos 
2888dffb485Schristos /* Dump the file header into the cds_items.  */
2898dffb485Schristos static int
2904b169a6bSchristos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
2918dffb485Schristos {
2928dffb485Schristos   char *str;
2934b169a6bSchristos   char *flagstr = NULL;
2948dffb485Schristos   const ctf_header_t *hp = fp->ctf_header;
2958dffb485Schristos   const char *vertab[] =
2968dffb485Schristos     {
2978dffb485Schristos      NULL, "CTF_VERSION_1",
2988dffb485Schristos      "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
2998dffb485Schristos      "boundaries)",
3008dffb485Schristos      "CTF_VERSION_2",
3018dffb485Schristos      "CTF_VERSION_3", NULL
3028dffb485Schristos     };
3038dffb485Schristos   const char *verstr = NULL;
3048dffb485Schristos 
3054b169a6bSchristos   if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
3068dffb485Schristos       goto err;
3078dffb485Schristos   ctf_dump_append (state, str);
3088dffb485Schristos 
3098dffb485Schristos   if (hp->cth_version <= CTF_VERSION)
3108dffb485Schristos     verstr = vertab[hp->cth_version];
3118dffb485Schristos 
3128dffb485Schristos   if (verstr == NULL)
3138dffb485Schristos     verstr = "(not a valid version)";
3148dffb485Schristos 
3158dffb485Schristos   if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
3168dffb485Schristos 		verstr) < 0)
3178dffb485Schristos     goto err;
3188dffb485Schristos   ctf_dump_append (state, str);
3198dffb485Schristos 
3208dffb485Schristos   /* Everything else is only printed if present.  */
3218dffb485Schristos 
3224b169a6bSchristos   /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
3238dffb485Schristos      flags representing compression, etc, are turned off as the file is
3248dffb485Schristos      decompressed.  So we store a copy of the flags before they are changed, for
3258dffb485Schristos      the dumper.  */
3268dffb485Schristos 
3278dffb485Schristos   if (fp->ctf_openflags > 0)
3288dffb485Schristos     {
3294b169a6bSchristos       if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
3304b169a6bSchristos 		    fp->ctf_openflags & CTF_F_COMPRESS
3314b169a6bSchristos 		    ? "CTF_F_COMPRESS": "",
3324b169a6bSchristos 		    (fp->ctf_openflags & CTF_F_COMPRESS)
3334b169a6bSchristos 		    && (fp->ctf_openflags & ~CTF_F_COMPRESS)
3344b169a6bSchristos 		    ? ", " : "",
3354b169a6bSchristos 		    fp->ctf_openflags & CTF_F_NEWFUNCINFO
3364b169a6bSchristos 		    ? "CTF_F_NEWFUNCINFO" : "",
337*12989c96Schristos 		    (fp->ctf_openflags & (CTF_F_NEWFUNCINFO))
3384b169a6bSchristos 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
3394b169a6bSchristos 		    ? ", " : "",
3404b169a6bSchristos 		    fp->ctf_openflags & CTF_F_IDXSORTED
3414b169a6bSchristos 		    ? "CTF_F_IDXSORTED" : "",
342*12989c96Schristos 		    fp->ctf_openflags & (CTF_F_IDXSORTED)
3434b169a6bSchristos 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
3444b169a6bSchristos 					      | CTF_F_IDXSORTED))
3454b169a6bSchristos 		    ? ", " : "",
3464b169a6bSchristos 		    fp->ctf_openflags & CTF_F_DYNSTR
3474b169a6bSchristos 		    ? "CTF_F_DYNSTR" : "") < 0)
3484b169a6bSchristos 	goto err;
3494b169a6bSchristos 
3504b169a6bSchristos       if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
3518dffb485Schristos 	goto err;
3528dffb485Schristos       ctf_dump_append (state, str);
3538dffb485Schristos     }
3548dffb485Schristos 
3558dffb485Schristos   if (ctf_dump_header_strfield (fp, state, "Parent label",
3568dffb485Schristos 				hp->cth_parlabel) < 0)
3578dffb485Schristos     goto err;
3588dffb485Schristos 
3598dffb485Schristos   if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
3608dffb485Schristos     goto err;
3618dffb485Schristos 
3628dffb485Schristos   if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
3638dffb485Schristos 				hp->cth_cuname) < 0)
3648dffb485Schristos     goto err;
3658dffb485Schristos 
3668dffb485Schristos   if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
3678dffb485Schristos 				 hp->cth_objtoff) < 0)
3688dffb485Schristos     goto err;
3698dffb485Schristos 
3708dffb485Schristos   if (ctf_dump_header_sectfield (fp, state, "Data object section",
3718dffb485Schristos 				 hp->cth_objtoff, hp->cth_funcoff) < 0)
3728dffb485Schristos     goto err;
3738dffb485Schristos 
3748dffb485Schristos   if (ctf_dump_header_sectfield (fp, state, "Function info section",
3754b169a6bSchristos 				 hp->cth_funcoff, hp->cth_objtidxoff) < 0)
3764b169a6bSchristos     goto err;
3774b169a6bSchristos 
3784b169a6bSchristos   if (ctf_dump_header_sectfield (fp, state, "Object index section",
3794b169a6bSchristos 				 hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
3804b169a6bSchristos     goto err;
3814b169a6bSchristos 
3824b169a6bSchristos   if (ctf_dump_header_sectfield (fp, state, "Function index section",
3834b169a6bSchristos 				 hp->cth_funcidxoff, hp->cth_varoff) < 0)
3848dffb485Schristos     goto err;
3858dffb485Schristos 
3868dffb485Schristos   if (ctf_dump_header_sectfield (fp, state, "Variable section",
3878dffb485Schristos 				 hp->cth_varoff, hp->cth_typeoff) < 0)
3888dffb485Schristos     goto err;
3898dffb485Schristos 
3908dffb485Schristos   if (ctf_dump_header_sectfield (fp, state, "Type section",
3918dffb485Schristos 				 hp->cth_typeoff, hp->cth_stroff) < 0)
3928dffb485Schristos     goto err;
3938dffb485Schristos 
3948dffb485Schristos   if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
3958dffb485Schristos 				 hp->cth_stroff + hp->cth_strlen + 1) < 0)
3968dffb485Schristos     goto err;
3978dffb485Schristos 
3988dffb485Schristos   return 0;
3998dffb485Schristos  err:
4004b169a6bSchristos   free (flagstr);
4018dffb485Schristos   return (ctf_set_errno (fp, errno));
4028dffb485Schristos }
4038dffb485Schristos 
4048dffb485Schristos /* Dump a single label into the cds_items.  */
4058dffb485Schristos 
4068dffb485Schristos static int
4078dffb485Schristos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
4088dffb485Schristos 		void *arg)
4098dffb485Schristos {
4108dffb485Schristos   char *str;
4118dffb485Schristos   char *typestr;
4128dffb485Schristos   ctf_dump_state_t *state = arg;
4138dffb485Schristos 
4148dffb485Schristos   if (asprintf (&str, "%s -> ", name) < 0)
4158dffb485Schristos     return (ctf_set_errno (state->cds_fp, errno));
4168dffb485Schristos 
4178dffb485Schristos   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
4184b169a6bSchristos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4198dffb485Schristos     {
4208dffb485Schristos       free (str);
4218dffb485Schristos       return 0;				/* Swallow the error.  */
4228dffb485Schristos     }
4238dffb485Schristos 
4248dffb485Schristos   str = str_append (str, typestr);
4258dffb485Schristos   free (typestr);
4268dffb485Schristos 
4278dffb485Schristos   ctf_dump_append (state, str);
4288dffb485Schristos   return 0;
4298dffb485Schristos }
4308dffb485Schristos 
4314b169a6bSchristos /* Dump all the object or function entries into the cds_items.  */
4328dffb485Schristos 
4338dffb485Schristos static int
4344b169a6bSchristos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
4358dffb485Schristos {
4364b169a6bSchristos   const char *name;
4374b169a6bSchristos   ctf_id_t id;
4384b169a6bSchristos   ctf_next_t *i = NULL;
4394b169a6bSchristos   char *str = NULL;
4408dffb485Schristos 
4414b169a6bSchristos   if ((functions && fp->ctf_funcidx_names)
4424b169a6bSchristos       || (!functions && fp->ctf_objtidx_names))
4434b169a6bSchristos     str = str_append (str, _("Section is indexed.\n"));
444*12989c96Schristos   else if (fp->ctf_ext_symtab.cts_data == NULL)
4454b169a6bSchristos     str = str_append (str, _("No symbol table.\n"));
4468dffb485Schristos 
4474b169a6bSchristos   while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
4488dffb485Schristos     {
4494b169a6bSchristos       char *typestr = NULL;
4508dffb485Schristos 
4514b169a6bSchristos       /* Emit the name, if we know it.  No trailing space: ctf_dump_format_type
4524b169a6bSchristos 	 has a leading one.   */
4534b169a6bSchristos       if (name)
4548dffb485Schristos 	{
4554b169a6bSchristos 	  if (asprintf (&str, "%s -> ", name) < 0)
4564b169a6bSchristos 	    goto oom;
4578dffb485Schristos 	}
4588dffb485Schristos       else
4594b169a6bSchristos 	str = xstrdup ("");
4608dffb485Schristos 
4614b169a6bSchristos       if ((typestr = ctf_dump_format_type (state->cds_fp, id,
4624b169a6bSchristos 					   CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4638dffb485Schristos 	{
4644b169a6bSchristos 	  ctf_dump_append (state, str);
4654b169a6bSchristos 	  continue;				/* Swallow the error.  */
4668dffb485Schristos 	}
4678dffb485Schristos 
4688dffb485Schristos       str = str_append (str, typestr);
4698dffb485Schristos       free (typestr);
4708dffb485Schristos       ctf_dump_append (state, str);
4718dffb485Schristos       continue;
4728dffb485Schristos 
4738dffb485Schristos     oom:
4744b169a6bSchristos       ctf_set_errno (fp, ENOMEM);
4754b169a6bSchristos       ctf_next_destroy (i);
4764b169a6bSchristos       return -1;
4778dffb485Schristos     }
4788dffb485Schristos   return 0;
4798dffb485Schristos }
4808dffb485Schristos 
4818dffb485Schristos /* Dump a single variable into the cds_items.  */
4828dffb485Schristos static int
4838dffb485Schristos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
4848dffb485Schristos {
4858dffb485Schristos   char *str;
4868dffb485Schristos   char *typestr;
4878dffb485Schristos   ctf_dump_state_t *state = arg;
4888dffb485Schristos 
4898dffb485Schristos   if (asprintf (&str, "%s -> ", name) < 0)
4908dffb485Schristos     return (ctf_set_errno (state->cds_fp, errno));
4918dffb485Schristos 
4928dffb485Schristos   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
4934b169a6bSchristos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4948dffb485Schristos     {
4958dffb485Schristos       free (str);
4968dffb485Schristos       return 0;			/* Swallow the error.  */
4978dffb485Schristos     }
4988dffb485Schristos 
4998dffb485Schristos   str = str_append (str, typestr);
5008dffb485Schristos   free (typestr);
5018dffb485Schristos 
5028dffb485Schristos   ctf_dump_append (state, str);
5038dffb485Schristos   return 0;
5048dffb485Schristos }
5058dffb485Schristos 
5064b169a6bSchristos /* Dump a single struct/union member into the string in the membstate.  */
5078dffb485Schristos static int
5088dffb485Schristos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
5098dffb485Schristos 		 int depth, void *arg)
5108dffb485Schristos {
5118dffb485Schristos   ctf_dump_membstate_t *state = arg;
5128dffb485Schristos   char *typestr = NULL;
5138dffb485Schristos   char *bit = NULL;
5148dffb485Schristos 
5154b169a6bSchristos   /* The struct/union itself has already been printed.  */
5164b169a6bSchristos   if (depth == 0)
5178dffb485Schristos     return 0;
5188dffb485Schristos 
5194b169a6bSchristos   if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
5204b169a6bSchristos     goto oom;
5214b169a6bSchristos   *state->cdm_str = str_append (*state->cdm_str, bit);
5224b169a6bSchristos   free (bit);
5234b169a6bSchristos 
5244b169a6bSchristos   if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
5254b169a6bSchristos 				       CTF_ADD_ROOT | CTF_FT_BITFIELD
5264b169a6bSchristos 				       | CTF_FT_ID)) == NULL)
5278dffb485Schristos     return -1;				/* errno is set for us.  */
5288dffb485Schristos 
5294b169a6bSchristos   if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
5308dffb485Schristos     goto oom;
5318dffb485Schristos 
5328dffb485Schristos   *state->cdm_str = str_append (*state->cdm_str, bit);
5338dffb485Schristos   free (typestr);
5348dffb485Schristos   free (bit);
5358dffb485Schristos   typestr = NULL;
5368dffb485Schristos   bit = NULL;
5378dffb485Schristos 
5388dffb485Schristos   return 0;
5398dffb485Schristos 
5408dffb485Schristos  oom:
5418dffb485Schristos   free (typestr);
5428dffb485Schristos   free (bit);
5438dffb485Schristos   return (ctf_set_errno (state->cdm_fp, errno));
5448dffb485Schristos }
5458dffb485Schristos 
5464b169a6bSchristos /* Report the number of digits in the hexadecimal representation of a type
5474b169a6bSchristos    ID.  */
5484b169a6bSchristos 
5494b169a6bSchristos static int
5504b169a6bSchristos type_hex_digits (ctf_id_t id)
5514b169a6bSchristos {
5524b169a6bSchristos   int i = 0;
5534b169a6bSchristos 
5544b169a6bSchristos   if (id == 0)
5554b169a6bSchristos     return 1;
5564b169a6bSchristos 
5574b169a6bSchristos   for (; id > 0; id >>= 4, i++);
5584b169a6bSchristos   return i;
5594b169a6bSchristos }
5604b169a6bSchristos 
5618dffb485Schristos /* Dump a single type into the cds_items.  */
5628dffb485Schristos static int
5638dffb485Schristos ctf_dump_type (ctf_id_t id, int flag, void *arg)
5648dffb485Schristos {
5658dffb485Schristos   char *str;
5664b169a6bSchristos   char *indent;
5678dffb485Schristos   ctf_dump_state_t *state = arg;
5684b169a6bSchristos   ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
5698dffb485Schristos 
5704b169a6bSchristos   /* Indent neatly.  */
5714b169a6bSchristos   if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
5724b169a6bSchristos     return (ctf_set_errno (state->cds_fp, ENOMEM));
5734b169a6bSchristos 
5744b169a6bSchristos   /* Dump the type itself.  */
5754b169a6bSchristos   if ((str = ctf_dump_format_type (state->cds_fp, id,
5764b169a6bSchristos 				   flag | CTF_FT_REFS)) == NULL)
5778dffb485Schristos     goto err;
5788dffb485Schristos   str = str_append (str, "\n");
5794b169a6bSchristos 
5804b169a6bSchristos   membstate.cdm_toplevel_indent = indent;
5814b169a6bSchristos 
5824b169a6bSchristos   /* Member dumping for structs, unions...  */
5834b169a6bSchristos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
5844b169a6bSchristos       || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
5854b169a6bSchristos     {
5868dffb485Schristos       if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
5878dffb485Schristos 	{
5888dffb485Schristos 	  if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
5898dffb485Schristos 	    {
5908dffb485Schristos 	      ctf_dump_append (state, str);
5918dffb485Schristos 	      return 0;
5928dffb485Schristos 	    }
5938dffb485Schristos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
5948dffb485Schristos 			_("cannot visit members dumping type 0x%lx"), id);
5958dffb485Schristos 	  goto err;
5968dffb485Schristos 	}
5974b169a6bSchristos     }
5988dffb485Schristos 
5994b169a6bSchristos   /* ... and enums, for which we dump the first and last few members and skip
6004b169a6bSchristos      the ones in the middle.  */
6014b169a6bSchristos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
6024b169a6bSchristos     {
6034b169a6bSchristos       int enum_count = ctf_member_count (state->cds_fp, id);
6044b169a6bSchristos       ctf_next_t *it = NULL;
6054b169a6bSchristos       int i = 0;
6064b169a6bSchristos       const char *enumerand;
6074b169a6bSchristos       char *bit;
6084b169a6bSchristos       int value;
6094b169a6bSchristos 
6104b169a6bSchristos       while ((enumerand = ctf_enum_next (state->cds_fp, id,
6114b169a6bSchristos 					 &it, &value)) != NULL)
6124b169a6bSchristos 	{
6134b169a6bSchristos 	  i++;
6144b169a6bSchristos 	  if ((i > 5) && (i < enum_count - 4))
6154b169a6bSchristos 	    continue;
6164b169a6bSchristos 
6174b169a6bSchristos 	  str = str_append (str, indent);
6184b169a6bSchristos 
6194b169a6bSchristos 	  if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
6204b169a6bSchristos 	    {
6214b169a6bSchristos 	      ctf_next_destroy (it);
6224b169a6bSchristos 	      goto oom;
6234b169a6bSchristos 	    }
6244b169a6bSchristos 	  str = str_append (str, bit);
6254b169a6bSchristos 	  free (bit);
6264b169a6bSchristos 
6274b169a6bSchristos 	  if ((i == 5) && (enum_count > 10))
6284b169a6bSchristos 	    {
6294b169a6bSchristos 	      str = str_append (str, indent);
6304b169a6bSchristos 	      str = str_append (str, "...\n");
6314b169a6bSchristos 	    }
6324b169a6bSchristos 	}
6334b169a6bSchristos       if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
6344b169a6bSchristos 	{
6354b169a6bSchristos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
6364b169a6bSchristos 			_("cannot visit enumerands dumping type 0x%lx"), id);
6374b169a6bSchristos 	  goto err;
6384b169a6bSchristos 	}
6394b169a6bSchristos     }
6408dffb485Schristos 
6418dffb485Schristos   ctf_dump_append (state, str);
6424b169a6bSchristos   free (indent);
6434b169a6bSchristos 
6448dffb485Schristos   return 0;
6458dffb485Schristos 
6468dffb485Schristos  err:
6474b169a6bSchristos   free (indent);
6488dffb485Schristos   free (str);
6494b169a6bSchristos 
6504b169a6bSchristos   /* Swallow the error: don't cause an error in one type to abort all
6514b169a6bSchristos      type dumping.  */
6524b169a6bSchristos   return 0;
6534b169a6bSchristos 
6544b169a6bSchristos  oom:
6554b169a6bSchristos   free (indent);
6564b169a6bSchristos   free (str);
6574b169a6bSchristos   return ctf_set_errno (state->cds_fp, ENOMEM);
6588dffb485Schristos }
6598dffb485Schristos 
6608dffb485Schristos /* Dump the string table into the cds_items.  */
6618dffb485Schristos 
6628dffb485Schristos static int
6634b169a6bSchristos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
6648dffb485Schristos {
6658dffb485Schristos   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
6668dffb485Schristos 
6678dffb485Schristos   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
6688dffb485Schristos 	 fp->ctf_str[CTF_STRTAB_0].cts_len;)
6698dffb485Schristos     {
6708dffb485Schristos       char *str;
6714b169a6bSchristos       if (asprintf (&str, "0x%lx: %s",
6728dffb485Schristos 		    (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
6738dffb485Schristos 		    s) < 0)
6748dffb485Schristos 	return (ctf_set_errno (fp, errno));
6758dffb485Schristos       ctf_dump_append (state, str);
6768dffb485Schristos       s += strlen (s) + 1;
6778dffb485Schristos     }
6788dffb485Schristos 
6798dffb485Schristos   return 0;
6808dffb485Schristos }
6818dffb485Schristos 
6828dffb485Schristos /* Dump a particular section of a CTF file, in textual form.  Call with a
6838dffb485Schristos    pointer to a NULL STATE: each call emits a dynamically allocated string
6848dffb485Schristos    containing a description of one entity in the specified section, in order.
6858dffb485Schristos    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
6868dffb485Schristos    has been entirely dumped, the call returns NULL and frees and annuls the
6878dffb485Schristos    STATE, ready for another section to be dumped.  The returned textual content
6888dffb485Schristos    may span multiple lines: between each call the FUNC is called with one
6898dffb485Schristos    textual line at a time, and should return a suitably decorated line (it can
6908dffb485Schristos    allocate a new one and return it if it likes).  */
6918dffb485Schristos 
6928dffb485Schristos char *
6934b169a6bSchristos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
6948dffb485Schristos 	  ctf_dump_decorate_f *func, void *arg)
6958dffb485Schristos {
6968dffb485Schristos   char *str;
6978dffb485Schristos   char *line;
6988dffb485Schristos   ctf_dump_state_t *state = NULL;
6998dffb485Schristos 
7008dffb485Schristos   if (*statep == NULL)
7018dffb485Schristos     {
7028dffb485Schristos       /* Data collection.  Transforming a call-at-a-time iterator into a
7038dffb485Schristos 	 return-at-a-time iterator in a language without call/cc is annoying. It
7048dffb485Schristos 	 is easiest to simply collect everything at once and then return it bit
7058dffb485Schristos 	 by bit.  The first call will take (much) longer than otherwise, but the
7068dffb485Schristos 	 amortized time needed is the same.  */
7078dffb485Schristos 
7088dffb485Schristos       if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
7098dffb485Schristos 	{
7108dffb485Schristos 	  ctf_set_errno (fp, ENOMEM);
7118dffb485Schristos 	  goto end;
7128dffb485Schristos 	}
7138dffb485Schristos       state = *statep;
7148dffb485Schristos 
7158dffb485Schristos       memset (state, 0, sizeof (struct ctf_dump_state));
7168dffb485Schristos       state->cds_fp = fp;
7178dffb485Schristos       state->cds_sect = sect;
7188dffb485Schristos 
7198dffb485Schristos       switch (sect)
7208dffb485Schristos 	{
7218dffb485Schristos 	case CTF_SECT_HEADER:
7228dffb485Schristos 	  ctf_dump_header (fp, state);
7238dffb485Schristos 	  break;
7248dffb485Schristos 	case CTF_SECT_LABEL:
7258dffb485Schristos 	  if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
7268dffb485Schristos 	    {
7278dffb485Schristos 	      if (ctf_errno (fp) != ECTF_NOLABELDATA)
7288dffb485Schristos 		goto end;		/* errno is set for us.  */
7298dffb485Schristos 	      ctf_set_errno (fp, 0);
7308dffb485Schristos 	    }
7318dffb485Schristos 	  break;
7328dffb485Schristos 	case CTF_SECT_OBJT:
7334b169a6bSchristos 	  if (ctf_dump_objts (fp, state, 0) < 0)
7348dffb485Schristos 	    goto end;			/* errno is set for us.  */
7358dffb485Schristos 	  break;
7368dffb485Schristos 	case CTF_SECT_FUNC:
7374b169a6bSchristos 	  if (ctf_dump_objts (fp, state, 1) < 0)
7388dffb485Schristos 	    goto end;			/* errno is set for us.  */
7398dffb485Schristos 	  break;
7408dffb485Schristos 	case CTF_SECT_VAR:
7418dffb485Schristos 	  if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
7428dffb485Schristos 	    goto end;			/* errno is set for us.  */
7438dffb485Schristos 	  break;
7448dffb485Schristos 	case CTF_SECT_TYPE:
7458dffb485Schristos 	  if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
7468dffb485Schristos 	    goto end;			/* errno is set for us.  */
7478dffb485Schristos 	  break;
7488dffb485Schristos 	case CTF_SECT_STR:
7498dffb485Schristos 	  ctf_dump_str (fp, state);
7508dffb485Schristos 	  break;
7518dffb485Schristos 	default:
7528dffb485Schristos 	  ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
7538dffb485Schristos 	  goto end;
7548dffb485Schristos 	}
7558dffb485Schristos     }
7568dffb485Schristos   else
7578dffb485Schristos     {
7588dffb485Schristos       state = *statep;
7598dffb485Schristos 
7608dffb485Schristos       if (state->cds_sect != sect)
7618dffb485Schristos 	{
7628dffb485Schristos 	  ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
7638dffb485Schristos 	  goto end;
7648dffb485Schristos 	}
7658dffb485Schristos     }
7668dffb485Schristos 
7678dffb485Schristos   if (state->cds_current == NULL)
7688dffb485Schristos     state->cds_current = ctf_list_next (&state->cds_items);
7698dffb485Schristos   else
7708dffb485Schristos     state->cds_current = ctf_list_next (state->cds_current);
7718dffb485Schristos 
7728dffb485Schristos   if (state->cds_current == NULL)
7738dffb485Schristos     goto end;
7748dffb485Schristos 
7758dffb485Schristos   /* Hookery.  There is some extra complexity to preserve linefeeds within each
7768dffb485Schristos      item while removing linefeeds at the end.  */
7778dffb485Schristos   if (func)
7788dffb485Schristos     {
7798dffb485Schristos       size_t len;
7808dffb485Schristos 
7818dffb485Schristos       str = NULL;
7828dffb485Schristos       for (line = state->cds_current->cdi_item; line && *line; )
7838dffb485Schristos 	{
7848dffb485Schristos 	  char *nline = line;
7858dffb485Schristos 	  char *ret;
7868dffb485Schristos 
7878dffb485Schristos 	  nline = strchr (line, '\n');
7888dffb485Schristos 	  if (nline)
7898dffb485Schristos 	    nline[0] = '\0';
7908dffb485Schristos 
7918dffb485Schristos 	  ret = func (sect, line, arg);
7928dffb485Schristos 	  str = str_append (str, ret);
7938dffb485Schristos 	  str = str_append (str, "\n");
7948dffb485Schristos 	  if (ret != line)
7958dffb485Schristos 	    free (ret);
7968dffb485Schristos 
7978dffb485Schristos 	  if (nline)
7988dffb485Schristos 	    {
7998dffb485Schristos 	      nline[0] = '\n';
8008dffb485Schristos 	      nline++;
8018dffb485Schristos 	    }
8028dffb485Schristos 
8038dffb485Schristos 	  line = nline;
8048dffb485Schristos 	}
8058dffb485Schristos 
8068dffb485Schristos       len = strlen (str);
8078dffb485Schristos 
8088dffb485Schristos       if (str[len-1] == '\n')
8098dffb485Schristos 	str[len-1] = '\0';
8108dffb485Schristos     }
8118dffb485Schristos   else
8128dffb485Schristos     {
8138dffb485Schristos       str = strdup (state->cds_current->cdi_item);
8148dffb485Schristos       if (!str)
8158dffb485Schristos 	{
8168dffb485Schristos 	  ctf_set_errno (fp, ENOMEM);
8178dffb485Schristos 	  return str;
8188dffb485Schristos 	}
8198dffb485Schristos     }
8208dffb485Schristos 
8218dffb485Schristos   ctf_set_errno (fp, 0);
8228dffb485Schristos   return str;
8238dffb485Schristos 
8248dffb485Schristos  end:
8258dffb485Schristos   ctf_dump_free (state);
8268dffb485Schristos   free (state);
8278dffb485Schristos   ctf_set_errno (fp, 0);
8288dffb485Schristos   *statep = NULL;
8298dffb485Schristos   return NULL;
8308dffb485Schristos }
831