18dffb485Schristos /* Textual dumping of CTF data. 2*12989c96Schristos Copyright (C) 2019-2024 Free Software Foundation, Inc. 38dffb485Schristos 48dffb485Schristos This file is part of libctf. 58dffb485Schristos 68dffb485Schristos libctf is free software; you can redistribute it and/or modify it under 78dffb485Schristos the terms of the GNU General Public License as published by the Free 88dffb485Schristos Software Foundation; either version 3, or (at your option) any later 98dffb485Schristos version. 108dffb485Schristos 118dffb485Schristos This program is distributed in the hope that it will be useful, but 128dffb485Schristos WITHOUT ANY WARRANTY; without even the implied warranty of 138dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 148dffb485Schristos See the GNU General Public License for more details. 158dffb485Schristos 168dffb485Schristos You should have received a copy of the GNU General Public License 178dffb485Schristos along with this program; see the file COPYING. If not see 188dffb485Schristos <http://www.gnu.org/licenses/>. */ 198dffb485Schristos 208dffb485Schristos #include <ctf-impl.h> 218dffb485Schristos #include <string.h> 228dffb485Schristos 238dffb485Schristos #define str_append(s, a) ctf_str_append_noerr (s, a) 248dffb485Schristos 258dffb485Schristos /* One item to be dumped, in string form. */ 268dffb485Schristos 278dffb485Schristos typedef struct ctf_dump_item 288dffb485Schristos { 298dffb485Schristos ctf_list_t cdi_list; 308dffb485Schristos char *cdi_item; 318dffb485Schristos } ctf_dump_item_t; 328dffb485Schristos 338dffb485Schristos /* Cross-call state for dumping. Basically just enough to track the section in 348dffb485Schristos use and a list of return strings. */ 358dffb485Schristos 368dffb485Schristos struct ctf_dump_state 378dffb485Schristos { 388dffb485Schristos ctf_sect_names_t cds_sect; 394b169a6bSchristos ctf_dict_t *cds_fp; 408dffb485Schristos ctf_dump_item_t *cds_current; 418dffb485Schristos ctf_list_t cds_items; 428dffb485Schristos }; 438dffb485Schristos 448dffb485Schristos /* Cross-call state for ctf_dump_member. */ 458dffb485Schristos 468dffb485Schristos typedef struct ctf_dump_membstate 478dffb485Schristos { 488dffb485Schristos char **cdm_str; 494b169a6bSchristos ctf_dict_t *cdm_fp; 504b169a6bSchristos const char *cdm_toplevel_indent; 518dffb485Schristos } ctf_dump_membstate_t; 528dffb485Schristos 538dffb485Schristos static int 548dffb485Schristos ctf_dump_append (ctf_dump_state_t *state, char *str) 558dffb485Schristos { 568dffb485Schristos ctf_dump_item_t *cdi; 578dffb485Schristos 588dffb485Schristos if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL) 598dffb485Schristos return (ctf_set_errno (state->cds_fp, ENOMEM)); 608dffb485Schristos 618dffb485Schristos cdi->cdi_item = str; 628dffb485Schristos ctf_list_append (&state->cds_items, cdi); 638dffb485Schristos return 0; 648dffb485Schristos } 658dffb485Schristos 668dffb485Schristos static void 678dffb485Schristos ctf_dump_free (ctf_dump_state_t *state) 688dffb485Schristos { 698dffb485Schristos ctf_dump_item_t *cdi, *next_cdi; 708dffb485Schristos 718dffb485Schristos if (state == NULL) 728dffb485Schristos return; 738dffb485Schristos 748dffb485Schristos for (cdi = ctf_list_next (&state->cds_items); cdi != NULL; 758dffb485Schristos cdi = next_cdi) 768dffb485Schristos { 778dffb485Schristos free (cdi->cdi_item); 788dffb485Schristos next_cdi = ctf_list_next (cdi); 798dffb485Schristos free (cdi); 808dffb485Schristos } 818dffb485Schristos } 828dffb485Schristos 834b169a6bSchristos /* Return a dump for a single type, without member info: but do optionally show 844b169a6bSchristos the type's references. */ 854b169a6bSchristos 864b169a6bSchristos #define CTF_FT_REFS 0x2 /* Print referenced types. */ 874b169a6bSchristos #define CTF_FT_BITFIELD 0x4 /* Print :BITS if a bitfield. */ 884b169a6bSchristos #define CTF_FT_ID 0x8 /* Print "ID: " in front of type IDs. */ 898dffb485Schristos 908dffb485Schristos static char * 914b169a6bSchristos ctf_dump_format_type (ctf_dict_t *fp, ctf_id_t id, int flag) 928dffb485Schristos { 938dffb485Schristos ctf_id_t new_id; 948dffb485Schristos char *str = NULL, *bit = NULL, *buf = NULL; 958dffb485Schristos 964b169a6bSchristos ctf_set_errno (fp, 0); 978dffb485Schristos new_id = id; 988dffb485Schristos do 998dffb485Schristos { 1004b169a6bSchristos ctf_encoding_t ep; 1014b169a6bSchristos ctf_arinfo_t ar; 1024b169a6bSchristos int kind, unsliced_kind; 1034b169a6bSchristos ssize_t size, align; 1048dffb485Schristos const char *nonroot_leader = ""; 1058dffb485Schristos const char *nonroot_trailer = ""; 1064b169a6bSchristos const char *idstr = ""; 1078dffb485Schristos 1088dffb485Schristos id = new_id; 1098dffb485Schristos if (flag == CTF_ADD_NONROOT) 1108dffb485Schristos { 1118dffb485Schristos nonroot_leader = "{"; 1128dffb485Schristos nonroot_trailer = "}"; 1138dffb485Schristos } 1148dffb485Schristos 1158dffb485Schristos buf = ctf_type_aname (fp, id); 1168dffb485Schristos if (!buf) 1178dffb485Schristos { 1188dffb485Schristos if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE) 1198dffb485Schristos { 1204b169a6bSchristos ctf_set_errno (fp, ECTF_NONREPRESENTABLE); 1218dffb485Schristos str = str_append (str, " (type not represented in CTF)"); 1224b169a6bSchristos return str; 1238dffb485Schristos } 1248dffb485Schristos 1258dffb485Schristos goto err; 1268dffb485Schristos } 1278dffb485Schristos 1284b169a6bSchristos if (flag & CTF_FT_ID) 1294b169a6bSchristos idstr = "ID "; 1304b169a6bSchristos if (asprintf (&bit, "%s%s0x%lx: (kind %i) ", nonroot_leader, idstr, 1314b169a6bSchristos id, ctf_type_kind (fp, id)) < 0) 1328dffb485Schristos goto oom; 1338dffb485Schristos str = str_append (str, bit); 1348dffb485Schristos free (bit); 1358dffb485Schristos bit = NULL; 1368dffb485Schristos 1378dffb485Schristos if (buf[0] != '\0') 1388dffb485Schristos str = str_append (str, buf); 1398dffb485Schristos 1408dffb485Schristos free (buf); 1418dffb485Schristos buf = NULL; 1428dffb485Schristos 1434b169a6bSchristos unsliced_kind = ctf_type_kind_unsliced (fp, id); 1444b169a6bSchristos kind = ctf_type_kind (fp, id); 1454b169a6bSchristos 1464b169a6bSchristos /* Report encodings of everything with an encoding other than enums: 1474b169a6bSchristos base-type enums cannot have a nonzero cte_offset or cte_bits value. 1484b169a6bSchristos (Slices of them can, but they are of kind CTF_K_SLICE.) */ 1494b169a6bSchristos if (unsliced_kind != CTF_K_ENUM && ctf_type_encoding (fp, id, &ep) == 0) 1508dffb485Schristos { 1514b169a6bSchristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT 1524b169a6bSchristos && flag & CTF_FT_BITFIELD) 1538dffb485Schristos { 1544b169a6bSchristos if (asprintf (&bit, ":%i", ep.cte_bits) < 0) 1558dffb485Schristos goto oom; 1568dffb485Schristos str = str_append (str, bit); 1578dffb485Schristos free (bit); 1588dffb485Schristos bit = NULL; 1594b169a6bSchristos } 1608dffb485Schristos 1614b169a6bSchristos if ((ssize_t) ep.cte_bits != ctf_type_size (fp, id) * CHAR_BIT 1624b169a6bSchristos || ep.cte_offset != 0) 1634b169a6bSchristos { 1644b169a6bSchristos const char *slice = ""; 1654b169a6bSchristos 1664b169a6bSchristos if (unsliced_kind == CTF_K_SLICE) 1674b169a6bSchristos slice = "slice "; 1684b169a6bSchristos 1694b169a6bSchristos if (asprintf (&bit, " [%s0x%x:0x%x]", 1704b169a6bSchristos slice, ep.cte_offset, ep.cte_bits) < 0) 1714b169a6bSchristos goto oom; 1724b169a6bSchristos str = str_append (str, bit); 1734b169a6bSchristos free (bit); 1744b169a6bSchristos bit = NULL; 1754b169a6bSchristos } 1764b169a6bSchristos 1774b169a6bSchristos if (asprintf (&bit, " (format 0x%x)", ep.cte_format) < 0) 1784b169a6bSchristos goto oom; 1794b169a6bSchristos str = str_append (str, bit); 1804b169a6bSchristos free (bit); 1814b169a6bSchristos bit = NULL; 1824b169a6bSchristos } 1834b169a6bSchristos 1844b169a6bSchristos size = ctf_type_size (fp, id); 1854b169a6bSchristos if (kind != CTF_K_FUNCTION && size >= 0) 1864b169a6bSchristos { 1874b169a6bSchristos if (asprintf (&bit, " (size 0x%lx)", (unsigned long int) size) < 0) 1888dffb485Schristos goto oom; 1898dffb485Schristos 1908dffb485Schristos str = str_append (str, bit); 1918dffb485Schristos free (bit); 1928dffb485Schristos bit = NULL; 1934b169a6bSchristos } 1948dffb485Schristos 1954b169a6bSchristos align = ctf_type_align (fp, id); 1964b169a6bSchristos if (align >= 0) 1974b169a6bSchristos { 1984b169a6bSchristos if (asprintf (&bit, " (aligned at 0x%lx)", 1994b169a6bSchristos (unsigned long int) align) < 0) 2004b169a6bSchristos goto oom; 2014b169a6bSchristos 2024b169a6bSchristos str = str_append (str, bit); 2034b169a6bSchristos free (bit); 2044b169a6bSchristos bit = NULL; 2054b169a6bSchristos } 2064b169a6bSchristos 2074b169a6bSchristos if (nonroot_trailer[0] != 0) 2084b169a6bSchristos str = str_append (str, nonroot_trailer); 2094b169a6bSchristos 2104b169a6bSchristos /* Just exit after one iteration if we are not showing the types this type 2114b169a6bSchristos references. */ 2124b169a6bSchristos if (!(flag & CTF_FT_REFS)) 2134b169a6bSchristos return str; 2144b169a6bSchristos 2154b169a6bSchristos /* Keep going as long as this type references another. We consider arrays 2164b169a6bSchristos to "reference" their element type. */ 2174b169a6bSchristos 2184b169a6bSchristos if (kind == CTF_K_ARRAY) 2194b169a6bSchristos { 2204b169a6bSchristos if (ctf_array_info (fp, id, &ar) < 0) 2214b169a6bSchristos goto err; 2224b169a6bSchristos new_id = ar.ctr_contents; 2234b169a6bSchristos } 2244b169a6bSchristos else 2258dffb485Schristos new_id = ctf_type_reference (fp, id); 2268dffb485Schristos if (new_id != CTF_ERR) 2278dffb485Schristos str = str_append (str, " -> "); 2284b169a6bSchristos } 2294b169a6bSchristos while (new_id != CTF_ERR); 2308dffb485Schristos 2318dffb485Schristos if (ctf_errno (fp) != ECTF_NOTREF) 2328dffb485Schristos { 2338dffb485Schristos free (str); 2348dffb485Schristos return NULL; 2358dffb485Schristos } 2368dffb485Schristos 2378dffb485Schristos return str; 2388dffb485Schristos 2398dffb485Schristos oom: 2408dffb485Schristos ctf_set_errno (fp, errno); 2418dffb485Schristos err: 242*12989c96Schristos ctf_err_warn (fp, 1, ctf_errno (fp), _("cannot format name dumping type 0x%lx"), 243*12989c96Schristos id); 2448dffb485Schristos free (buf); 2458dffb485Schristos free (str); 2468dffb485Schristos free (bit); 2478dffb485Schristos return NULL; 2488dffb485Schristos } 2498dffb485Schristos 2508dffb485Schristos /* Dump one string field from the file header into the cds_items. */ 2518dffb485Schristos static int 2524b169a6bSchristos ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state, 2538dffb485Schristos const char *name, uint32_t value) 2548dffb485Schristos { 2558dffb485Schristos char *str; 2568dffb485Schristos if (value) 2578dffb485Schristos { 2588dffb485Schristos if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0) 2598dffb485Schristos goto err; 2608dffb485Schristos ctf_dump_append (state, str); 2618dffb485Schristos } 2628dffb485Schristos return 0; 2638dffb485Schristos 2648dffb485Schristos err: 2658dffb485Schristos return (ctf_set_errno (fp, errno)); 2668dffb485Schristos } 2678dffb485Schristos 2688dffb485Schristos /* Dump one section-offset field from the file header into the cds_items. */ 2698dffb485Schristos static int 2704b169a6bSchristos ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state, 2718dffb485Schristos const char *sect, uint32_t off, uint32_t nextoff) 2728dffb485Schristos { 2738dffb485Schristos char *str; 2748dffb485Schristos if (nextoff - off) 2758dffb485Schristos { 2768dffb485Schristos if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect, 2778dffb485Schristos (unsigned long) off, (unsigned long) (nextoff - 1), 2788dffb485Schristos (unsigned long) (nextoff - off)) < 0) 2798dffb485Schristos goto err; 2808dffb485Schristos ctf_dump_append (state, str); 2818dffb485Schristos } 2828dffb485Schristos return 0; 2838dffb485Schristos 2848dffb485Schristos err: 2858dffb485Schristos return (ctf_set_errno (fp, errno)); 2868dffb485Schristos } 2878dffb485Schristos 2888dffb485Schristos /* Dump the file header into the cds_items. */ 2898dffb485Schristos static int 2904b169a6bSchristos ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state) 2918dffb485Schristos { 2928dffb485Schristos char *str; 2934b169a6bSchristos char *flagstr = NULL; 2948dffb485Schristos const ctf_header_t *hp = fp->ctf_header; 2958dffb485Schristos const char *vertab[] = 2968dffb485Schristos { 2978dffb485Schristos NULL, "CTF_VERSION_1", 2988dffb485Schristos "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type " 2998dffb485Schristos "boundaries)", 3008dffb485Schristos "CTF_VERSION_2", 3018dffb485Schristos "CTF_VERSION_3", NULL 3028dffb485Schristos }; 3038dffb485Schristos const char *verstr = NULL; 3048dffb485Schristos 3054b169a6bSchristos if (asprintf (&str, "Magic number: 0x%x\n", hp->cth_magic) < 0) 3068dffb485Schristos goto err; 3078dffb485Schristos ctf_dump_append (state, str); 3088dffb485Schristos 3098dffb485Schristos if (hp->cth_version <= CTF_VERSION) 3108dffb485Schristos verstr = vertab[hp->cth_version]; 3118dffb485Schristos 3128dffb485Schristos if (verstr == NULL) 3138dffb485Schristos verstr = "(not a valid version)"; 3148dffb485Schristos 3158dffb485Schristos if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version, 3168dffb485Schristos verstr) < 0) 3178dffb485Schristos goto err; 3188dffb485Schristos ctf_dump_append (state, str); 3198dffb485Schristos 3208dffb485Schristos /* Everything else is only printed if present. */ 3218dffb485Schristos 3224b169a6bSchristos /* The flags are unusual in that they represent the ctf_dict_t *in memory*: 3238dffb485Schristos flags representing compression, etc, are turned off as the file is 3248dffb485Schristos decompressed. So we store a copy of the flags before they are changed, for 3258dffb485Schristos the dumper. */ 3268dffb485Schristos 3278dffb485Schristos if (fp->ctf_openflags > 0) 3288dffb485Schristos { 3294b169a6bSchristos if (asprintf (&flagstr, "%s%s%s%s%s%s%s", 3304b169a6bSchristos fp->ctf_openflags & CTF_F_COMPRESS 3314b169a6bSchristos ? "CTF_F_COMPRESS": "", 3324b169a6bSchristos (fp->ctf_openflags & CTF_F_COMPRESS) 3334b169a6bSchristos && (fp->ctf_openflags & ~CTF_F_COMPRESS) 3344b169a6bSchristos ? ", " : "", 3354b169a6bSchristos fp->ctf_openflags & CTF_F_NEWFUNCINFO 3364b169a6bSchristos ? "CTF_F_NEWFUNCINFO" : "", 337*12989c96Schristos (fp->ctf_openflags & (CTF_F_NEWFUNCINFO)) 3384b169a6bSchristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO)) 3394b169a6bSchristos ? ", " : "", 3404b169a6bSchristos fp->ctf_openflags & CTF_F_IDXSORTED 3414b169a6bSchristos ? "CTF_F_IDXSORTED" : "", 342*12989c96Schristos fp->ctf_openflags & (CTF_F_IDXSORTED) 3434b169a6bSchristos && (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO 3444b169a6bSchristos | CTF_F_IDXSORTED)) 3454b169a6bSchristos ? ", " : "", 3464b169a6bSchristos fp->ctf_openflags & CTF_F_DYNSTR 3474b169a6bSchristos ? "CTF_F_DYNSTR" : "") < 0) 3484b169a6bSchristos goto err; 3494b169a6bSchristos 3504b169a6bSchristos if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags, flagstr) < 0) 3518dffb485Schristos goto err; 3528dffb485Schristos ctf_dump_append (state, str); 3538dffb485Schristos } 3548dffb485Schristos 3558dffb485Schristos if (ctf_dump_header_strfield (fp, state, "Parent label", 3568dffb485Schristos hp->cth_parlabel) < 0) 3578dffb485Schristos goto err; 3588dffb485Schristos 3598dffb485Schristos if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0) 3608dffb485Schristos goto err; 3618dffb485Schristos 3628dffb485Schristos if (ctf_dump_header_strfield (fp, state, "Compilation unit name", 3638dffb485Schristos hp->cth_cuname) < 0) 3648dffb485Schristos goto err; 3658dffb485Schristos 3668dffb485Schristos if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff, 3678dffb485Schristos hp->cth_objtoff) < 0) 3688dffb485Schristos goto err; 3698dffb485Schristos 3708dffb485Schristos if (ctf_dump_header_sectfield (fp, state, "Data object section", 3718dffb485Schristos hp->cth_objtoff, hp->cth_funcoff) < 0) 3728dffb485Schristos goto err; 3738dffb485Schristos 3748dffb485Schristos if (ctf_dump_header_sectfield (fp, state, "Function info section", 3754b169a6bSchristos hp->cth_funcoff, hp->cth_objtidxoff) < 0) 3764b169a6bSchristos goto err; 3774b169a6bSchristos 3784b169a6bSchristos if (ctf_dump_header_sectfield (fp, state, "Object index section", 3794b169a6bSchristos hp->cth_objtidxoff, hp->cth_funcidxoff) < 0) 3804b169a6bSchristos goto err; 3814b169a6bSchristos 3824b169a6bSchristos if (ctf_dump_header_sectfield (fp, state, "Function index section", 3834b169a6bSchristos hp->cth_funcidxoff, hp->cth_varoff) < 0) 3848dffb485Schristos goto err; 3858dffb485Schristos 3868dffb485Schristos if (ctf_dump_header_sectfield (fp, state, "Variable section", 3878dffb485Schristos hp->cth_varoff, hp->cth_typeoff) < 0) 3888dffb485Schristos goto err; 3898dffb485Schristos 3908dffb485Schristos if (ctf_dump_header_sectfield (fp, state, "Type section", 3918dffb485Schristos hp->cth_typeoff, hp->cth_stroff) < 0) 3928dffb485Schristos goto err; 3938dffb485Schristos 3948dffb485Schristos if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff, 3958dffb485Schristos hp->cth_stroff + hp->cth_strlen + 1) < 0) 3968dffb485Schristos goto err; 3978dffb485Schristos 3988dffb485Schristos return 0; 3998dffb485Schristos err: 4004b169a6bSchristos free (flagstr); 4018dffb485Schristos return (ctf_set_errno (fp, errno)); 4028dffb485Schristos } 4038dffb485Schristos 4048dffb485Schristos /* Dump a single label into the cds_items. */ 4058dffb485Schristos 4068dffb485Schristos static int 4078dffb485Schristos ctf_dump_label (const char *name, const ctf_lblinfo_t *info, 4088dffb485Schristos void *arg) 4098dffb485Schristos { 4108dffb485Schristos char *str; 4118dffb485Schristos char *typestr; 4128dffb485Schristos ctf_dump_state_t *state = arg; 4138dffb485Schristos 4148dffb485Schristos if (asprintf (&str, "%s -> ", name) < 0) 4158dffb485Schristos return (ctf_set_errno (state->cds_fp, errno)); 4168dffb485Schristos 4178dffb485Schristos if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type, 4184b169a6bSchristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL) 4198dffb485Schristos { 4208dffb485Schristos free (str); 4218dffb485Schristos return 0; /* Swallow the error. */ 4228dffb485Schristos } 4238dffb485Schristos 4248dffb485Schristos str = str_append (str, typestr); 4258dffb485Schristos free (typestr); 4268dffb485Schristos 4278dffb485Schristos ctf_dump_append (state, str); 4288dffb485Schristos return 0; 4298dffb485Schristos } 4308dffb485Schristos 4314b169a6bSchristos /* Dump all the object or function entries into the cds_items. */ 4328dffb485Schristos 4338dffb485Schristos static int 4344b169a6bSchristos ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions) 4358dffb485Schristos { 4364b169a6bSchristos const char *name; 4374b169a6bSchristos ctf_id_t id; 4384b169a6bSchristos ctf_next_t *i = NULL; 4394b169a6bSchristos char *str = NULL; 4408dffb485Schristos 4414b169a6bSchristos if ((functions && fp->ctf_funcidx_names) 4424b169a6bSchristos || (!functions && fp->ctf_objtidx_names)) 4434b169a6bSchristos str = str_append (str, _("Section is indexed.\n")); 444*12989c96Schristos else if (fp->ctf_ext_symtab.cts_data == NULL) 4454b169a6bSchristos str = str_append (str, _("No symbol table.\n")); 4468dffb485Schristos 4474b169a6bSchristos while ((id = ctf_symbol_next (fp, &i, &name, functions)) != CTF_ERR) 4488dffb485Schristos { 4494b169a6bSchristos char *typestr = NULL; 4508dffb485Schristos 4514b169a6bSchristos /* Emit the name, if we know it. No trailing space: ctf_dump_format_type 4524b169a6bSchristos has a leading one. */ 4534b169a6bSchristos if (name) 4548dffb485Schristos { 4554b169a6bSchristos if (asprintf (&str, "%s -> ", name) < 0) 4564b169a6bSchristos goto oom; 4578dffb485Schristos } 4588dffb485Schristos else 4594b169a6bSchristos str = xstrdup (""); 4608dffb485Schristos 4614b169a6bSchristos if ((typestr = ctf_dump_format_type (state->cds_fp, id, 4624b169a6bSchristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL) 4638dffb485Schristos { 4644b169a6bSchristos ctf_dump_append (state, str); 4654b169a6bSchristos continue; /* Swallow the error. */ 4668dffb485Schristos } 4678dffb485Schristos 4688dffb485Schristos str = str_append (str, typestr); 4698dffb485Schristos free (typestr); 4708dffb485Schristos ctf_dump_append (state, str); 4718dffb485Schristos continue; 4728dffb485Schristos 4738dffb485Schristos oom: 4744b169a6bSchristos ctf_set_errno (fp, ENOMEM); 4754b169a6bSchristos ctf_next_destroy (i); 4764b169a6bSchristos return -1; 4778dffb485Schristos } 4788dffb485Schristos return 0; 4798dffb485Schristos } 4808dffb485Schristos 4818dffb485Schristos /* Dump a single variable into the cds_items. */ 4828dffb485Schristos static int 4838dffb485Schristos ctf_dump_var (const char *name, ctf_id_t type, void *arg) 4848dffb485Schristos { 4858dffb485Schristos char *str; 4868dffb485Schristos char *typestr; 4878dffb485Schristos ctf_dump_state_t *state = arg; 4888dffb485Schristos 4898dffb485Schristos if (asprintf (&str, "%s -> ", name) < 0) 4908dffb485Schristos return (ctf_set_errno (state->cds_fp, errno)); 4918dffb485Schristos 4928dffb485Schristos if ((typestr = ctf_dump_format_type (state->cds_fp, type, 4934b169a6bSchristos CTF_ADD_ROOT | CTF_FT_REFS)) == NULL) 4948dffb485Schristos { 4958dffb485Schristos free (str); 4968dffb485Schristos return 0; /* Swallow the error. */ 4978dffb485Schristos } 4988dffb485Schristos 4998dffb485Schristos str = str_append (str, typestr); 5008dffb485Schristos free (typestr); 5018dffb485Schristos 5028dffb485Schristos ctf_dump_append (state, str); 5038dffb485Schristos return 0; 5048dffb485Schristos } 5058dffb485Schristos 5064b169a6bSchristos /* Dump a single struct/union member into the string in the membstate. */ 5078dffb485Schristos static int 5088dffb485Schristos ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset, 5098dffb485Schristos int depth, void *arg) 5108dffb485Schristos { 5118dffb485Schristos ctf_dump_membstate_t *state = arg; 5128dffb485Schristos char *typestr = NULL; 5138dffb485Schristos char *bit = NULL; 5148dffb485Schristos 5154b169a6bSchristos /* The struct/union itself has already been printed. */ 5164b169a6bSchristos if (depth == 0) 5178dffb485Schristos return 0; 5188dffb485Schristos 5194b169a6bSchristos if (asprintf (&bit, "%s%*s", state->cdm_toplevel_indent, (depth-1)*4, "") < 0) 5204b169a6bSchristos goto oom; 5214b169a6bSchristos *state->cdm_str = str_append (*state->cdm_str, bit); 5224b169a6bSchristos free (bit); 5234b169a6bSchristos 5244b169a6bSchristos if ((typestr = ctf_dump_format_type (state->cdm_fp, id, 5254b169a6bSchristos CTF_ADD_ROOT | CTF_FT_BITFIELD 5264b169a6bSchristos | CTF_FT_ID)) == NULL) 5278dffb485Schristos return -1; /* errno is set for us. */ 5288dffb485Schristos 5294b169a6bSchristos if (asprintf (&bit, "[0x%lx] %s: %s\n", offset, name, typestr) < 0) 5308dffb485Schristos goto oom; 5318dffb485Schristos 5328dffb485Schristos *state->cdm_str = str_append (*state->cdm_str, bit); 5338dffb485Schristos free (typestr); 5348dffb485Schristos free (bit); 5358dffb485Schristos typestr = NULL; 5368dffb485Schristos bit = NULL; 5378dffb485Schristos 5388dffb485Schristos return 0; 5398dffb485Schristos 5408dffb485Schristos oom: 5418dffb485Schristos free (typestr); 5428dffb485Schristos free (bit); 5438dffb485Schristos return (ctf_set_errno (state->cdm_fp, errno)); 5448dffb485Schristos } 5458dffb485Schristos 5464b169a6bSchristos /* Report the number of digits in the hexadecimal representation of a type 5474b169a6bSchristos ID. */ 5484b169a6bSchristos 5494b169a6bSchristos static int 5504b169a6bSchristos type_hex_digits (ctf_id_t id) 5514b169a6bSchristos { 5524b169a6bSchristos int i = 0; 5534b169a6bSchristos 5544b169a6bSchristos if (id == 0) 5554b169a6bSchristos return 1; 5564b169a6bSchristos 5574b169a6bSchristos for (; id > 0; id >>= 4, i++); 5584b169a6bSchristos return i; 5594b169a6bSchristos } 5604b169a6bSchristos 5618dffb485Schristos /* Dump a single type into the cds_items. */ 5628dffb485Schristos static int 5638dffb485Schristos ctf_dump_type (ctf_id_t id, int flag, void *arg) 5648dffb485Schristos { 5658dffb485Schristos char *str; 5664b169a6bSchristos char *indent; 5678dffb485Schristos ctf_dump_state_t *state = arg; 5684b169a6bSchristos ctf_dump_membstate_t membstate = { &str, state->cds_fp, NULL }; 5698dffb485Schristos 5704b169a6bSchristos /* Indent neatly. */ 5714b169a6bSchristos if (asprintf (&indent, " %*s", type_hex_digits (id), "") < 0) 5724b169a6bSchristos return (ctf_set_errno (state->cds_fp, ENOMEM)); 5734b169a6bSchristos 5744b169a6bSchristos /* Dump the type itself. */ 5754b169a6bSchristos if ((str = ctf_dump_format_type (state->cds_fp, id, 5764b169a6bSchristos flag | CTF_FT_REFS)) == NULL) 5778dffb485Schristos goto err; 5788dffb485Schristos str = str_append (str, "\n"); 5794b169a6bSchristos 5804b169a6bSchristos membstate.cdm_toplevel_indent = indent; 5814b169a6bSchristos 5824b169a6bSchristos /* Member dumping for structs, unions... */ 5834b169a6bSchristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_STRUCT 5844b169a6bSchristos || ctf_type_kind (state->cds_fp, id) == CTF_K_UNION) 5854b169a6bSchristos { 5868dffb485Schristos if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0) 5878dffb485Schristos { 5888dffb485Schristos if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE) 5898dffb485Schristos { 5908dffb485Schristos ctf_dump_append (state, str); 5918dffb485Schristos return 0; 5928dffb485Schristos } 5938dffb485Schristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp), 5948dffb485Schristos _("cannot visit members dumping type 0x%lx"), id); 5958dffb485Schristos goto err; 5968dffb485Schristos } 5974b169a6bSchristos } 5988dffb485Schristos 5994b169a6bSchristos /* ... and enums, for which we dump the first and last few members and skip 6004b169a6bSchristos the ones in the middle. */ 6014b169a6bSchristos if (ctf_type_kind (state->cds_fp, id) == CTF_K_ENUM) 6024b169a6bSchristos { 6034b169a6bSchristos int enum_count = ctf_member_count (state->cds_fp, id); 6044b169a6bSchristos ctf_next_t *it = NULL; 6054b169a6bSchristos int i = 0; 6064b169a6bSchristos const char *enumerand; 6074b169a6bSchristos char *bit; 6084b169a6bSchristos int value; 6094b169a6bSchristos 6104b169a6bSchristos while ((enumerand = ctf_enum_next (state->cds_fp, id, 6114b169a6bSchristos &it, &value)) != NULL) 6124b169a6bSchristos { 6134b169a6bSchristos i++; 6144b169a6bSchristos if ((i > 5) && (i < enum_count - 4)) 6154b169a6bSchristos continue; 6164b169a6bSchristos 6174b169a6bSchristos str = str_append (str, indent); 6184b169a6bSchristos 6194b169a6bSchristos if (asprintf (&bit, "%s: %i\n", enumerand, value) < 0) 6204b169a6bSchristos { 6214b169a6bSchristos ctf_next_destroy (it); 6224b169a6bSchristos goto oom; 6234b169a6bSchristos } 6244b169a6bSchristos str = str_append (str, bit); 6254b169a6bSchristos free (bit); 6264b169a6bSchristos 6274b169a6bSchristos if ((i == 5) && (enum_count > 10)) 6284b169a6bSchristos { 6294b169a6bSchristos str = str_append (str, indent); 6304b169a6bSchristos str = str_append (str, "...\n"); 6314b169a6bSchristos } 6324b169a6bSchristos } 6334b169a6bSchristos if (ctf_errno (state->cds_fp) != ECTF_NEXT_END) 6344b169a6bSchristos { 6354b169a6bSchristos ctf_err_warn (state->cds_fp, 1, ctf_errno (state->cds_fp), 6364b169a6bSchristos _("cannot visit enumerands dumping type 0x%lx"), id); 6374b169a6bSchristos goto err; 6384b169a6bSchristos } 6394b169a6bSchristos } 6408dffb485Schristos 6418dffb485Schristos ctf_dump_append (state, str); 6424b169a6bSchristos free (indent); 6434b169a6bSchristos 6448dffb485Schristos return 0; 6458dffb485Schristos 6468dffb485Schristos err: 6474b169a6bSchristos free (indent); 6488dffb485Schristos free (str); 6494b169a6bSchristos 6504b169a6bSchristos /* Swallow the error: don't cause an error in one type to abort all 6514b169a6bSchristos type dumping. */ 6524b169a6bSchristos return 0; 6534b169a6bSchristos 6544b169a6bSchristos oom: 6554b169a6bSchristos free (indent); 6564b169a6bSchristos free (str); 6574b169a6bSchristos return ctf_set_errno (state->cds_fp, ENOMEM); 6588dffb485Schristos } 6598dffb485Schristos 6608dffb485Schristos /* Dump the string table into the cds_items. */ 6618dffb485Schristos 6628dffb485Schristos static int 6634b169a6bSchristos ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state) 6648dffb485Schristos { 6658dffb485Schristos const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs; 6668dffb485Schristos 6678dffb485Schristos for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs + 6688dffb485Schristos fp->ctf_str[CTF_STRTAB_0].cts_len;) 6698dffb485Schristos { 6708dffb485Schristos char *str; 6714b169a6bSchristos if (asprintf (&str, "0x%lx: %s", 6728dffb485Schristos (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs), 6738dffb485Schristos s) < 0) 6748dffb485Schristos return (ctf_set_errno (fp, errno)); 6758dffb485Schristos ctf_dump_append (state, str); 6768dffb485Schristos s += strlen (s) + 1; 6778dffb485Schristos } 6788dffb485Schristos 6798dffb485Schristos return 0; 6808dffb485Schristos } 6818dffb485Schristos 6828dffb485Schristos /* Dump a particular section of a CTF file, in textual form. Call with a 6838dffb485Schristos pointer to a NULL STATE: each call emits a dynamically allocated string 6848dffb485Schristos containing a description of one entity in the specified section, in order. 6858dffb485Schristos Only the first call (with a NULL state) may vary SECT. Once the CTF section 6868dffb485Schristos has been entirely dumped, the call returns NULL and frees and annuls the 6878dffb485Schristos STATE, ready for another section to be dumped. The returned textual content 6888dffb485Schristos may span multiple lines: between each call the FUNC is called with one 6898dffb485Schristos textual line at a time, and should return a suitably decorated line (it can 6908dffb485Schristos allocate a new one and return it if it likes). */ 6918dffb485Schristos 6928dffb485Schristos char * 6934b169a6bSchristos ctf_dump (ctf_dict_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect, 6948dffb485Schristos ctf_dump_decorate_f *func, void *arg) 6958dffb485Schristos { 6968dffb485Schristos char *str; 6978dffb485Schristos char *line; 6988dffb485Schristos ctf_dump_state_t *state = NULL; 6998dffb485Schristos 7008dffb485Schristos if (*statep == NULL) 7018dffb485Schristos { 7028dffb485Schristos /* Data collection. Transforming a call-at-a-time iterator into a 7038dffb485Schristos return-at-a-time iterator in a language without call/cc is annoying. It 7048dffb485Schristos is easiest to simply collect everything at once and then return it bit 7058dffb485Schristos by bit. The first call will take (much) longer than otherwise, but the 7068dffb485Schristos amortized time needed is the same. */ 7078dffb485Schristos 7088dffb485Schristos if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL) 7098dffb485Schristos { 7108dffb485Schristos ctf_set_errno (fp, ENOMEM); 7118dffb485Schristos goto end; 7128dffb485Schristos } 7138dffb485Schristos state = *statep; 7148dffb485Schristos 7158dffb485Schristos memset (state, 0, sizeof (struct ctf_dump_state)); 7168dffb485Schristos state->cds_fp = fp; 7178dffb485Schristos state->cds_sect = sect; 7188dffb485Schristos 7198dffb485Schristos switch (sect) 7208dffb485Schristos { 7218dffb485Schristos case CTF_SECT_HEADER: 7228dffb485Schristos ctf_dump_header (fp, state); 7238dffb485Schristos break; 7248dffb485Schristos case CTF_SECT_LABEL: 7258dffb485Schristos if (ctf_label_iter (fp, ctf_dump_label, state) < 0) 7268dffb485Schristos { 7278dffb485Schristos if (ctf_errno (fp) != ECTF_NOLABELDATA) 7288dffb485Schristos goto end; /* errno is set for us. */ 7298dffb485Schristos ctf_set_errno (fp, 0); 7308dffb485Schristos } 7318dffb485Schristos break; 7328dffb485Schristos case CTF_SECT_OBJT: 7334b169a6bSchristos if (ctf_dump_objts (fp, state, 0) < 0) 7348dffb485Schristos goto end; /* errno is set for us. */ 7358dffb485Schristos break; 7368dffb485Schristos case CTF_SECT_FUNC: 7374b169a6bSchristos if (ctf_dump_objts (fp, state, 1) < 0) 7388dffb485Schristos goto end; /* errno is set for us. */ 7398dffb485Schristos break; 7408dffb485Schristos case CTF_SECT_VAR: 7418dffb485Schristos if (ctf_variable_iter (fp, ctf_dump_var, state) < 0) 7428dffb485Schristos goto end; /* errno is set for us. */ 7438dffb485Schristos break; 7448dffb485Schristos case CTF_SECT_TYPE: 7458dffb485Schristos if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0) 7468dffb485Schristos goto end; /* errno is set for us. */ 7478dffb485Schristos break; 7488dffb485Schristos case CTF_SECT_STR: 7498dffb485Schristos ctf_dump_str (fp, state); 7508dffb485Schristos break; 7518dffb485Schristos default: 7528dffb485Schristos ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN); 7538dffb485Schristos goto end; 7548dffb485Schristos } 7558dffb485Schristos } 7568dffb485Schristos else 7578dffb485Schristos { 7588dffb485Schristos state = *statep; 7598dffb485Schristos 7608dffb485Schristos if (state->cds_sect != sect) 7618dffb485Schristos { 7628dffb485Schristos ctf_set_errno (fp, ECTF_DUMPSECTCHANGED); 7638dffb485Schristos goto end; 7648dffb485Schristos } 7658dffb485Schristos } 7668dffb485Schristos 7678dffb485Schristos if (state->cds_current == NULL) 7688dffb485Schristos state->cds_current = ctf_list_next (&state->cds_items); 7698dffb485Schristos else 7708dffb485Schristos state->cds_current = ctf_list_next (state->cds_current); 7718dffb485Schristos 7728dffb485Schristos if (state->cds_current == NULL) 7738dffb485Schristos goto end; 7748dffb485Schristos 7758dffb485Schristos /* Hookery. There is some extra complexity to preserve linefeeds within each 7768dffb485Schristos item while removing linefeeds at the end. */ 7778dffb485Schristos if (func) 7788dffb485Schristos { 7798dffb485Schristos size_t len; 7808dffb485Schristos 7818dffb485Schristos str = NULL; 7828dffb485Schristos for (line = state->cds_current->cdi_item; line && *line; ) 7838dffb485Schristos { 7848dffb485Schristos char *nline = line; 7858dffb485Schristos char *ret; 7868dffb485Schristos 7878dffb485Schristos nline = strchr (line, '\n'); 7888dffb485Schristos if (nline) 7898dffb485Schristos nline[0] = '\0'; 7908dffb485Schristos 7918dffb485Schristos ret = func (sect, line, arg); 7928dffb485Schristos str = str_append (str, ret); 7938dffb485Schristos str = str_append (str, "\n"); 7948dffb485Schristos if (ret != line) 7958dffb485Schristos free (ret); 7968dffb485Schristos 7978dffb485Schristos if (nline) 7988dffb485Schristos { 7998dffb485Schristos nline[0] = '\n'; 8008dffb485Schristos nline++; 8018dffb485Schristos } 8028dffb485Schristos 8038dffb485Schristos line = nline; 8048dffb485Schristos } 8058dffb485Schristos 8068dffb485Schristos len = strlen (str); 8078dffb485Schristos 8088dffb485Schristos if (str[len-1] == '\n') 8098dffb485Schristos str[len-1] = '\0'; 8108dffb485Schristos } 8118dffb485Schristos else 8128dffb485Schristos { 8138dffb485Schristos str = strdup (state->cds_current->cdi_item); 8148dffb485Schristos if (!str) 8158dffb485Schristos { 8168dffb485Schristos ctf_set_errno (fp, ENOMEM); 8178dffb485Schristos return str; 8188dffb485Schristos } 8198dffb485Schristos } 8208dffb485Schristos 8218dffb485Schristos ctf_set_errno (fp, 0); 8228dffb485Schristos return str; 8238dffb485Schristos 8248dffb485Schristos end: 8258dffb485Schristos ctf_dump_free (state); 8268dffb485Schristos free (state); 8278dffb485Schristos ctf_set_errno (fp, 0); 8288dffb485Schristos *statep = NULL; 8298dffb485Schristos return NULL; 8308dffb485Schristos } 831