xref: /netbsd-src/external/gpl3/binutils.old/dist/libctf/ctf-dump.c (revision c42dbd0ed2e61fe6eda8590caa852ccf34719964)
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
ctf_dump_append(ctf_dump_state_t * state,char * str)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
ctf_dump_free(ctf_dump_state_t * state)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 *
ctf_dump_format_type(ctf_dict_t * fp,ctf_id_t id,int flag)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
ctf_dump_header_strfield(ctf_dict_t * fp,ctf_dump_state_t * state,const char * name,uint32_t value)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
ctf_dump_header_sectfield(ctf_dict_t * fp,ctf_dump_state_t * state,const char * sect,uint32_t off,uint32_t nextoff)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
ctf_dump_header(ctf_dict_t * fp,ctf_dump_state_t * state)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
ctf_dump_label(const char * name,const ctf_lblinfo_t * info,void * arg)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
ctf_dump_objts(ctf_dict_t * fp,ctf_dump_state_t * state,int functions)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
ctf_dump_var(const char * name,ctf_id_t type,void * arg)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
ctf_dump_member(const char * name,ctf_id_t id,unsigned long offset,int depth,void * arg)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
type_hex_digits(ctf_id_t id)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
ctf_dump_type(ctf_id_t id,int flag,void * arg)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
ctf_dump_str(ctf_dict_t * fp,ctf_dump_state_t * state)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 *
ctf_dump(ctf_dict_t * fp,ctf_dump_state_t ** statep,ctf_sect_names_t sect,ctf_dump_decorate_f * func,void * arg)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