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