17d62b00eSchristos /* Textual dumping of CTF data. 2*6881a400Schristos Copyright (C) 2019-2022 Free Software Foundation, Inc. 37d62b00eSchristos 47d62b00eSchristos This file is part of libctf. 57d62b00eSchristos 67d62b00eSchristos libctf is free software; you can redistribute it and/or modify it under 77d62b00eSchristos the terms of the GNU General Public License as published by the Free 87d62b00eSchristos Software Foundation; either version 3, or (at your option) any later 97d62b00eSchristos version. 107d62b00eSchristos 117d62b00eSchristos This program is distributed in the hope that it will be useful, but 127d62b00eSchristos WITHOUT ANY WARRANTY; without even the implied warranty of 137d62b00eSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 147d62b00eSchristos See the GNU General Public License for more details. 157d62b00eSchristos 167d62b00eSchristos You should have received a copy of the GNU General Public License 177d62b00eSchristos along with this program; see the file COPYING. If not see 187d62b00eSchristos <http://www.gnu.org/licenses/>. */ 197d62b00eSchristos 207d62b00eSchristos #include <ctf-impl.h> 217d62b00eSchristos #include <string.h> 227d62b00eSchristos 237d62b00eSchristos #define str_append(s, a) ctf_str_append_noerr (s, a) 247d62b00eSchristos 257d62b00eSchristos /* One item to be dumped, in string form. */ 267d62b00eSchristos 277d62b00eSchristos typedef struct ctf_dump_item 287d62b00eSchristos { 297d62b00eSchristos ctf_list_t cdi_list; 307d62b00eSchristos char *cdi_item; 317d62b00eSchristos } ctf_dump_item_t; 327d62b00eSchristos 337d62b00eSchristos /* Cross-call state for dumping. Basically just enough to track the section in 347d62b00eSchristos use and a list of return strings. */ 357d62b00eSchristos 367d62b00eSchristos struct ctf_dump_state 377d62b00eSchristos { 387d62b00eSchristos ctf_sect_names_t cds_sect; 39*6881a400Schristos ctf_dict_t *cds_fp; 407d62b00eSchristos ctf_dump_item_t *cds_current; 417d62b00eSchristos ctf_list_t cds_items; 427d62b00eSchristos }; 437d62b00eSchristos 447d62b00eSchristos /* Cross-call state for ctf_dump_member. */ 457d62b00eSchristos 467d62b00eSchristos typedef struct ctf_dump_membstate 477d62b00eSchristos { 487d62b00eSchristos char **cdm_str; 49*6881a400Schristos ctf_dict_t *cdm_fp; 50*6881a400Schristos const char *cdm_toplevel_indent; 517d62b00eSchristos } ctf_dump_membstate_t; 527d62b00eSchristos 537d62b00eSchristos static int 547d62b00eSchristos ctf_dump_append (ctf_dump_state_t *state, char *str) 557d62b00eSchristos { 567d62b00eSchristos ctf_dump_item_t *cdi; 577d62b00eSchristos 587d62b00eSchristos if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL) 597d62b00eSchristos return (ctf_set_errno (state->cds_fp, ENOMEM)); 607d62b00eSchristos 617d62b00eSchristos cdi->cdi_item = str; 627d62b00eSchristos ctf_list_append (&state->cds_items, cdi); 637d62b00eSchristos return 0; 647d62b00eSchristos } 657d62b00eSchristos 667d62b00eSchristos static void 677d62b00eSchristos ctf_dump_free (ctf_dump_state_t *state) 687d62b00eSchristos { 697d62b00eSchristos ctf_dump_item_t *cdi, *next_cdi; 707d62b00eSchristos 717d62b00eSchristos if (state == NULL) 727d62b00eSchristos return; 737d62b00eSchristos 747d62b00eSchristos for (cdi = ctf_list_next (&state->cds_items); cdi != NULL; 757d62b00eSchristos cdi = next_cdi) 767d62b00eSchristos { 777d62b00eSchristos free (cdi->cdi_item); 787d62b00eSchristos next_cdi = ctf_list_next (cdi); 797d62b00eSchristos free (cdi); 807d62b00eSchristos } 817d62b00eSchristos } 827d62b00eSchristos 83*6881a400Schristos /* Return a dump for a single type, without member info: but do optionally show 84*6881a400Schristos the type's references. */ 85*6881a400Schristos 86*6881a400Schristos #define CTF_FT_REFS 0x2 /* Print referenced types. */ 87*6881a400Schristos #define CTF_FT_BITFIELD 0x4 /* Print :BITS if a bitfield. */ 88*6881a400Schristos #define CTF_FT_ID 0x8 /* Print "ID: " in front of type IDs. */ 897d62b00eSchristos 907d62b00eSchristos static char * 91*6881a400Schristos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag) 927d62b00eSchristos { 937d62b00eSchristos ctf_id_t new_id; 947d62b00eSchristos char *str = NULL, *bit = NULL, *buf = NULL; 957d62b00eSchristos 96*6881a400Schristos ctf_set_errno (fp, 0); 977d62b00eSchristos new_id = id; 987d62b00eSchristos do 997d62b00eSchristos { 100*6881a400Schristos ctf_encoding_t ep; 101*6881a400Schristos ctf_arinfo_t ar; 102*6881a400Schristos int kind, unsliced_kind; 103*6881a400Schristos ssize_t size, align; 1047d62b00eSchristos const char *nonroot_leader = ""; 1057d62b00eSchristos const char *nonroot_trailer = ""; 106*6881a400Schristos const char *idstr = ""; 1077d62b00eSchristos 1087d62b00eSchristos id = new_id; 1097d62b00eSchristos if (flag == CTF_ADD_NONROOT) 1107d62b00eSchristos { 1117d62b00eSchristos nonroot_leader = "{"; 1127d62b00eSchristos nonroot_trailer = "}"; 1137d62b00eSchristos } 1147d62b00eSchristos 1157d62b00eSchristos buf = ctf_type_aname (fp, id); 1167d62b00eSchristos if (!buf) 1177d62b00eSchristos { 1187d62b00eSchristos if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE) 1197d62b00eSchristos { 120*6881a400Schristos ctf_set_errno (fp, ECTF_NONREPRESENTABLE); 1217d62b00eSchristos str = str_append (str, " (type not represented in CTF)"); 122*6881a400Schristos return str; 1237d62b00eSchristos } 1247d62b00eSchristos 1257d62b00eSchristos goto err; 1267d62b00eSchristos } 1277d62b00eSchristos 128*6881a400Schristos if (flag & CTF_FT_ID) 129*6881a400Schristos idstr = "ID "; 130*6881a400Schristos if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr, 131*6881a400Schristos id, ctf_type_kind (fp, id)) < 0) 1327d62b00eSchristos goto oom; 1337d62b00eSchristos str = str_append (str, bit); 1347d62b00eSchristos free (bit); 1357d62b00eSchristos bit = NULL; 1367d62b00eSchristos 1377d62b00eSchristos if (buf[0] != '\0') 1387d62b00eSchristos str = str_append (str, buf); 1397d62b00eSchristos 1407d62b00eSchristos free (buf); 1417d62b00eSchristos buf = NULL; 1427d62b00eSchristos 143*6881a400Schristos unsliced_kind = ctf_type_kind_unsliced (fp, id); 144*6881a400Schristos kind = ctf_type_kind (fp, id); 145*6881a400Schristos 146*6881a400Schristos /* Report encodings of everything with an encoding other than enums: 147*6881a400Schristos base-type enums cannot have a nonzero cte_offset or cte_bits value. 148*6881a400Schristos (Slices of them can, but they are of kind CTF_K_SLICE.) */ 149*6881a400Schristos if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0) 1507d62b00eSchristos { 151*6881a400Schristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT 152*6881a400Schristos && flag & CTF_FT_BITFIELD) 1537d62b00eSchristos { 154*6881a400Schristos if (asprintf (&bit, ":%i", ep.cte_bits) < 0) 1557d62b00eSchristos goto oom; 1567d62b00eSchristos str = str_append (str, bit); 1577d62b00eSchristos free (bit); 1587d62b00eSchristos bit = NULL; 159*6881a400Schristos } 1607d62b00eSchristos 161*6881a400Schristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT 162*6881a400Schristos || ep.cte_offset != 0) 163*6881a400Schristos { 164*6881a400Schristos const char *slice = ""; 165*6881a400Schristos 166*6881a400Schristos if (unsliced_kind == CTF_K_SLICE) 167*6881a400Schristos slice = "slice "; 168*6881a400Schristos 169*6881a400Schristos if (asprintf (&bit, " [%s0x%x:0x%x]", 170*6881a400Schristos slice, ep.cte_offset, ep.cte_bits) < 0) 171*6881a400Schristos goto oom; 172*6881a400Schristos str = str_append (str, bit); 173*6881a400Schristos free (bit); 174*6881a400Schristos bit = NULL; 175*6881a400Schristos } 176*6881a400Schristos 177*6881a400Schristos if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0) 178*6881a400Schristos goto oom; 179*6881a400Schristos str = str_append (str, bit); 180*6881a400Schristos free (bit); 181*6881a400Schristos bit = NULL; 182*6881a400Schristos } 183*6881a400Schristos 184*6881a400Schristos size = ctf_type_size (fp, id); 185*6881a400Schristos if (kind != CTF_K_FUNCTION && size >= 0) 186*6881a400Schristos { 187*6881a400Schristos if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0) 1887d62b00eSchristos goto oom; 1897d62b00eSchristos 1907d62b00eSchristos str = str_append (str, bit); 1917d62b00eSchristos free (bit); 1927d62b00eSchristos bit = NULL; 193*6881a400Schristos } 1947d62b00eSchristos 195*6881a400Schristos align = ctf_type_align (fp, id); 196*6881a400Schristos if (align >= 0) 197*6881a400Schristos { 198*6881a400Schristos if (asprintf (&bit, " (aligned at 0x%lx)", 199*6881a400Schristos (unsigned long int) align) < 0) 200*6881a400Schristos goto oom; 201*6881a400Schristos 202*6881a400Schristos str = str_append (str, bit); 203*6881a400Schristos free (bit); 204*6881a400Schristos bit = NULL; 205*6881a400Schristos } 206*6881a400Schristos 207*6881a400Schristos if (nonroot_trailer[0] != 0) 208*6881a400Schristos str = str_append (str, nonroot_trailer); 209*6881a400Schristos 210*6881a400Schristos /* Just exit after one iteration if we are not showing the types this type 211*6881a400Schristos references. */ 212*6881a400Schristos if (!(flag & CTF_FT_REFS)) 213*6881a400Schristos return str; 214*6881a400Schristos 215*6881a400Schristos /* Keep going as long as this type references another. We consider arrays 216*6881a400Schristos to "reference" their element type. */ 217*6881a400Schristos 218*6881a400Schristos if (kind == CTF_K_ARRAY) 219*6881a400Schristos { 220*6881a400Schristos if (ctf_array_info (fp, id, &ar) < 0) 221*6881a400Schristos goto err; 222*6881a400Schristos new_id = ar.ctr_contents; 223*6881a400Schristos } 224*6881a400Schristos else 2257d62b00eSchristos new_id = ctf_type_reference (fp, id); 2267d62b00eSchristos if (new_id != CTF_ERR) 2277d62b00eSchristos str = str_append (str, " -> "); 228*6881a400Schristos } 229*6881a400Schristos while (new_id != CTF_ERR); 2307d62b00eSchristos 2317d62b00eSchristos if (ctf_errno (fp) != ECTF_NOTREF) 2327d62b00eSchristos { 2337d62b00eSchristos free (str); 2347d62b00eSchristos return NULL; 2357d62b00eSchristos } 2367d62b00eSchristos 2377d62b00eSchristos return str; 2387d62b00eSchristos 2397d62b00eSchristos oom: 2407d62b00eSchristos ctf_set_errno (fp, errno); 2417d62b00eSchristos err: 2427d62b00eSchristos ctf_err_warn (fp, 1, 0, _("cannot format name dumping type 0x%lx"), id); 2437d62b00eSchristos free (buf); 2447d62b00eSchristos free (str); 2457d62b00eSchristos free (bit); 2467d62b00eSchristos return NULL; 2477d62b00eSchristos } 2487d62b00eSchristos 2497d62b00eSchristos /* Dump one string field from the file header into the cds_items. */ 2507d62b00eSchristos static int 251*6881a400Schristos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state, 2527d62b00eSchristos const char *name, uint32_t value) 2537d62b00eSchristos { 2547d62b00eSchristos char *str; 2557d62b00eSchristos if (value) 2567d62b00eSchristos { 2577d62b00eSchristos if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0) 2587d62b00eSchristos goto err; 2597d62b00eSchristos ctf_dump_append (state, str); 2607d62b00eSchristos } 2617d62b00eSchristos return 0; 2627d62b00eSchristos 2637d62b00eSchristos err: 2647d62b00eSchristos return (ctf_set_errno (fp, errno)); 2657d62b00eSchristos } 2667d62b00eSchristos 2677d62b00eSchristos /* Dump one section-offset field from the file header into the cds_items. */ 2687d62b00eSchristos static int 269*6881a400Schristos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state, 2707d62b00eSchristos const char *sect, uint32_t off, uint32_t nextoff) 2717d62b00eSchristos { 2727d62b00eSchristos char *str; 2737d62b00eSchristos if (nextoff - off) 2747d62b00eSchristos { 2757d62b00eSchristos if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect, 2767d62b00eSchristos (unsigned long) off, (unsigned long) (nextoff - 1), 2777d62b00eSchristos (unsigned long) (nextoff - off)) < 0) 2787d62b00eSchristos goto err; 2797d62b00eSchristos ctf_dump_append (state, str); 2807d62b00eSchristos } 2817d62b00eSchristos return 0; 2827d62b00eSchristos 2837d62b00eSchristos err: 2847d62b00eSchristos return (ctf_set_errno (fp, errno)); 2857d62b00eSchristos } 2867d62b00eSchristos 2877d62b00eSchristos /* Dump the file header into the cds_items. */ 2887d62b00eSchristos static int 289*6881a400Schristos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state) 2907d62b00eSchristos { 2917d62b00eSchristos char *str; 292*6881a400Schristos char *flagstr = NULL; 2937d62b00eSchristos const ctf_header_t *hp = fp->ctf_header; 2947d62b00eSchristos const char *vertab[] = 2957d62b00eSchristos { 2967d62b00eSchristos NULL, "CTF_VERSION_1", 2977d62b00eSchristos "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type " 2987d62b00eSchristos "boundaries)", 2997d62b00eSchristos "CTF_VERSION_2", 3007d62b00eSchristos "CTF_VERSION_3", NULL 3017d62b00eSchristos }; 3027d62b00eSchristos const char *verstr = NULL; 3037d62b00eSchristos 304*6881a400Schristos if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0) 3057d62b00eSchristos goto err; 3067d62b00eSchristos ctf_dump_append (state, str); 3077d62b00eSchristos 3087d62b00eSchristos if (hp->cth_version <= CTF_VERSION) 3097d62b00eSchristos verstr = vertab[hp->cth_version]; 3107d62b00eSchristos 3117d62b00eSchristos if (verstr == NULL) 3127d62b00eSchristos verstr = "(not a valid version)"; 3137d62b00eSchristos 3147d62b00eSchristos if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version, 3157d62b00eSchristos verstr) < 0) 3167d62b00eSchristos goto err; 3177d62b00eSchristos ctf_dump_append (state, str); 3187d62b00eSchristos 3197d62b00eSchristos /* Everything else is only printed if present. */ 3207d62b00eSchristos 321*6881a400Schristos /* The flags are unusual in that they represent the ctf_dict_t *in memory*: 3227d62b00eSchristos flags representing compression, etc, are turned off as the file is 3237d62b00eSchristos decompressed. So we store a copy of the flags before they are changed, for 3247d62b00eSchristos the dumper. */ 3257d62b00eSchristos 3267d62b00eSchristos if (fp->ctf_openflags > 0) 3277d62b00eSchristos { 328*6881a400Schristos if (asprintf (&flagstr, "%s%s%s%s%s%s%s", 329*6881a400Schristos fp->ctf_openflags & CTF_F_COMPRESS 330*6881a400Schristos ? "CTF_F_COMPRESS": "", 331*6881a400Schristos (fp->ctf_openflags & CTF_F_COMPRESS) 332*6881a400Schristos && (fp->ctf_openflags & ~CTF_F_COMPRESS) 333*6881a400Schristos ? ", " : "", 334*6881a400Schristos fp->ctf_openflags & CTF_F_NEWFUNCINFO 335*6881a400Schristos ? "CTF_F_NEWFUNCINFO" : "", 336*6881a400Schristos (fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO)) 337*6881a400Schristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO)) 338*6881a400Schristos ? ", " : "", 339*6881a400Schristos fp->ctf_openflags & CTF_F_IDXSORTED 340*6881a400Schristos ? "CTF_F_IDXSORTED" : "", 341*6881a400Schristos fp->ctf_openflags & (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO 342*6881a400Schristos | CTF_F_IDXSORTED) 343*6881a400Schristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO 344*6881a400Schristos | CTF_F_IDXSORTED)) 345*6881a400Schristos ? ", " : "", 346*6881a400Schristos fp->ctf_openflags & CTF_F_DYNSTR 347*6881a400Schristos ? "CTF_F_DYNSTR" : "") < 0) 348*6881a400Schristos goto err; 349*6881a400Schristos 350*6881a400Schristos if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0) 3517d62b00eSchristos goto err; 3527d62b00eSchristos ctf_dump_append (state, str); 3537d62b00eSchristos } 3547d62b00eSchristos 3557d62b00eSchristos if (ctf_dump_header_strfield (fp, state, "Parent label", 3567d62b00eSchristos hp->cth_parlabel) < 0) 3577d62b00eSchristos goto err; 3587d62b00eSchristos 3597d62b00eSchristos if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0) 3607d62b00eSchristos goto err; 3617d62b00eSchristos 3627d62b00eSchristos if (ctf_dump_header_strfield (fp, state, "Compilation unit name", 3637d62b00eSchristos hp->cth_cuname) < 0) 3647d62b00eSchristos goto err; 3657d62b00eSchristos 3667d62b00eSchristos if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff, 3677d62b00eSchristos hp->cth_objtoff) < 0) 3687d62b00eSchristos goto err; 3697d62b00eSchristos 3707d62b00eSchristos if (ctf_dump_header_sectfield (fp, state, "Data object section", 3717d62b00eSchristos hp->cth_objtoff, hp->cth_funcoff) < 0) 3727d62b00eSchristos goto err; 3737d62b00eSchristos 3747d62b00eSchristos if (ctf_dump_header_sectfield (fp, state, "Function info section", 375*6881a400Schristos hp->cth_funcoff, hp->cth_objtidxoff) < 0) 376*6881a400Schristos goto err; 377*6881a400Schristos 378*6881a400Schristos if (ctf_dump_header_sectfield (fp, state, "Object index section", 379*6881a400Schristos hp->cth_objtidxoff, hp->cth_funcidxoff) < 0) 380*6881a400Schristos goto err; 381*6881a400Schristos 382*6881a400Schristos if (ctf_dump_header_sectfield (fp, state, "Function index section", 383*6881a400Schristos hp->cth_funcidxoff, hp->cth_varoff) < 0) 3847d62b00eSchristos goto err; 3857d62b00eSchristos 3867d62b00eSchristos if (ctf_dump_header_sectfield (fp, state, "Variable section", 3877d62b00eSchristos hp->cth_varoff, hp->cth_typeoff) < 0) 3887d62b00eSchristos goto err; 3897d62b00eSchristos 3907d62b00eSchristos if (ctf_dump_header_sectfield (fp, state, "Type section", 3917d62b00eSchristos hp->cth_typeoff, hp->cth_stroff) < 0) 3927d62b00eSchristos goto err; 3937d62b00eSchristos 3947d62b00eSchristos if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff, 3957d62b00eSchristos hp->cth_stroff + hp->cth_strlen + 1) < 0) 3967d62b00eSchristos goto err; 3977d62b00eSchristos 3987d62b00eSchristos return 0; 3997d62b00eSchristos err: 400*6881a400Schristos free (flagstr); 4017d62b00eSchristos return (ctf_set_errno (fp, errno)); 4027d62b00eSchristos } 4037d62b00eSchristos 4047d62b00eSchristos /* Dump a single label into the cds_items. */ 4057d62b00eSchristos 4067d62b00eSchristos static int 4077d62b00eSchristos ctf_dump_label (const char *name, const ctf_lblinfo_t *info, 4087d62b00eSchristos void *arg) 4097d62b00eSchristos { 4107d62b00eSchristos char *str; 4117d62b00eSchristos char *typestr; 4127d62b00eSchristos ctf_dump_state_t *state = arg; 4137d62b00eSchristos 4147d62b00eSchristos if (asprintf (&str, "%s -> ", name) < 0) 4157d62b00eSchristos return (ctf_set_errno (state->cds_fp, errno)); 4167d62b00eSchristos 4177d62b00eSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type, 418*6881a400Schristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL) 4197d62b00eSchristos { 4207d62b00eSchristos free (str); 4217d62b00eSchristos return 0; /* Swallow the error. */ 4227d62b00eSchristos } 4237d62b00eSchristos 4247d62b00eSchristos str = str_append (str, typestr); 4257d62b00eSchristos free (typestr); 4267d62b00eSchristos 4277d62b00eSchristos ctf_dump_append (state, str); 4287d62b00eSchristos return 0; 4297d62b00eSchristos } 4307d62b00eSchristos 431*6881a400Schristos /* Dump all the object or function entries into the cds_items. */ 4327d62b00eSchristos 4337d62b00eSchristos static int 434*6881a400Schristos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions) 4357d62b00eSchristos { 436*6881a400Schristos const char *name; 437*6881a400Schristos ctf_id_t id; 438*6881a400Schristos ctf_next_t *i = NULL; 439*6881a400Schristos char *str = NULL; 4407d62b00eSchristos 441*6881a400Schristos if ((functions && fp->ctf_funcidx_names) 442*6881a400Schristos || (!functions && fp->ctf_objtidx_names)) 443*6881a400Schristos str = str_append (str, _("Section is indexed.\n")); 444*6881a400Schristos else if (fp->ctf_symtab.cts_data == NULL) 445*6881a400Schristos str = str_append (str, _("No symbol table.\n")); 4467d62b00eSchristos 447*6881a400Schristos while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR) 4487d62b00eSchristos { 449*6881a400Schristos char *typestr = NULL; 4507d62b00eSchristos 451*6881a400Schristos /* Emit the name, if we know it. No trailing space: ctf_dump_format_type 452*6881a400Schristos has a leading one. */ 453*6881a400Schristos if (name) 4547d62b00eSchristos { 455*6881a400Schristos if (asprintf (&str, "%s -> ", name) < 0) 456*6881a400Schristos goto oom; 4577d62b00eSchristos } 4587d62b00eSchristos else 459*6881a400Schristos str = xstrdup (""); 4607d62b00eSchristos 461*6881a400Schristos if ((typestr = ctf_dump_format_type (state->cds_fp, id, 462*6881a400Schristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL) 4637d62b00eSchristos { 464*6881a400Schristos ctf_dump_append (state, str); 465*6881a400Schristos continue; /* Swallow the error. */ 4667d62b00eSchristos } 4677d62b00eSchristos 4687d62b00eSchristos str = str_append (str, typestr); 4697d62b00eSchristos free (typestr); 4707d62b00eSchristos ctf_dump_append (state, str); 4717d62b00eSchristos continue; 4727d62b00eSchristos 4737d62b00eSchristos oom: 474*6881a400Schristos ctf_set_errno (fp, ENOMEM); 475*6881a400Schristos ctf_next_destroy (i); 476*6881a400Schristos return -1; 4777d62b00eSchristos } 4787d62b00eSchristos return 0; 4797d62b00eSchristos } 4807d62b00eSchristos 4817d62b00eSchristos /* Dump a single variable into the cds_items. */ 4827d62b00eSchristos static int 4837d62b00eSchristos ctf_dump_var (const char *name, ctf_id_t type, void *arg) 4847d62b00eSchristos { 4857d62b00eSchristos char *str; 4867d62b00eSchristos char *typestr; 4877d62b00eSchristos ctf_dump_state_t *state = arg; 4887d62b00eSchristos 4897d62b00eSchristos if (asprintf (&str, "%s -> ", name) < 0) 4907d62b00eSchristos return (ctf_set_errno (state->cds_fp, errno)); 4917d62b00eSchristos 4927d62b00eSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, type, 493*6881a400Schristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL) 4947d62b00eSchristos { 4957d62b00eSchristos free (str); 4967d62b00eSchristos return 0; /* Swallow the error. */ 4977d62b00eSchristos } 4987d62b00eSchristos 4997d62b00eSchristos str = str_append (str, typestr); 5007d62b00eSchristos free (typestr); 5017d62b00eSchristos 5027d62b00eSchristos ctf_dump_append (state, str); 5037d62b00eSchristos return 0; 5047d62b00eSchristos } 5057d62b00eSchristos 506*6881a400Schristos /* Dump a single struct/union member into the string in the membstate. */ 5077d62b00eSchristos static int 5087d62b00eSchristos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset, 5097d62b00eSchristos int depth, void *arg) 5107d62b00eSchristos { 5117d62b00eSchristos ctf_dump_membstate_t *state = arg; 5127d62b00eSchristos char *typestr = NULL; 5137d62b00eSchristos char *bit = NULL; 5147d62b00eSchristos 515*6881a400Schristos /* The struct/union itself has already been printed. */ 516*6881a400Schristos if (depth == 0) 5177d62b00eSchristos return 0; 5187d62b00eSchristos 519*6881a400Schristos if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0) 520*6881a400Schristos goto oom; 521*6881a400Schristos *state->cdm_str = str_append (*state->cdm_str, bit); 522*6881a400Schristos free (bit); 523*6881a400Schristos 524*6881a400Schristos if ((typestr = ctf_dump_format_type (state->cdm_fp, id, 525*6881a400Schristos CTF_ADD_ROOT | CTF_FT_BITFIELD 526*6881a400Schristos | CTF_FT_ID)) == NULL) 5277d62b00eSchristos return -1; /* errno is set for us. */ 5287d62b00eSchristos 529*6881a400Schristos if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0) 5307d62b00eSchristos goto oom; 5317d62b00eSchristos 5327d62b00eSchristos *state->cdm_str = str_append (*state->cdm_str, bit); 5337d62b00eSchristos free (typestr); 5347d62b00eSchristos free (bit); 5357d62b00eSchristos typestr = NULL; 5367d62b00eSchristos bit = NULL; 5377d62b00eSchristos 5387d62b00eSchristos return 0; 5397d62b00eSchristos 5407d62b00eSchristos oom: 5417d62b00eSchristos free (typestr); 5427d62b00eSchristos free (bit); 5437d62b00eSchristos return (ctf_set_errno (state->cdm_fp, errno)); 5447d62b00eSchristos } 5457d62b00eSchristos 546*6881a400Schristos /* Report the number of digits in the hexadecimal representation of a type 547*6881a400Schristos ID. */ 548*6881a400Schristos 549*6881a400Schristos static int 550*6881a400Schristos type_hex_digits (ctf_id_t id) 551*6881a400Schristos { 552*6881a400Schristos int i = 0; 553*6881a400Schristos 554*6881a400Schristos if (id == 0) 555*6881a400Schristos return 1; 556*6881a400Schristos 557*6881a400Schristos for (; id > 0; id >>= 4, i++); 558*6881a400Schristos return i; 559*6881a400Schristos } 560*6881a400Schristos 5617d62b00eSchristos /* Dump a single type into the cds_items. */ 5627d62b00eSchristos static int 5637d62b00eSchristos ctf_dump_type (ctf_id_t id, int flag, void *arg) 5647d62b00eSchristos { 5657d62b00eSchristos char *str; 566*6881a400Schristos char *indent; 5677d62b00eSchristos ctf_dump_state_t *state = arg; 568*6881a400Schristos ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL }; 5697d62b00eSchristos 570*6881a400Schristos /* Indent neatly. */ 571*6881a400Schristos if (asprintf (&indent, " %*s", type_hex_digits (id), "") < 0) 572*6881a400Schristos return (ctf_set_errno (state->cds_fp, ENOMEM)); 573*6881a400Schristos 574*6881a400Schristos /* Dump the type itself. */ 575*6881a400Schristos if ((str = ctf_dump_format_type (state->cds_fp, id, 576*6881a400Schristos flag | CTF_FT_REFS)) == NULL) 5777d62b00eSchristos goto err; 5787d62b00eSchristos str = str_append (str, "\n"); 579*6881a400Schristos 580*6881a400Schristos membstate.cdm_toplevel_indent = indent; 581*6881a400Schristos 582*6881a400Schristos /* Member dumping for structs, unions... */ 583*6881a400Schristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT 584*6881a400Schristos || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION) 585*6881a400Schristos { 5867d62b00eSchristos if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0) 5877d62b00eSchristos { 5887d62b00eSchristos if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE) 5897d62b00eSchristos { 5907d62b00eSchristos ctf_dump_append (state, str); 5917d62b00eSchristos return 0; 5927d62b00eSchristos } 5937d62b00eSchristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp), 5947d62b00eSchristos _("cannot visit members dumping type 0x%lx"), id); 5957d62b00eSchristos goto err; 5967d62b00eSchristos } 597*6881a400Schristos } 5987d62b00eSchristos 599*6881a400Schristos /* ... and enums, for which we dump the first and last few members and skip 600*6881a400Schristos the ones in the middle. */ 601*6881a400Schristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM) 602*6881a400Schristos { 603*6881a400Schristos int enum_count = ctf_member_count (state->cds_fp, id); 604*6881a400Schristos ctf_next_t *it = NULL; 605*6881a400Schristos int i = 0; 606*6881a400Schristos const char *enumerand; 607*6881a400Schristos char *bit; 608*6881a400Schristos int value; 609*6881a400Schristos 610*6881a400Schristos while ((enumerand = ctf_enum_next (state->cds_fp, id, 611*6881a400Schristos &it, &value)) != NULL) 612*6881a400Schristos { 613*6881a400Schristos i++; 614*6881a400Schristos if ((i > 5) && (i < enum_count - 4)) 615*6881a400Schristos continue; 616*6881a400Schristos 617*6881a400Schristos str = str_append (str, indent); 618*6881a400Schristos 619*6881a400Schristos if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0) 620*6881a400Schristos { 621*6881a400Schristos ctf_next_destroy (it); 622*6881a400Schristos goto oom; 623*6881a400Schristos } 624*6881a400Schristos str = str_append (str, bit); 625*6881a400Schristos free (bit); 626*6881a400Schristos 627*6881a400Schristos if ((i == 5) && (enum_count > 10)) 628*6881a400Schristos { 629*6881a400Schristos str = str_append (str, indent); 630*6881a400Schristos str = str_append (str, "...\n"); 631*6881a400Schristos } 632*6881a400Schristos } 633*6881a400Schristos if (ctf_errno (state->cds_fp) != ECTF_NEXT_END) 634*6881a400Schristos { 635*6881a400Schristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp), 636*6881a400Schristos _("cannot visit enumerands dumping type 0x%lx"), id); 637*6881a400Schristos goto err; 638*6881a400Schristos } 639*6881a400Schristos } 6407d62b00eSchristos 6417d62b00eSchristos ctf_dump_append (state, str); 642*6881a400Schristos free (indent); 643*6881a400Schristos 6447d62b00eSchristos return 0; 6457d62b00eSchristos 6467d62b00eSchristos err: 647*6881a400Schristos free (indent); 6487d62b00eSchristos free (str); 649*6881a400Schristos 650*6881a400Schristos /* Swallow the error: don't cause an error in one type to abort all 651*6881a400Schristos type dumping. */ 652*6881a400Schristos return 0; 653*6881a400Schristos 654*6881a400Schristos oom: 655*6881a400Schristos free (indent); 656*6881a400Schristos free (str); 657*6881a400Schristos return ctf_set_errno (state->cds_fp, ENOMEM); 6587d62b00eSchristos } 6597d62b00eSchristos 6607d62b00eSchristos /* Dump the string table into the cds_items. */ 6617d62b00eSchristos 6627d62b00eSchristos static int 663*6881a400Schristos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state) 6647d62b00eSchristos { 6657d62b00eSchristos const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs; 6667d62b00eSchristos 6677d62b00eSchristos for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs + 6687d62b00eSchristos fp->ctf_str[CTF_STRTAB_0].cts_len;) 6697d62b00eSchristos { 6707d62b00eSchristos char *str; 671*6881a400Schristos if (asprintf (&str, "0x%lx: %s", 6727d62b00eSchristos (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs), 6737d62b00eSchristos s) < 0) 6747d62b00eSchristos return (ctf_set_errno (fp, errno)); 6757d62b00eSchristos ctf_dump_append (state, str); 6767d62b00eSchristos s += strlen (s) + 1; 6777d62b00eSchristos } 6787d62b00eSchristos 6797d62b00eSchristos return 0; 6807d62b00eSchristos } 6817d62b00eSchristos 6827d62b00eSchristos /* Dump a particular section of a CTF file, in textual form. Call with a 6837d62b00eSchristos pointer to a NULL STATE: each call emits a dynamically allocated string 6847d62b00eSchristos containing a description of one entity in the specified section, in order. 6857d62b00eSchristos Only the first call (with a NULL state) may vary SECT. Once the CTF section 6867d62b00eSchristos has been entirely dumped, the call returns NULL and frees and annuls the 6877d62b00eSchristos STATE, ready for another section to be dumped. The returned textual content 6887d62b00eSchristos may span multiple lines: between each call the FUNC is called with one 6897d62b00eSchristos textual line at a time, and should return a suitably decorated line (it can 6907d62b00eSchristos allocate a new one and return it if it likes). */ 6917d62b00eSchristos 6927d62b00eSchristos char * 693*6881a400Schristos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect, 6947d62b00eSchristos ctf_dump_decorate_f *func, void *arg) 6957d62b00eSchristos { 6967d62b00eSchristos char *str; 6977d62b00eSchristos char *line; 6987d62b00eSchristos ctf_dump_state_t *state = NULL; 6997d62b00eSchristos 7007d62b00eSchristos if (*statep == NULL) 7017d62b00eSchristos { 7027d62b00eSchristos /* Data collection. Transforming a call-at-a-time iterator into a 7037d62b00eSchristos return-at-a-time iterator in a language without call/cc is annoying. It 7047d62b00eSchristos is easiest to simply collect everything at once and then return it bit 7057d62b00eSchristos by bit. The first call will take (much) longer than otherwise, but the 7067d62b00eSchristos amortized time needed is the same. */ 7077d62b00eSchristos 7087d62b00eSchristos if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL) 7097d62b00eSchristos { 7107d62b00eSchristos ctf_set_errno (fp, ENOMEM); 7117d62b00eSchristos goto end; 7127d62b00eSchristos } 7137d62b00eSchristos state = *statep; 7147d62b00eSchristos 7157d62b00eSchristos memset (state, 0, sizeof (struct ctf_dump_state)); 7167d62b00eSchristos state->cds_fp = fp; 7177d62b00eSchristos state->cds_sect = sect; 7187d62b00eSchristos 7197d62b00eSchristos switch (sect) 7207d62b00eSchristos { 7217d62b00eSchristos case CTF_SECT_HEADER: 7227d62b00eSchristos ctf_dump_header (fp, state); 7237d62b00eSchristos break; 7247d62b00eSchristos case CTF_SECT_LABEL: 7257d62b00eSchristos if (ctf_label_iter (fp, ctf_dump_label, state) < 0) 7267d62b00eSchristos { 7277d62b00eSchristos if (ctf_errno (fp) != ECTF_NOLABELDATA) 7287d62b00eSchristos goto end; /* errno is set for us. */ 7297d62b00eSchristos ctf_set_errno (fp, 0); 7307d62b00eSchristos } 7317d62b00eSchristos break; 7327d62b00eSchristos case CTF_SECT_OBJT: 733*6881a400Schristos if (ctf_dump_objts (fp, state, 0) < 0) 7347d62b00eSchristos goto end; /* errno is set for us. */ 7357d62b00eSchristos break; 7367d62b00eSchristos case CTF_SECT_FUNC: 737*6881a400Schristos if (ctf_dump_objts (fp, state, 1) < 0) 7387d62b00eSchristos goto end; /* errno is set for us. */ 7397d62b00eSchristos break; 7407d62b00eSchristos case CTF_SECT_VAR: 7417d62b00eSchristos if (ctf_variable_iter (fp, ctf_dump_var, state) < 0) 7427d62b00eSchristos goto end; /* errno is set for us. */ 7437d62b00eSchristos break; 7447d62b00eSchristos case CTF_SECT_TYPE: 7457d62b00eSchristos if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0) 7467d62b00eSchristos goto end; /* errno is set for us. */ 7477d62b00eSchristos break; 7487d62b00eSchristos case CTF_SECT_STR: 7497d62b00eSchristos ctf_dump_str (fp, state); 7507d62b00eSchristos break; 7517d62b00eSchristos default: 7527d62b00eSchristos ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN); 7537d62b00eSchristos goto end; 7547d62b00eSchristos } 7557d62b00eSchristos } 7567d62b00eSchristos else 7577d62b00eSchristos { 7587d62b00eSchristos state = *statep; 7597d62b00eSchristos 7607d62b00eSchristos if (state->cds_sect != sect) 7617d62b00eSchristos { 7627d62b00eSchristos ctf_set_errno (fp, ECTF_DUMPSECTCHANGED); 7637d62b00eSchristos goto end; 7647d62b00eSchristos } 7657d62b00eSchristos } 7667d62b00eSchristos 7677d62b00eSchristos if (state->cds_current == NULL) 7687d62b00eSchristos state->cds_current = ctf_list_next (&state->cds_items); 7697d62b00eSchristos else 7707d62b00eSchristos state->cds_current = ctf_list_next (state->cds_current); 7717d62b00eSchristos 7727d62b00eSchristos if (state->cds_current == NULL) 7737d62b00eSchristos goto end; 7747d62b00eSchristos 7757d62b00eSchristos /* Hookery. There is some extra complexity to preserve linefeeds within each 7767d62b00eSchristos item while removing linefeeds at the end. */ 7777d62b00eSchristos if (func) 7787d62b00eSchristos { 7797d62b00eSchristos size_t len; 7807d62b00eSchristos 7817d62b00eSchristos str = NULL; 7827d62b00eSchristos for (line = state->cds_current->cdi_item; line && *line; ) 7837d62b00eSchristos { 7847d62b00eSchristos char *nline = line; 7857d62b00eSchristos char *ret; 7867d62b00eSchristos 7877d62b00eSchristos nline = strchr (line, '\n'); 7887d62b00eSchristos if (nline) 7897d62b00eSchristos nline[0] = '\0'; 7907d62b00eSchristos 7917d62b00eSchristos ret = func (sect, line, arg); 7927d62b00eSchristos str = str_append (str, ret); 7937d62b00eSchristos str = str_append (str, "\n"); 7947d62b00eSchristos if (ret != line) 7957d62b00eSchristos free (ret); 7967d62b00eSchristos 7977d62b00eSchristos if (nline) 7987d62b00eSchristos { 7997d62b00eSchristos nline[0] = '\n'; 8007d62b00eSchristos nline++; 8017d62b00eSchristos } 8027d62b00eSchristos 8037d62b00eSchristos line = nline; 8047d62b00eSchristos } 8057d62b00eSchristos 8067d62b00eSchristos len = strlen (str); 8077d62b00eSchristos 8087d62b00eSchristos if (str[len-1] == '\n') 8097d62b00eSchristos str[len-1] = '\0'; 8107d62b00eSchristos } 8117d62b00eSchristos else 8127d62b00eSchristos { 8137d62b00eSchristos str = strdup (state->cds_current->cdi_item); 8147d62b00eSchristos if (!str) 8157d62b00eSchristos { 8167d62b00eSchristos ctf_set_errno (fp, ENOMEM); 8177d62b00eSchristos return str; 8187d62b00eSchristos } 8197d62b00eSchristos } 8207d62b00eSchristos 8217d62b00eSchristos ctf_set_errno (fp, 0); 8227d62b00eSchristos return str; 8237d62b00eSchristos 8247d62b00eSchristos end: 8257d62b00eSchristos ctf_dump_free (state); 8267d62b00eSchristos free (state); 8277d62b00eSchristos ctf_set_errno (fp, 0); 8287d62b00eSchristos *statep = NULL; 8297d62b00eSchristos return NULL; 8307d62b00eSchristos } 831