xref: /netbsd-src/external/gpl3/gdb.old/dist/libctf/ctf-dump.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
17d62b00eSchristos /* Textual dumping of CTF data.
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 <string.h>
227d62b00eSchristos 
237d62b00eSchristos #define str_append(s, a) ctf_str_append_noerr (s, a)
247d62b00eSchristos 
257d62b00eSchristos /* One item to be dumped, in string form.  */
267d62b00eSchristos 
277d62b00eSchristos typedef struct ctf_dump_item
287d62b00eSchristos {
297d62b00eSchristos   ctf_list_t cdi_list;
307d62b00eSchristos   char *cdi_item;
317d62b00eSchristos } ctf_dump_item_t;
327d62b00eSchristos 
337d62b00eSchristos /* Cross-call state for dumping.  Basically just enough to track the section in
347d62b00eSchristos    use and a list of return strings.  */
357d62b00eSchristos 
367d62b00eSchristos struct ctf_dump_state
377d62b00eSchristos {
387d62b00eSchristos   ctf_sect_names_t cds_sect;
39*6881a400Schristos   ctf_dict_t *cds_fp;
407d62b00eSchristos   ctf_dump_item_t *cds_current;
417d62b00eSchristos   ctf_list_t cds_items;
427d62b00eSchristos };
437d62b00eSchristos 
447d62b00eSchristos /* Cross-call state for ctf_dump_member. */
457d62b00eSchristos 
467d62b00eSchristos typedef struct ctf_dump_membstate
477d62b00eSchristos {
487d62b00eSchristos   char **cdm_str;
49*6881a400Schristos   ctf_dict_t *cdm_fp;
50*6881a400Schristos   const char *cdm_toplevel_indent;
517d62b00eSchristos } ctf_dump_membstate_t;
527d62b00eSchristos 
537d62b00eSchristos static int
547d62b00eSchristos ctf_dump_append (ctf_dump_state_t *state, char *str)
557d62b00eSchristos {
567d62b00eSchristos   ctf_dump_item_t *cdi;
577d62b00eSchristos 
587d62b00eSchristos   if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
597d62b00eSchristos     return (ctf_set_errno (state->cds_fp, ENOMEM));
607d62b00eSchristos 
617d62b00eSchristos   cdi->cdi_item = str;
627d62b00eSchristos   ctf_list_append (&state->cds_items, cdi);
637d62b00eSchristos   return 0;
647d62b00eSchristos }
657d62b00eSchristos 
667d62b00eSchristos static void
677d62b00eSchristos ctf_dump_free (ctf_dump_state_t *state)
687d62b00eSchristos {
697d62b00eSchristos   ctf_dump_item_t *cdi, *next_cdi;
707d62b00eSchristos 
717d62b00eSchristos   if (state == NULL)
727d62b00eSchristos     return;
737d62b00eSchristos 
747d62b00eSchristos   for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
757d62b00eSchristos        cdi = next_cdi)
767d62b00eSchristos     {
777d62b00eSchristos       free (cdi->cdi_item);
787d62b00eSchristos       next_cdi = ctf_list_next (cdi);
797d62b00eSchristos       free (cdi);
807d62b00eSchristos     }
817d62b00eSchristos }
827d62b00eSchristos 
83*6881a400Schristos /* Return a dump for a single type, without member info: but do optionally show
84*6881a400Schristos    the type's references.  */
85*6881a400Schristos 
86*6881a400Schristos #define CTF_FT_REFS     0x2 	/* Print referenced types.  */
87*6881a400Schristos #define CTF_FT_BITFIELD 0x4	/* Print :BITS if a bitfield.  */
88*6881a400Schristos #define CTF_FT_ID       0x8	/* Print "ID: " in front of type IDs.  */
897d62b00eSchristos 
907d62b00eSchristos static char *
91*6881a400Schristos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
927d62b00eSchristos {
937d62b00eSchristos   ctf_id_t new_id;
947d62b00eSchristos   char *str = NULL, *bit = NULL, *buf = NULL;
957d62b00eSchristos 
96*6881a400Schristos   ctf_set_errno (fp, 0);
977d62b00eSchristos   new_id = id;
987d62b00eSchristos   do
997d62b00eSchristos     {
100*6881a400Schristos       ctf_encoding_t ep;
101*6881a400Schristos       ctf_arinfo_t ar;
102*6881a400Schristos       int kind, unsliced_kind;
103*6881a400Schristos       ssize_t size, align;
1047d62b00eSchristos       const char *nonroot_leader = "";
1057d62b00eSchristos       const char *nonroot_trailer = "";
106*6881a400Schristos       const char *idstr = "";
1077d62b00eSchristos 
1087d62b00eSchristos       id = new_id;
1097d62b00eSchristos       if (flag == CTF_ADD_NONROOT)
1107d62b00eSchristos 	{
1117d62b00eSchristos 	  nonroot_leader = "{";
1127d62b00eSchristos 	  nonroot_trailer = "}";
1137d62b00eSchristos 	}
1147d62b00eSchristos 
1157d62b00eSchristos       buf = ctf_type_aname (fp, id);
1167d62b00eSchristos       if (!buf)
1177d62b00eSchristos 	{
1187d62b00eSchristos 	  if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
1197d62b00eSchristos 	    {
120*6881a400Schristos 	      ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
1217d62b00eSchristos 	      str = str_append (str, " (type not represented in CTF)");
122*6881a400Schristos 	      return str;
1237d62b00eSchristos 	    }
1247d62b00eSchristos 
1257d62b00eSchristos 	  goto err;
1267d62b00eSchristos 	}
1277d62b00eSchristos 
128*6881a400Schristos       if (flag & CTF_FT_ID)
129*6881a400Schristos 	idstr = "ID ";
130*6881a400Schristos       if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
131*6881a400Schristos 		    id, ctf_type_kind (fp, id)) < 0)
1327d62b00eSchristos 	goto oom;
1337d62b00eSchristos       str = str_append (str, bit);
1347d62b00eSchristos       free (bit);
1357d62b00eSchristos       bit = NULL;
1367d62b00eSchristos 
1377d62b00eSchristos       if (buf[0] != '\0')
1387d62b00eSchristos 	str = str_append (str, buf);
1397d62b00eSchristos 
1407d62b00eSchristos       free (buf);
1417d62b00eSchristos       buf = NULL;
1427d62b00eSchristos 
143*6881a400Schristos       unsliced_kind = ctf_type_kind_unsliced (fp, id);
144*6881a400Schristos       kind = ctf_type_kind (fp, id);
145*6881a400Schristos 
146*6881a400Schristos       /* Report encodings of everything with an encoding other than enums:
147*6881a400Schristos 	 base-type enums cannot have a nonzero cte_offset or cte_bits value.
148*6881a400Schristos 	 (Slices of them can, but they are of kind CTF_K_SLICE.)  */
149*6881a400Schristos       if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
1507d62b00eSchristos 	{
151*6881a400Schristos 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
152*6881a400Schristos 	      && flag & CTF_FT_BITFIELD)
1537d62b00eSchristos 	    {
154*6881a400Schristos 	      if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
1557d62b00eSchristos 		goto oom;
1567d62b00eSchristos 	      str = str_append (str, bit);
1577d62b00eSchristos 	      free (bit);
1587d62b00eSchristos 	      bit = NULL;
159*6881a400Schristos 	    }
1607d62b00eSchristos 
161*6881a400Schristos 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
162*6881a400Schristos 	      || ep.cte_offset != 0)
163*6881a400Schristos 	    {
164*6881a400Schristos 	      const char *slice = "";
165*6881a400Schristos 
166*6881a400Schristos 	      if (unsliced_kind == CTF_K_SLICE)
167*6881a400Schristos 		slice = "slice ";
168*6881a400Schristos 
169*6881a400Schristos 	      if (asprintf (&bit, " [%s0x%x:0x%x]",
170*6881a400Schristos 			    slice, ep.cte_offset, ep.cte_bits) < 0)
171*6881a400Schristos 		goto oom;
172*6881a400Schristos 	      str = str_append (str, bit);
173*6881a400Schristos 	      free (bit);
174*6881a400Schristos 	      bit = NULL;
175*6881a400Schristos 	    }
176*6881a400Schristos 
177*6881a400Schristos 	  if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
178*6881a400Schristos 	    goto oom;
179*6881a400Schristos 	  str = str_append (str, bit);
180*6881a400Schristos 	  free (bit);
181*6881a400Schristos 	  bit = NULL;
182*6881a400Schristos 	}
183*6881a400Schristos 
184*6881a400Schristos       size = ctf_type_size (fp, id);
185*6881a400Schristos       if (kind != CTF_K_FUNCTION && size >= 0)
186*6881a400Schristos 	{
187*6881a400Schristos 	  if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
1887d62b00eSchristos 	    goto oom;
1897d62b00eSchristos 
1907d62b00eSchristos 	  str = str_append (str, bit);
1917d62b00eSchristos 	  free (bit);
1927d62b00eSchristos 	  bit = NULL;
193*6881a400Schristos 	}
1947d62b00eSchristos 
195*6881a400Schristos       align = ctf_type_align (fp, id);
196*6881a400Schristos       if (align >= 0)
197*6881a400Schristos 	{
198*6881a400Schristos 	  if (asprintf (&bit, " (aligned at 0x%lx)",
199*6881a400Schristos 			(unsigned long int) align) < 0)
200*6881a400Schristos 	    goto oom;
201*6881a400Schristos 
202*6881a400Schristos 	  str = str_append (str, bit);
203*6881a400Schristos 	  free (bit);
204*6881a400Schristos 	  bit = NULL;
205*6881a400Schristos 	}
206*6881a400Schristos 
207*6881a400Schristos       if (nonroot_trailer[0] != 0)
208*6881a400Schristos 	str = str_append (str, nonroot_trailer);
209*6881a400Schristos 
210*6881a400Schristos       /* Just exit after one iteration if we are not showing the types this type
211*6881a400Schristos 	 references.  */
212*6881a400Schristos       if (!(flag & CTF_FT_REFS))
213*6881a400Schristos 	return str;
214*6881a400Schristos 
215*6881a400Schristos       /* Keep going as long as this type references another.  We consider arrays
216*6881a400Schristos 	 to "reference" their element type. */
217*6881a400Schristos 
218*6881a400Schristos       if (kind == CTF_K_ARRAY)
219*6881a400Schristos 	{
220*6881a400Schristos 	  if (ctf_array_info (fp, id, &ar) < 0)
221*6881a400Schristos 	    goto err;
222*6881a400Schristos 	  new_id = ar.ctr_contents;
223*6881a400Schristos 	}
224*6881a400Schristos       else
2257d62b00eSchristos 	new_id = ctf_type_reference (fp, id);
2267d62b00eSchristos       if (new_id != CTF_ERR)
2277d62b00eSchristos 	str = str_append (str, " -> ");
228*6881a400Schristos     }
229*6881a400Schristos   while (new_id != CTF_ERR);
2307d62b00eSchristos 
2317d62b00eSchristos   if (ctf_errno (fp) != ECTF_NOTREF)
2327d62b00eSchristos     {
2337d62b00eSchristos       free (str);
2347d62b00eSchristos       return NULL;
2357d62b00eSchristos     }
2367d62b00eSchristos 
2377d62b00eSchristos   return str;
2387d62b00eSchristos 
2397d62b00eSchristos  oom:
2407d62b00eSchristos   ctf_set_errno (fp, errno);
2417d62b00eSchristos  err:
2427d62b00eSchristos   ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
2437d62b00eSchristos   free (buf);
2447d62b00eSchristos   free (str);
2457d62b00eSchristos   free (bit);
2467d62b00eSchristos   return NULL;
2477d62b00eSchristos }
2487d62b00eSchristos 
2497d62b00eSchristos /* Dump one string field from the file header into the cds_items.  */
2507d62b00eSchristos static int
251*6881a400Schristos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
2527d62b00eSchristos 			  const char *name, uint32_t value)
2537d62b00eSchristos {
2547d62b00eSchristos   char *str;
2557d62b00eSchristos   if (value)
2567d62b00eSchristos     {
2577d62b00eSchristos       if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
2587d62b00eSchristos 	goto err;
2597d62b00eSchristos       ctf_dump_append (state, str);
2607d62b00eSchristos     }
2617d62b00eSchristos   return 0;
2627d62b00eSchristos 
2637d62b00eSchristos  err:
2647d62b00eSchristos   return (ctf_set_errno (fp, errno));
2657d62b00eSchristos }
2667d62b00eSchristos 
2677d62b00eSchristos /* Dump one section-offset field from the file header into the cds_items.  */
2687d62b00eSchristos static int
269*6881a400Schristos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
2707d62b00eSchristos 			   const char *sect, uint32_t off, uint32_t nextoff)
2717d62b00eSchristos {
2727d62b00eSchristos   char *str;
2737d62b00eSchristos   if (nextoff - off)
2747d62b00eSchristos     {
2757d62b00eSchristos       if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
2767d62b00eSchristos 		    (unsigned long) off, (unsigned long) (nextoff - 1),
2777d62b00eSchristos 		    (unsigned long) (nextoff - off)) < 0)
2787d62b00eSchristos 	goto err;
2797d62b00eSchristos       ctf_dump_append (state, str);
2807d62b00eSchristos     }
2817d62b00eSchristos   return 0;
2827d62b00eSchristos 
2837d62b00eSchristos  err:
2847d62b00eSchristos   return (ctf_set_errno (fp, errno));
2857d62b00eSchristos }
2867d62b00eSchristos 
2877d62b00eSchristos /* Dump the file header into the cds_items.  */
2887d62b00eSchristos static int
289*6881a400Schristos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
2907d62b00eSchristos {
2917d62b00eSchristos   char *str;
292*6881a400Schristos   char *flagstr = NULL;
2937d62b00eSchristos   const ctf_header_t *hp = fp->ctf_header;
2947d62b00eSchristos   const char *vertab[] =
2957d62b00eSchristos     {
2967d62b00eSchristos      NULL, "CTF_VERSION_1",
2977d62b00eSchristos      "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
2987d62b00eSchristos      "boundaries)",
2997d62b00eSchristos      "CTF_VERSION_2",
3007d62b00eSchristos      "CTF_VERSION_3", NULL
3017d62b00eSchristos     };
3027d62b00eSchristos   const char *verstr = NULL;
3037d62b00eSchristos 
304*6881a400Schristos   if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
3057d62b00eSchristos       goto err;
3067d62b00eSchristos   ctf_dump_append (state, str);
3077d62b00eSchristos 
3087d62b00eSchristos   if (hp->cth_version <= CTF_VERSION)
3097d62b00eSchristos     verstr = vertab[hp->cth_version];
3107d62b00eSchristos 
3117d62b00eSchristos   if (verstr == NULL)
3127d62b00eSchristos     verstr = "(not a valid version)";
3137d62b00eSchristos 
3147d62b00eSchristos   if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
3157d62b00eSchristos 		verstr) < 0)
3167d62b00eSchristos     goto err;
3177d62b00eSchristos   ctf_dump_append (state, str);
3187d62b00eSchristos 
3197d62b00eSchristos   /* Everything else is only printed if present.  */
3207d62b00eSchristos 
321*6881a400Schristos   /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
3227d62b00eSchristos      flags representing compression, etc, are turned off as the file is
3237d62b00eSchristos      decompressed.  So we store a copy of the flags before they are changed, for
3247d62b00eSchristos      the dumper.  */
3257d62b00eSchristos 
3267d62b00eSchristos   if (fp->ctf_openflags > 0)
3277d62b00eSchristos     {
328*6881a400Schristos       if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
329*6881a400Schristos 		    fp->ctf_openflags & CTF_F_COMPRESS
330*6881a400Schristos 		    ? "CTF_F_COMPRESS": "",
331*6881a400Schristos 		    (fp->ctf_openflags & CTF_F_COMPRESS)
332*6881a400Schristos 		    && (fp->ctf_openflags & ~CTF_F_COMPRESS)
333*6881a400Schristos 		    ? ", " : "",
334*6881a400Schristos 		    fp->ctf_openflags & CTF_F_NEWFUNCINFO
335*6881a400Schristos 		    ? "CTF_F_NEWFUNCINFO" : "",
336*6881a400Schristos 		    (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
337*6881a400Schristos 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
338*6881a400Schristos 		    ? ", " : "",
339*6881a400Schristos 		    fp->ctf_openflags & CTF_F_IDXSORTED
340*6881a400Schristos 		    ? "CTF_F_IDXSORTED" : "",
341*6881a400Schristos 		    fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
342*6881a400Schristos 					 | CTF_F_IDXSORTED)
343*6881a400Schristos 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
344*6881a400Schristos 					      | CTF_F_IDXSORTED))
345*6881a400Schristos 		    ? ", " : "",
346*6881a400Schristos 		    fp->ctf_openflags & CTF_F_DYNSTR
347*6881a400Schristos 		    ? "CTF_F_DYNSTR" : "") < 0)
348*6881a400Schristos 	goto err;
349*6881a400Schristos 
350*6881a400Schristos       if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
3517d62b00eSchristos 	goto err;
3527d62b00eSchristos       ctf_dump_append (state, str);
3537d62b00eSchristos     }
3547d62b00eSchristos 
3557d62b00eSchristos   if (ctf_dump_header_strfield (fp, state, "Parent label",
3567d62b00eSchristos 				hp->cth_parlabel) < 0)
3577d62b00eSchristos     goto err;
3587d62b00eSchristos 
3597d62b00eSchristos   if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
3607d62b00eSchristos     goto err;
3617d62b00eSchristos 
3627d62b00eSchristos   if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
3637d62b00eSchristos 				hp->cth_cuname) < 0)
3647d62b00eSchristos     goto err;
3657d62b00eSchristos 
3667d62b00eSchristos   if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
3677d62b00eSchristos 				 hp->cth_objtoff) < 0)
3687d62b00eSchristos     goto err;
3697d62b00eSchristos 
3707d62b00eSchristos   if (ctf_dump_header_sectfield (fp, state, "Data object section",
3717d62b00eSchristos 				 hp->cth_objtoff, hp->cth_funcoff) < 0)
3727d62b00eSchristos     goto err;
3737d62b00eSchristos 
3747d62b00eSchristos   if (ctf_dump_header_sectfield (fp, state, "Function info section",
375*6881a400Schristos 				 hp->cth_funcoff, hp->cth_objtidxoff) < 0)
376*6881a400Schristos     goto err;
377*6881a400Schristos 
378*6881a400Schristos   if (ctf_dump_header_sectfield (fp, state, "Object index section",
379*6881a400Schristos 				 hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
380*6881a400Schristos     goto err;
381*6881a400Schristos 
382*6881a400Schristos   if (ctf_dump_header_sectfield (fp, state, "Function index section",
383*6881a400Schristos 				 hp->cth_funcidxoff, hp->cth_varoff) < 0)
3847d62b00eSchristos     goto err;
3857d62b00eSchristos 
3867d62b00eSchristos   if (ctf_dump_header_sectfield (fp, state, "Variable section",
3877d62b00eSchristos 				 hp->cth_varoff, hp->cth_typeoff) < 0)
3887d62b00eSchristos     goto err;
3897d62b00eSchristos 
3907d62b00eSchristos   if (ctf_dump_header_sectfield (fp, state, "Type section",
3917d62b00eSchristos 				 hp->cth_typeoff, hp->cth_stroff) < 0)
3927d62b00eSchristos     goto err;
3937d62b00eSchristos 
3947d62b00eSchristos   if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
3957d62b00eSchristos 				 hp->cth_stroff + hp->cth_strlen + 1) < 0)
3967d62b00eSchristos     goto err;
3977d62b00eSchristos 
3987d62b00eSchristos   return 0;
3997d62b00eSchristos  err:
400*6881a400Schristos   free (flagstr);
4017d62b00eSchristos   return (ctf_set_errno (fp, errno));
4027d62b00eSchristos }
4037d62b00eSchristos 
4047d62b00eSchristos /* Dump a single label into the cds_items.  */
4057d62b00eSchristos 
4067d62b00eSchristos static int
4077d62b00eSchristos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
4087d62b00eSchristos 		void *arg)
4097d62b00eSchristos {
4107d62b00eSchristos   char *str;
4117d62b00eSchristos   char *typestr;
4127d62b00eSchristos   ctf_dump_state_t *state = arg;
4137d62b00eSchristos 
4147d62b00eSchristos   if (asprintf (&str, "%s -> ", name) < 0)
4157d62b00eSchristos     return (ctf_set_errno (state->cds_fp, errno));
4167d62b00eSchristos 
4177d62b00eSchristos   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
418*6881a400Schristos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4197d62b00eSchristos     {
4207d62b00eSchristos       free (str);
4217d62b00eSchristos       return 0;				/* Swallow the error.  */
4227d62b00eSchristos     }
4237d62b00eSchristos 
4247d62b00eSchristos   str = str_append (str, typestr);
4257d62b00eSchristos   free (typestr);
4267d62b00eSchristos 
4277d62b00eSchristos   ctf_dump_append (state, str);
4287d62b00eSchristos   return 0;
4297d62b00eSchristos }
4307d62b00eSchristos 
431*6881a400Schristos /* Dump all the object or function entries into the cds_items.  */
4327d62b00eSchristos 
4337d62b00eSchristos static int
434*6881a400Schristos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
4357d62b00eSchristos {
436*6881a400Schristos   const char *name;
437*6881a400Schristos   ctf_id_t id;
438*6881a400Schristos   ctf_next_t *i = NULL;
439*6881a400Schristos   char *str = NULL;
4407d62b00eSchristos 
441*6881a400Schristos   if ((functions && fp->ctf_funcidx_names)
442*6881a400Schristos       || (!functions && fp->ctf_objtidx_names))
443*6881a400Schristos     str = str_append (str, _("Section is indexed.\n"));
444*6881a400Schristos   else if (fp->ctf_symtab.cts_data == NULL)
445*6881a400Schristos     str = str_append (str, _("No symbol table.\n"));
4467d62b00eSchristos 
447*6881a400Schristos   while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
4487d62b00eSchristos     {
449*6881a400Schristos       char *typestr = NULL;
4507d62b00eSchristos 
451*6881a400Schristos       /* Emit the name, if we know it.  No trailing space: ctf_dump_format_type
452*6881a400Schristos 	 has a leading one.   */
453*6881a400Schristos       if (name)
4547d62b00eSchristos 	{
455*6881a400Schristos 	  if (asprintf (&str, "%s -> ", name) < 0)
456*6881a400Schristos 	    goto oom;
4577d62b00eSchristos 	}
4587d62b00eSchristos       else
459*6881a400Schristos 	str = xstrdup ("");
4607d62b00eSchristos 
461*6881a400Schristos       if ((typestr = ctf_dump_format_type (state->cds_fp, id,
462*6881a400Schristos 					   CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4637d62b00eSchristos 	{
464*6881a400Schristos 	  ctf_dump_append (state, str);
465*6881a400Schristos 	  continue;				/* Swallow the error.  */
4667d62b00eSchristos 	}
4677d62b00eSchristos 
4687d62b00eSchristos       str = str_append (str, typestr);
4697d62b00eSchristos       free (typestr);
4707d62b00eSchristos       ctf_dump_append (state, str);
4717d62b00eSchristos       continue;
4727d62b00eSchristos 
4737d62b00eSchristos     oom:
474*6881a400Schristos       ctf_set_errno (fp, ENOMEM);
475*6881a400Schristos       ctf_next_destroy (i);
476*6881a400Schristos       return -1;
4777d62b00eSchristos     }
4787d62b00eSchristos   return 0;
4797d62b00eSchristos }
4807d62b00eSchristos 
4817d62b00eSchristos /* Dump a single variable into the cds_items.  */
4827d62b00eSchristos static int
4837d62b00eSchristos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
4847d62b00eSchristos {
4857d62b00eSchristos   char *str;
4867d62b00eSchristos   char *typestr;
4877d62b00eSchristos   ctf_dump_state_t *state = arg;
4887d62b00eSchristos 
4897d62b00eSchristos   if (asprintf (&str, "%s -> ", name) < 0)
4907d62b00eSchristos     return (ctf_set_errno (state->cds_fp, errno));
4917d62b00eSchristos 
4927d62b00eSchristos   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
493*6881a400Schristos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
4947d62b00eSchristos     {
4957d62b00eSchristos       free (str);
4967d62b00eSchristos       return 0;			/* Swallow the error.  */
4977d62b00eSchristos     }
4987d62b00eSchristos 
4997d62b00eSchristos   str = str_append (str, typestr);
5007d62b00eSchristos   free (typestr);
5017d62b00eSchristos 
5027d62b00eSchristos   ctf_dump_append (state, str);
5037d62b00eSchristos   return 0;
5047d62b00eSchristos }
5057d62b00eSchristos 
506*6881a400Schristos /* Dump a single struct/union member into the string in the membstate.  */
5077d62b00eSchristos static int
5087d62b00eSchristos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
5097d62b00eSchristos 		 int depth, void *arg)
5107d62b00eSchristos {
5117d62b00eSchristos   ctf_dump_membstate_t *state = arg;
5127d62b00eSchristos   char *typestr = NULL;
5137d62b00eSchristos   char *bit = NULL;
5147d62b00eSchristos 
515*6881a400Schristos   /* The struct/union itself has already been printed.  */
516*6881a400Schristos   if (depth == 0)
5177d62b00eSchristos     return 0;
5187d62b00eSchristos 
519*6881a400Schristos   if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
520*6881a400Schristos     goto oom;
521*6881a400Schristos   *state->cdm_str = str_append (*state->cdm_str, bit);
522*6881a400Schristos   free (bit);
523*6881a400Schristos 
524*6881a400Schristos   if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
525*6881a400Schristos 				       CTF_ADD_ROOT | CTF_FT_BITFIELD
526*6881a400Schristos 				       | CTF_FT_ID)) == NULL)
5277d62b00eSchristos     return -1;				/* errno is set for us.  */
5287d62b00eSchristos 
529*6881a400Schristos   if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
5307d62b00eSchristos     goto oom;
5317d62b00eSchristos 
5327d62b00eSchristos   *state->cdm_str = str_append (*state->cdm_str, bit);
5337d62b00eSchristos   free (typestr);
5347d62b00eSchristos   free (bit);
5357d62b00eSchristos   typestr = NULL;
5367d62b00eSchristos   bit = NULL;
5377d62b00eSchristos 
5387d62b00eSchristos   return 0;
5397d62b00eSchristos 
5407d62b00eSchristos  oom:
5417d62b00eSchristos   free (typestr);
5427d62b00eSchristos   free (bit);
5437d62b00eSchristos   return (ctf_set_errno (state->cdm_fp, errno));
5447d62b00eSchristos }
5457d62b00eSchristos 
546*6881a400Schristos /* Report the number of digits in the hexadecimal representation of a type
547*6881a400Schristos    ID.  */
548*6881a400Schristos 
549*6881a400Schristos static int
550*6881a400Schristos type_hex_digits (ctf_id_t id)
551*6881a400Schristos {
552*6881a400Schristos   int i = 0;
553*6881a400Schristos 
554*6881a400Schristos   if (id == 0)
555*6881a400Schristos     return 1;
556*6881a400Schristos 
557*6881a400Schristos   for (; id > 0; id >>= 4, i++);
558*6881a400Schristos   return i;
559*6881a400Schristos }
560*6881a400Schristos 
5617d62b00eSchristos /* Dump a single type into the cds_items.  */
5627d62b00eSchristos static int
5637d62b00eSchristos ctf_dump_type (ctf_id_t id, int flag, void *arg)
5647d62b00eSchristos {
5657d62b00eSchristos   char *str;
566*6881a400Schristos   char *indent;
5677d62b00eSchristos   ctf_dump_state_t *state = arg;
568*6881a400Schristos   ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
5697d62b00eSchristos 
570*6881a400Schristos   /* Indent neatly.  */
571*6881a400Schristos   if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
572*6881a400Schristos     return (ctf_set_errno (state->cds_fp, ENOMEM));
573*6881a400Schristos 
574*6881a400Schristos   /* Dump the type itself.  */
575*6881a400Schristos   if ((str = ctf_dump_format_type (state->cds_fp, id,
576*6881a400Schristos 				   flag | CTF_FT_REFS)) == NULL)
5777d62b00eSchristos     goto err;
5787d62b00eSchristos   str = str_append (str, "\n");
579*6881a400Schristos 
580*6881a400Schristos   membstate.cdm_toplevel_indent = indent;
581*6881a400Schristos 
582*6881a400Schristos   /* Member dumping for structs, unions...  */
583*6881a400Schristos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
584*6881a400Schristos       || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
585*6881a400Schristos     {
5867d62b00eSchristos       if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
5877d62b00eSchristos 	{
5887d62b00eSchristos 	  if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
5897d62b00eSchristos 	    {
5907d62b00eSchristos 	      ctf_dump_append (state, str);
5917d62b00eSchristos 	      return 0;
5927d62b00eSchristos 	    }
5937d62b00eSchristos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
5947d62b00eSchristos 			_("cannot visit members dumping type 0x%lx"), id);
5957d62b00eSchristos 	  goto err;
5967d62b00eSchristos 	}
597*6881a400Schristos     }
5987d62b00eSchristos 
599*6881a400Schristos   /* ... and enums, for which we dump the first and last few members and skip
600*6881a400Schristos      the ones in the middle.  */
601*6881a400Schristos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
602*6881a400Schristos     {
603*6881a400Schristos       int enum_count = ctf_member_count (state->cds_fp, id);
604*6881a400Schristos       ctf_next_t *it = NULL;
605*6881a400Schristos       int i = 0;
606*6881a400Schristos       const char *enumerand;
607*6881a400Schristos       char *bit;
608*6881a400Schristos       int value;
609*6881a400Schristos 
610*6881a400Schristos       while ((enumerand = ctf_enum_next (state->cds_fp, id,
611*6881a400Schristos 					 &it, &value)) != NULL)
612*6881a400Schristos 	{
613*6881a400Schristos 	  i++;
614*6881a400Schristos 	  if ((i > 5) && (i < enum_count - 4))
615*6881a400Schristos 	    continue;
616*6881a400Schristos 
617*6881a400Schristos 	  str = str_append (str, indent);
618*6881a400Schristos 
619*6881a400Schristos 	  if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
620*6881a400Schristos 	    {
621*6881a400Schristos 	      ctf_next_destroy (it);
622*6881a400Schristos 	      goto oom;
623*6881a400Schristos 	    }
624*6881a400Schristos 	  str = str_append (str, bit);
625*6881a400Schristos 	  free (bit);
626*6881a400Schristos 
627*6881a400Schristos 	  if ((i == 5) && (enum_count > 10))
628*6881a400Schristos 	    {
629*6881a400Schristos 	      str = str_append (str, indent);
630*6881a400Schristos 	      str = str_append (str, "...\n");
631*6881a400Schristos 	    }
632*6881a400Schristos 	}
633*6881a400Schristos       if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
634*6881a400Schristos 	{
635*6881a400Schristos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
636*6881a400Schristos 			_("cannot visit enumerands dumping type 0x%lx"), id);
637*6881a400Schristos 	  goto err;
638*6881a400Schristos 	}
639*6881a400Schristos     }
6407d62b00eSchristos 
6417d62b00eSchristos   ctf_dump_append (state, str);
642*6881a400Schristos   free (indent);
643*6881a400Schristos 
6447d62b00eSchristos   return 0;
6457d62b00eSchristos 
6467d62b00eSchristos  err:
647*6881a400Schristos   free (indent);
6487d62b00eSchristos   free (str);
649*6881a400Schristos 
650*6881a400Schristos   /* Swallow the error: don't cause an error in one type to abort all
651*6881a400Schristos      type dumping.  */
652*6881a400Schristos   return 0;
653*6881a400Schristos 
654*6881a400Schristos  oom:
655*6881a400Schristos   free (indent);
656*6881a400Schristos   free (str);
657*6881a400Schristos   return ctf_set_errno (state->cds_fp, ENOMEM);
6587d62b00eSchristos }
6597d62b00eSchristos 
6607d62b00eSchristos /* Dump the string table into the cds_items.  */
6617d62b00eSchristos 
6627d62b00eSchristos static int
663*6881a400Schristos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
6647d62b00eSchristos {
6657d62b00eSchristos   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
6667d62b00eSchristos 
6677d62b00eSchristos   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
6687d62b00eSchristos 	 fp->ctf_str[CTF_STRTAB_0].cts_len;)
6697d62b00eSchristos     {
6707d62b00eSchristos       char *str;
671*6881a400Schristos       if (asprintf (&str, "0x%lx: %s",
6727d62b00eSchristos 		    (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
6737d62b00eSchristos 		    s) < 0)
6747d62b00eSchristos 	return (ctf_set_errno (fp, errno));
6757d62b00eSchristos       ctf_dump_append (state, str);
6767d62b00eSchristos       s += strlen (s) + 1;
6777d62b00eSchristos     }
6787d62b00eSchristos 
6797d62b00eSchristos   return 0;
6807d62b00eSchristos }
6817d62b00eSchristos 
6827d62b00eSchristos /* Dump a particular section of a CTF file, in textual form.  Call with a
6837d62b00eSchristos    pointer to a NULL STATE: each call emits a dynamically allocated string
6847d62b00eSchristos    containing a description of one entity in the specified section, in order.
6857d62b00eSchristos    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
6867d62b00eSchristos    has been entirely dumped, the call returns NULL and frees and annuls the
6877d62b00eSchristos    STATE, ready for another section to be dumped.  The returned textual content
6887d62b00eSchristos    may span multiple lines: between each call the FUNC is called with one
6897d62b00eSchristos    textual line at a time, and should return a suitably decorated line (it can
6907d62b00eSchristos    allocate a new one and return it if it likes).  */
6917d62b00eSchristos 
6927d62b00eSchristos char *
693*6881a400Schristos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
6947d62b00eSchristos 	  ctf_dump_decorate_f *func, void *arg)
6957d62b00eSchristos {
6967d62b00eSchristos   char *str;
6977d62b00eSchristos   char *line;
6987d62b00eSchristos   ctf_dump_state_t *state = NULL;
6997d62b00eSchristos 
7007d62b00eSchristos   if (*statep == NULL)
7017d62b00eSchristos     {
7027d62b00eSchristos       /* Data collection.  Transforming a call-at-a-time iterator into a
7037d62b00eSchristos 	 return-at-a-time iterator in a language without call/cc is annoying. It
7047d62b00eSchristos 	 is easiest to simply collect everything at once and then return it bit
7057d62b00eSchristos 	 by bit.  The first call will take (much) longer than otherwise, but the
7067d62b00eSchristos 	 amortized time needed is the same.  */
7077d62b00eSchristos 
7087d62b00eSchristos       if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
7097d62b00eSchristos 	{
7107d62b00eSchristos 	  ctf_set_errno (fp, ENOMEM);
7117d62b00eSchristos 	  goto end;
7127d62b00eSchristos 	}
7137d62b00eSchristos       state = *statep;
7147d62b00eSchristos 
7157d62b00eSchristos       memset (state, 0, sizeof (struct ctf_dump_state));
7167d62b00eSchristos       state->cds_fp = fp;
7177d62b00eSchristos       state->cds_sect = sect;
7187d62b00eSchristos 
7197d62b00eSchristos       switch (sect)
7207d62b00eSchristos 	{
7217d62b00eSchristos 	case CTF_SECT_HEADER:
7227d62b00eSchristos 	  ctf_dump_header (fp, state);
7237d62b00eSchristos 	  break;
7247d62b00eSchristos 	case CTF_SECT_LABEL:
7257d62b00eSchristos 	  if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
7267d62b00eSchristos 	    {
7277d62b00eSchristos 	      if (ctf_errno (fp) != ECTF_NOLABELDATA)
7287d62b00eSchristos 		goto end;		/* errno is set for us.  */
7297d62b00eSchristos 	      ctf_set_errno (fp, 0);
7307d62b00eSchristos 	    }
7317d62b00eSchristos 	  break;
7327d62b00eSchristos 	case CTF_SECT_OBJT:
733*6881a400Schristos 	  if (ctf_dump_objts (fp, state, 0) < 0)
7347d62b00eSchristos 	    goto end;			/* errno is set for us.  */
7357d62b00eSchristos 	  break;
7367d62b00eSchristos 	case CTF_SECT_FUNC:
737*6881a400Schristos 	  if (ctf_dump_objts (fp, state, 1) < 0)
7387d62b00eSchristos 	    goto end;			/* errno is set for us.  */
7397d62b00eSchristos 	  break;
7407d62b00eSchristos 	case CTF_SECT_VAR:
7417d62b00eSchristos 	  if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
7427d62b00eSchristos 	    goto end;			/* errno is set for us.  */
7437d62b00eSchristos 	  break;
7447d62b00eSchristos 	case CTF_SECT_TYPE:
7457d62b00eSchristos 	  if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
7467d62b00eSchristos 	    goto end;			/* errno is set for us.  */
7477d62b00eSchristos 	  break;
7487d62b00eSchristos 	case CTF_SECT_STR:
7497d62b00eSchristos 	  ctf_dump_str (fp, state);
7507d62b00eSchristos 	  break;
7517d62b00eSchristos 	default:
7527d62b00eSchristos 	  ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
7537d62b00eSchristos 	  goto end;
7547d62b00eSchristos 	}
7557d62b00eSchristos     }
7567d62b00eSchristos   else
7577d62b00eSchristos     {
7587d62b00eSchristos       state = *statep;
7597d62b00eSchristos 
7607d62b00eSchristos       if (state->cds_sect != sect)
7617d62b00eSchristos 	{
7627d62b00eSchristos 	  ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
7637d62b00eSchristos 	  goto end;
7647d62b00eSchristos 	}
7657d62b00eSchristos     }
7667d62b00eSchristos 
7677d62b00eSchristos   if (state->cds_current == NULL)
7687d62b00eSchristos     state->cds_current = ctf_list_next (&state->cds_items);
7697d62b00eSchristos   else
7707d62b00eSchristos     state->cds_current = ctf_list_next (state->cds_current);
7717d62b00eSchristos 
7727d62b00eSchristos   if (state->cds_current == NULL)
7737d62b00eSchristos     goto end;
7747d62b00eSchristos 
7757d62b00eSchristos   /* Hookery.  There is some extra complexity to preserve linefeeds within each
7767d62b00eSchristos      item while removing linefeeds at the end.  */
7777d62b00eSchristos   if (func)
7787d62b00eSchristos     {
7797d62b00eSchristos       size_t len;
7807d62b00eSchristos 
7817d62b00eSchristos       str = NULL;
7827d62b00eSchristos       for (line = state->cds_current->cdi_item; line && *line; )
7837d62b00eSchristos 	{
7847d62b00eSchristos 	  char *nline = line;
7857d62b00eSchristos 	  char *ret;
7867d62b00eSchristos 
7877d62b00eSchristos 	  nline = strchr (line, '\n');
7887d62b00eSchristos 	  if (nline)
7897d62b00eSchristos 	    nline[0] = '\0';
7907d62b00eSchristos 
7917d62b00eSchristos 	  ret = func (sect, line, arg);
7927d62b00eSchristos 	  str = str_append (str, ret);
7937d62b00eSchristos 	  str = str_append (str, "\n");
7947d62b00eSchristos 	  if (ret != line)
7957d62b00eSchristos 	    free (ret);
7967d62b00eSchristos 
7977d62b00eSchristos 	  if (nline)
7987d62b00eSchristos 	    {
7997d62b00eSchristos 	      nline[0] = '\n';
8007d62b00eSchristos 	      nline++;
8017d62b00eSchristos 	    }
8027d62b00eSchristos 
8037d62b00eSchristos 	  line = nline;
8047d62b00eSchristos 	}
8057d62b00eSchristos 
8067d62b00eSchristos       len = strlen (str);
8077d62b00eSchristos 
8087d62b00eSchristos       if (str[len-1] == '\n')
8097d62b00eSchristos 	str[len-1] = '\0';
8107d62b00eSchristos     }
8117d62b00eSchristos   else
8127d62b00eSchristos     {
8137d62b00eSchristos       str = strdup (state->cds_current->cdi_item);
8147d62b00eSchristos       if (!str)
8157d62b00eSchristos 	{
8167d62b00eSchristos 	  ctf_set_errno (fp, ENOMEM);
8177d62b00eSchristos 	  return str;
8187d62b00eSchristos 	}
8197d62b00eSchristos     }
8207d62b00eSchristos 
8217d62b00eSchristos   ctf_set_errno (fp, 0);
8227d62b00eSchristos   return str;
8237d62b00eSchristos 
8247d62b00eSchristos  end:
8257d62b00eSchristos   ctf_dump_free (state);
8267d62b00eSchristos   free (state);
8277d62b00eSchristos   ctf_set_errno (fp, 0);
8287d62b00eSchristos   *statep = NULL;
8297d62b00eSchristos   return NULL;
8307d62b00eSchristos }
831