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