]> Git Repo - binutils.git/blob - libctf/ctf-dump.c
libctf: add linking of the variable section
[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_NOTFUNC:
405           case ECTF_NOFUNCDAT:
406             continue;
407           }
408       if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
409         return (ctf_set_errno (fp, ENOMEM));
410
411       /* Return type.  */
412       if ((str = ctf_type_aname (state->cds_fp, type)) == NULL)
413         goto err;
414
415       str = ctf_str_append (str, " ");
416
417       /* Function name.  */
418
419       sym_name = ctf_lookup_symbol_name (fp, i);
420       if (sym_name[0] == '\0')
421         {
422           if (asprintf (&bit, "0x%lx ", (unsigned long) i) < 0)
423             goto oom;
424         }
425       else
426         {
427           if (asprintf (&bit, "%s (0x%lx) ", sym_name, (unsigned long) i) < 0)
428             goto oom;
429         }
430       str = ctf_str_append (str, bit);
431       str = ctf_str_append (str, " (");
432       free (bit);
433
434       /* Function arguments.  */
435
436       if (ctf_func_args (state->cds_fp, i, fi.ctc_argc, args) < 0)
437         goto err;
438
439       for (j = 0; j < fi.ctc_argc; j++)
440         {
441           if ((bit = ctf_type_aname (state->cds_fp, args[j])) == NULL)
442             goto err;
443           str = ctf_str_append (str, bit);
444           if ((j < fi.ctc_argc - 1) || (fi.ctc_flags & CTF_FUNC_VARARG))
445             str = ctf_str_append (str, ", ");
446           free (bit);
447         }
448
449       if (fi.ctc_flags & CTF_FUNC_VARARG)
450         str = ctf_str_append (str, "...");
451       str = ctf_str_append (str, ")");
452
453       free (args);
454       ctf_dump_append (state, str);
455       continue;
456
457     oom:
458       free (args);
459       free (str);
460       return (ctf_set_errno (fp, ENOMEM));
461     err:
462       free (args);
463       free (str);
464       return -1;                /* errno is set for us.  */
465     }
466   return 0;
467 }
468
469 /* Dump a single variable into the cds_items.  */
470 static int
471 ctf_dump_var (const char *name, ctf_id_t type, void *arg)
472 {
473   char *str;
474   char *typestr;
475   ctf_dump_state_t *state = arg;
476
477   if (asprintf (&str, "%s -> ", name) < 0)
478     return (ctf_set_errno (state->cds_fp, ENOMEM));
479
480   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
481                                        CTF_ADD_ROOT)) == NULL)
482     {
483       free (str);
484       return -1;                        /* errno is set for us.  */
485     }
486
487   str = ctf_str_append (str, typestr);
488   free (typestr);
489
490   ctf_dump_append (state, str);
491   return 0;
492 }
493
494 /* Dump a single member into the string in the membstate.  */
495 static int
496 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
497                   int depth, void *arg)
498 {
499   ctf_dump_membstate_t *state = arg;
500   char *typestr = NULL;
501   char *bit = NULL;
502   ctf_encoding_t ep;
503   ssize_t i;
504
505   for (i = 0; i < depth; i++)
506     *state->cdm_str = ctf_str_append (*state->cdm_str, "    ");
507
508   if ((typestr = ctf_type_aname (state->cdm_fp, id)) == NULL)
509     goto oom;
510
511   if (asprintf (&bit, "    [0x%lx] (ID 0x%lx) (kind %i) %s %s (aligned at 0x%lx",
512                 offset, id, ctf_type_kind (state->cdm_fp, id), typestr, name,
513                 (unsigned long) ctf_type_align (state->cdm_fp, id)) < 0)
514     goto oom;
515   *state->cdm_str = ctf_str_append (*state->cdm_str, bit);
516   free (typestr);
517   free (bit);
518   typestr = NULL;
519   bit = NULL;
520
521   if ((ctf_type_kind (state->cdm_fp, id) == CTF_K_INTEGER)
522       || (ctf_type_kind (state->cdm_fp, id) == CTF_K_FLOAT)
523       || (ctf_is_slice (state->cdm_fp, id, &ep) == CTF_K_ENUM))
524     {
525       ctf_type_encoding (state->cdm_fp, id, &ep);
526       if (asprintf (&bit, ", format 0x%x, offset:bits 0x%x:0x%x", ep.cte_format,
527                     ep.cte_offset, ep.cte_bits) < 0)
528         goto oom;
529       *state->cdm_str = ctf_str_append (*state->cdm_str, bit);
530       free (bit);
531       bit = NULL;
532     }
533
534   *state->cdm_str = ctf_str_append (*state->cdm_str, ")\n");
535   return 0;
536
537  oom:
538   free (typestr);
539   free (bit);
540   return (ctf_set_errno (state->cdm_fp, ENOMEM));
541 }
542
543 /* Dump a single type into the cds_items.  */
544
545 static int
546 ctf_dump_type (ctf_id_t id, int flag, void *arg)
547 {
548   char *str;
549   ctf_dump_state_t *state = arg;
550   ctf_dump_membstate_t membstate = { &str, state->cds_fp };
551   size_t len;
552
553   if ((str = ctf_dump_format_type (state->cds_fp, id, flag)) == NULL)
554     goto err;
555
556   str = ctf_str_append (str, "\n");
557   if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
558     goto err;
559
560   /* Trim off the last linefeed added by ctf_dump_member().  */
561   len = strlen (str);
562   if (str[len-1] == '\n')
563     str[len-1] = '\0';
564
565   ctf_dump_append (state, str);
566   return 0;
567
568  err:
569   free (str);
570   return -1;                            /* errno is set for us.  */
571 }
572
573 /* Dump the string table into the cds_items.  */
574
575 static int
576 ctf_dump_str (ctf_file_t *fp, ctf_dump_state_t *state)
577 {
578   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
579
580   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
581          fp->ctf_str[CTF_STRTAB_0].cts_len;)
582     {
583       char *str;
584       if (asprintf (&str, "%lx: %s",
585                     (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
586                     s) < 0)
587         return (ctf_set_errno (fp, ENOMEM));
588       ctf_dump_append (state, str);
589       s += strlen (s) + 1;
590     }
591
592   return 0;
593 }
594
595 /* Dump a particular section of a CTF file, in textual form.  Call with a
596    pointer to a NULL STATE: each call emits a dynamically allocated string
597    containing a description of one entity in the specified section, in order.
598    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
599    has been entirely dumped, the call returns NULL and frees and annuls the
600    STATE, ready for another section to be dumped.  The returned textual content
601    may span multiple lines: between each call the FUNC is called with one
602    textual line at a time, and should return a suitably decorated line (it can
603    allocate a new one and return it if it likes).  */
604
605 char *
606 ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
607           ctf_dump_decorate_f *func, void *arg)
608 {
609   char *str;
610   char *line;
611   ctf_dump_state_t *state = NULL;
612
613   if (*statep == NULL)
614     {
615       /* Data collection.  Transforming a call-at-a-time iterator into a
616          return-at-a-time iterator in a language without call/cc is annoying. It
617          is easiest to simply collect everything at once and then return it bit
618          by bit.  The first call will take (much) longer than otherwise, but the
619          amortized time needed is the same.  */
620
621       if ((*statep = ctf_alloc (sizeof (struct ctf_dump_state))) == NULL)
622         {
623           ctf_set_errno (fp, ENOMEM);
624           goto end;
625         }
626       state = *statep;
627
628       memset (state, 0, sizeof (struct ctf_dump_state));
629       state->cds_fp = fp;
630       state->cds_sect = sect;
631
632       switch (sect)
633         {
634         case CTF_SECT_HEADER:
635           ctf_dump_header (fp, state);
636           break;
637         case CTF_SECT_LABEL:
638           if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
639             {
640               if (ctf_errno (fp) != ECTF_NOLABELDATA)
641                 goto end;               /* errno is set for us.  */
642               ctf_set_errno (fp, 0);
643             }
644           break;
645         case CTF_SECT_OBJT:
646           if (ctf_dump_objts (fp, state) < 0)
647             goto end;                   /* errno is set for us.  */
648           break;
649         case CTF_SECT_FUNC:
650           if (ctf_dump_funcs (fp, state) < 0)
651             goto end;                   /* errno is set for us.  */
652           break;
653         case CTF_SECT_VAR:
654           if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
655             goto end;                   /* errno is set for us.  */
656           break;
657         case CTF_SECT_TYPE:
658           if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
659             goto end;                   /* errno is set for us.  */
660           break;
661         case CTF_SECT_STR:
662           ctf_dump_str (fp, state);
663           break;
664         default:
665           ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
666           goto end;
667         }
668     }
669   else
670     {
671       state = *statep;
672
673       if (state->cds_sect != sect)
674         {
675           ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
676           goto end;
677         }
678     }
679
680   if (state->cds_current == NULL)
681     state->cds_current = ctf_list_next (&state->cds_items);
682   else
683     state->cds_current = ctf_list_next (state->cds_current);
684
685   if (state->cds_current == NULL)
686     goto end;
687
688   /* Hookery.  There is some extra complexity to preserve linefeeds within each
689      item while removing linefeeds at the end.  */
690   if (func)
691     {
692       size_t len;
693
694       str = NULL;
695       for (line = state->cds_current->cdi_item; line && *line; )
696         {
697           char *nline = line;
698           char *ret;
699
700           nline = strchr (line, '\n');
701           if (nline)
702             nline[0] = '\0';
703
704           ret = func (sect, line, arg);
705           str = ctf_str_append (str, ret);
706           str = ctf_str_append (str, "\n");
707           if (ret != line)
708             free (ret);
709
710           if (nline)
711             {
712               nline[0] = '\n';
713               nline++;
714             }
715
716           line = nline;
717         }
718
719       len = strlen (str);
720
721       if (str[len-1] == '\n')
722         str[len-1] = '\0';
723     }
724   else
725     str = strdup (state->cds_current->cdi_item);
726
727   ctf_set_errno (fp, 0);
728   return str;
729
730  end:
731   ctf_dump_free (state);
732   ctf_free (state);
733   ctf_set_errno (fp, 0);
734   *statep = NULL;
735   return NULL;
736 }
This page took 0.06557 seconds and 4 git commands to generate.