Home | History | Annotate | Line # | Download | only in libctf
ctf-dump.c revision 1.1.1.2
      1 /* Textual dumping of CTF data.
      2    Copyright (C) 2019-2022 Free Software Foundation, Inc.
      3 
      4    This file is part of libctf.
      5 
      6    libctf is free software; you can redistribute it and/or modify it under
      7    the terms of the GNU General Public License as published by the Free
      8    Software Foundation; either version 3, or (at your option) any later
      9    version.
     10 
     11    This program is distributed in the hope that it will be useful, but
     12    WITHOUT ANY WARRANTY; without even the implied warranty of
     13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     14    See the GNU General Public License for more details.
     15 
     16    You should have received a copy of the GNU General Public License
     17    along with this program; see the file COPYING.  If not see
     18    <http://www.gnu.org/licenses/>.  */
     19 
     20 #include <ctf-impl.h>
     21 #include <string.h>
     22 
     23 #define str_append(s, a) ctf_str_append_noerr (s, a)
     24 
     25 /* One item to be dumped, in string form.  */
     26 
     27 typedef struct ctf_dump_item
     28 {
     29   ctf_list_t cdi_list;
     30   char *cdi_item;
     31 } ctf_dump_item_t;
     32 
     33 /* Cross-call state for dumping.  Basically just enough to track the section in
     34    use and a list of return strings.  */
     35 
     36 struct ctf_dump_state
     37 {
     38   ctf_sect_names_t cds_sect;
     39   ctf_dict_t *cds_fp;
     40   ctf_dump_item_t *cds_current;
     41   ctf_list_t cds_items;
     42 };
     43 
     44 /* Cross-call state for ctf_dump_member. */
     45 
     46 typedef struct ctf_dump_membstate
     47 {
     48   char **cdm_str;
     49   ctf_dict_t *cdm_fp;
     50   const char *cdm_toplevel_indent;
     51 } ctf_dump_membstate_t;
     52 
     53 static int
     54 ctf_dump_append (ctf_dump_state_t *state, char *str)
     55 {
     56   ctf_dump_item_t *cdi;
     57 
     58   if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
     59     return (ctf_set_errno (state->cds_fp, ENOMEM));
     60 
     61   cdi->cdi_item = str;
     62   ctf_list_append (&state->cds_items, cdi);
     63   return 0;
     64 }
     65 
     66 static void
     67 ctf_dump_free (ctf_dump_state_t *state)
     68 {
     69   ctf_dump_item_t *cdi, *next_cdi;
     70 
     71   if (state == NULL)
     72     return;
     73 
     74   for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
     75        cdi = next_cdi)
     76     {
     77       free (cdi->cdi_item);
     78       next_cdi = ctf_list_next (cdi);
     79       free (cdi);
     80     }
     81 }
     82 
     83 /* Return a dump for a single type, without member info: but do optionally show
     84    the type's references.  */
     85 
     86 #define CTF_FT_REFS     0x2 	/* Print referenced types.  */
     87 #define CTF_FT_BITFIELD 0x4	/* Print :BITS if a bitfield.  */
     88 #define CTF_FT_ID       0x8	/* Print "ID: " in front of type IDs.  */
     89 
     90 static char *
     91 ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
     92 {
     93   ctf_id_t new_id;
     94   char *str = NULL, *bit = NULL, *buf = NULL;
     95 
     96   ctf_set_errno (fp, 0);
     97   new_id = id;
     98   do
     99     {
    100       ctf_encoding_t ep;
    101       ctf_arinfo_t ar;
    102       int kind, unsliced_kind;
    103       ssize_t size, align;
    104       const char *nonroot_leader = "";
    105       const char *nonroot_trailer = "";
    106       const char *idstr = "";
    107 
    108       id = new_id;
    109       if (flag == CTF_ADD_NONROOT)
    110 	{
    111 	  nonroot_leader = "{";
    112 	  nonroot_trailer = "}";
    113 	}
    114 
    115       buf = ctf_type_aname (fp, id);
    116       if (!buf)
    117 	{
    118 	  if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
    119 	    {
    120 	      ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
    121 	      str = str_append (str, " (type not represented in CTF)");
    122 	      return str;
    123 	    }
    124 
    125 	  goto err;
    126 	}
    127 
    128       if (flag & CTF_FT_ID)
    129 	idstr = "ID ";
    130       if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
    131 		    id, ctf_type_kind (fp, id)) < 0)
    132 	goto oom;
    133       str = str_append (str, bit);
    134       free (bit);
    135       bit = NULL;
    136 
    137       if (buf[0] != '\0')
    138 	str = str_append (str, buf);
    139 
    140       free (buf);
    141       buf = NULL;
    142 
    143       unsliced_kind = ctf_type_kind_unsliced (fp, id);
    144       kind = ctf_type_kind (fp, id);
    145 
    146       /* Report encodings of everything with an encoding other than enums:
    147 	 base-type enums cannot have a nonzero cte_offset or cte_bits value.
    148 	 (Slices of them can, but they are of kind CTF_K_SLICE.)  */
    149       if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
    150 	{
    151 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
    152 	      && flag & CTF_FT_BITFIELD)
    153 	    {
    154 	      if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
    155 		goto oom;
    156 	      str = str_append (str, bit);
    157 	      free (bit);
    158 	      bit = NULL;
    159 	    }
    160 
    161 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
    162 	      || ep.cte_offset != 0)
    163 	    {
    164 	      const char *slice = "";
    165 
    166 	      if (unsliced_kind == CTF_K_SLICE)
    167 		slice = "slice ";
    168 
    169 	      if (asprintf (&bit, " [%s0x%x:0x%x]",
    170 			    slice, ep.cte_offset, ep.cte_bits) < 0)
    171 		goto oom;
    172 	      str = str_append (str, bit);
    173 	      free (bit);
    174 	      bit = NULL;
    175 	    }
    176 
    177 	  if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
    178 	    goto oom;
    179 	  str = str_append (str, bit);
    180 	  free (bit);
    181 	  bit = NULL;
    182 	}
    183 
    184       size = ctf_type_size (fp, id);
    185       if (kind != CTF_K_FUNCTION && size >= 0)
    186 	{
    187 	  if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
    188 	    goto oom;
    189 
    190 	  str = str_append (str, bit);
    191 	  free (bit);
    192 	  bit = NULL;
    193 	}
    194 
    195       align = ctf_type_align (fp, id);
    196       if (align >= 0)
    197 	{
    198 	  if (asprintf (&bit, " (aligned at 0x%lx)",
    199 			(unsigned long int) align) < 0)
    200 	    goto oom;
    201 
    202 	  str = str_append (str, bit);
    203 	  free (bit);
    204 	  bit = NULL;
    205 	}
    206 
    207       if (nonroot_trailer[0] != 0)
    208 	str = str_append (str, nonroot_trailer);
    209 
    210       /* Just exit after one iteration if we are not showing the types this type
    211 	 references.  */
    212       if (!(flag & CTF_FT_REFS))
    213 	return str;
    214 
    215       /* Keep going as long as this type references another.  We consider arrays
    216 	 to "reference" their element type. */
    217 
    218       if (kind == CTF_K_ARRAY)
    219 	{
    220 	  if (ctf_array_info (fp, id, &ar) < 0)
    221 	    goto err;
    222 	  new_id = ar.ctr_contents;
    223 	}
    224       else
    225 	new_id = ctf_type_reference (fp, id);
    226       if (new_id != CTF_ERR)
    227 	str = str_append (str, " -> ");
    228     }
    229   while (new_id != CTF_ERR);
    230 
    231   if (ctf_errno (fp) != ECTF_NOTREF)
    232     {
    233       free (str);
    234       return NULL;
    235     }
    236 
    237   return str;
    238 
    239  oom:
    240   ctf_set_errno (fp, errno);
    241  err:
    242   ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
    243   free (buf);
    244   free (str);
    245   free (bit);
    246   return NULL;
    247 }
    248 
    249 /* Dump one string field from the file header into the cds_items.  */
    250 static int
    251 ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    252 			  const char *name, uint32_t value)
    253 {
    254   char *str;
    255   if (value)
    256     {
    257       if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
    258 	goto err;
    259       ctf_dump_append (state, str);
    260     }
    261   return 0;
    262 
    263  err:
    264   return (ctf_set_errno (fp, errno));
    265 }
    266 
    267 /* Dump one section-offset field from the file header into the cds_items.  */
    268 static int
    269 ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    270 			   const char *sect, uint32_t off, uint32_t nextoff)
    271 {
    272   char *str;
    273   if (nextoff - off)
    274     {
    275       if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
    276 		    (unsigned long) off, (unsigned long) (nextoff - 1),
    277 		    (unsigned long) (nextoff - off)) < 0)
    278 	goto err;
    279       ctf_dump_append (state, str);
    280     }
    281   return 0;
    282 
    283  err:
    284   return (ctf_set_errno (fp, errno));
    285 }
    286 
    287 /* Dump the file header into the cds_items.  */
    288 static int
    289 ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
    290 {
    291   char *str;
    292   char *flagstr = NULL;
    293   const ctf_header_t *hp = fp->ctf_header;
    294   const char *vertab[] =
    295     {
    296      NULL, "CTF_VERSION_1",
    297      "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
    298      "boundaries)",
    299      "CTF_VERSION_2",
    300      "CTF_VERSION_3", NULL
    301     };
    302   const char *verstr = NULL;
    303 
    304   if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
    305       goto err;
    306   ctf_dump_append (state, str);
    307 
    308   if (hp->cth_version <= CTF_VERSION)
    309     verstr = vertab[hp->cth_version];
    310 
    311   if (verstr == NULL)
    312     verstr = "(not a valid version)";
    313 
    314   if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
    315 		verstr) < 0)
    316     goto err;
    317   ctf_dump_append (state, str);
    318 
    319   /* Everything else is only printed if present.  */
    320 
    321   /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
    322      flags representing compression, etc, are turned off as the file is
    323      decompressed.  So we store a copy of the flags before they are changed, for
    324      the dumper.  */
    325 
    326   if (fp->ctf_openflags > 0)
    327     {
    328       if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
    329 		    fp->ctf_openflags & CTF_F_COMPRESS
    330 		    ? "CTF_F_COMPRESS": "",
    331 		    (fp->ctf_openflags & CTF_F_COMPRESS)
    332 		    && (fp->ctf_openflags & ~CTF_F_COMPRESS)
    333 		    ? ", " : "",
    334 		    fp->ctf_openflags & CTF_F_NEWFUNCINFO
    335 		    ? "CTF_F_NEWFUNCINFO" : "",
    336 		    (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
    337 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
    338 		    ? ", " : "",
    339 		    fp->ctf_openflags & CTF_F_IDXSORTED
    340 		    ? "CTF_F_IDXSORTED" : "",
    341 		    fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
    342 					 | CTF_F_IDXSORTED)
    343 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
    344 					      | CTF_F_IDXSORTED))
    345 		    ? ", " : "",
    346 		    fp->ctf_openflags & CTF_F_DYNSTR
    347 		    ? "CTF_F_DYNSTR" : "") < 0)
    348 	goto err;
    349 
    350       if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
    351 	goto err;
    352       ctf_dump_append (state, str);
    353     }
    354 
    355   if (ctf_dump_header_strfield (fp, state, "Parent label",
    356 				hp->cth_parlabel) < 0)
    357     goto err;
    358 
    359   if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
    360     goto err;
    361 
    362   if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
    363 				hp->cth_cuname) < 0)
    364     goto err;
    365 
    366   if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
    367 				 hp->cth_objtoff) < 0)
    368     goto err;
    369 
    370   if (ctf_dump_header_sectfield (fp, state, "Data object section",
    371 				 hp->cth_objtoff, hp->cth_funcoff) < 0)
    372     goto err;
    373 
    374   if (ctf_dump_header_sectfield (fp, state, "Function info section",
    375 				 hp->cth_funcoff, hp->cth_objtidxoff) < 0)
    376     goto err;
    377 
    378   if (ctf_dump_header_sectfield (fp, state, "Object index section",
    379 				 hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
    380     goto err;
    381 
    382   if (ctf_dump_header_sectfield (fp, state, "Function index section",
    383 				 hp->cth_funcidxoff, hp->cth_varoff) < 0)
    384     goto err;
    385 
    386   if (ctf_dump_header_sectfield (fp, state, "Variable section",
    387 				 hp->cth_varoff, hp->cth_typeoff) < 0)
    388     goto err;
    389 
    390   if (ctf_dump_header_sectfield (fp, state, "Type section",
    391 				 hp->cth_typeoff, hp->cth_stroff) < 0)
    392     goto err;
    393 
    394   if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
    395 				 hp->cth_stroff + hp->cth_strlen + 1) < 0)
    396     goto err;
    397 
    398   return 0;
    399  err:
    400   free (flagstr);
    401   return (ctf_set_errno (fp, errno));
    402 }
    403 
    404 /* Dump a single label into the cds_items.  */
    405 
    406 static int
    407 ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
    408 		void *arg)
    409 {
    410   char *str;
    411   char *typestr;
    412   ctf_dump_state_t *state = arg;
    413 
    414   if (asprintf (&str, "%s -> ", name) < 0)
    415     return (ctf_set_errno (state->cds_fp, errno));
    416 
    417   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
    418 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    419     {
    420       free (str);
    421       return 0;				/* Swallow the error.  */
    422     }
    423 
    424   str = str_append (str, typestr);
    425   free (typestr);
    426 
    427   ctf_dump_append (state, str);
    428   return 0;
    429 }
    430 
    431 /* Dump all the object or function entries into the cds_items.  */
    432 
    433 static int
    434 ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
    435 {
    436   const char *name;
    437   ctf_id_t id;
    438   ctf_next_t *i = NULL;
    439   char *str = NULL;
    440 
    441   if ((functions && fp->ctf_funcidx_names)
    442       || (!functions && fp->ctf_objtidx_names))
    443     str = str_append (str, _("Section is indexed.\n"));
    444   else if (fp->ctf_symtab.cts_data == NULL)
    445     str = str_append (str, _("No symbol table.\n"));
    446 
    447   while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
    448     {
    449       char *typestr = NULL;
    450 
    451       /* Emit the name, if we know it.  No trailing space: ctf_dump_format_type
    452 	 has a leading one.   */
    453       if (name)
    454 	{
    455 	  if (asprintf (&str, "%s -> ", name) < 0)
    456 	    goto oom;
    457 	}
    458       else
    459 	str = xstrdup ("");
    460 
    461       if ((typestr = ctf_dump_format_type (state->cds_fp, id,
    462 					   CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    463 	{
    464 	  ctf_dump_append (state, str);
    465 	  continue;				/* Swallow the error.  */
    466 	}
    467 
    468       str = str_append (str, typestr);
    469       free (typestr);
    470       ctf_dump_append (state, str);
    471       continue;
    472 
    473     oom:
    474       ctf_set_errno (fp, ENOMEM);
    475       ctf_next_destroy (i);
    476       return -1;
    477     }
    478   return 0;
    479 }
    480 
    481 /* Dump a single variable into the cds_items.  */
    482 static int
    483 ctf_dump_var (const char *name, ctf_id_t type, void *arg)
    484 {
    485   char *str;
    486   char *typestr;
    487   ctf_dump_state_t *state = arg;
    488 
    489   if (asprintf (&str, "%s -> ", name) < 0)
    490     return (ctf_set_errno (state->cds_fp, errno));
    491 
    492   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
    493 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    494     {
    495       free (str);
    496       return 0;			/* Swallow the error.  */
    497     }
    498 
    499   str = str_append (str, typestr);
    500   free (typestr);
    501 
    502   ctf_dump_append (state, str);
    503   return 0;
    504 }
    505 
    506 /* Dump a single struct/union member into the string in the membstate.  */
    507 static int
    508 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
    509 		 int depth, void *arg)
    510 {
    511   ctf_dump_membstate_t *state = arg;
    512   char *typestr = NULL;
    513   char *bit = NULL;
    514 
    515   /* The struct/union itself has already been printed.  */
    516   if (depth == 0)
    517     return 0;
    518 
    519   if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
    520     goto oom;
    521   *state->cdm_str = str_append (*state->cdm_str, bit);
    522   free (bit);
    523 
    524   if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
    525 				       CTF_ADD_ROOT | CTF_FT_BITFIELD
    526 				       | CTF_FT_ID)) == NULL)
    527     return -1;				/* errno is set for us.  */
    528 
    529   if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
    530     goto oom;
    531 
    532   *state->cdm_str = str_append (*state->cdm_str, bit);
    533   free (typestr);
    534   free (bit);
    535   typestr = NULL;
    536   bit = NULL;
    537 
    538   return 0;
    539 
    540  oom:
    541   free (typestr);
    542   free (bit);
    543   return (ctf_set_errno (state->cdm_fp, errno));
    544 }
    545 
    546 /* Report the number of digits in the hexadecimal representation of a type
    547    ID.  */
    548 
    549 static int
    550 type_hex_digits (ctf_id_t id)
    551 {
    552   int i = 0;
    553 
    554   if (id == 0)
    555     return 1;
    556 
    557   for (; id > 0; id >>= 4, i++);
    558   return i;
    559 }
    560 
    561 /* Dump a single type into the cds_items.  */
    562 static int
    563 ctf_dump_type (ctf_id_t id, int flag, void *arg)
    564 {
    565   char *str;
    566   char *indent;
    567   ctf_dump_state_t *state = arg;
    568   ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
    569 
    570   /* Indent neatly.  */
    571   if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
    572     return (ctf_set_errno (state->cds_fp, ENOMEM));
    573 
    574   /* Dump the type itself.  */
    575   if ((str = ctf_dump_format_type (state->cds_fp, id,
    576 				   flag | CTF_FT_REFS)) == NULL)
    577     goto err;
    578   str = str_append (str, "\n");
    579 
    580   membstate.cdm_toplevel_indent = indent;
    581 
    582   /* Member dumping for structs, unions...  */
    583   if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
    584       || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
    585     {
    586       if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
    587 	{
    588 	  if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
    589 	    {
    590 	      ctf_dump_append (state, str);
    591 	      return 0;
    592 	    }
    593 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    594 			_("cannot visit members dumping type 0x%lx"), id);
    595 	  goto err;
    596 	}
    597     }
    598 
    599   /* ... and enums, for which we dump the first and last few members and skip
    600      the ones in the middle.  */
    601   if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
    602     {
    603       int enum_count = ctf_member_count (state->cds_fp, id);
    604       ctf_next_t *it = NULL;
    605       int i = 0;
    606       const char *enumerand;
    607       char *bit;
    608       int value;
    609 
    610       while ((enumerand = ctf_enum_next (state->cds_fp, id,
    611 					 &it, &value)) != NULL)
    612 	{
    613 	  i++;
    614 	  if ((i > 5) && (i < enum_count - 4))
    615 	    continue;
    616 
    617 	  str = str_append (str, indent);
    618 
    619 	  if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
    620 	    {
    621 	      ctf_next_destroy (it);
    622 	      goto oom;
    623 	    }
    624 	  str = str_append (str, bit);
    625 	  free (bit);
    626 
    627 	  if ((i == 5) && (enum_count > 10))
    628 	    {
    629 	      str = str_append (str, indent);
    630 	      str = str_append (str, "...\n");
    631 	    }
    632 	}
    633       if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
    634 	{
    635 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    636 			_("cannot visit enumerands dumping type 0x%lx"), id);
    637 	  goto err;
    638 	}
    639     }
    640 
    641   ctf_dump_append (state, str);
    642   free (indent);
    643 
    644   return 0;
    645 
    646  err:
    647   free (indent);
    648   free (str);
    649 
    650   /* Swallow the error: don't cause an error in one type to abort all
    651      type dumping.  */
    652   return 0;
    653 
    654  oom:
    655   free (indent);
    656   free (str);
    657   return ctf_set_errno (state->cds_fp, ENOMEM);
    658 }
    659 
    660 /* Dump the string table into the cds_items.  */
    661 
    662 static int
    663 ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
    664 {
    665   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
    666 
    667   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
    668 	 fp->ctf_str[CTF_STRTAB_0].cts_len;)
    669     {
    670       char *str;
    671       if (asprintf (&str, "0x%lx: %s",
    672 		    (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
    673 		    s) < 0)
    674 	return (ctf_set_errno (fp, errno));
    675       ctf_dump_append (state, str);
    676       s += strlen (s) + 1;
    677     }
    678 
    679   return 0;
    680 }
    681 
    682 /* Dump a particular section of a CTF file, in textual form.  Call with a
    683    pointer to a NULL STATE: each call emits a dynamically allocated string
    684    containing a description of one entity in the specified section, in order.
    685    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
    686    has been entirely dumped, the call returns NULL and frees and annuls the
    687    STATE, ready for another section to be dumped.  The returned textual content
    688    may span multiple lines: between each call the FUNC is called with one
    689    textual line at a time, and should return a suitably decorated line (it can
    690    allocate a new one and return it if it likes).  */
    691 
    692 char *
    693 ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
    694 	  ctf_dump_decorate_f *func, void *arg)
    695 {
    696   char *str;
    697   char *line;
    698   ctf_dump_state_t *state = NULL;
    699 
    700   if (*statep == NULL)
    701     {
    702       /* Data collection.  Transforming a call-at-a-time iterator into a
    703 	 return-at-a-time iterator in a language without call/cc is annoying. It
    704 	 is easiest to simply collect everything at once and then return it bit
    705 	 by bit.  The first call will take (much) longer than otherwise, but the
    706 	 amortized time needed is the same.  */
    707 
    708       if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
    709 	{
    710 	  ctf_set_errno (fp, ENOMEM);
    711 	  goto end;
    712 	}
    713       state = *statep;
    714 
    715       memset (state, 0, sizeof (struct ctf_dump_state));
    716       state->cds_fp = fp;
    717       state->cds_sect = sect;
    718 
    719       switch (sect)
    720 	{
    721 	case CTF_SECT_HEADER:
    722 	  ctf_dump_header (fp, state);
    723 	  break;
    724 	case CTF_SECT_LABEL:
    725 	  if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
    726 	    {
    727 	      if (ctf_errno (fp) != ECTF_NOLABELDATA)
    728 		goto end;		/* errno is set for us.  */
    729 	      ctf_set_errno (fp, 0);
    730 	    }
    731 	  break;
    732 	case CTF_SECT_OBJT:
    733 	  if (ctf_dump_objts (fp, state, 0) < 0)
    734 	    goto end;			/* errno is set for us.  */
    735 	  break;
    736 	case CTF_SECT_FUNC:
    737 	  if (ctf_dump_objts (fp, state, 1) < 0)
    738 	    goto end;			/* errno is set for us.  */
    739 	  break;
    740 	case CTF_SECT_VAR:
    741 	  if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
    742 	    goto end;			/* errno is set for us.  */
    743 	  break;
    744 	case CTF_SECT_TYPE:
    745 	  if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
    746 	    goto end;			/* errno is set for us.  */
    747 	  break;
    748 	case CTF_SECT_STR:
    749 	  ctf_dump_str (fp, state);
    750 	  break;
    751 	default:
    752 	  ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
    753 	  goto end;
    754 	}
    755     }
    756   else
    757     {
    758       state = *statep;
    759 
    760       if (state->cds_sect != sect)
    761 	{
    762 	  ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
    763 	  goto end;
    764 	}
    765     }
    766 
    767   if (state->cds_current == NULL)
    768     state->cds_current = ctf_list_next (&state->cds_items);
    769   else
    770     state->cds_current = ctf_list_next (state->cds_current);
    771 
    772   if (state->cds_current == NULL)
    773     goto end;
    774 
    775   /* Hookery.  There is some extra complexity to preserve linefeeds within each
    776      item while removing linefeeds at the end.  */
    777   if (func)
    778     {
    779       size_t len;
    780 
    781       str = NULL;
    782       for (line = state->cds_current->cdi_item; line && *line; )
    783 	{
    784 	  char *nline = line;
    785 	  char *ret;
    786 
    787 	  nline = strchr (line, '\n');
    788 	  if (nline)
    789 	    nline[0] = '\0';
    790 
    791 	  ret = func (sect, line, arg);
    792 	  str = str_append (str, ret);
    793 	  str = str_append (str, "\n");
    794 	  if (ret != line)
    795 	    free (ret);
    796 
    797 	  if (nline)
    798 	    {
    799 	      nline[0] = '\n';
    800 	      nline++;
    801 	    }
    802 
    803 	  line = nline;
    804 	}
    805 
    806       len = strlen (str);
    807 
    808       if (str[len-1] == '\n')
    809 	str[len-1] = '\0';
    810     }
    811   else
    812     {
    813       str = strdup (state->cds_current->cdi_item);
    814       if (!str)
    815 	{
    816 	  ctf_set_errno (fp, ENOMEM);
    817 	  return str;
    818 	}
    819     }
    820 
    821   ctf_set_errno (fp, 0);
    822   return str;
    823 
    824  end:
    825   ctf_dump_free (state);
    826   free (state);
    827   ctf_set_errno (fp, 0);
    828   *statep = NULL;
    829   return NULL;
    830 }
    831