]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - libctf/ctf-dump.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / libctf / ctf-dump.c
1 /* Textual dumping of CTF data.
2 Copyright (C) 2019-2021 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 } ctf_dump_membstate_t;
51
52 static int
53 ctf_dump_append (ctf_dump_state_t *state, char *str)
54 {
55 ctf_dump_item_t *cdi;
56
57 if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
58 return (ctf_set_errno (state->cds_fp, ENOMEM));
59
60 cdi->cdi_item = str;
61 ctf_list_append (&state->cds_items, cdi);
62 return 0;
63 }
64
65 static void
66 ctf_dump_free (ctf_dump_state_t *state)
67 {
68 ctf_dump_item_t *cdi, *next_cdi;
69
70 if (state == NULL)
71 return;
72
73 for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
74 cdi = next_cdi)
75 {
76 free (cdi->cdi_item);
77 next_cdi = ctf_list_next (cdi);
78 free (cdi);
79 }
80 }
81
82 /* Return a dump for a single type, without member info: but do show the
83 type's references. */
84
85 static char *
86 ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
87 {
88 ctf_id_t new_id;
89 char *str = NULL, *bit = NULL, *buf = NULL;
90
91 new_id = id;
92 do
93 {
94 ctf_encoding_t enc;
95 const char *nonroot_leader = "";
96 const char *nonroot_trailer = "";
97
98 id = new_id;
99 if (flag == CTF_ADD_NONROOT)
100 {
101 nonroot_leader = "{";
102 nonroot_trailer = "}";
103 }
104
105 buf = ctf_type_aname (fp, id);
106 if (!buf)
107 {
108 if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
109 {
110 str = str_append (str, " (type not represented in CTF)");
111 ctf_set_errno (fp, ECTF_NOTREF);
112 break;
113 }
114
115 goto err;
116 }
117
118 if (asprintf (&bit, " %s%lx: ", nonroot_leader, id) < 0)
119 goto oom;
120 str = str_append (str, bit);
121 free (bit);
122 bit = NULL;
123
124 if (buf[0] != '\0')
125 {
126 str = str_append (str, buf);
127 str = str_append (str, " ");
128 }
129
130 free (buf);
131 buf = NULL;
132
133 /* Slices get a different print representation. */
134 if (ctf_type_kind_unsliced (fp, id) == CTF_K_SLICE)
135 {
136 ctf_type_encoding (fp, id, &enc);
137 if (asprintf (&bit, "[slice 0x%x:0x%x] ",
138 enc.cte_offset, enc.cte_bits) < 0)
139 goto oom;
140 }
141 else if (ctf_type_kind (fp, id) == CTF_K_INTEGER)
142 {
143 ctf_type_encoding (fp, id, &enc);
144 if (asprintf (&bit, "[0x%x:0x%x] ",
145 enc.cte_offset, enc.cte_bits) < 0)
146 goto oom;
147 }
148 str = str_append (str, bit);
149 free (bit);
150 bit = NULL;
151
152 if (asprintf (&bit, "(size 0x%lx)%s",
153 (unsigned long) ctf_type_size (fp, id),
154 nonroot_trailer) < 0)
155 goto oom;
156
157 str = str_append (str, bit);
158 free (bit);
159 bit = NULL;
160
161 new_id = ctf_type_reference (fp, id);
162 if (new_id != CTF_ERR)
163 str = str_append (str, " ->");
164 } while (new_id != CTF_ERR);
165
166 if (ctf_errno (fp) != ECTF_NOTREF)
167 {
168 free (str);
169 return NULL;
170 }
171
172 return str;
173
174 oom:
175 ctf_set_errno (fp, errno);
176 err:
177 ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
178 free (buf);
179 free (str);
180 free (bit);
181 return NULL;
182 }
183
184 /* Dump one string field from the file header into the cds_items. */
185 static int
186 ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
187 const char *name, uint32_t value)
188 {
189 char *str;
190 if (value)
191 {
192 if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
193 goto err;
194 ctf_dump_append (state, str);
195 }
196 return 0;
197
198 err:
199 return (ctf_set_errno (fp, errno));
200 }
201
202 /* Dump one section-offset field from the file header into the cds_items. */
203 static int
204 ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
205 const char *sect, uint32_t off, uint32_t nextoff)
206 {
207 char *str;
208 if (nextoff - off)
209 {
210 if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
211 (unsigned long) off, (unsigned long) (nextoff - 1),
212 (unsigned long) (nextoff - off)) < 0)
213 goto err;
214 ctf_dump_append (state, str);
215 }
216 return 0;
217
218 err:
219 return (ctf_set_errno (fp, errno));
220 }
221
222 /* Dump the file header into the cds_items. */
223 static int
224 ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
225 {
226 char *str;
227 char *flagstr = NULL;
228 const ctf_header_t *hp = fp->ctf_header;
229 const char *vertab[] =
230 {
231 NULL, "CTF_VERSION_1",
232 "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
233 "boundaries)",
234 "CTF_VERSION_2",
235 "CTF_VERSION_3", NULL
236 };
237 const char *verstr = NULL;
238
239 if (asprintf (&str, "Magic number: %x\n", hp->cth_magic) < 0)
240 goto err;
241 ctf_dump_append (state, str);
242
243 if (hp->cth_version <= CTF_VERSION)
244 verstr = vertab[hp->cth_version];
245
246 if (verstr == NULL)
247 verstr = "(not a valid version)";
248
249 if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
250 verstr) < 0)
251 goto err;
252 ctf_dump_append (state, str);
253
254 /* Everything else is only printed if present. */
255
256 /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
257 flags representing compression, etc, are turned off as the file is
258 decompressed. So we store a copy of the flags before they are changed, for
259 the dumper. */
260
261 if (fp->ctf_openflags > 0)
262 {
263 if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
264 fp->ctf_openflags & CTF_F_COMPRESS
265 ? "CTF_F_COMPRESS": "",
266 (fp->ctf_openflags & CTF_F_COMPRESS)
267 && (fp->ctf_openflags & ~CTF_F_COMPRESS)
268 ? ", " : "",
269 fp->ctf_openflags & CTF_F_NEWFUNCINFO
270 ? "CTF_F_NEWFUNCINFO" : "",
271 (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
272 && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
273 ? ", " : "",
274 fp->ctf_openflags & CTF_F_IDXSORTED
275 ? "CTF_F_IDXSORTED" : "",
276 fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
277 | CTF_F_IDXSORTED)
278 && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
279 | CTF_F_IDXSORTED))
280 ? ", " : "",
281 fp->ctf_openflags & CTF_F_DYNSTR
282 ? "CTF_F_DYNSTR" : "") < 0)
283 goto err;
284
285 if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
286 goto err;
287 ctf_dump_append (state, str);
288 }
289
290 if (ctf_dump_header_strfield (fp, state, "Parent label",
291 hp->cth_parlabel) < 0)
292 goto err;
293
294 if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
295 goto err;
296
297 if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
298 hp->cth_cuname) < 0)
299 goto err;
300
301 if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
302 hp->cth_objtoff) < 0)
303 goto err;
304
305 if (ctf_dump_header_sectfield (fp, state, "Data object section",
306 hp->cth_objtoff, hp->cth_funcoff) < 0)
307 goto err;
308
309 if (ctf_dump_header_sectfield (fp, state, "Function info section",
310 hp->cth_funcoff, hp->cth_objtidxoff) < 0)
311 goto err;
312
313 if (ctf_dump_header_sectfield (fp, state, "Object index section",
314 hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
315 goto err;
316
317 if (ctf_dump_header_sectfield (fp, state, "Function index section",
318 hp->cth_funcidxoff, hp->cth_varoff) < 0)
319 goto err;
320
321 if (ctf_dump_header_sectfield (fp, state, "Variable section",
322 hp->cth_varoff, hp->cth_typeoff) < 0)
323 goto err;
324
325 if (ctf_dump_header_sectfield (fp, state, "Type section",
326 hp->cth_typeoff, hp->cth_stroff) < 0)
327 goto err;
328
329 if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
330 hp->cth_stroff + hp->cth_strlen + 1) < 0)
331 goto err;
332
333 return 0;
334 err:
335 free (flagstr);
336 return (ctf_set_errno (fp, errno));
337 }
338
339 /* Dump a single label into the cds_items. */
340
341 static int
342 ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
343 void *arg)
344 {
345 char *str;
346 char *typestr;
347 ctf_dump_state_t *state = arg;
348
349 if (asprintf (&str, "%s -> ", name) < 0)
350 return (ctf_set_errno (state->cds_fp, errno));
351
352 if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
353 CTF_ADD_ROOT)) == NULL)
354 {
355 free (str);
356 return 0; /* Swallow the error. */
357 }
358
359 str = str_append (str, typestr);
360 free (typestr);
361
362 ctf_dump_append (state, str);
363 return 0;
364 }
365
366 /* Dump all the object or function entries into the cds_items. */
367
368 static int
369 ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
370 {
371 const char *name;
372 ctf_id_t id;
373 ctf_next_t *i = NULL;
374 char *str = NULL;
375
376 if ((functions && fp->ctf_funcidx_names)
377 || (!functions && fp->ctf_objtidx_names))
378 str = str_append (str, _("Section is indexed.\n"));
379 else if (fp->ctf_symtab.cts_data == NULL)
380 str = str_append (str, _("No symbol table.\n"));
381
382 while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
383 {
384 char *typestr = NULL;
385 int err = 0;
386
387 /* Emit the name, if we know it. */
388 if (name)
389 {
390 if (asprintf (&str, "%s -> ", name) < 0)
391 goto oom;
392 }
393 else
394 str = xstrdup ("");
395
396 if ((typestr = ctf_type_aname (fp, id)) == NULL)
397 {
398 if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
399 {
400 if (asprintf (&typestr, " (%s)", _("type not represented in CTF")) < 0)
401 goto oom;
402
403 goto out;
404 }
405
406 if (asprintf (&typestr, _("error: %s"), ctf_errmsg (ctf_errno (fp))) < 0)
407 goto oom;
408
409 err = -1;
410 goto out;
411 }
412
413 str = str_append (str, typestr);
414 str = str_append (str, "\n");
415 ctf_dump_append (state, str);
416 continue;
417
418 oom:
419 ctf_set_errno (fp, ENOMEM);
420 ctf_next_destroy (i);
421 return -1;
422 out:
423 str = str_append (str, typestr);
424 free (typestr);
425 ctf_dump_append (state, str);
426 ctf_next_destroy (i);
427 return err; /* errno is set for us. */
428 }
429 return 0;
430 }
431
432 /* Dump a single variable into the cds_items. */
433 static int
434 ctf_dump_var (const char *name, ctf_id_t type, void *arg)
435 {
436 char *str;
437 char *typestr;
438 ctf_dump_state_t *state = arg;
439
440 if (asprintf (&str, "%s -> ", name) < 0)
441 return (ctf_set_errno (state->cds_fp, errno));
442
443 if ((typestr = ctf_dump_format_type (state->cds_fp, type,
444 CTF_ADD_ROOT)) == NULL)
445 {
446 free (str);
447 return 0; /* Swallow the error. */
448 }
449
450 str = str_append (str, typestr);
451 free (typestr);
452
453 ctf_dump_append (state, str);
454 return 0;
455 }
456
457 /* Dump a single member into the string in the membstate. */
458 static int
459 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
460 int depth, void *arg)
461 {
462 ctf_dump_membstate_t *state = arg;
463 char *typestr = NULL;
464 char *bit = NULL;
465 ctf_encoding_t ep;
466 int has_encoding = 0;
467 ssize_t i;
468
469 for (i = 0; i < depth; i++)
470 *state->cdm_str = str_append (*state->cdm_str, " ");
471
472 if ((typestr = ctf_type_aname (state->cdm_fp, id)) == NULL)
473 {
474 if (id == 0 || ctf_errno (state->cdm_fp) == ECTF_NONREPRESENTABLE)
475 {
476 if (asprintf (&bit, " [0x%lx] (type not represented in CTF)",
477 offset) < 0)
478 goto oom;
479
480 *state->cdm_str = str_append (*state->cdm_str, bit);
481 free (typestr);
482 free (bit);
483 return 0;
484 }
485
486 return -1; /* errno is set for us. */
487 }
488
489 if (ctf_type_encoding (state->cdm_fp, id, &ep) == 0)
490 {
491 has_encoding = 1;
492 ctf_type_encoding (state->cdm_fp, id, &ep);
493
494 if (asprintf (&bit, " [0x%lx] (ID 0x%lx) (kind %i) %s%s%s:%i "
495 "(aligned at 0x%lx", offset, id,
496 ctf_type_kind (state->cdm_fp, id), typestr,
497 (name[0] != 0 && typestr[0] != 0) ? " " : "", name,
498 ep.cte_bits, (unsigned long) ctf_type_align (state->cdm_fp,
499 id)) < 0)
500 goto oom;
501 }
502 else
503 {
504 if (asprintf (&bit, " [0x%lx] (ID 0x%lx) (kind %i) %s%s%s "
505 "(aligned at 0x%lx", offset, id,
506 ctf_type_kind (state->cdm_fp, id), typestr,
507 (name[0] != 0 && typestr[0] != 0) ? " " : "", name,
508 (unsigned long) ctf_type_align (state->cdm_fp, id)) < 0)
509 goto oom;
510 }
511
512 *state->cdm_str = str_append (*state->cdm_str, bit);
513 free (typestr);
514 free (bit);
515 typestr = NULL;
516 bit = NULL;
517
518 if (has_encoding)
519 {
520 if (asprintf (&bit, ", format 0x%x, offset:bits 0x%x:0x%x", ep.cte_format,
521 ep.cte_offset, ep.cte_bits) < 0)
522 goto oom;
523 *state->cdm_str = str_append (*state->cdm_str, bit);
524 free (bit);
525 bit = NULL;
526 }
527
528 *state->cdm_str = str_append (*state->cdm_str, ")\n");
529 return 0;
530
531 oom:
532 free (typestr);
533 free (bit);
534 return (ctf_set_errno (state->cdm_fp, errno));
535 }
536
537 /* Dump a single type into the cds_items. */
538 static int
539 ctf_dump_type (ctf_id_t id, int flag, void *arg)
540 {
541 char *str;
542 ctf_dump_state_t *state = arg;
543 ctf_dump_membstate_t membstate = { &str, state->cds_fp };
544 size_t len;
545
546 if ((str = ctf_dump_format_type (state->cds_fp, id, flag)) == NULL)
547 goto err;
548
549 str = str_append (str, "\n");
550 if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
551 {
552 if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
553 {
554 ctf_dump_append (state, str);
555 return 0;
556 }
557 ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
558 _("cannot visit members dumping type 0x%lx"), id);
559 goto err;
560 }
561
562 /* Trim off the last linefeed added by ctf_dump_member(). */
563 len = strlen (str);
564 if (str[len-1] == '\n')
565 str[len-1] = '\0';
566
567 ctf_dump_append (state, str);
568 return 0;
569
570 err:
571 free (str);
572 return 0; /* Swallow the error. */
573 }
574
575 /* Dump the string table into the cds_items. */
576
577 static int
578 ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
579 {
580 const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
581
582 for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
583 fp->ctf_str[CTF_STRTAB_0].cts_len;)
584 {
585 char *str;
586 if (asprintf (&str, "%lx: %s",
587 (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
588 s) < 0)
589 return (ctf_set_errno (fp, errno));
590 ctf_dump_append (state, str);
591 s += strlen (s) + 1;
592 }
593
594 return 0;
595 }
596
597 /* Dump a particular section of a CTF file, in textual form. Call with a
598 pointer to a NULL STATE: each call emits a dynamically allocated string
599 containing a description of one entity in the specified section, in order.
600 Only the first call (with a NULL state) may vary SECT. Once the CTF section
601 has been entirely dumped, the call returns NULL and frees and annuls the
602 STATE, ready for another section to be dumped. The returned textual content
603 may span multiple lines: between each call the FUNC is called with one
604 textual line at a time, and should return a suitably decorated line (it can
605 allocate a new one and return it if it likes). */
606
607 char *
608 ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
609 ctf_dump_decorate_f *func, void *arg)
610 {
611 char *str;
612 char *line;
613 ctf_dump_state_t *state = NULL;
614
615 if (*statep == NULL)
616 {
617 /* Data collection. Transforming a call-at-a-time iterator into a
618 return-at-a-time iterator in a language without call/cc is annoying. It
619 is easiest to simply collect everything at once and then return it bit
620 by bit. The first call will take (much) longer than otherwise, but the
621 amortized time needed is the same. */
622
623 if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
624 {
625 ctf_set_errno (fp, ENOMEM);
626 goto end;
627 }
628 state = *statep;
629
630 memset (state, 0, sizeof (struct ctf_dump_state));
631 state->cds_fp = fp;
632 state->cds_sect = sect;
633
634 switch (sect)
635 {
636 case CTF_SECT_HEADER:
637 ctf_dump_header (fp, state);
638 break;
639 case CTF_SECT_LABEL:
640 if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
641 {
642 if (ctf_errno (fp) != ECTF_NOLABELDATA)
643 goto end; /* errno is set for us. */
644 ctf_set_errno (fp, 0);
645 }
646 break;
647 case CTF_SECT_OBJT:
648 if (ctf_dump_objts (fp, state, 0) < 0)
649 goto end; /* errno is set for us. */
650 break;
651 case CTF_SECT_FUNC:
652 if (ctf_dump_objts (fp, state, 1) < 0)
653 goto end; /* errno is set for us. */
654 break;
655 case CTF_SECT_VAR:
656 if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
657 goto end; /* errno is set for us. */
658 break;
659 case CTF_SECT_TYPE:
660 if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
661 goto end; /* errno is set for us. */
662 break;
663 case CTF_SECT_STR:
664 ctf_dump_str (fp, state);
665 break;
666 default:
667 ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
668 goto end;
669 }
670 }
671 else
672 {
673 state = *statep;
674
675 if (state->cds_sect != sect)
676 {
677 ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
678 goto end;
679 }
680 }
681
682 if (state->cds_current == NULL)
683 state->cds_current = ctf_list_next (&state->cds_items);
684 else
685 state->cds_current = ctf_list_next (state->cds_current);
686
687 if (state->cds_current == NULL)
688 goto end;
689
690 /* Hookery. There is some extra complexity to preserve linefeeds within each
691 item while removing linefeeds at the end. */
692 if (func)
693 {
694 size_t len;
695
696 str = NULL;
697 for (line = state->cds_current->cdi_item; line && *line; )
698 {
699 char *nline = line;
700 char *ret;
701
702 nline = strchr (line, '\n');
703 if (nline)
704 nline[0] = '\0';
705
706 ret = func (sect, line, arg);
707 str = str_append (str, ret);
708 str = str_append (str, "\n");
709 if (ret != line)
710 free (ret);
711
712 if (nline)
713 {
714 nline[0] = '\n';
715 nline++;
716 }
717
718 line = nline;
719 }
720
721 len = strlen (str);
722
723 if (str[len-1] == '\n')
724 str[len-1] = '\0';
725 }
726 else
727 {
728 str = strdup (state->cds_current->cdi_item);
729 if (!str)
730 {
731 ctf_set_errno (fp, ENOMEM);
732 return str;
733 }
734 }
735
736 ctf_set_errno (fp, 0);
737 return str;
738
739 end:
740 ctf_dump_free (state);
741 free (state);
742 ctf_set_errno (fp, 0);
743 *statep = NULL;
744 return NULL;
745 }