Home | History | Annotate | Line # | Download | only in libctf
      1      1.1  christos /* Textual dumping of CTF data.
      2  1.1.1.3  christos    Copyright (C) 2019-2024 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.1.4  christos       if (!(flag & CTF_ADD_ROOT))
    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.1.3  christos   ctf_err_warn (fp, 1, ctf_errno (fp), _("cannot format name dumping type 0x%lx"),
    243  1.1.1.3  christos 		id);
    244      1.1  christos   free (buf);
    245      1.1  christos   free (str);
    246      1.1  christos   free (bit);
    247      1.1  christos   return NULL;
    248      1.1  christos }
    249      1.1  christos 
    250      1.1  christos /* Dump one string field from the file header into the cds_items.  */
    251      1.1  christos static int
    252  1.1.1.2  christos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    253      1.1  christos 			  const char *name, uint32_t value)
    254      1.1  christos {
    255      1.1  christos   char *str;
    256      1.1  christos   if (value)
    257      1.1  christos     {
    258      1.1  christos       if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
    259      1.1  christos 	goto err;
    260      1.1  christos       ctf_dump_append (state, str);
    261      1.1  christos     }
    262      1.1  christos   return 0;
    263      1.1  christos 
    264      1.1  christos  err:
    265      1.1  christos   return (ctf_set_errno (fp, errno));
    266      1.1  christos }
    267      1.1  christos 
    268      1.1  christos /* Dump one section-offset field from the file header into the cds_items.  */
    269      1.1  christos static int
    270  1.1.1.2  christos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
    271      1.1  christos 			   const char *sect, uint32_t off, uint32_t nextoff)
    272      1.1  christos {
    273      1.1  christos   char *str;
    274      1.1  christos   if (nextoff - off)
    275      1.1  christos     {
    276      1.1  christos       if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
    277      1.1  christos 		    (unsigned long) off, (unsigned long) (nextoff - 1),
    278      1.1  christos 		    (unsigned long) (nextoff - off)) < 0)
    279      1.1  christos 	goto err;
    280      1.1  christos       ctf_dump_append (state, str);
    281      1.1  christos     }
    282      1.1  christos   return 0;
    283      1.1  christos 
    284      1.1  christos  err:
    285      1.1  christos   return (ctf_set_errno (fp, errno));
    286      1.1  christos }
    287      1.1  christos 
    288      1.1  christos /* Dump the file header into the cds_items.  */
    289      1.1  christos static int
    290  1.1.1.2  christos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
    291      1.1  christos {
    292      1.1  christos   char *str;
    293  1.1.1.2  christos   char *flagstr = NULL;
    294      1.1  christos   const ctf_header_t *hp = fp->ctf_header;
    295      1.1  christos   const char *vertab[] =
    296      1.1  christos     {
    297      1.1  christos      NULL, "CTF_VERSION_1",
    298      1.1  christos      "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
    299      1.1  christos      "boundaries)",
    300      1.1  christos      "CTF_VERSION_2",
    301      1.1  christos      "CTF_VERSION_3", NULL
    302      1.1  christos     };
    303      1.1  christos   const char *verstr = NULL;
    304      1.1  christos 
    305  1.1.1.2  christos   if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0)
    306      1.1  christos       goto err;
    307      1.1  christos   ctf_dump_append (state, str);
    308      1.1  christos 
    309      1.1  christos   if (hp->cth_version <= CTF_VERSION)
    310      1.1  christos     verstr = vertab[hp->cth_version];
    311      1.1  christos 
    312      1.1  christos   if (verstr == NULL)
    313      1.1  christos     verstr = "(not a valid version)";
    314      1.1  christos 
    315      1.1  christos   if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
    316      1.1  christos 		verstr) < 0)
    317      1.1  christos     goto err;
    318      1.1  christos   ctf_dump_append (state, str);
    319      1.1  christos 
    320      1.1  christos   /* Everything else is only printed if present.  */
    321      1.1  christos 
    322  1.1.1.2  christos   /* The flags are unusual in that they represent the ctf_dict_t *in memory*:
    323      1.1  christos      flags representing compression, etc, are turned off as the file is
    324      1.1  christos      decompressed.  So we store a copy of the flags before they are changed, for
    325      1.1  christos      the dumper.  */
    326      1.1  christos 
    327      1.1  christos   if (fp->ctf_openflags > 0)
    328      1.1  christos     {
    329  1.1.1.2  christos       if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
    330  1.1.1.2  christos 		    fp->ctf_openflags & CTF_F_COMPRESS
    331  1.1.1.2  christos 		    ? "CTF_F_COMPRESS": "",
    332  1.1.1.2  christos 		    (fp->ctf_openflags & CTF_F_COMPRESS)
    333  1.1.1.2  christos 		    && (fp->ctf_openflags & ~CTF_F_COMPRESS)
    334  1.1.1.2  christos 		    ? ", " : "",
    335  1.1.1.2  christos 		    fp->ctf_openflags & CTF_F_NEWFUNCINFO
    336  1.1.1.2  christos 		    ? "CTF_F_NEWFUNCINFO" : "",
    337  1.1.1.3  christos 		    (fp->ctf_openflags & (CTF_F_NEWFUNCINFO))
    338  1.1.1.2  christos 		    && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO))
    339  1.1.1.2  christos 		    ? ", " : "",
    340  1.1.1.2  christos 		    fp->ctf_openflags & CTF_F_IDXSORTED
    341  1.1.1.2  christos 		    ? "CTF_F_IDXSORTED" : "",
    342  1.1.1.3  christos 		    fp->ctf_openflags & (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.1.4  christos       free (flagstr);
    353      1.1  christos       ctf_dump_append (state, str);
    354      1.1  christos     }
    355      1.1  christos 
    356      1.1  christos   if (ctf_dump_header_strfield (fp, state, "Parent label",
    357      1.1  christos 				hp->cth_parlabel) < 0)
    358      1.1  christos     goto err;
    359      1.1  christos 
    360      1.1  christos   if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
    361      1.1  christos     goto err;
    362      1.1  christos 
    363      1.1  christos   if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
    364      1.1  christos 				hp->cth_cuname) < 0)
    365      1.1  christos     goto err;
    366      1.1  christos 
    367      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
    368      1.1  christos 				 hp->cth_objtoff) < 0)
    369      1.1  christos     goto err;
    370      1.1  christos 
    371      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Data object section",
    372      1.1  christos 				 hp->cth_objtoff, hp->cth_funcoff) < 0)
    373      1.1  christos     goto err;
    374      1.1  christos 
    375      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Function info section",
    376  1.1.1.2  christos 				 hp->cth_funcoff, hp->cth_objtidxoff) < 0)
    377  1.1.1.2  christos     goto err;
    378  1.1.1.2  christos 
    379  1.1.1.2  christos   if (ctf_dump_header_sectfield (fp, state, "Object index section",
    380  1.1.1.2  christos 				 hp->cth_objtidxoff, hp->cth_funcidxoff) < 0)
    381  1.1.1.2  christos     goto err;
    382  1.1.1.2  christos 
    383  1.1.1.2  christos   if (ctf_dump_header_sectfield (fp, state, "Function index section",
    384  1.1.1.2  christos 				 hp->cth_funcidxoff, hp->cth_varoff) < 0)
    385      1.1  christos     goto err;
    386      1.1  christos 
    387      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Variable section",
    388      1.1  christos 				 hp->cth_varoff, hp->cth_typeoff) < 0)
    389      1.1  christos     goto err;
    390      1.1  christos 
    391      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "Type section",
    392      1.1  christos 				 hp->cth_typeoff, hp->cth_stroff) < 0)
    393      1.1  christos     goto err;
    394      1.1  christos 
    395      1.1  christos   if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
    396      1.1  christos 				 hp->cth_stroff + hp->cth_strlen + 1) < 0)
    397      1.1  christos     goto err;
    398      1.1  christos 
    399      1.1  christos   return 0;
    400      1.1  christos  err:
    401  1.1.1.2  christos   free (flagstr);
    402      1.1  christos   return (ctf_set_errno (fp, errno));
    403      1.1  christos }
    404      1.1  christos 
    405      1.1  christos /* Dump a single label into the cds_items.  */
    406      1.1  christos 
    407      1.1  christos static int
    408      1.1  christos ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
    409      1.1  christos 		void *arg)
    410      1.1  christos {
    411      1.1  christos   char *str;
    412      1.1  christos   char *typestr;
    413      1.1  christos   ctf_dump_state_t *state = arg;
    414      1.1  christos 
    415      1.1  christos   if (asprintf (&str, "%s -> ", name) < 0)
    416      1.1  christos     return (ctf_set_errno (state->cds_fp, errno));
    417      1.1  christos 
    418      1.1  christos   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
    419  1.1.1.2  christos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    420      1.1  christos     {
    421      1.1  christos       free (str);
    422      1.1  christos       return 0;				/* Swallow the error.  */
    423      1.1  christos     }
    424      1.1  christos 
    425      1.1  christos   str = str_append (str, typestr);
    426      1.1  christos   free (typestr);
    427      1.1  christos 
    428      1.1  christos   ctf_dump_append (state, str);
    429      1.1  christos   return 0;
    430      1.1  christos }
    431      1.1  christos 
    432  1.1.1.2  christos /* Dump all the object or function entries into the cds_items.  */
    433      1.1  christos 
    434      1.1  christos static int
    435  1.1.1.2  christos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
    436      1.1  christos {
    437  1.1.1.2  christos   const char *name;
    438  1.1.1.2  christos   ctf_id_t id;
    439  1.1.1.2  christos   ctf_next_t *i = NULL;
    440  1.1.1.2  christos   char *str = NULL;
    441      1.1  christos 
    442  1.1.1.2  christos   if ((functions && fp->ctf_funcidx_names)
    443  1.1.1.2  christos       || (!functions && fp->ctf_objtidx_names))
    444  1.1.1.2  christos     str = str_append (str, _("Section is indexed.\n"));
    445  1.1.1.3  christos   else if (fp->ctf_ext_symtab.cts_data == NULL)
    446  1.1.1.2  christos     str = str_append (str, _("No symbol table.\n"));
    447  1.1.1.2  christos 
    448  1.1.1.2  christos   while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR)
    449      1.1  christos     {
    450  1.1.1.2  christos       char *typestr = NULL;
    451      1.1  christos 
    452  1.1.1.2  christos       /* Emit the name, if we know it.  No trailing space: ctf_dump_format_type
    453  1.1.1.2  christos 	 has a leading one.   */
    454  1.1.1.2  christos       if (name)
    455      1.1  christos 	{
    456  1.1.1.2  christos 	  if (asprintf (&str, "%s -> ", name) < 0)
    457  1.1.1.2  christos 	    goto oom;
    458      1.1  christos 	}
    459      1.1  christos       else
    460  1.1.1.2  christos 	str = xstrdup ("");
    461      1.1  christos 
    462  1.1.1.2  christos       if ((typestr = ctf_dump_format_type (state->cds_fp, id,
    463  1.1.1.2  christos 					   CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    464      1.1  christos 	{
    465  1.1.1.2  christos 	  ctf_dump_append (state, str);
    466  1.1.1.2  christos 	  continue;				/* Swallow the error.  */
    467      1.1  christos 	}
    468      1.1  christos 
    469      1.1  christos       str = str_append (str, typestr);
    470      1.1  christos       free (typestr);
    471      1.1  christos       ctf_dump_append (state, str);
    472      1.1  christos       continue;
    473      1.1  christos 
    474      1.1  christos     oom:
    475  1.1.1.2  christos       ctf_set_errno (fp, ENOMEM);
    476  1.1.1.2  christos       ctf_next_destroy (i);
    477  1.1.1.2  christos       return -1;
    478      1.1  christos     }
    479      1.1  christos   return 0;
    480      1.1  christos }
    481      1.1  christos 
    482      1.1  christos /* Dump a single variable into the cds_items.  */
    483      1.1  christos static int
    484      1.1  christos ctf_dump_var (const char *name, ctf_id_t type, void *arg)
    485      1.1  christos {
    486      1.1  christos   char *str;
    487      1.1  christos   char *typestr;
    488      1.1  christos   ctf_dump_state_t *state = arg;
    489      1.1  christos 
    490      1.1  christos   if (asprintf (&str, "%s -> ", name) < 0)
    491      1.1  christos     return (ctf_set_errno (state->cds_fp, errno));
    492      1.1  christos 
    493      1.1  christos   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
    494  1.1.1.2  christos 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
    495      1.1  christos     {
    496      1.1  christos       free (str);
    497      1.1  christos       return 0;			/* Swallow the error.  */
    498      1.1  christos     }
    499      1.1  christos 
    500      1.1  christos   str = str_append (str, typestr);
    501      1.1  christos   free (typestr);
    502      1.1  christos 
    503      1.1  christos   ctf_dump_append (state, str);
    504      1.1  christos   return 0;
    505      1.1  christos }
    506      1.1  christos 
    507  1.1.1.2  christos /* Dump a single struct/union member into the string in the membstate.  */
    508      1.1  christos static int
    509      1.1  christos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
    510  1.1.1.2  christos 		 int depth, void *arg)
    511      1.1  christos {
    512      1.1  christos   ctf_dump_membstate_t *state = arg;
    513      1.1  christos   char *typestr = NULL;
    514      1.1  christos   char *bit = NULL;
    515      1.1  christos 
    516  1.1.1.2  christos   /* The struct/union itself has already been printed.  */
    517  1.1.1.2  christos   if (depth == 0)
    518  1.1.1.2  christos     return 0;
    519      1.1  christos 
    520  1.1.1.2  christos   if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0)
    521  1.1.1.2  christos     goto oom;
    522  1.1.1.2  christos   *state->cdm_str = str_append (*state->cdm_str, bit);
    523  1.1.1.2  christos   free (bit);
    524      1.1  christos 
    525  1.1.1.2  christos   if ((typestr = ctf_dump_format_type (state->cdm_fp, id,
    526  1.1.1.2  christos 				       CTF_ADD_ROOT | CTF_FT_BITFIELD
    527  1.1.1.2  christos 				       | CTF_FT_ID)) == NULL)
    528  1.1.1.2  christos     return -1;				/* errno is set for us.  */
    529      1.1  christos 
    530  1.1.1.2  christos   if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0)
    531  1.1.1.2  christos     goto oom;
    532      1.1  christos 
    533      1.1  christos   *state->cdm_str = str_append (*state->cdm_str, bit);
    534      1.1  christos   free (typestr);
    535      1.1  christos   free (bit);
    536      1.1  christos   typestr = NULL;
    537      1.1  christos   bit = NULL;
    538      1.1  christos 
    539      1.1  christos   return 0;
    540      1.1  christos 
    541      1.1  christos  oom:
    542      1.1  christos   free (typestr);
    543      1.1  christos   free (bit);
    544      1.1  christos   return (ctf_set_errno (state->cdm_fp, errno));
    545      1.1  christos }
    546      1.1  christos 
    547  1.1.1.2  christos /* Report the number of digits in the hexadecimal representation of a type
    548  1.1.1.2  christos    ID.  */
    549  1.1.1.2  christos 
    550  1.1.1.2  christos static int
    551  1.1.1.2  christos type_hex_digits (ctf_id_t id)
    552  1.1.1.2  christos {
    553  1.1.1.2  christos   int i = 0;
    554  1.1.1.2  christos 
    555  1.1.1.2  christos   if (id == 0)
    556  1.1.1.2  christos     return 1;
    557  1.1.1.2  christos 
    558  1.1.1.2  christos   for (; id > 0; id >>= 4, i++);
    559  1.1.1.2  christos   return i;
    560  1.1.1.2  christos }
    561  1.1.1.2  christos 
    562      1.1  christos /* Dump a single type into the cds_items.  */
    563      1.1  christos static int
    564      1.1  christos ctf_dump_type (ctf_id_t id, int flag, void *arg)
    565      1.1  christos {
    566      1.1  christos   char *str;
    567  1.1.1.2  christos   char *indent;
    568      1.1  christos   ctf_dump_state_t *state = arg;
    569  1.1.1.2  christos   ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL };
    570      1.1  christos 
    571  1.1.1.2  christos   /* Indent neatly.  */
    572  1.1.1.2  christos   if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
    573  1.1.1.2  christos     return (ctf_set_errno (state->cds_fp, ENOMEM));
    574      1.1  christos 
    575  1.1.1.2  christos   /* Dump the type itself.  */
    576  1.1.1.2  christos   if ((str = ctf_dump_format_type (state->cds_fp, id,
    577  1.1.1.2  christos 				   flag | CTF_FT_REFS)) == NULL)
    578  1.1.1.2  christos     goto err;
    579      1.1  christos   str = str_append (str, "\n");
    580  1.1.1.2  christos 
    581  1.1.1.2  christos   membstate.cdm_toplevel_indent = indent;
    582  1.1.1.2  christos 
    583  1.1.1.2  christos   /* Member dumping for structs, unions...  */
    584  1.1.1.2  christos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT
    585  1.1.1.2  christos       || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION)
    586      1.1  christos     {
    587  1.1.1.2  christos       if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
    588      1.1  christos 	{
    589  1.1.1.2  christos 	  if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
    590  1.1.1.2  christos 	    {
    591  1.1.1.2  christos 	      ctf_dump_append (state, str);
    592  1.1.1.2  christos 	      return 0;
    593  1.1.1.2  christos 	    }
    594  1.1.1.2  christos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    595  1.1.1.2  christos 			_("cannot visit members dumping type 0x%lx"), id);
    596  1.1.1.2  christos 	  goto err;
    597      1.1  christos 	}
    598      1.1  christos     }
    599      1.1  christos 
    600  1.1.1.2  christos   /* ... and enums, for which we dump the first and last few members and skip
    601  1.1.1.2  christos      the ones in the middle.  */
    602  1.1.1.2  christos   if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM)
    603  1.1.1.2  christos     {
    604  1.1.1.2  christos       int enum_count = ctf_member_count (state->cds_fp, id);
    605  1.1.1.2  christos       ctf_next_t *it = NULL;
    606  1.1.1.2  christos       int i = 0;
    607  1.1.1.2  christos       const char *enumerand;
    608  1.1.1.2  christos       char *bit;
    609  1.1.1.2  christos       int value;
    610  1.1.1.2  christos 
    611  1.1.1.2  christos       while ((enumerand = ctf_enum_next (state->cds_fp, id,
    612  1.1.1.2  christos 					 &it, &value)) != NULL)
    613  1.1.1.2  christos 	{
    614  1.1.1.2  christos 	  i++;
    615  1.1.1.2  christos 	  if ((i > 5) && (i < enum_count - 4))
    616  1.1.1.2  christos 	    continue;
    617  1.1.1.2  christos 
    618  1.1.1.2  christos 	  str = str_append (str, indent);
    619  1.1.1.2  christos 
    620  1.1.1.2  christos 	  if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0)
    621  1.1.1.2  christos 	    {
    622  1.1.1.2  christos 	      ctf_next_destroy (it);
    623  1.1.1.2  christos 	      goto oom;
    624  1.1.1.2  christos 	    }
    625  1.1.1.2  christos 	  str = str_append (str, bit);
    626  1.1.1.2  christos 	  free (bit);
    627  1.1.1.2  christos 
    628  1.1.1.2  christos 	  if ((i == 5) && (enum_count > 10))
    629  1.1.1.2  christos 	    {
    630  1.1.1.2  christos 	      str = str_append (str, indent);
    631  1.1.1.2  christos 	      str = str_append (str, "...\n");
    632  1.1.1.2  christos 	    }
    633  1.1.1.2  christos 	}
    634  1.1.1.2  christos       if (ctf_errno (state->cds_fp) != ECTF_NEXT_END)
    635  1.1.1.2  christos 	{
    636  1.1.1.2  christos 	  ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp),
    637  1.1.1.2  christos 			_("cannot visit enumerands dumping type 0x%lx"), id);
    638  1.1.1.2  christos 	  goto err;
    639  1.1.1.2  christos 	}
    640  1.1.1.2  christos     }
    641      1.1  christos 
    642      1.1  christos   ctf_dump_append (state, str);
    643  1.1.1.2  christos   free (indent);
    644  1.1.1.2  christos 
    645      1.1  christos   return 0;
    646      1.1  christos 
    647      1.1  christos  err:
    648  1.1.1.2  christos   free (indent);
    649  1.1.1.2  christos   free (str);
    650  1.1.1.2  christos 
    651  1.1.1.2  christos   /* Swallow the error: don't cause an error in one type to abort all
    652  1.1.1.2  christos      type dumping.  */
    653  1.1.1.2  christos   return 0;
    654  1.1.1.2  christos 
    655  1.1.1.2  christos  oom:
    656  1.1.1.2  christos   free (indent);
    657      1.1  christos   free (str);
    658  1.1.1.2  christos   return ctf_set_errno (state->cds_fp, ENOMEM);
    659      1.1  christos }
    660      1.1  christos 
    661      1.1  christos /* Dump the string table into the cds_items.  */
    662      1.1  christos 
    663      1.1  christos static int
    664  1.1.1.2  christos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
    665      1.1  christos {
    666      1.1  christos   const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
    667      1.1  christos 
    668      1.1  christos   for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
    669      1.1  christos 	 fp->ctf_str[CTF_STRTAB_0].cts_len;)
    670      1.1  christos     {
    671      1.1  christos       char *str;
    672  1.1.1.2  christos       if (asprintf (&str, "0x%lx: %s",
    673      1.1  christos 		    (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
    674      1.1  christos 		    s) < 0)
    675      1.1  christos 	return (ctf_set_errno (fp, errno));
    676      1.1  christos       ctf_dump_append (state, str);
    677      1.1  christos       s += strlen (s) + 1;
    678      1.1  christos     }
    679      1.1  christos 
    680      1.1  christos   return 0;
    681      1.1  christos }
    682      1.1  christos 
    683      1.1  christos /* Dump a particular section of a CTF file, in textual form.  Call with a
    684      1.1  christos    pointer to a NULL STATE: each call emits a dynamically allocated string
    685      1.1  christos    containing a description of one entity in the specified section, in order.
    686      1.1  christos    Only the first call (with a NULL state) may vary SECT.  Once the CTF section
    687      1.1  christos    has been entirely dumped, the call returns NULL and frees and annuls the
    688      1.1  christos    STATE, ready for another section to be dumped.  The returned textual content
    689      1.1  christos    may span multiple lines: between each call the FUNC is called with one
    690      1.1  christos    textual line at a time, and should return a suitably decorated line (it can
    691      1.1  christos    allocate a new one and return it if it likes).  */
    692      1.1  christos 
    693      1.1  christos char *
    694  1.1.1.2  christos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
    695      1.1  christos 	  ctf_dump_decorate_f *func, void *arg)
    696      1.1  christos {
    697      1.1  christos   char *str;
    698      1.1  christos   char *line;
    699      1.1  christos   ctf_dump_state_t *state = NULL;
    700      1.1  christos 
    701      1.1  christos   if (*statep == NULL)
    702      1.1  christos     {
    703      1.1  christos       /* Data collection.  Transforming a call-at-a-time iterator into a
    704      1.1  christos 	 return-at-a-time iterator in a language without call/cc is annoying. It
    705      1.1  christos 	 is easiest to simply collect everything at once and then return it bit
    706      1.1  christos 	 by bit.  The first call will take (much) longer than otherwise, but the
    707      1.1  christos 	 amortized time needed is the same.  */
    708      1.1  christos 
    709      1.1  christos       if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
    710      1.1  christos 	{
    711      1.1  christos 	  ctf_set_errno (fp, ENOMEM);
    712      1.1  christos 	  goto end;
    713      1.1  christos 	}
    714      1.1  christos       state = *statep;
    715      1.1  christos 
    716      1.1  christos       memset (state, 0, sizeof (struct ctf_dump_state));
    717      1.1  christos       state->cds_fp = fp;
    718      1.1  christos       state->cds_sect = sect;
    719      1.1  christos 
    720      1.1  christos       switch (sect)
    721      1.1  christos 	{
    722      1.1  christos 	case CTF_SECT_HEADER:
    723      1.1  christos 	  ctf_dump_header (fp, state);
    724      1.1  christos 	  break;
    725      1.1  christos 	case CTF_SECT_LABEL:
    726      1.1  christos 	  if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
    727      1.1  christos 	    {
    728      1.1  christos 	      if (ctf_errno (fp) != ECTF_NOLABELDATA)
    729      1.1  christos 		goto end;		/* errno is set for us.  */
    730      1.1  christos 	      ctf_set_errno (fp, 0);
    731      1.1  christos 	    }
    732      1.1  christos 	  break;
    733      1.1  christos 	case CTF_SECT_OBJT:
    734  1.1.1.2  christos 	  if (ctf_dump_objts (fp, state, 0) < 0)
    735      1.1  christos 	    goto end;			/* errno is set for us.  */
    736      1.1  christos 	  break;
    737      1.1  christos 	case CTF_SECT_FUNC:
    738  1.1.1.2  christos 	  if (ctf_dump_objts (fp, state, 1) < 0)
    739      1.1  christos 	    goto end;			/* errno is set for us.  */
    740      1.1  christos 	  break;
    741      1.1  christos 	case CTF_SECT_VAR:
    742      1.1  christos 	  if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
    743      1.1  christos 	    goto end;			/* errno is set for us.  */
    744      1.1  christos 	  break;
    745      1.1  christos 	case CTF_SECT_TYPE:
    746      1.1  christos 	  if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
    747      1.1  christos 	    goto end;			/* errno is set for us.  */
    748      1.1  christos 	  break;
    749      1.1  christos 	case CTF_SECT_STR:
    750      1.1  christos 	  ctf_dump_str (fp, state);
    751      1.1  christos 	  break;
    752      1.1  christos 	default:
    753      1.1  christos 	  ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
    754      1.1  christos 	  goto end;
    755      1.1  christos 	}
    756      1.1  christos     }
    757      1.1  christos   else
    758      1.1  christos     {
    759      1.1  christos       state = *statep;
    760      1.1  christos 
    761      1.1  christos       if (state->cds_sect != sect)
    762      1.1  christos 	{
    763      1.1  christos 	  ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
    764      1.1  christos 	  goto end;
    765      1.1  christos 	}
    766      1.1  christos     }
    767      1.1  christos 
    768      1.1  christos   if (state->cds_current == NULL)
    769      1.1  christos     state->cds_current = ctf_list_next (&state->cds_items);
    770      1.1  christos   else
    771      1.1  christos     state->cds_current = ctf_list_next (state->cds_current);
    772      1.1  christos 
    773      1.1  christos   if (state->cds_current == NULL)
    774      1.1  christos     goto end;
    775      1.1  christos 
    776      1.1  christos   /* Hookery.  There is some extra complexity to preserve linefeeds within each
    777      1.1  christos      item while removing linefeeds at the end.  */
    778      1.1  christos   if (func)
    779      1.1  christos     {
    780      1.1  christos       size_t len;
    781      1.1  christos 
    782      1.1  christos       str = NULL;
    783      1.1  christos       for (line = state->cds_current->cdi_item; line && *line; )
    784      1.1  christos 	{
    785      1.1  christos 	  char *nline = line;
    786      1.1  christos 	  char *ret;
    787      1.1  christos 
    788      1.1  christos 	  nline = strchr (line, '\n');
    789      1.1  christos 	  if (nline)
    790      1.1  christos 	    nline[0] = '\0';
    791      1.1  christos 
    792      1.1  christos 	  ret = func (sect, line, arg);
    793      1.1  christos 	  str = str_append (str, ret);
    794      1.1  christos 	  str = str_append (str, "\n");
    795      1.1  christos 	  if (ret != line)
    796      1.1  christos 	    free (ret);
    797      1.1  christos 
    798      1.1  christos 	  if (nline)
    799      1.1  christos 	    {
    800      1.1  christos 	      nline[0] = '\n';
    801      1.1  christos 	      nline++;
    802      1.1  christos 	    }
    803      1.1  christos 
    804      1.1  christos 	  line = nline;
    805      1.1  christos 	}
    806      1.1  christos 
    807      1.1  christos       len = strlen (str);
    808      1.1  christos 
    809      1.1  christos       if (str[len-1] == '\n')
    810      1.1  christos 	str[len-1] = '\0';
    811      1.1  christos     }
    812      1.1  christos   else
    813      1.1  christos     {
    814      1.1  christos       str = strdup (state->cds_current->cdi_item);
    815      1.1  christos       if (!str)
    816      1.1  christos 	{
    817      1.1  christos 	  ctf_set_errno (fp, ENOMEM);
    818  1.1.1.4  christos 	  return NULL;
    819      1.1  christos 	}
    820      1.1  christos     }
    821      1.1  christos 
    822      1.1  christos   ctf_set_errno (fp, 0);
    823      1.1  christos   return str;
    824      1.1  christos 
    825      1.1  christos  end:
    826      1.1  christos   ctf_dump_free (state);
    827      1.1  christos   free (state);
    828      1.1  christos   ctf_set_errno (fp, 0);
    829      1.1  christos   *statep = NULL;
    830      1.1  christos   return NULL;
    831      1.1  christos }
    832