Home | History | Annotate | Line # | Download | only in libctf
ctf-dump.c revision 1.1.1.2
      1      1.1  christos /* Textual dumping of CTF data.
      2  1.1.1.2  christos    Copyright (C) 2019-2022 Free Software Foundation, Inc.
      3      1.1  christos 
      4      1.1  christos    This file is part of libctf.
      5      1.1  christos 
      6      1.1  christos    libctf is free software; you can redistribute it and/or modify it under
      7      1.1  christos    the terms of the GNU General Public License as published by the Free
      8      1.1  christos    Software Foundation; either version 3, or (at your option) any later
      9      1.1  christos    version.
     10      1.1  christos 
     11      1.1  christos    This program is distributed in the hope that it will be useful, but
     12      1.1  christos    WITHOUT ANY WARRANTY; without even the implied warranty of
     13      1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     14      1.1  christos    See the GNU General Public License for more details.
     15      1.1  christos 
     16      1.1  christos    You should have received a copy of the GNU General Public License
     17      1.1  christos    along with this program; see the file COPYING.  If not see
     18      1.1  christos    <http://www.gnu.org/licenses/>.  */
     19      1.1  christos 
     20      1.1  christos #include <ctf-impl.h>
     21      1.1  christos #include <string.h>
     22      1.1  christos 
     23      1.1  christos #define str_append(s, a) ctf_str_append_noerr (s, a)
     24      1.1  christos 
     25      1.1  christos /* One item to be dumped, in string form.  */
     26      1.1  christos 
     27      1.1  christos typedef struct ctf_dump_item
     28      1.1  christos {
     29      1.1  christos   ctf_list_t cdi_list;
     30      1.1  christos   char *cdi_item;
     31      1.1  christos } ctf_dump_item_t;
     32      1.1  christos 
     33      1.1  christos /* Cross-call state for dumping.  Basically just enough to track the section in
     34      1.1  christos    use and a list of return strings.  */
     35      1.1  christos 
     36      1.1  christos struct ctf_dump_state
     37      1.1  christos {
     38      1.1  christos   ctf_sect_names_t cds_sect;
     39  1.1.1.2  christos   ctf_dict_t *cds_fp;
     40      1.1  christos   ctf_dump_item_t *cds_current;
     41      1.1  christos   ctf_list_t cds_items;
     42      1.1  christos };
     43      1.1  christos 
     44      1.1  christos /* Cross-call state for ctf_dump_member. */
     45      1.1  christos 
     46      1.1  christos typedef struct ctf_dump_membstate
     47      1.1  christos {
     48      1.1  christos   char **cdm_str;
     49  1.1.1.2  christos   ctf_dict_t *cdm_fp;
     50  1.1.1.2  christos   const char *cdm_toplevel_indent;
     51      1.1  christos } ctf_dump_membstate_t;
     52      1.1  christos 
     53      1.1  christos static int
     54      1.1  christos ctf_dump_append (ctf_dump_state_t *state, char *str)
     55      1.1  christos {
     56      1.1  christos   ctf_dump_item_t *cdi;
     57      1.1  christos 
     58      1.1  christos   if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
     59      1.1  christos     return (ctf_set_errno (state->cds_fp, ENOMEM));
     60      1.1  christos 
     61      1.1  christos   cdi->cdi_item = str;
     62      1.1  christos   ctf_list_append (&state->cds_items, cdi);
     63      1.1  christos   return 0;
     64      1.1  christos }
     65      1.1  christos 
     66      1.1  christos static void
     67      1.1  christos ctf_dump_free (ctf_dump_state_t *state)
     68      1.1  christos {
     69      1.1  christos   ctf_dump_item_t *cdi, *next_cdi;
     70      1.1  christos 
     71      1.1  christos   if (state == NULL)
     72      1.1  christos     return;
     73      1.1  christos 
     74      1.1  christos   for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
     75      1.1  christos        cdi = next_cdi)
     76      1.1  christos     {
     77      1.1  christos       free (cdi->cdi_item);
     78      1.1  christos       next_cdi = ctf_list_next (cdi);
     79      1.1  christos       free (cdi);
     80      1.1  christos     }
     81      1.1  christos }
     82      1.1  christos 
     83  1.1.1.2  christos /* Return a dump for a single type, without member info: but do optionally show
     84  1.1.1.2  christos    the type's references.  */
     85  1.1.1.2  christos 
     86  1.1.1.2  christos #define CTF_FT_REFS     0x2 	/* Print referenced types.  */
     87  1.1.1.2  christos #define CTF_FT_BITFIELD 0x4	/* Print :BITS if a bitfield.  */
     88  1.1.1.2  christos #define CTF_FT_ID       0x8	/* Print "ID: " in front of type IDs.  */
     89      1.1  christos 
     90      1.1  christos static char *
     91  1.1.1.2  christos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag)
     92      1.1  christos {
     93      1.1  christos   ctf_id_t new_id;
     94      1.1  christos   char *str = NULL, *bit = NULL, *buf = NULL;
     95      1.1  christos 
     96  1.1.1.2  christos   ctf_set_errno (fp, 0);
     97      1.1  christos   new_id = id;
     98      1.1  christos   do
     99      1.1  christos     {
    100  1.1.1.2  christos       ctf_encoding_t ep;
    101  1.1.1.2  christos       ctf_arinfo_t ar;
    102  1.1.1.2  christos       int kind, unsliced_kind;
    103  1.1.1.2  christos       ssize_t size, align;
    104      1.1  christos       const char *nonroot_leader = "";
    105      1.1  christos       const char *nonroot_trailer = "";
    106  1.1.1.2  christos       const char *idstr = "";
    107      1.1  christos 
    108      1.1  christos       id = new_id;
    109      1.1  christos       if (flag == CTF_ADD_NONROOT)
    110      1.1  christos 	{
    111      1.1  christos 	  nonroot_leader = "{";
    112      1.1  christos 	  nonroot_trailer = "}";
    113      1.1  christos 	}
    114      1.1  christos 
    115      1.1  christos       buf = ctf_type_aname (fp, id);
    116      1.1  christos       if (!buf)
    117      1.1  christos 	{
    118      1.1  christos 	  if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
    119      1.1  christos 	    {
    120  1.1.1.2  christos 	      ctf_set_errno (fp, ECTF_NONREPRESENTABLE);
    121      1.1  christos 	      str = str_append (str, " (type not represented in CTF)");
    122  1.1.1.2  christos 	      return str;
    123      1.1  christos 	    }
    124      1.1  christos 
    125      1.1  christos 	  goto err;
    126      1.1  christos 	}
    127      1.1  christos 
    128  1.1.1.2  christos       if (flag & CTF_FT_ID)
    129  1.1.1.2  christos 	idstr = "ID ";
    130  1.1.1.2  christos       if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr,
    131  1.1.1.2  christos 		    id, ctf_type_kind (fp, id)) < 0)
    132      1.1  christos 	goto oom;
    133      1.1  christos       str = str_append (str, bit);
    134      1.1  christos       free (bit);
    135      1.1  christos       bit = NULL;
    136      1.1  christos 
    137      1.1  christos       if (buf[0] != '\0')
    138  1.1.1.2  christos 	str = str_append (str, buf);
    139      1.1  christos 
    140      1.1  christos       free (buf);
    141      1.1  christos       buf = NULL;
    142      1.1  christos 
    143  1.1.1.2  christos       unsliced_kind = ctf_type_kind_unsliced (fp, id);
    144  1.1.1.2  christos       kind = ctf_type_kind (fp, id);
    145  1.1.1.2  christos 
    146  1.1.1.2  christos       /* Report encodings of everything with an encoding other than enums:
    147  1.1.1.2  christos 	 base-type enums cannot have a nonzero cte_offset or cte_bits value.
    148  1.1.1.2  christos 	 (Slices of them can, but they are of kind CTF_K_SLICE.)  */
    149  1.1.1.2  christos       if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0)
    150      1.1  christos 	{
    151  1.1.1.2  christos 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
    152  1.1.1.2  christos 	      && flag & CTF_FT_BITFIELD)
    153  1.1.1.2  christos 	    {
    154  1.1.1.2  christos 	      if (asprintf (&bit, ":%i", ep.cte_bits) < 0)
    155  1.1.1.2  christos 		goto oom;
    156  1.1.1.2  christos 	      str = str_append (str, bit);
    157  1.1.1.2  christos 	      free (bit);
    158  1.1.1.2  christos 	      bit = NULL;
    159  1.1.1.2  christos 	    }
    160  1.1.1.2  christos 
    161  1.1.1.2  christos 	  if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT
    162  1.1.1.2  christos 	      || ep.cte_offset != 0)
    163  1.1.1.2  christos 	    {
    164  1.1.1.2  christos 	      const char *slice = "";
    165  1.1.1.2  christos 
    166  1.1.1.2  christos 	      if (unsliced_kind == CTF_K_SLICE)
    167  1.1.1.2  christos 		slice = "slice ";
    168  1.1.1.2  christos 
    169  1.1.1.2  christos 	      if (asprintf (&bit, " [%s0x%x:0x%x]",
    170  1.1.1.2  christos 			    slice, ep.cte_offset, ep.cte_bits) < 0)
    171  1.1.1.2  christos 		goto oom;
    172  1.1.1.2  christos 	      str = str_append (str, bit);
    173  1.1.1.2  christos 	      free (bit);
    174  1.1.1.2  christos 	      bit = NULL;
    175  1.1.1.2  christos 	    }
    176  1.1.1.2  christos 
    177  1.1.1.2  christos 	  if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0)
    178      1.1  christos 	    goto oom;
    179  1.1.1.2  christos 	  str = str_append (str, bit);
    180  1.1.1.2  christos 	  free (bit);
    181  1.1.1.2  christos 	  bit = NULL;
    182      1.1  christos 	}
    183  1.1.1.2  christos 
    184  1.1.1.2  christos       size = ctf_type_size (fp, id);
    185  1.1.1.2  christos       if (kind != CTF_K_FUNCTION && size >= 0)
    186      1.1  christos 	{
    187  1.1.1.2  christos 	  if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0)
    188      1.1  christos 	    goto oom;
    189  1.1.1.2  christos 
    190  1.1.1.2  christos 	  str = str_append (str, bit);
    191  1.1.1.2  christos 	  free (bit);
    192  1.1.1.2  christos 	  bit = NULL;
    193      1.1  christos 	}
    194      1.1  christos 
    195  1.1.1.2  christos       align = ctf_type_align (fp, id);
    196  1.1.1.2  christos       if (align >= 0)
    197  1.1.1.2  christos 	{
    198  1.1.1.2  christos 	  if (asprintf (&bit, " (aligned at 0x%lx)",
    199  1.1.1.2  christos 			(unsigned long int) align) < 0)
    200  1.1.1.2  christos 	    goto oom;
    201      1.1  christos 
    202  1.1.1.2  christos 	  str = str_append (str, bit);
    203  1.1.1.2  christos 	  free (bit);
    204  1.1.1.2  christos 	  bit = NULL;
    205  1.1.1.2  christos 	}
    206  1.1.1.2  christos 
    207  1.1.1.2  christos       if (nonroot_trailer[0] != 0)
    208  1.1.1.2  christos 	str = str_append (str, nonroot_trailer);
    209  1.1.1.2  christos 
    210  1.1.1.2  christos       /* Just exit after one iteration if we are not showing the types this type
    211  1.1.1.2  christos 	 references.  */
    212  1.1.1.2  christos       if (!(flag & CTF_FT_REFS))
    213  1.1.1.2  christos 	return str;
    214      1.1  christos 
    215  1.1.1.2  christos       /* Keep going as long as this type references another.  We consider arrays
    216  1.1.1.2  christos 	 to "reference" their element type. */
    217  1.1.1.2  christos 
    218  1.1.1.2  christos       if (kind == CTF_K_ARRAY)
    219  1.1.1.2  christos 	{
    220  1.1.1.2  christos 	  if (ctf_array_info (fp, id, &ar) < 0)
    221  1.1.1.2  christos 	    goto err;
    222  1.1.1.2  christos 	  new_id = ar.ctr_contents;
    223  1.1.1.2  christos 	}
    224  1.1.1.2  christos       else
    225  1.1.1.2  christos 	new_id = ctf_type_reference (fp, id);
    226      1.1  christos       if (new_id != CTF_ERR)
    227  1.1.1.2  christos 	str = str_append (str, " -> ");
    228  1.1.1.2  christos     }
    229  1.1.1.2  christos   while (new_id != CTF_ERR);
    230      1.1  christos 
    231      1.1  christos   if (ctf_errno (fp) != ECTF_NOTREF)
    232      1.1  christos     {
    233      1.1  christos       free (str);
    234      1.1  christos       return NULL;
    235      1.1  christos     }
    236      1.1  christos 
    237      1.1  christos   return str;
    238      1.1  christos 
    239      1.1  christos  oom:
    240      1.1  christos   ctf_set_errno (fp, errno);
    241      1.1  christos  err:
    242      1.1  christos   ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id);
    243      1.1  christos   free (buf);
    244      1.1  christos   free (str);
    245      1.1  christos   free (bit);
    246      1.1  christos   return NULL;
    247      1.1  christos }
    248      1.1  christos 
    249      1.1  christos /* Dump one string field from the file header into the cds_items.  */
    250      1.1  christos static int
    251  1.1.1.2  christos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    252      1.1  christos 			  const char *name, uint32_t value)
    253      1.1  christos {
    254      1.1  christos   char *str;
    255      1.1  christos   if (value)
    256      1.1  christos     {
    257      1.1  christos       if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
    258      1.1  christos 	goto err;
    259      1.1  christos       ctf_dump_append (state, str);
    260      1.1  christos     }
    261      1.1  christos   return 0;
    262      1.1  christos 
    263      1.1  christos  err:
    264      1.1  christos   return (ctf_set_errno (fp, errno));
    265      1.1  christos }
    266      1.1  christos 
    267      1.1  christos /* Dump one section-offset field from the file header into the cds_items.  */
    268      1.1  christos static int
    269  1.1.1.2  christos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    270      1.1  christos 			   const char *sect, uint32_t off, uint32_t nextoff)
    271      1.1  christos {
    272      1.1  christos   char *str;
    273      1.1  christos   if (nextoff - off)
    274      1.1  christos     {
    275      1.1  christos       if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
    276      1.1  christos 		    (unsigned long) off, (unsigned long) (nextoff - 1),
    277      1.1  christos 		    (unsigned long) (nextoff - off)) < 0)
    278      1.1  christos 	goto err;
    279      1.1  christos       ctf_dump_append (state, str);
    280      1.1  christos     }
    281      1.1  christos   return 0;
    282      1.1  christos 
    283      1.1  christos  err:
    284      1.1  christos   return (ctf_set_errno (fp, errno));
    285      1.1  christos }
    286      1.1  christos 
    287      1.1  christos /* Dump the file header into the cds_items.  */
    288      1.1  christos static int
    289  1.1.1.2  christos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
    290      1.1  christos {
    291      1.1  christos   char *str;
    292  1.1.1.2  christos   char *flagstr = NULL;
    293      1.1  christos   const ctf_header_t *hp = fp->ctf_header;
    294      1.1  christos   const char *vertab[] =
    295      1.1  christos     {
    296      1.1  christos      NULL, "CTF_VERSION_1",
    297      1.1  christos      "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
    298      1.1  christos      "boundaries)",
    299      1.1  christos      "CTF_VERSION_2",
    300      1.1  christos      "CTF_VERSION_3", NULL
    301      1.1  christos     };
    302      1.1  christos   const char *verstr = NULL;
    303      1.1  christos 
    304  1.1.1.2  christos   if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
    305      1.1  christos       goto err;
    306      1.1  christos   ctf_dump_append (state, str);
    307      1.1  christos 
    308      1.1  christos   if (hp->cth_version <= CTF_VERSION)
    309      1.1  christos     verstr = vertab[hp->cth_version];
    310      1.1  christos 
    311      1.1  christos   if (verstr == NULL)
    312      1.1  christos     verstr = "(not a valid version)";
    313      1.1  christos 
    314      1.1  christos   if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
    315      1.1  christos 		verstr) < 0)
    316      1.1  christos     goto err;
    317      1.1  christos   ctf_dump_append (state, str);
    318      1.1  christos 
    319      1.1  christos   /* Everything else is only printed if present.  */
    320      1.1  christos 
    321  1.1.1.2  christos   /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
    322      1.1  christos      flags representing compression, etc, are turned off as the file is
    323      1.1  christos      decompressed.  So we store a copy of the flags before they are changed, for
    324      1.1  christos      the dumper.  */
    325      1.1  christos 
    326      1.1  christos   if (fp->ctf_openflags > 0)
    327      1.1  christos     {
    328  1.1.1.2  christos       if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
    329  1.1.1.2  christos 		    fp->ctf_openflags & CTF_F_COMPRESS
    330  1.1.1.2  christos 		    ? "CTF_F_COMPRESS": "",
    331  1.1.1.2  christos 		    (fp->ctf_openflags & CTF_F_COMPRESS)
    332  1.1.1.2  christos 		    && (fp->ctf_openflags & ~CTF_F_COMPRESS)
    333  1.1.1.2  christos 		    ? ", " : "",
    334  1.1.1.2  christos 		    fp->ctf_openflags & CTF_F_NEWFUNCINFO
    335  1.1.1.2  christos 		    ? "CTF_F_NEWFUNCINFO" : "",
    336  1.1.1.2  christos 		    (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
    337  1.1.1.2  christos 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
    338  1.1.1.2  christos 		    ? ", " : "",
    339  1.1.1.2  christos 		    fp->ctf_openflags & CTF_F_IDXSORTED
    340  1.1.1.2  christos 		    ? "CTF_F_IDXSORTED" : "",
    341  1.1.1.2  christos 		    fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
    342  1.1.1.2  christos 					 | CTF_F_IDXSORTED)
    343  1.1.1.2  christos 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
    344  1.1.1.2  christos 					      | CTF_F_IDXSORTED))
    345  1.1.1.2  christos 		    ? ", " : "",
    346  1.1.1.2  christos 		    fp->ctf_openflags & CTF_F_DYNSTR
    347  1.1.1.2  christos 		    ? "CTF_F_DYNSTR" : "") < 0)
    348  1.1.1.2  christos 	goto err;
    349  1.1.1.2  christos 
    350  1.1.1.2  christos       if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0)
    351      1.1  christos 	goto err;
    352      1.1  christos       ctf_dump_append (state, str);
    353      1.1  christos     }
    354      1.1  christos 
    355      1.1  christos   if (ctf_dump_header_strfield (fp, state, "Parent label",
    356      1.1  christos 				hp->cth_parlabel) < 0)
    357      1.1  christos     goto err;
    358      1.1  christos 
    359      1.1  christos   if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
    360      1.1  christos     goto err;
    361      1.1  christos 
    362      1.1  christos   if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
    363      1.1  christos 				hp->cth_cuname) < 0)
    364      1.1  christos     goto err;
    365      1.1  christos 
    366      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
    367      1.1  christos 				 hp->cth_objtoff) < 0)
    368      1.1  christos     goto err;
    369      1.1  christos 
    370      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Data object section",
    371      1.1  christos 				 hp->cth_objtoff, hp->cth_funcoff) < 0)
    372      1.1  christos     goto err;
    373      1.1  christos 
    374      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Function info section",
    375  1.1.1.2  christos 				 hp->cth_funcoff, hp->cth_objtidxoff) < 0)
    376  1.1.1.2  christos     goto err;
    377  1.1.1.2  christos 
    378  1.1.1.2  christos   if (ctf_dump_header_sectfield (fp, state, "Object index section",
    379  1.1.1.2  christos 				 hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
    380  1.1.1.2  christos     goto err;
    381  1.1.1.2  christos 
    382  1.1.1.2  christos   if (ctf_dump_header_sectfield (fp, state, "Function index section",
    383  1.1.1.2  christos 				 hp->cth_funcidxoff, hp->cth_varoff) < 0)
    384      1.1  christos     goto err;
    385      1.1  christos 
    386      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Variable section",
    387      1.1  christos 				 hp->cth_varoff, hp->cth_typeoff) < 0)
    388      1.1  christos     goto err;
    389      1.1  christos 
    390      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Type section",
    391      1.1  christos 				 hp->cth_typeoff, hp->cth_stroff) < 0)
    392      1.1  christos     goto err;
    393      1.1  christos 
    394      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
    395      1.1  christos 				 hp->cth_stroff + hp->cth_strlen + 1) < 0)
    396      1.1  christos     goto err;
    397      1.1  christos 
    398      1.1  christos   return 0;
    399      1.1  christos  err:
    400  1.1.1.2  christos   free (flagstr);
    401      1.1  christos   return (ctf_set_errno (fp, errno));
    402      1.1  christos }
    403      1.1  christos 
    404      1.1  christos /* Dump a single label into the cds_items.  */
    405      1.1  christos 
    406      1.1  christos static int
    407      1.1  christos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
    408      1.1  christos 		void *arg)
    409      1.1  christos {
    410      1.1  christos   char *str;
    411      1.1  christos   char *typestr;
    412      1.1  christos   ctf_dump_state_t *state = arg;
    413      1.1  christos 
    414      1.1  christos   if (asprintf (&str, "%s -> ", name) < 0)
    415      1.1  christos     return (ctf_set_errno (state->cds_fp, errno));
    416      1.1  christos 
    417      1.1  christos   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
    418  1.1.1.2  christos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    419      1.1  christos     {
    420      1.1  christos       free (str);
    421      1.1  christos       return 0;				/* Swallow the error.  */
    422      1.1  christos     }
    423      1.1  christos 
    424      1.1  christos   str = str_append (str, typestr);
    425      1.1  christos   free (typestr);
    426      1.1  christos 
    427      1.1  christos   ctf_dump_append (state, str);
    428      1.1  christos   return 0;
    429      1.1  christos }
    430      1.1  christos 
    431  1.1.1.2  christos /* Dump all the object or function entries into the cds_items.  */
    432      1.1  christos 
    433      1.1  christos static int
    434  1.1.1.2  christos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
    435      1.1  christos {
    436  1.1.1.2  christos   const char *name;
    437  1.1.1.2  christos   ctf_id_t id;
    438  1.1.1.2  christos   ctf_next_t *i = NULL;
    439  1.1.1.2  christos   char *str = NULL;
    440      1.1  christos 
    441  1.1.1.2  christos   if ((functions && fp->ctf_funcidx_names)
    442  1.1.1.2  christos       || (!functions && fp->ctf_objtidx_names))
    443  1.1.1.2  christos     str = str_append (str, _("Section is indexed.\n"));
    444  1.1.1.2  christos   else if (fp->ctf_symtab.cts_data == NULL)
    445  1.1.1.2  christos     str = str_append (str, _("No symbol table.\n"));
    446  1.1.1.2  christos 
    447  1.1.1.2  christos   while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
    448      1.1  christos     {
    449  1.1.1.2  christos       char *typestr = NULL;
    450      1.1  christos 
    451  1.1.1.2  christos       /* Emit the name, if we know it.  No trailing space: ctf_dump_format_type
    452  1.1.1.2  christos 	 has a leading one.   */
    453  1.1.1.2  christos       if (name)
    454      1.1  christos 	{
    455  1.1.1.2  christos 	  if (asprintf (&str, "%s -> ", name) < 0)
    456  1.1.1.2  christos 	    goto oom;
    457      1.1  christos 	}
    458      1.1  christos       else
    459  1.1.1.2  christos 	str = xstrdup ("");
    460      1.1  christos 
    461  1.1.1.2  christos       if ((typestr = ctf_dump_format_type (state->cds_fp, id,
    462  1.1.1.2  christos 					   CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    463      1.1  christos 	{
    464  1.1.1.2  christos 	  ctf_dump_append (state, str);
    465  1.1.1.2  christos 	  continue;				/* Swallow the error.  */
    466      1.1  christos 	}
    467      1.1  christos 
    468      1.1  christos       str = str_append (str, typestr);
    469      1.1  christos       free (typestr);
    470      1.1  christos       ctf_dump_append (state, str);
    471      1.1  christos       continue;
    472      1.1  christos 
    473      1.1  christos     oom:
    474  1.1.1.2  christos       ctf_set_errno (fp, ENOMEM);
    475  1.1.1.2  christos       ctf_next_destroy (i);
    476  1.1.1.2  christos       return -1;
    477      1.1  christos     }
    478      1.1  christos   return 0;
    479      1.1  christos }
    480      1.1  christos 
    481      1.1  christos /* Dump a single variable into the cds_items.  */
    482      1.1  christos static int
    483      1.1  christos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
    484      1.1  christos {
    485      1.1  christos   char *str;
    486      1.1  christos   char *typestr;
    487      1.1  christos   ctf_dump_state_t *state = arg;
    488      1.1  christos 
    489      1.1  christos   if (asprintf (&str, "%s -> ", name) < 0)
    490      1.1  christos     return (ctf_set_errno (state->cds_fp, errno));
    491      1.1  christos 
    492      1.1  christos   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
    493  1.1.1.2  christos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    494      1.1  christos     {
    495      1.1  christos       free (str);
    496      1.1  christos       return 0;			/* Swallow the error.  */
    497      1.1  christos     }
    498      1.1  christos 
    499      1.1  christos   str = str_append (str, typestr);
    500      1.1  christos   free (typestr);
    501      1.1  christos 
    502      1.1  christos   ctf_dump_append (state, str);
    503      1.1  christos   return 0;
    504      1.1  christos }
    505      1.1  christos 
    506  1.1.1.2  christos /* Dump a single struct/union member into the string in the membstate.  */
    507      1.1  christos static int
    508      1.1  christos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
    509  1.1.1.2  christos 		 int depth, void *arg)
    510      1.1  christos {
    511      1.1  christos   ctf_dump_membstate_t *state = arg;
    512      1.1  christos   char *typestr = NULL;
    513      1.1  christos   char *bit = NULL;
    514      1.1  christos 
    515  1.1.1.2  christos   /* The struct/union itself has already been printed.  */
    516  1.1.1.2  christos   if (depth == 0)
    517  1.1.1.2  christos     return 0;
    518      1.1  christos 
    519  1.1.1.2  christos   if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
    520  1.1.1.2  christos     goto oom;
    521  1.1.1.2  christos   *state->cdm_str = str_append (*state->cdm_str, bit);
    522  1.1.1.2  christos   free (bit);
    523      1.1  christos 
    524  1.1.1.2  christos   if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
    525  1.1.1.2  christos 				       CTF_ADD_ROOT | CTF_FT_BITFIELD
    526  1.1.1.2  christos 				       | CTF_FT_ID)) == NULL)
    527  1.1.1.2  christos     return -1;				/* errno is set for us.  */
    528      1.1  christos 
    529  1.1.1.2  christos   if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
    530  1.1.1.2  christos     goto oom;
    531      1.1  christos 
    532      1.1  christos   *state->cdm_str = str_append (*state->cdm_str, bit);
    533      1.1  christos   free (typestr);
    534      1.1  christos   free (bit);
    535      1.1  christos   typestr = NULL;
    536      1.1  christos   bit = NULL;
    537      1.1  christos 
    538      1.1  christos   return 0;
    539      1.1  christos 
    540      1.1  christos  oom:
    541      1.1  christos   free (typestr);
    542      1.1  christos   free (bit);
    543      1.1  christos   return (ctf_set_errno (state->cdm_fp, errno));
    544      1.1  christos }
    545      1.1  christos 
    546  1.1.1.2  christos /* Report the number of digits in the hexadecimal representation of a type
    547  1.1.1.2  christos    ID.  */
    548  1.1.1.2  christos 
    549  1.1.1.2  christos static int
    550  1.1.1.2  christos type_hex_digits (ctf_id_t id)
    551  1.1.1.2  christos {
    552  1.1.1.2  christos   int i = 0;
    553  1.1.1.2  christos 
    554  1.1.1.2  christos   if (id == 0)
    555  1.1.1.2  christos     return 1;
    556  1.1.1.2  christos 
    557  1.1.1.2  christos   for (; id > 0; id >>= 4, i++);
    558  1.1.1.2  christos   return i;
    559  1.1.1.2  christos }
    560  1.1.1.2  christos 
    561      1.1  christos /* Dump a single type into the cds_items.  */
    562      1.1  christos static int
    563      1.1  christos ctf_dump_type (ctf_id_t id, int flag, void *arg)
    564      1.1  christos {
    565      1.1  christos   char *str;
    566  1.1.1.2  christos   char *indent;
    567      1.1  christos   ctf_dump_state_t *state = arg;
    568  1.1.1.2  christos   ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
    569      1.1  christos 
    570  1.1.1.2  christos   /* Indent neatly.  */
    571  1.1.1.2  christos   if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
    572  1.1.1.2  christos     return (ctf_set_errno (state->cds_fp, ENOMEM));
    573      1.1  christos 
    574  1.1.1.2  christos   /* Dump the type itself.  */
    575  1.1.1.2  christos   if ((str = ctf_dump_format_type (state->cds_fp, id,
    576  1.1.1.2  christos 				   flag | CTF_FT_REFS)) == NULL)
    577  1.1.1.2  christos     goto err;
    578      1.1  christos   str = str_append (str, "\n");
    579  1.1.1.2  christos 
    580  1.1.1.2  christos   membstate.cdm_toplevel_indent = indent;
    581  1.1.1.2  christos 
    582  1.1.1.2  christos   /* Member dumping for structs, unions...  */
    583  1.1.1.2  christos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
    584  1.1.1.2  christos       || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
    585      1.1  christos     {
    586  1.1.1.2  christos       if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
    587      1.1  christos 	{
    588  1.1.1.2  christos 	  if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
    589  1.1.1.2  christos 	    {
    590  1.1.1.2  christos 	      ctf_dump_append (state, str);
    591  1.1.1.2  christos 	      return 0;
    592  1.1.1.2  christos 	    }
    593  1.1.1.2  christos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    594  1.1.1.2  christos 			_("cannot visit members dumping type 0x%lx"), id);
    595  1.1.1.2  christos 	  goto err;
    596      1.1  christos 	}
    597      1.1  christos     }
    598      1.1  christos 
    599  1.1.1.2  christos   /* ... and enums, for which we dump the first and last few members and skip
    600  1.1.1.2  christos      the ones in the middle.  */
    601  1.1.1.2  christos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
    602  1.1.1.2  christos     {
    603  1.1.1.2  christos       int enum_count = ctf_member_count (state->cds_fp, id);
    604  1.1.1.2  christos       ctf_next_t *it = NULL;
    605  1.1.1.2  christos       int i = 0;
    606  1.1.1.2  christos       const char *enumerand;
    607  1.1.1.2  christos       char *bit;
    608  1.1.1.2  christos       int value;
    609  1.1.1.2  christos 
    610  1.1.1.2  christos       while ((enumerand = ctf_enum_next (state->cds_fp, id,
    611  1.1.1.2  christos 					 &it, &value)) != NULL)
    612  1.1.1.2  christos 	{
    613  1.1.1.2  christos 	  i++;
    614  1.1.1.2  christos 	  if ((i > 5) && (i < enum_count - 4))
    615  1.1.1.2  christos 	    continue;
    616  1.1.1.2  christos 
    617  1.1.1.2  christos 	  str = str_append (str, indent);
    618  1.1.1.2  christos 
    619  1.1.1.2  christos 	  if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
    620  1.1.1.2  christos 	    {
    621  1.1.1.2  christos 	      ctf_next_destroy (it);
    622  1.1.1.2  christos 	      goto oom;
    623  1.1.1.2  christos 	    }
    624  1.1.1.2  christos 	  str = str_append (str, bit);
    625  1.1.1.2  christos 	  free (bit);
    626  1.1.1.2  christos 
    627  1.1.1.2  christos 	  if ((i == 5) && (enum_count > 10))
    628  1.1.1.2  christos 	    {
    629  1.1.1.2  christos 	      str = str_append (str, indent);
    630  1.1.1.2  christos 	      str = str_append (str, "...\n");
    631  1.1.1.2  christos 	    }
    632  1.1.1.2  christos 	}
    633  1.1.1.2  christos       if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
    634  1.1.1.2  christos 	{
    635  1.1.1.2  christos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    636  1.1.1.2  christos 			_("cannot visit enumerands dumping type 0x%lx"), id);
    637  1.1.1.2  christos 	  goto err;
    638  1.1.1.2  christos 	}
    639  1.1.1.2  christos     }
    640      1.1  christos 
    641      1.1  christos   ctf_dump_append (state, str);
    642  1.1.1.2  christos   free (indent);
    643  1.1.1.2  christos 
    644      1.1  christos   return 0;
    645      1.1  christos 
    646      1.1  christos  err:
    647  1.1.1.2  christos   free (indent);
    648  1.1.1.2  christos   free (str);
    649  1.1.1.2  christos 
    650  1.1.1.2  christos   /* Swallow the error: don't cause an error in one type to abort all
    651  1.1.1.2  christos      type dumping.  */
    652  1.1.1.2  christos   return 0;
    653  1.1.1.2  christos 
    654  1.1.1.2  christos  oom:
    655  1.1.1.2  christos   free (indent);
    656      1.1  christos   free (str);
    657  1.1.1.2  christos   return ctf_set_errno (state->cds_fp, ENOMEM);
    658      1.1  christos }
    659      1.1  christos 
    660      1.1  christos /* Dump the string table into the cds_items.  */
    661      1.1  christos 
    662      1.1  christos static int
    663  1.1.1.2  christos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
    664      1.1  christos {
    665      1.1  christos   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
    666      1.1  christos 
    667      1.1  christos   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
    668      1.1  christos 	 fp->ctf_str[CTF_STRTAB_0].cts_len;)
    669      1.1  christos     {
    670      1.1  christos       char *str;
    671  1.1.1.2  christos       if (asprintf (&str, "0x%lx: %s",
    672      1.1  christos 		    (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
    673      1.1  christos 		    s) < 0)
    674      1.1  christos 	return (ctf_set_errno (fp, errno));
    675      1.1  christos       ctf_dump_append (state, str);
    676      1.1  christos       s += strlen (s) + 1;
    677      1.1  christos     }
    678      1.1  christos 
    679      1.1  christos   return 0;
    680      1.1  christos }
    681      1.1  christos 
    682      1.1  christos /* Dump a particular section of a CTF file, in textual form.  Call with a
    683      1.1  christos    pointer to a NULL STATE: each call emits a dynamically allocated string
    684      1.1  christos    containing a description of one entity in the specified section, in order.
    685      1.1  christos    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
    686      1.1  christos    has been entirely dumped, the call returns NULL and frees and annuls the
    687      1.1  christos    STATE, ready for another section to be dumped.  The returned textual content
    688      1.1  christos    may span multiple lines: between each call the FUNC is called with one
    689      1.1  christos    textual line at a time, and should return a suitably decorated line (it can
    690      1.1  christos    allocate a new one and return it if it likes).  */
    691      1.1  christos 
    692      1.1  christos char *
    693  1.1.1.2  christos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
    694      1.1  christos 	  ctf_dump_decorate_f *func, void *arg)
    695      1.1  christos {
    696      1.1  christos   char *str;
    697      1.1  christos   char *line;
    698      1.1  christos   ctf_dump_state_t *state = NULL;
    699      1.1  christos 
    700      1.1  christos   if (*statep == NULL)
    701      1.1  christos     {
    702      1.1  christos       /* Data collection.  Transforming a call-at-a-time iterator into a
    703      1.1  christos 	 return-at-a-time iterator in a language without call/cc is annoying. It
    704      1.1  christos 	 is easiest to simply collect everything at once and then return it bit
    705      1.1  christos 	 by bit.  The first call will take (much) longer than otherwise, but the
    706      1.1  christos 	 amortized time needed is the same.  */
    707      1.1  christos 
    708      1.1  christos       if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
    709      1.1  christos 	{
    710      1.1  christos 	  ctf_set_errno (fp, ENOMEM);
    711      1.1  christos 	  goto end;
    712      1.1  christos 	}
    713      1.1  christos       state = *statep;
    714      1.1  christos 
    715      1.1  christos       memset (state, 0, sizeof (struct ctf_dump_state));
    716      1.1  christos       state->cds_fp = fp;
    717      1.1  christos       state->cds_sect = sect;
    718      1.1  christos 
    719      1.1  christos       switch (sect)
    720      1.1  christos 	{
    721      1.1  christos 	case CTF_SECT_HEADER:
    722      1.1  christos 	  ctf_dump_header (fp, state);
    723      1.1  christos 	  break;
    724      1.1  christos 	case CTF_SECT_LABEL:
    725      1.1  christos 	  if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
    726      1.1  christos 	    {
    727      1.1  christos 	      if (ctf_errno (fp) != ECTF_NOLABELDATA)
    728      1.1  christos 		goto end;		/* errno is set for us.  */
    729      1.1  christos 	      ctf_set_errno (fp, 0);
    730      1.1  christos 	    }
    731      1.1  christos 	  break;
    732      1.1  christos 	case CTF_SECT_OBJT:
    733  1.1.1.2  christos 	  if (ctf_dump_objts (fp, state, 0) < 0)
    734      1.1  christos 	    goto end;			/* errno is set for us.  */
    735      1.1  christos 	  break;
    736      1.1  christos 	case CTF_SECT_FUNC:
    737  1.1.1.2  christos 	  if (ctf_dump_objts (fp, state, 1) < 0)
    738      1.1  christos 	    goto end;			/* errno is set for us.  */
    739      1.1  christos 	  break;
    740      1.1  christos 	case CTF_SECT_VAR:
    741      1.1  christos 	  if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
    742      1.1  christos 	    goto end;			/* errno is set for us.  */
    743      1.1  christos 	  break;
    744      1.1  christos 	case CTF_SECT_TYPE:
    745      1.1  christos 	  if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
    746      1.1  christos 	    goto end;			/* errno is set for us.  */
    747      1.1  christos 	  break;
    748      1.1  christos 	case CTF_SECT_STR:
    749      1.1  christos 	  ctf_dump_str (fp, state);
    750      1.1  christos 	  break;
    751      1.1  christos 	default:
    752      1.1  christos 	  ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
    753      1.1  christos 	  goto end;
    754      1.1  christos 	}
    755      1.1  christos     }
    756      1.1  christos   else
    757      1.1  christos     {
    758      1.1  christos       state = *statep;
    759      1.1  christos 
    760      1.1  christos       if (state->cds_sect != sect)
    761      1.1  christos 	{
    762      1.1  christos 	  ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
    763      1.1  christos 	  goto end;
    764      1.1  christos 	}
    765      1.1  christos     }
    766      1.1  christos 
    767      1.1  christos   if (state->cds_current == NULL)
    768      1.1  christos     state->cds_current = ctf_list_next (&state->cds_items);
    769      1.1  christos   else
    770      1.1  christos     state->cds_current = ctf_list_next (state->cds_current);
    771      1.1  christos 
    772      1.1  christos   if (state->cds_current == NULL)
    773      1.1  christos     goto end;
    774      1.1  christos 
    775      1.1  christos   /* Hookery.  There is some extra complexity to preserve linefeeds within each
    776      1.1  christos      item while removing linefeeds at the end.  */
    777      1.1  christos   if (func)
    778      1.1  christos     {
    779      1.1  christos       size_t len;
    780      1.1  christos 
    781      1.1  christos       str = NULL;
    782      1.1  christos       for (line = state->cds_current->cdi_item; line && *line; )
    783      1.1  christos 	{
    784      1.1  christos 	  char *nline = line;
    785      1.1  christos 	  char *ret;
    786      1.1  christos 
    787      1.1  christos 	  nline = strchr (line, '\n');
    788      1.1  christos 	  if (nline)
    789      1.1  christos 	    nline[0] = '\0';
    790      1.1  christos 
    791      1.1  christos 	  ret = func (sect, line, arg);
    792      1.1  christos 	  str = str_append (str, ret);
    793      1.1  christos 	  str = str_append (str, "\n");
    794      1.1  christos 	  if (ret != line)
    795      1.1  christos 	    free (ret);
    796      1.1  christos 
    797      1.1  christos 	  if (nline)
    798      1.1  christos 	    {
    799      1.1  christos 	      nline[0] = '\n';
    800      1.1  christos 	      nline++;
    801      1.1  christos 	    }
    802      1.1  christos 
    803      1.1  christos 	  line = nline;
    804      1.1  christos 	}
    805      1.1  christos 
    806      1.1  christos       len = strlen (str);
    807      1.1  christos 
    808      1.1  christos       if (str[len-1] == '\n')
    809      1.1  christos 	str[len-1] = '\0';
    810      1.1  christos     }
    811      1.1  christos   else
    812      1.1  christos     {
    813      1.1  christos       str = strdup (state->cds_current->cdi_item);
    814      1.1  christos       if (!str)
    815      1.1  christos 	{
    816      1.1  christos 	  ctf_set_errno (fp, ENOMEM);
    817      1.1  christos 	  return str;
    818      1.1  christos 	}
    819      1.1  christos     }
    820      1.1  christos 
    821      1.1  christos   ctf_set_errno (fp, 0);
    822      1.1  christos   return str;
    823      1.1  christos 
    824      1.1  christos  end:
    825      1.1  christos   ctf_dump_free (state);
    826      1.1  christos   free (state);
    827      1.1  christos   ctf_set_errno (fp, 0);
    828      1.1  christos   *statep = NULL;
    829      1.1  christos   return NULL;
    830      1.1  christos }
    831