Home | History | Annotate | Line # | Download | only in libctf
ctf-dump.c revision 1.1.1.4
      1 /* Textual dumping of CTF data.
      2    Copyright (C) 2019-2024 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_ROOT))
    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, ctf_errno (fp), _("cannot format name dumping type 0x%lx"),
    243 		id);
    244   free (buf);
    245   free (str);
    246   free (bit);
    247   return NULL;
    248 }
    249 
    250 /* Dump one string field from the file header into the cds_items.  */
    251 static int
    252 ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    253 			  const char *name, uint32_t value)
    254 {
    255   char *str;
    256   if (value)
    257     {
    258       if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
    259 	goto err;
    260       ctf_dump_append (state, str);
    261     }
    262   return 0;
    263 
    264  err:
    265   return (ctf_set_errno (fp, errno));
    266 }
    267 
    268 /* Dump one section-offset field from the file header into the cds_items.  */
    269 static int
    270 ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    271 			   const char *sect, uint32_t off, uint32_t nextoff)
    272 {
    273   char *str;
    274   if (nextoff - off)
    275     {
    276       if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
    277 		    (unsigned long) off, (unsigned long) (nextoff - 1),
    278 		    (unsigned long) (nextoff - off)) < 0)
    279 	goto err;
    280       ctf_dump_append (state, str);
    281     }
    282   return 0;
    283 
    284  err:
    285   return (ctf_set_errno (fp, errno));
    286 }
    287 
    288 /* Dump the file header into the cds_items.  */
    289 static int
    290 ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
    291 {
    292   char *str;
    293   char *flagstr = NULL;
    294   const ctf_header_t *hp = fp->ctf_header;
    295   const char *vertab[] =
    296     {
    297      NULL, "CTF_VERSION_1",
    298      "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
    299      "boundaries)",
    300      "CTF_VERSION_2",
    301      "CTF_VERSION_3", NULL
    302     };
    303   const char *verstr = NULL;
    304 
    305   if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
    306       goto err;
    307   ctf_dump_append (state, str);
    308 
    309   if (hp->cth_version <= CTF_VERSION)
    310     verstr = vertab[hp->cth_version];
    311 
    312   if (verstr == NULL)
    313     verstr = "(not a valid version)";
    314 
    315   if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
    316 		verstr) < 0)
    317     goto err;
    318   ctf_dump_append (state, str);
    319 
    320   /* Everything else is only printed if present.  */
    321 
    322   /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
    323      flags representing compression, etc, are turned off as the file is
    324      decompressed.  So we store a copy of the flags before they are changed, for
    325      the dumper.  */
    326 
    327   if (fp->ctf_openflags > 0)
    328     {
    329       if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
    330 		    fp->ctf_openflags & CTF_F_COMPRESS
    331 		    ? "CTF_F_COMPRESS": "",
    332 		    (fp->ctf_openflags & CTF_F_COMPRESS)
    333 		    && (fp->ctf_openflags & ~CTF_F_COMPRESS)
    334 		    ? ", " : "",
    335 		    fp->ctf_openflags & CTF_F_NEWFUNCINFO
    336 		    ? "CTF_F_NEWFUNCINFO" : "",
    337 		    (fp->ctf_openflags & (CTF_F_NEWFUNCINFO))
    338 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
    339 		    ? ", " : "",
    340 		    fp->ctf_openflags & CTF_F_IDXSORTED
    341 		    ? "CTF_F_IDXSORTED" : "",
    342 		    fp->ctf_openflags & (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       free (flagstr);
    353       ctf_dump_append (state, str);
    354     }
    355 
    356   if (ctf_dump_header_strfield (fp, state, "Parent label",
    357 				hp->cth_parlabel) < 0)
    358     goto err;
    359 
    360   if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
    361     goto err;
    362 
    363   if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
    364 				hp->cth_cuname) < 0)
    365     goto err;
    366 
    367   if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
    368 				 hp->cth_objtoff) < 0)
    369     goto err;
    370 
    371   if (ctf_dump_header_sectfield (fp, state, "Data object section",
    372 				 hp->cth_objtoff, hp->cth_funcoff) < 0)
    373     goto err;
    374 
    375   if (ctf_dump_header_sectfield (fp, state, "Function info section",
    376 				 hp->cth_funcoff, hp->cth_objtidxoff) < 0)
    377     goto err;
    378 
    379   if (ctf_dump_header_sectfield (fp, state, "Object index section",
    380 				 hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
    381     goto err;
    382 
    383   if (ctf_dump_header_sectfield (fp, state, "Function index section",
    384 				 hp->cth_funcidxoff, hp->cth_varoff) < 0)
    385     goto err;
    386 
    387   if (ctf_dump_header_sectfield (fp, state, "Variable section",
    388 				 hp->cth_varoff, hp->cth_typeoff) < 0)
    389     goto err;
    390 
    391   if (ctf_dump_header_sectfield (fp, state, "Type section",
    392 				 hp->cth_typeoff, hp->cth_stroff) < 0)
    393     goto err;
    394 
    395   if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
    396 				 hp->cth_stroff + hp->cth_strlen + 1) < 0)
    397     goto err;
    398 
    399   return 0;
    400  err:
    401   free (flagstr);
    402   return (ctf_set_errno (fp, errno));
    403 }
    404 
    405 /* Dump a single label into the cds_items.  */
    406 
    407 static int
    408 ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
    409 		void *arg)
    410 {
    411   char *str;
    412   char *typestr;
    413   ctf_dump_state_t *state = arg;
    414 
    415   if (asprintf (&str, "%s -> ", name) < 0)
    416     return (ctf_set_errno (state->cds_fp, errno));
    417 
    418   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
    419 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    420     {
    421       free (str);
    422       return 0;				/* Swallow the error.  */
    423     }
    424 
    425   str = str_append (str, typestr);
    426   free (typestr);
    427 
    428   ctf_dump_append (state, str);
    429   return 0;
    430 }
    431 
    432 /* Dump all the object or function entries into the cds_items.  */
    433 
    434 static int
    435 ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
    436 {
    437   const char *name;
    438   ctf_id_t id;
    439   ctf_next_t *i = NULL;
    440   char *str = NULL;
    441 
    442   if ((functions && fp->ctf_funcidx_names)
    443       || (!functions && fp->ctf_objtidx_names))
    444     str = str_append (str, _("Section is indexed.\n"));
    445   else if (fp->ctf_ext_symtab.cts_data == NULL)
    446     str = str_append (str, _("No symbol table.\n"));
    447 
    448   while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
    449     {
    450       char *typestr = NULL;
    451 
    452       /* Emit the name, if we know it.  No trailing space: ctf_dump_format_type
    453 	 has a leading one.   */
    454       if (name)
    455 	{
    456 	  if (asprintf (&str, "%s -> ", name) < 0)
    457 	    goto oom;
    458 	}
    459       else
    460 	str = xstrdup ("");
    461 
    462       if ((typestr = ctf_dump_format_type (state->cds_fp, id,
    463 					   CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    464 	{
    465 	  ctf_dump_append (state, str);
    466 	  continue;				/* Swallow the error.  */
    467 	}
    468 
    469       str = str_append (str, typestr);
    470       free (typestr);
    471       ctf_dump_append (state, str);
    472       continue;
    473 
    474     oom:
    475       ctf_set_errno (fp, ENOMEM);
    476       ctf_next_destroy (i);
    477       return -1;
    478     }
    479   return 0;
    480 }
    481 
    482 /* Dump a single variable into the cds_items.  */
    483 static int
    484 ctf_dump_var (const char *name, ctf_id_t type, void *arg)
    485 {
    486   char *str;
    487   char *typestr;
    488   ctf_dump_state_t *state = arg;
    489 
    490   if (asprintf (&str, "%s -> ", name) < 0)
    491     return (ctf_set_errno (state->cds_fp, errno));
    492 
    493   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
    494 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    495     {
    496       free (str);
    497       return 0;			/* Swallow the error.  */
    498     }
    499 
    500   str = str_append (str, typestr);
    501   free (typestr);
    502 
    503   ctf_dump_append (state, str);
    504   return 0;
    505 }
    506 
    507 /* Dump a single struct/union member into the string in the membstate.  */
    508 static int
    509 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
    510 		 int depth, void *arg)
    511 {
    512   ctf_dump_membstate_t *state = arg;
    513   char *typestr = NULL;
    514   char *bit = NULL;
    515 
    516   /* The struct/union itself has already been printed.  */
    517   if (depth == 0)
    518     return 0;
    519 
    520   if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
    521     goto oom;
    522   *state->cdm_str = str_append (*state->cdm_str, bit);
    523   free (bit);
    524 
    525   if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
    526 				       CTF_ADD_ROOT | CTF_FT_BITFIELD
    527 				       | CTF_FT_ID)) == NULL)
    528     return -1;				/* errno is set for us.  */
    529 
    530   if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
    531     goto oom;
    532 
    533   *state->cdm_str = str_append (*state->cdm_str, bit);
    534   free (typestr);
    535   free (bit);
    536   typestr = NULL;
    537   bit = NULL;
    538 
    539   return 0;
    540 
    541  oom:
    542   free (typestr);
    543   free (bit);
    544   return (ctf_set_errno (state->cdm_fp, errno));
    545 }
    546 
    547 /* Report the number of digits in the hexadecimal representation of a type
    548    ID.  */
    549 
    550 static int
    551 type_hex_digits (ctf_id_t id)
    552 {
    553   int i = 0;
    554 
    555   if (id == 0)
    556     return 1;
    557 
    558   for (; id > 0; id >>= 4, i++);
    559   return i;
    560 }
    561 
    562 /* Dump a single type into the cds_items.  */
    563 static int
    564 ctf_dump_type (ctf_id_t id, int flag, void *arg)
    565 {
    566   char *str;
    567   char *indent;
    568   ctf_dump_state_t *state = arg;
    569   ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
    570 
    571   /* Indent neatly.  */
    572   if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
    573     return (ctf_set_errno (state->cds_fp, ENOMEM));
    574 
    575   /* Dump the type itself.  */
    576   if ((str = ctf_dump_format_type (state->cds_fp, id,
    577 				   flag | CTF_FT_REFS)) == NULL)
    578     goto err;
    579   str = str_append (str, "\n");
    580 
    581   membstate.cdm_toplevel_indent = indent;
    582 
    583   /* Member dumping for structs, unions...  */
    584   if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
    585       || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
    586     {
    587       if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
    588 	{
    589 	  if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
    590 	    {
    591 	      ctf_dump_append (state, str);
    592 	      return 0;
    593 	    }
    594 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    595 			_("cannot visit members dumping type 0x%lx"), id);
    596 	  goto err;
    597 	}
    598     }
    599 
    600   /* ... and enums, for which we dump the first and last few members and skip
    601      the ones in the middle.  */
    602   if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
    603     {
    604       int enum_count = ctf_member_count (state->cds_fp, id);
    605       ctf_next_t *it = NULL;
    606       int i = 0;
    607       const char *enumerand;
    608       char *bit;
    609       int value;
    610 
    611       while ((enumerand = ctf_enum_next (state->cds_fp, id,
    612 					 &it, &value)) != NULL)
    613 	{
    614 	  i++;
    615 	  if ((i > 5) && (i < enum_count - 4))
    616 	    continue;
    617 
    618 	  str = str_append (str, indent);
    619 
    620 	  if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
    621 	    {
    622 	      ctf_next_destroy (it);
    623 	      goto oom;
    624 	    }
    625 	  str = str_append (str, bit);
    626 	  free (bit);
    627 
    628 	  if ((i == 5) && (enum_count > 10))
    629 	    {
    630 	      str = str_append (str, indent);
    631 	      str = str_append (str, "...\n");
    632 	    }
    633 	}
    634       if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
    635 	{
    636 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    637 			_("cannot visit enumerands dumping type 0x%lx"), id);
    638 	  goto err;
    639 	}
    640     }
    641 
    642   ctf_dump_append (state, str);
    643   free (indent);
    644 
    645   return 0;
    646 
    647  err:
    648   free (indent);
    649   free (str);
    650 
    651   /* Swallow the error: don't cause an error in one type to abort all
    652      type dumping.  */
    653   return 0;
    654 
    655  oom:
    656   free (indent);
    657   free (str);
    658   return ctf_set_errno (state->cds_fp, ENOMEM);
    659 }
    660 
    661 /* Dump the string table into the cds_items.  */
    662 
    663 static int
    664 ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
    665 {
    666   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
    667 
    668   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
    669 	 fp->ctf_str[CTF_STRTAB_0].cts_len;)
    670     {
    671       char *str;
    672       if (asprintf (&str, "0x%lx: %s",
    673 		    (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
    674 		    s) < 0)
    675 	return (ctf_set_errno (fp, errno));
    676       ctf_dump_append (state, str);
    677       s += strlen (s) + 1;
    678     }
    679 
    680   return 0;
    681 }
    682 
    683 /* Dump a particular section of a CTF file, in textual form.  Call with a
    684    pointer to a NULL STATE: each call emits a dynamically allocated string
    685    containing a description of one entity in the specified section, in order.
    686    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
    687    has been entirely dumped, the call returns NULL and frees and annuls the
    688    STATE, ready for another section to be dumped.  The returned textual content
    689    may span multiple lines: between each call the FUNC is called with one
    690    textual line at a time, and should return a suitably decorated line (it can
    691    allocate a new one and return it if it likes).  */
    692 
    693 char *
    694 ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
    695 	  ctf_dump_decorate_f *func, void *arg)
    696 {
    697   char *str;
    698   char *line;
    699   ctf_dump_state_t *state = NULL;
    700 
    701   if (*statep == NULL)
    702     {
    703       /* Data collection.  Transforming a call-at-a-time iterator into a
    704 	 return-at-a-time iterator in a language without call/cc is annoying. It
    705 	 is easiest to simply collect everything at once and then return it bit
    706 	 by bit.  The first call will take (much) longer than otherwise, but the
    707 	 amortized time needed is the same.  */
    708 
    709       if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
    710 	{
    711 	  ctf_set_errno (fp, ENOMEM);
    712 	  goto end;
    713 	}
    714       state = *statep;
    715 
    716       memset (state, 0, sizeof (struct ctf_dump_state));
    717       state->cds_fp = fp;
    718       state->cds_sect = sect;
    719 
    720       switch (sect)
    721 	{
    722 	case CTF_SECT_HEADER:
    723 	  ctf_dump_header (fp, state);
    724 	  break;
    725 	case CTF_SECT_LABEL:
    726 	  if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
    727 	    {
    728 	      if (ctf_errno (fp) != ECTF_NOLABELDATA)
    729 		goto end;		/* errno is set for us.  */
    730 	      ctf_set_errno (fp, 0);
    731 	    }
    732 	  break;
    733 	case CTF_SECT_OBJT:
    734 	  if (ctf_dump_objts (fp, state, 0) < 0)
    735 	    goto end;			/* errno is set for us.  */
    736 	  break;
    737 	case CTF_SECT_FUNC:
    738 	  if (ctf_dump_objts (fp, state, 1) < 0)
    739 	    goto end;			/* errno is set for us.  */
    740 	  break;
    741 	case CTF_SECT_VAR:
    742 	  if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
    743 	    goto end;			/* errno is set for us.  */
    744 	  break;
    745 	case CTF_SECT_TYPE:
    746 	  if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
    747 	    goto end;			/* errno is set for us.  */
    748 	  break;
    749 	case CTF_SECT_STR:
    750 	  ctf_dump_str (fp, state);
    751 	  break;
    752 	default:
    753 	  ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
    754 	  goto end;
    755 	}
    756     }
    757   else
    758     {
    759       state = *statep;
    760 
    761       if (state->cds_sect != sect)
    762 	{
    763 	  ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
    764 	  goto end;
    765 	}
    766     }
    767 
    768   if (state->cds_current == NULL)
    769     state->cds_current = ctf_list_next (&state->cds_items);
    770   else
    771     state->cds_current = ctf_list_next (state->cds_current);
    772 
    773   if (state->cds_current == NULL)
    774     goto end;
    775 
    776   /* Hookery.  There is some extra complexity to preserve linefeeds within each
    777      item while removing linefeeds at the end.  */
    778   if (func)
    779     {
    780       size_t len;
    781 
    782       str = NULL;
    783       for (line = state->cds_current->cdi_item; line && *line; )
    784 	{
    785 	  char *nline = line;
    786 	  char *ret;
    787 
    788 	  nline = strchr (line, '\n');
    789 	  if (nline)
    790 	    nline[0] = '\0';
    791 
    792 	  ret = func (sect, line, arg);
    793 	  str = str_append (str, ret);
    794 	  str = str_append (str, "\n");
    795 	  if (ret != line)
    796 	    free (ret);
    797 
    798 	  if (nline)
    799 	    {
    800 	      nline[0] = '\n';
    801 	      nline++;
    802 	    }
    803 
    804 	  line = nline;
    805 	}
    806 
    807       len = strlen (str);
    808 
    809       if (str[len-1] == '\n')
    810 	str[len-1] = '\0';
    811     }
    812   else
    813     {
    814       str = strdup (state->cds_current->cdi_item);
    815       if (!str)
    816 	{
    817 	  ctf_set_errno (fp, ENOMEM);
    818 	  return NULL;
    819 	}
    820     }
    821 
    822   ctf_set_errno (fp, 0);
    823   return str;
    824 
    825  end:
    826   ctf_dump_free (state);
    827   free (state);
    828   ctf_set_errno (fp, 0);
    829   *statep = NULL;
    830   return NULL;
    831 }
    832