X-Git-Url: https://repo.jachan.dev/binutils.git/blobdiff_plain/143dce8481f09f60704ab52b98cf8fe6d8b29fc9..HEAD:/libctf/ctf-open.c diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index 5230d09a97..f0e203e0a1 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -1,5 +1,5 @@ /* Opening CTF files. - Copyright (C) 2019 Free Software Foundation, Inc. + Copyright (C) 2019-2022 Free Software Foundation, Inc. This file is part of libctf. @@ -22,13 +22,10 @@ #include #include #include -#include #include "swap.h" #include #include -#include "elf-bfd.h" - static const ctf_dmodel_t _libctf_models[] = { {"ILP32", CTF_MODEL_ILP32, 4, 1, 2, 4, 4}, {"LP64", CTF_MODEL_LP64, 8, 1, 2, 4, 8}, @@ -77,7 +74,7 @@ get_vlen_v2 (uint32_t info) } static inline ssize_t -get_ctt_size_common (const ctf_file_t *fp _libctf_unused_, +get_ctt_size_common (const ctf_dict_t *fp _libctf_unused_, const ctf_type_t *tp _libctf_unused_, ssize_t *sizep, ssize_t *incrementp, size_t lsize, size_t csize, size_t ctf_type_size, @@ -105,7 +102,7 @@ get_ctt_size_common (const ctf_file_t *fp _libctf_unused_, } static ssize_t -get_ctt_size_v1 (const ctf_file_t *fp, const ctf_type_t *tp, +get_ctt_size_v1 (const ctf_dict_t *fp, const ctf_type_t *tp, ssize_t *sizep, ssize_t *incrementp) { ctf_type_v1_t *t1p = (ctf_type_v1_t *) tp; @@ -119,7 +116,7 @@ get_ctt_size_v1 (const ctf_file_t *fp, const ctf_type_t *tp, /* Return the size that a v1 will be once it is converted to v2. */ static ssize_t -get_ctt_size_v2_unconverted (const ctf_file_t *fp, const ctf_type_t *tp, +get_ctt_size_v2_unconverted (const ctf_dict_t *fp, const ctf_type_t *tp, ssize_t *sizep, ssize_t *incrementp) { ctf_type_v1_t *t1p = (ctf_type_v1_t *) tp; @@ -131,7 +128,7 @@ get_ctt_size_v2_unconverted (const ctf_file_t *fp, const ctf_type_t *tp, } static ssize_t -get_ctt_size_v2 (const ctf_file_t *fp, const ctf_type_t *tp, +get_ctt_size_v2 (const ctf_dict_t *fp, const ctf_type_t *tp, ssize_t *sizep, ssize_t *incrementp) { return (get_ctt_size_common (fp, tp, sizep, incrementp, @@ -141,8 +138,8 @@ get_ctt_size_v2 (const ctf_file_t *fp, const ctf_type_t *tp, } static ssize_t -get_vbytes_common (unsigned short kind, ssize_t size _libctf_unused_, - size_t vlen) +get_vbytes_common (ctf_dict_t *fp, unsigned short kind, + ssize_t size _libctf_unused_, size_t vlen) { switch (kind) { @@ -150,8 +147,7 @@ get_vbytes_common (unsigned short kind, ssize_t size _libctf_unused_, case CTF_K_FLOAT: return (sizeof (uint32_t)); case CTF_K_SLICE: - return (offsetof (ctf_slice_t, cts_bits) + - sizeof (((ctf_slice_t *)0)->cts_bits)); + return (sizeof (ctf_slice_t)); case CTF_K_ENUM: return (sizeof (ctf_enum_t) * vlen); case CTF_K_FORWARD: @@ -163,13 +159,14 @@ get_vbytes_common (unsigned short kind, ssize_t size _libctf_unused_, case CTF_K_RESTRICT: return 0; default: - ctf_dprintf ("detected invalid CTF kind -- %x\n", kind); - return ECTF_CORRUPT; + ctf_set_errno (fp, ECTF_CORRUPT); + ctf_err_warn (fp, 0, 0, _("detected invalid CTF kind: %x"), kind); + return -1; } } static ssize_t -get_vbytes_v1 (unsigned short kind, ssize_t size, size_t vlen) +get_vbytes_v1 (ctf_dict_t *fp, unsigned short kind, ssize_t size, size_t vlen) { switch (kind) { @@ -185,11 +182,11 @@ get_vbytes_v1 (unsigned short kind, ssize_t size, size_t vlen) return (sizeof (ctf_lmember_v1_t) * vlen); } - return (get_vbytes_common (kind, size, vlen)); + return (get_vbytes_common (fp, kind, size, vlen)); } static ssize_t -get_vbytes_v2 (unsigned short kind, ssize_t size, size_t vlen) +get_vbytes_v2 (ctf_dict_t *fp, unsigned short kind, ssize_t size, size_t vlen) { switch (kind) { @@ -205,10 +202,10 @@ get_vbytes_v2 (unsigned short kind, ssize_t size, size_t vlen) return (sizeof (ctf_lmember_t) * vlen); } - return (get_vbytes_common (kind, size, vlen)); + return (get_vbytes_common (fp, kind, size, vlen)); } -static const ctf_fileops_t ctf_fileops[] = { +static const ctf_dictops_t ctf_dictops[] = { {NULL, NULL, NULL, NULL, NULL}, /* CTF_VERSION_1 */ {get_kind_v1, get_root_v1, get_vlen_v1, get_ctt_size_v1, get_vbytes_v1}, @@ -220,55 +217,95 @@ static const ctf_fileops_t ctf_fileops[] = { {get_kind_v2, get_root_v2, get_vlen_v2, get_ctt_size_v2, get_vbytes_v2}, }; -/* Initialize the symtab translation table by filling each entry with the - offset of the CTF type or function data corresponding to each STT_FUNC or - STT_OBJECT entry in the symbol table. */ +/* Initialize the symtab translation table as appropriate for its indexing + state. For unindexed symtypetabs, fill each entry with the offset of the CTF + type or function data corresponding to each STT_FUNC or STT_OBJECT entry in + the symbol table. For indexed symtypetabs, do nothing: the needed + initialization for indexed lookups may be quite expensive, so it is done only + as needed, when lookups happen. (In particular, the majority of indexed + symtypetabs come from the compiler, and all the linker does is iteration over + all entries, which doesn't need this initialization.) + + The SP symbol table section may be NULL if there is no symtab. + + If init_symtab works on one call, it cannot fail on future calls to the same + fp: ctf_symsect_endianness relies on this. */ static int -init_symtab (ctf_file_t *fp, const ctf_header_t *hp, - const ctf_sect_t *sp, const ctf_sect_t *strp) +init_symtab (ctf_dict_t *fp, const ctf_header_t *hp, const ctf_sect_t *sp) { - const unsigned char *symp = sp->cts_data; + const unsigned char *symp; + int skip_func_info = 0; + int i; uint32_t *xp = fp->ctf_sxlate; - uint32_t *xend = xp + fp->ctf_nsyms; + uint32_t *xend = PTR_ADD (xp, fp->ctf_nsyms); uint32_t objtoff = hp->cth_objtoff; uint32_t funcoff = hp->cth_funcoff; - uint32_t info, vlen; - Elf64_Sym sym, *gsp; - const char *name; - - /* The CTF data object and function type sections are ordered to match - the relative order of the respective symbol types in the symtab. - If no type information is available for a symbol table entry, a - pad is inserted in the CTF section. As a further optimization, - anonymous or undefined symbols are omitted from the CTF data. */ - - for (; xp < xend; xp++, symp += sp->cts_entsize) + /* If the CTF_F_NEWFUNCINFO flag is not set, pretend the func info section + is empty: this compiler is too old to emit a function info section we + understand. */ + + if (!(hp->cth_flags & CTF_F_NEWFUNCINFO)) + skip_func_info = 1; + + if (hp->cth_objtidxoff < hp->cth_funcidxoff) + fp->ctf_objtidx_names = (uint32_t *) (fp->ctf_buf + hp->cth_objtidxoff); + if (hp->cth_funcidxoff < hp->cth_varoff && !skip_func_info) + fp->ctf_funcidx_names = (uint32_t *) (fp->ctf_buf + hp->cth_funcidxoff); + + /* Don't bother doing the rest if everything is indexed, or if we don't have a + symbol table: we will never use it. */ + if ((fp->ctf_objtidx_names && fp->ctf_funcidx_names) || !sp || !sp->cts_data) + return 0; + + /* The CTF data object and function type sections are ordered to match the + relative order of the respective symbol types in the symtab, unless there + is an index section, in which case the order is arbitrary and the index + gives the mapping. If no type information is available for a symbol table + entry, a pad is inserted in the CTF section. As a further optimization, + anonymous or undefined symbols are omitted from the CTF data. If an + index is available for function symbols but not object symbols, or vice + versa, we populate the xslate table for the unindexed symbols only. */ + + for (i = 0, symp = sp->cts_data; xp < xend; xp++, symp += sp->cts_entsize, + i++) { - if (sp->cts_entsize == sizeof (Elf32_Sym)) - gsp = ctf_sym_to_elf64 ((Elf32_Sym *) (uintptr_t) symp, &sym); - else - gsp = (Elf64_Sym *) (uintptr_t) symp; + ctf_link_sym_t sym; - if (gsp->st_name < strp->cts_size) - name = (const char *) strp->cts_data + gsp->st_name; - else - name = _CTF_NULLSTR; + switch (sp->cts_entsize) + { + case sizeof (Elf64_Sym): + { + const Elf64_Sym *symp64 = (Elf64_Sym *) (uintptr_t) symp; + ctf_elf64_to_link_sym (fp, &sym, symp64, i); + } + break; + case sizeof (Elf32_Sym): + { + const Elf32_Sym *symp32 = (Elf32_Sym *) (uintptr_t) symp; + ctf_elf32_to_link_sym (fp, &sym, symp32, i); + } + break; + default: + return ECTF_SYMTAB; + } - if (gsp->st_name == 0 || gsp->st_shndx == SHN_UNDEF - || strcmp (name, "_START_") == 0 || strcmp (name, "_END_") == 0) + /* This call may be led astray if our idea of the symtab's endianness is + wrong, but when this is fixed by a call to ctf_symsect_endianness, + init_symtab will be called again with the right endianness in + force. */ + if (ctf_symtab_skippable (&sym)) { *xp = -1u; continue; } - switch (ELF64_ST_TYPE (gsp->st_info)) + switch (sym.st_type) { case STT_OBJECT: - if (objtoff >= hp->cth_funcoff - || (gsp->st_shndx == SHN_EXTABS && gsp->st_value == 0)) + if (fp->ctf_objtidx_names || objtoff >= hp->cth_funcoff) { *xp = -1u; break; @@ -279,25 +316,15 @@ init_symtab (ctf_file_t *fp, const ctf_header_t *hp, break; case STT_FUNC: - if (funcoff >= hp->cth_typeoff) + if (fp->ctf_funcidx_names || funcoff >= hp->cth_objtidxoff + || skip_func_info) { *xp = -1u; break; } *xp = funcoff; - - info = *(uint32_t *) ((uintptr_t) fp->ctf_buf + funcoff); - vlen = LCTF_INFO_VLEN (fp, info); - - /* If we encounter a zero pad at the end, just skip it. Otherwise - skip over the function and its return type (+2) and the argument - list (vlen). - */ - if (LCTF_INFO_KIND (fp, info) == CTF_K_UNKNOWN && vlen == 0) - funcoff += sizeof (uint32_t); /* Skip pad. */ - else - funcoff += sizeof (uint32_t) * (vlen + 2); + funcoff += sizeof (uint32_t); break; default: @@ -310,14 +337,18 @@ init_symtab (ctf_file_t *fp, const ctf_header_t *hp, return 0; } -/* Set the CTF base pointer and derive the buf pointer from it, initializing - everything in the ctf_file that depends on the base or buf pointers. */ +/* Reset the CTF base pointer and derive the buf pointer from it, initializing + everything in the ctf_dict that depends on the base or buf pointers. + + The original gap between the buf and base pointers, if any -- the original, + unconverted CTF header -- is kept, but its contents are not specified and are + never used. */ static void -ctf_set_base (ctf_file_t *fp, const ctf_header_t *hp, void *base) +ctf_set_base (ctf_dict_t *fp, const ctf_header_t *hp, unsigned char *base) { + fp->ctf_buf = base + (fp->ctf_buf - fp->ctf_base); fp->ctf_base = base; - fp->ctf_buf = fp->ctf_base + sizeof (ctf_header_t); fp->ctf_vars = (ctf_varent_t *) ((const char *) fp->ctf_buf + hp->cth_varoff); fp->ctf_nvars = (hp->cth_typeoff - hp->cth_varoff) / sizeof (ctf_varent_t); @@ -326,8 +357,8 @@ ctf_set_base (ctf_file_t *fp, const ctf_header_t *hp, void *base) + hp->cth_stroff; fp->ctf_str[CTF_STRTAB_0].cts_len = hp->cth_strlen; - /* If we have a parent container name and label, store the relocated - string pointers in the CTF container for easy access later. */ + /* If we have a parent dict name and label, store the relocated string + pointers in the CTF dict for easy access later. */ /* Note: before conversion, these will be set to values that will be immediately invalidated by the conversion process, but the conversion @@ -337,34 +368,17 @@ ctf_set_base (ctf_file_t *fp, const ctf_header_t *hp, void *base) fp->ctf_parlabel = ctf_strptr (fp, hp->cth_parlabel); if (hp->cth_parname != 0) fp->ctf_parname = ctf_strptr (fp, hp->cth_parname); - - ctf_dprintf ("ctf_set_base: parent name %s (label %s)\n", - fp->ctf_parname ? fp->ctf_parname : "", + if (hp->cth_cuname != 0) + fp->ctf_cuname = ctf_strptr (fp, hp->cth_cuname); + + if (fp->ctf_cuname) + ctf_dprintf ("ctf_set_base: CU name %s\n", fp->ctf_cuname); + if (fp->ctf_parname) + ctf_dprintf ("ctf_set_base: parent name %s (label %s)\n", + fp->ctf_parname, fp->ctf_parlabel ? fp->ctf_parlabel : ""); } -/* Free a ctf_base pointer: the pointer passed, or (if NULL) fp->ctf_base. */ -static void -ctf_free_base (ctf_file_t *fp, unsigned char *ctf_base, size_t ctf_size) -{ - unsigned char *base; - size_t size; - - if (ctf_base) - { - base = ctf_base; - size = ctf_size; - } - else - { - base = (unsigned char *) fp->ctf_base; - size = fp->ctf_size; - } - - if (base != fp->ctf_data.cts_data && base != NULL) - ctf_data_free (base, size); -} - /* Set the version of the CTF file. */ /* When this is reset, LCTF_* changes behaviour, but there is no guarantee that @@ -372,33 +386,54 @@ ctf_free_base (ctf_file_t *fp, unsigned char *ctf_base, size_t ctf_size) caller must ensure this has been done in advance. */ static void -ctf_set_version (ctf_file_t * fp, ctf_header_t * cth, int ctf_version) +ctf_set_version (ctf_dict_t *fp, ctf_header_t *cth, int ctf_version) { fp->ctf_version = ctf_version; cth->cth_version = ctf_version; - fp->ctf_fileops = &ctf_fileops[ctf_version]; + fp->ctf_dictops = &ctf_dictops[ctf_version]; } -/* Upgrade the type table to CTF_VERSION_3 (really CTF_VERSION_1_UPGRADED_3). + +/* Upgrade the header to CTF_VERSION_3. The upgrade is done in-place. */ +static void +upgrade_header (ctf_header_t *hp) +{ + ctf_header_v2_t *oldhp = (ctf_header_v2_t *) hp; + + hp->cth_strlen = oldhp->cth_strlen; + hp->cth_stroff = oldhp->cth_stroff; + hp->cth_typeoff = oldhp->cth_typeoff; + hp->cth_varoff = oldhp->cth_varoff; + hp->cth_funcidxoff = hp->cth_varoff; /* No index sections. */ + hp->cth_objtidxoff = hp->cth_funcidxoff; + hp->cth_funcoff = oldhp->cth_funcoff; + hp->cth_objtoff = oldhp->cth_objtoff; + hp->cth_lbloff = oldhp->cth_lbloff; + hp->cth_cuname = 0; /* No CU name. */ +} + +/* Upgrade the type table to CTF_VERSION_3 (really CTF_VERSION_1_UPGRADED_3) + from CTF_VERSION_1. The upgrade is not done in-place: the ctf_base is moved. ctf_strptr() must not be called before reallocation is complete. + Sections not checked here due to nonexistence or nonpopulated state in older + formats: objtidx, funcidx. + Type kinds not checked here due to nonexistence in older formats: CTF_K_SLICE. */ static int -upgrade_types (ctf_file_t *fp, ctf_header_t *cth) +upgrade_types_v1 (ctf_dict_t *fp, ctf_header_t *cth) { const ctf_type_v1_t *tbuf; const ctf_type_v1_t *tend; - unsigned char *ctf_base, *old_ctf_base = (unsigned char *) fp->ctf_base; - size_t old_ctf_size = fp->ctf_size; + unsigned char *ctf_base, *old_ctf_base = (unsigned char *) fp->ctf_dynbase; ctf_type_t *t2buf; ssize_t increase = 0, size, increment, v2increment, vbytes, v2bytes; const ctf_type_v1_t *tp; ctf_type_t *t2p; - ctf_header_t *new_cth; tbuf = (ctf_type_v1_t *) (fp->ctf_buf + cth->cth_typeoff); tend = (ctf_type_v1_t *) (fp->ctf_buf + cth->cth_stroff); @@ -421,11 +456,11 @@ upgrade_types (ctf_file_t *fp, ctf_header_t *cth) unsigned long vlen = CTF_V1_INFO_VLEN (tp->ctt_info); size = get_ctt_size_v1 (fp, (const ctf_type_t *) tp, NULL, &increment); - vbytes = get_vbytes_v1 (kind, size, vlen); + vbytes = get_vbytes_v1 (fp, kind, size, vlen); get_ctt_size_v2_unconverted (fp, (const ctf_type_t *) tp, NULL, &v2increment); - v2bytes = get_vbytes_v2 (kind, size, vlen); + v2bytes = get_vbytes_v2 (fp, kind, size, vlen); if ((vbytes < 0) || (size < 0)) return ECTF_CORRUPT; @@ -434,35 +469,33 @@ upgrade_types (ctf_file_t *fp, ctf_header_t *cth) increase += v2bytes - vbytes; } - /* Allocate enough room for the new buffer, then copy everything but the - type section into place, and reset the base accordingly. Leave the - version number unchanged, so that LCTF_INFO_* still works on the + /* Allocate enough room for the new buffer, then copy everything but the type + section into place, and reset the base accordingly. Leave the version + number unchanged, so that LCTF_INFO_* still works on the as-yet-untranslated type info. */ - if ((ctf_base = ctf_data_alloc (fp->ctf_size + increase)) == NULL) + if ((ctf_base = malloc (fp->ctf_size + increase)) == NULL) return ECTF_ZALLOC; - memcpy (ctf_base, fp->ctf_base, sizeof (ctf_header_t) + cth->cth_typeoff); - memcpy (ctf_base + sizeof (ctf_header_t) + cth->cth_stroff + increase, - fp->ctf_base + sizeof (ctf_header_t) + cth->cth_stroff, - cth->cth_strlen); + /* Start at ctf_buf, not ctf_base, to squeeze out the original header: we + never use it and it is unconverted. */ - memset (ctf_base + sizeof (ctf_header_t) + cth->cth_typeoff, 0, - cth->cth_stroff - cth->cth_typeoff + increase); + memcpy (ctf_base, fp->ctf_buf, cth->cth_typeoff); + memcpy (ctf_base + cth->cth_stroff + increase, + fp->ctf_buf + cth->cth_stroff, cth->cth_strlen); - /* The cth here is an automatic variable in ctf_bufopen(), and transient - (a copy maintained because at that stage the header read out of the - ctf file may be read-only). We make all modifications in the - canonical copy at ctf_base (by now, writable), then copy it back into - cth at the end. */ + memset (ctf_base + cth->cth_typeoff, 0, cth->cth_stroff - cth->cth_typeoff + + increase); - new_cth = (ctf_header_t *) ctf_base; - new_cth->cth_stroff += increase; + cth->cth_stroff += increase; fp->ctf_size += increase; - assert (new_cth->cth_stroff >= new_cth->cth_typeoff); - ctf_set_base (fp, new_cth, ctf_base); + assert (cth->cth_stroff >= cth->cth_typeoff); + fp->ctf_base = ctf_base; + fp->ctf_buf = ctf_base; + fp->ctf_dynbase = ctf_base; + ctf_set_base (fp, cth, ctf_base); - t2buf = (ctf_type_t *) (fp->ctf_buf + new_cth->cth_typeoff); + t2buf = (ctf_type_t *) (fp->ctf_buf + cth->cth_typeoff); /* Iterate through all the types again, upgrading them. @@ -480,7 +513,7 @@ upgrade_types (ctf_file_t *fp, ctf_header_t *cth) void *vdata, *v2data; size = get_ctt_size_v1 (fp, (const ctf_type_t *) tp, NULL, &increment); - vbytes = get_vbytes_v1 (kind, size, vlen); + vbytes = get_vbytes_v1 (fp, kind, size, vlen); t2p->ctt_name = tp->ctt_name; t2p->ctt_info = CTF_TYPE_INFO (kind, isroot, vlen); @@ -503,7 +536,7 @@ upgrade_types (ctf_file_t *fp, ctf_header_t *cth) case CTF_K_UNION: case CTF_K_ENUM: case CTF_K_UNKNOWN: - if (size <= CTF_MAX_SIZE) + if ((size_t) size <= CTF_MAX_SIZE) t2p->ctt_size = size; else { @@ -514,7 +547,7 @@ upgrade_types (ctf_file_t *fp, ctf_header_t *cth) } v2size = get_ctt_size_v2 (fp, t2p, NULL, &v2increment); - v2bytes = get_vbytes_v2 (kind, v2size, vlen); + v2bytes = get_vbytes_v2 (fp, kind, v2size, vlen); /* Catch out-of-sync get_ctt_size_*(). The count goes wrong if these are not identical (and having them different makes no @@ -605,12 +638,35 @@ upgrade_types (ctf_file_t *fp, ctf_header_t *cth) converting too much, or too little (leading to a buffer overrun either here or at read time, in init_types().) */ - assert ((size_t) t2p - (size_t) fp->ctf_buf == new_cth->cth_stroff); + assert ((size_t) t2p - (size_t) fp->ctf_buf == cth->cth_stroff); + + ctf_set_version (fp, cth, CTF_VERSION_1_UPGRADED_3); + free (old_ctf_base); - ctf_set_version (fp, (ctf_header_t *) ctf_base, CTF_VERSION_1_UPGRADED_3); - ctf_free_base (fp, old_ctf_base, old_ctf_size); - memcpy (cth, new_cth, sizeof (ctf_header_t)); + return 0; +} +/* Upgrade from any earlier version. */ +static int +upgrade_types (ctf_dict_t *fp, ctf_header_t *cth) +{ + switch (cth->cth_version) + { + /* v1 requires a full pass and reformatting. */ + case CTF_VERSION_1: + upgrade_types_v1 (fp, cth); + /* FALLTHRU */ + /* Already-converted v1 is just like later versions except that its + parent/child boundary is unchanged (and much lower). */ + + case CTF_VERSION_1_UPGRADED_3: + fp->ctf_parmax = CTF_MAX_PTYPE_V1; + + /* v2 is just the same as v3 except for new types and sections: + no upgrading required. */ + case CTF_VERSION_2: ; + /* FALLTHRU */ + } return 0; } @@ -620,24 +676,25 @@ upgrade_types (ctf_file_t *fp, ctf_header_t *cth) recension of libctf supports upgrading. */ static int -init_types (ctf_file_t *fp, ctf_header_t *cth) +init_types (ctf_dict_t *fp, ctf_header_t *cth) { const ctf_type_t *tbuf; const ctf_type_t *tend; unsigned long pop[CTF_K_MAX + 1] = { 0 }; const ctf_type_t *tp; - ctf_hash_t *hp; - uint32_t id, dst; + uint32_t id; uint32_t *xp; - /* We determine whether the container is a child or a parent based on - the value of cth_parname. */ + /* We determine whether the dict is a child or a parent based on the value of + cth_parname. */ int child = cth->cth_parname != 0; int nlstructs = 0, nlunions = 0; int err; + assert (!(fp->ctf_flags & LCTF_RDWR)); + if (_libctf_unlikely_ (fp->ctf_version == CTF_VERSION_1)) { int err; @@ -663,58 +720,58 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) if (vbytes < 0) return ECTF_CORRUPT; + /* For forward declarations, ctt_type is the CTF_K_* kind for the tag, + so bump that population count too. */ if (kind == CTF_K_FORWARD) - { - /* For forward declarations, ctt_type is the CTF_K_* kind for the tag, - so bump that population count too. If ctt_type is unknown, treat - the tag as a struct. */ + pop[tp->ctt_type]++; - if (tp->ctt_type == CTF_K_UNKNOWN || tp->ctt_type >= CTF_K_MAX) - pop[CTF_K_STRUCT]++; - else - pop[tp->ctt_type]++; - } tp = (ctf_type_t *) ((uintptr_t) tp + increment + vbytes); pop[kind]++; } if (child) { - ctf_dprintf ("CTF container %p is a child\n", (void *) fp); + ctf_dprintf ("CTF dict %p is a child\n", (void *) fp); fp->ctf_flags |= LCTF_CHILD; } else - ctf_dprintf ("CTF container %p is a parent\n", (void *) fp); + ctf_dprintf ("CTF dict %p is a parent\n", (void *) fp); /* Now that we've counted up the number of each type, we can allocate the hash tables, type translation table, and pointer table. */ - if ((fp->ctf_structs = ctf_hash_create (pop[CTF_K_STRUCT], ctf_hash_string, - ctf_hash_eq_string)) == NULL) + if ((fp->ctf_structs.ctn_readonly + = ctf_hash_create (pop[CTF_K_STRUCT], ctf_hash_string, + ctf_hash_eq_string)) == NULL) return ENOMEM; - if ((fp->ctf_unions = ctf_hash_create (pop[CTF_K_UNION], ctf_hash_string, - ctf_hash_eq_string)) == NULL) + if ((fp->ctf_unions.ctn_readonly + = ctf_hash_create (pop[CTF_K_UNION], ctf_hash_string, + ctf_hash_eq_string)) == NULL) return ENOMEM; - if ((fp->ctf_enums = ctf_hash_create (pop[CTF_K_ENUM], ctf_hash_string, - ctf_hash_eq_string)) == NULL) + if ((fp->ctf_enums.ctn_readonly + = ctf_hash_create (pop[CTF_K_ENUM], ctf_hash_string, + ctf_hash_eq_string)) == NULL) return ENOMEM; - if ((fp->ctf_names = ctf_hash_create (pop[CTF_K_INTEGER] + - pop[CTF_K_FLOAT] + - pop[CTF_K_FUNCTION] + - pop[CTF_K_TYPEDEF] + - pop[CTF_K_POINTER] + - pop[CTF_K_VOLATILE] + - pop[CTF_K_CONST] + - pop[CTF_K_RESTRICT], - ctf_hash_string, - ctf_hash_eq_string)) == NULL) + if ((fp->ctf_names.ctn_readonly + = ctf_hash_create (pop[CTF_K_UNKNOWN] + + pop[CTF_K_INTEGER] + + pop[CTF_K_FLOAT] + + pop[CTF_K_FUNCTION] + + pop[CTF_K_TYPEDEF] + + pop[CTF_K_POINTER] + + pop[CTF_K_VOLATILE] + + pop[CTF_K_CONST] + + pop[CTF_K_RESTRICT], + ctf_hash_string, + ctf_hash_eq_string)) == NULL) return ENOMEM; - fp->ctf_txlate = ctf_alloc (sizeof (uint32_t) * (fp->ctf_typemax + 1)); - fp->ctf_ptrtab = ctf_alloc (sizeof (uint32_t) * (fp->ctf_typemax + 1)); + fp->ctf_txlate = malloc (sizeof (uint32_t) * (fp->ctf_typemax + 1)); + fp->ctf_ptrtab_len = fp->ctf_typemax + 1; + fp->ctf_ptrtab = malloc (sizeof (uint32_t) * fp->ctf_ptrtab_len); if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL) return ENOMEM; /* Memory allocation failed. */ @@ -731,7 +788,7 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) for (id = 1, tp = tbuf; tp < tend; xp++, id++) { unsigned short kind = LCTF_INFO_KIND (fp, tp->ctt_info); - unsigned short flag = LCTF_INFO_ISROOT (fp, tp->ctt_info); + unsigned short isroot = LCTF_INFO_ISROOT (fp, tp->ctt_info); unsigned long vlen = LCTF_INFO_VLEN (fp, tp->ctt_info); ssize_t size, increment, vbytes; @@ -739,10 +796,12 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) (void) ctf_get_ctt_size (fp, tp, &size, &increment); name = ctf_strptr (fp, tp->ctt_name); + /* Cannot fail: shielded by call in loop above. */ vbytes = LCTF_VBYTES (fp, kind, size, vlen); switch (kind) { + case CTF_K_UNKNOWN: case CTF_K_INTEGER: case CTF_K_FLOAT: /* Names are reused by bit-fields, which are differentiated by their @@ -751,13 +810,14 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) root-visible version so that we can be sure to find it when checking for conflicting definitions in ctf_add_type(). */ - if (((ctf_hash_lookup_type (fp->ctf_names, fp, name)) == 0) - || (flag & CTF_ADD_ROOT)) + if (((ctf_hash_lookup_type (fp->ctf_names.ctn_readonly, + fp, name)) == 0) + || isroot) { - err = ctf_hash_define_type (fp->ctf_names, fp, + err = ctf_hash_define_type (fp->ctf_names.ctn_readonly, fp, LCTF_INDEX_TO_TYPE (fp, id, child), tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) + if (err != 0) return err; } break; @@ -769,86 +829,94 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) break; case CTF_K_FUNCTION: - err = ctf_hash_insert_type (fp->ctf_names, fp, + if (!isroot) + break; + + err = ctf_hash_insert_type (fp->ctf_names.ctn_readonly, fp, LCTF_INDEX_TO_TYPE (fp, id, child), tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) + if (err != 0) return err; break; case CTF_K_STRUCT: - err = ctf_hash_define_type (fp->ctf_structs, fp, + if (size >= CTF_LSTRUCT_THRESH) + nlstructs++; + + if (!isroot) + break; + + err = ctf_hash_define_type (fp->ctf_structs.ctn_readonly, fp, LCTF_INDEX_TO_TYPE (fp, id, child), tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) + if (err != 0) return err; - if (size >= CTF_LSTRUCT_THRESH) - nlstructs++; break; case CTF_K_UNION: - err = ctf_hash_define_type (fp->ctf_unions, fp, + if (size >= CTF_LSTRUCT_THRESH) + nlunions++; + + if (!isroot) + break; + + err = ctf_hash_define_type (fp->ctf_unions.ctn_readonly, fp, LCTF_INDEX_TO_TYPE (fp, id, child), tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) + if (err != 0) return err; - - if (size >= CTF_LSTRUCT_THRESH) - nlunions++; break; case CTF_K_ENUM: - err = ctf_hash_define_type (fp->ctf_enums, fp, + if (!isroot) + break; + + err = ctf_hash_define_type (fp->ctf_enums.ctn_readonly, fp, LCTF_INDEX_TO_TYPE (fp, id, child), tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) + if (err != 0) return err; break; case CTF_K_TYPEDEF: - err = ctf_hash_insert_type (fp->ctf_names, fp, + if (!isroot) + break; + + err = ctf_hash_insert_type (fp->ctf_names.ctn_readonly, fp, LCTF_INDEX_TO_TYPE (fp, id, child), tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) + if (err != 0) return err; break; case CTF_K_FORWARD: - /* Only insert forward tags into the given hash if the type or tag - name is not already present. */ - switch (tp->ctt_type) - { - case CTF_K_STRUCT: - hp = fp->ctf_structs; - break; - case CTF_K_UNION: - hp = fp->ctf_unions; - break; - case CTF_K_ENUM: - hp = fp->ctf_enums; + { + ctf_names_t *np = ctf_name_table (fp, tp->ctt_type); + + if (!isroot) break; - default: - hp = fp->ctf_structs; - } - if (ctf_hash_lookup_type (hp, fp, name) == 0) - { - err = ctf_hash_insert_type (hp, fp, - LCTF_INDEX_TO_TYPE (fp, id, child), - tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) - return err; - } - break; + /* Only insert forward tags into the given hash if the type or tag + name is not already present. */ + if (ctf_hash_lookup_type (np->ctn_readonly, fp, name) == 0) + { + err = ctf_hash_insert_type (np->ctn_readonly, fp, + LCTF_INDEX_TO_TYPE (fp, id, child), + tp->ctt_name); + if (err != 0) + return err; + } + break; + } case CTF_K_POINTER: - /* If the type referenced by the pointer is in this CTF container, - then store the index of the pointer type in - fp->ctf_ptrtab[ index of referenced type ]. */ + /* If the type referenced by the pointer is in this CTF dict, then + store the index of the pointer type in fp->ctf_ptrtab[ index of + referenced type ]. */ if (LCTF_TYPE_ISCHILD (fp, tp->ctt_type) == child && LCTF_TYPE_TO_INDEX (fp, tp->ctt_type) <= fp->ctf_typemax) @@ -858,12 +926,19 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: - err = ctf_hash_insert_type (fp->ctf_names, fp, + if (!isroot) + break; + + err = ctf_hash_insert_type (fp->ctf_names.ctn_readonly, fp, LCTF_INDEX_TO_TYPE (fp, id, child), tp->ctt_name); - if (err != 0 && err != ECTF_STRTAB) + if (err != 0) return err; break; + default: + ctf_err_warn (fp, 0, ECTF_CORRUPT, + _("init_types(): unhandled CTF kind: %x"), kind); + return ECTF_CORRUPT; } *xp = (uint32_t) ((uintptr_t) tp - (uintptr_t) fp->ctf_buf); @@ -871,31 +946,14 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) } ctf_dprintf ("%lu total types processed\n", fp->ctf_typemax); - ctf_dprintf ("%u enum names hashed\n", ctf_hash_size (fp->ctf_enums)); + ctf_dprintf ("%u enum names hashed\n", + ctf_hash_size (fp->ctf_enums.ctn_readonly)); ctf_dprintf ("%u struct names hashed (%d long)\n", - ctf_hash_size (fp->ctf_structs), nlstructs); + ctf_hash_size (fp->ctf_structs.ctn_readonly), nlstructs); ctf_dprintf ("%u union names hashed (%d long)\n", - ctf_hash_size (fp->ctf_unions), nlunions); - ctf_dprintf ("%u base type names hashed\n", ctf_hash_size (fp->ctf_names)); - - /* Make an additional pass through the pointer table to find pointers that - point to anonymous typedef nodes. If we find one, modify the pointer table - so that the pointer is also known to point to the node that is referenced - by the anonymous typedef node. */ - - for (id = 1; id <= fp->ctf_typemax; id++) - { - if ((dst = fp->ctf_ptrtab[id]) != 0) - { - tp = LCTF_INDEX_TO_TYPEPTR (fp, id); - - if (LCTF_INFO_KIND (fp, tp->ctt_info) == CTF_K_TYPEDEF && - strcmp (ctf_strptr (fp, tp->ctt_name), "") == 0 && - LCTF_TYPE_ISCHILD (fp, tp->ctt_type) == child && - LCTF_TYPE_TO_INDEX (fp, tp->ctt_type) <= fp->ctf_typemax) - fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, tp->ctt_type)] = dst; - } - } + ctf_hash_size (fp->ctf_unions.ctn_readonly), nlunions); + ctf_dprintf ("%u base type names hashed\n", + ctf_hash_size (fp->ctf_names.ctn_readonly)); return 0; } @@ -905,40 +963,21 @@ init_types (ctf_file_t *fp, ctf_header_t *cth) We flip everything, mindlessly, even 1-byte entities, so that future expansions do not require changes to this code. */ -/* < C11? define away static assertions. */ - -#if !defined (__STDC_VERSION__) || __STDC_VERSION__ < 201112L -#define _Static_assert(cond, err) -#endif - -/* Swap the endianness of something. */ - -#define swap_thing(x) \ - do { \ - _Static_assert (sizeof (x) == 1 || (sizeof (x) % 2 == 0 \ - && sizeof (x) <= 8), \ - "Invalid size, update endianness code"); \ - switch (sizeof (x)) { \ - case 2: x = bswap_16 (x); break; \ - case 4: x = bswap_32 (x); break; \ - case 8: x = bswap_64 (x); break; \ - case 1: /* Nothing needs doing */ \ - break; \ - } \ - } while (0); - /* Flip the endianness of the CTF header. */ -static void -flip_header (ctf_header_t *cth) +void +ctf_flip_header (ctf_header_t *cth) { swap_thing (cth->cth_preamble.ctp_magic); swap_thing (cth->cth_preamble.ctp_version); swap_thing (cth->cth_preamble.ctp_flags); swap_thing (cth->cth_parlabel); swap_thing (cth->cth_parname); + swap_thing (cth->cth_cuname); swap_thing (cth->cth_objtoff); swap_thing (cth->cth_funcoff); + swap_thing (cth->cth_objtidxoff); + swap_thing (cth->cth_funcidxoff); swap_thing (cth->cth_varoff); swap_thing (cth->cth_typeoff); swap_thing (cth->cth_stroff); @@ -951,25 +990,25 @@ static void flip_lbls (void *start, size_t len) { ctf_lblent_t *lbl = start; + ssize_t i; - for (ssize_t i = len / sizeof (struct ctf_lblent); i > 0; lbl++, i--) + for (i = len / sizeof (struct ctf_lblent); i > 0; lbl++, i--) { swap_thing (lbl->ctl_label); swap_thing (lbl->ctl_type); } } -/* Flip the endianness of the data-object or function sections, an array of - uint32_t. (The function section has more internal structure, but that - structure is an array of uint32_t, so can be treated as one big array for - byte-swapping.) */ +/* Flip the endianness of the data-object or function sections or their indexes, + all arrays of uint32_t. */ static void flip_objts (void *start, size_t len) { uint32_t *obj = start; + ssize_t i; - for (ssize_t i = len / sizeof (uint32_t); i > 0; obj++, i--) + for (i = len / sizeof (uint32_t); i > 0; obj++, i--) swap_thing (*obj); } @@ -979,8 +1018,9 @@ static void flip_vars (void *start, size_t len) { ctf_varent_t *var = start; + ssize_t i; - for (ssize_t i = len / sizeof (struct ctf_varent); i > 0; var++, i--) + for (i = len / sizeof (struct ctf_varent); i > 0; var++, i--) { swap_thing (var->ctv_name); swap_thing (var->ctv_type); @@ -991,26 +1031,48 @@ flip_vars (void *start, size_t len) ctf_stype followed by variable data. */ static int -flip_types (void *start, size_t len) +flip_types (ctf_dict_t *fp, void *start, size_t len, int to_foreign) { ctf_type_t *t = start; while ((uintptr_t) t < ((uintptr_t) start) + len) { + uint32_t kind; + size_t size; + uint32_t vlen; + size_t vbytes; + + if (to_foreign) + { + kind = CTF_V2_INFO_KIND (t->ctt_info); + size = t->ctt_size; + vlen = CTF_V2_INFO_VLEN (t->ctt_info); + vbytes = get_vbytes_v2 (fp, kind, size, vlen); + } + swap_thing (t->ctt_name); swap_thing (t->ctt_info); swap_thing (t->ctt_size); - uint32_t kind = CTF_V2_INFO_KIND (t->ctt_info); - size_t size = t->ctt_size; - uint32_t vlen = CTF_V2_INFO_VLEN (t->ctt_info); - size_t vbytes = get_vbytes_v2 (kind, size, vlen); + if (!to_foreign) + { + kind = CTF_V2_INFO_KIND (t->ctt_info); + size = t->ctt_size; + vlen = CTF_V2_INFO_VLEN (t->ctt_info); + vbytes = get_vbytes_v2 (fp, kind, size, vlen); + } if (_libctf_unlikely_ (size == CTF_LSIZE_SENT)) { + if (to_foreign) + size = CTF_TYPE_LSIZE (t); + swap_thing (t->ctt_lsizehi); swap_thing (t->ctt_lsizelo); - size = CTF_TYPE_LSIZE (t); + + if (!to_foreign) + size = CTF_TYPE_LSIZE (t); + t = (ctf_type_t *) ((uintptr_t) t + sizeof (ctf_type_t)); } else @@ -1045,8 +1107,9 @@ flip_types (void *start, size_t len) /* This type has a bunch of uint32_ts. */ uint32_t *item = (uint32_t *) t; + ssize_t i; - for (ssize_t i = vlen; i > 0; item++, i--) + for (i = vlen; i > 0; item++, i--) swap_thing (*item); break; } @@ -1090,7 +1153,8 @@ flip_types (void *start, size_t len) if (_libctf_unlikely_ (size >= CTF_LSTRUCT_THRESH)) { ctf_lmember_t *lm = (ctf_lmember_t *) t; - for (ssize_t i = vlen; i > 0; i--, lm++) + ssize_t i; + for (i = vlen; i > 0; i--, lm++) { swap_thing (lm->ctlm_name); swap_thing (lm->ctlm_offsethi); @@ -1101,7 +1165,8 @@ flip_types (void *start, size_t len) else { ctf_member_t *m = (ctf_member_t *) t; - for (ssize_t i = vlen; i > 0; i--, m++) + ssize_t i; + for (i = vlen; i > 0; i--, m++) { swap_thing (m->ctm_name); swap_thing (m->ctm_offset); @@ -1116,8 +1181,9 @@ flip_types (void *start, size_t len) /* This has an array of ctf_enum_t. */ ctf_enum_t *item = (ctf_enum_t *) t; + ssize_t i; - for (ssize_t i = vlen; i > 0; item++, i--) + for (i = vlen; i > 0; item++, i--) { swap_thing (item->cte_name); swap_thing (item->cte_value); @@ -1125,8 +1191,9 @@ flip_types (void *start, size_t len) break; } default: - ctf_dprintf ("unhandled CTF kind in endianness conversion -- %x\n", - kind); + ctf_err_warn (fp, 0, ECTF_CORRUPT, + _("unhandled CTF kind in endianness conversion: %x"), + kind); return ECTF_CORRUPT; } @@ -1136,31 +1203,75 @@ flip_types (void *start, size_t len) return 0; } -/* Flip the endianness of BASE, given the offsets in the (already endian- - converted) CTH. +/* Flip the endianness of BUF, given the offsets in the (already endian- + converted) CTH. If TO_FOREIGN is set, flip to foreign-endianness; if not, + flip away. All of this stuff happens before the header is fully initialized, so the LCTF_*() macros cannot be used yet. Since we do not try to endian-convert v1 data, this is no real loss. */ -static int -flip_ctf (ctf_header_t *cth, unsigned char *base) +int +ctf_flip (ctf_dict_t *fp, ctf_header_t *cth, unsigned char *buf, + int to_foreign) { - base += sizeof (ctf_header_t); + ctf_dprintf("flipping endianness\n"); + + flip_lbls (buf + cth->cth_lbloff, cth->cth_objtoff - cth->cth_lbloff); + flip_objts (buf + cth->cth_objtoff, cth->cth_funcoff - cth->cth_objtoff); + flip_objts (buf + cth->cth_funcoff, cth->cth_objtidxoff - cth->cth_funcoff); + flip_objts (buf + cth->cth_objtidxoff, cth->cth_funcidxoff - cth->cth_objtidxoff); + flip_objts (buf + cth->cth_funcidxoff, cth->cth_varoff - cth->cth_funcidxoff); + flip_vars (buf + cth->cth_varoff, cth->cth_typeoff - cth->cth_varoff); + return flip_types (fp, buf + cth->cth_typeoff, + cth->cth_stroff - cth->cth_typeoff, to_foreign); +} - flip_lbls (base + cth->cth_lbloff, cth->cth_objtoff - cth->cth_lbloff); - flip_objts (base + cth->cth_objtoff, cth->cth_funcoff - cth->cth_objtoff); - flip_objts (base + cth->cth_funcoff, cth->cth_varoff - cth->cth_funcoff); - flip_vars (base + cth->cth_varoff, cth->cth_typeoff - cth->cth_varoff); - return flip_types (base + cth->cth_typeoff, cth->cth_stroff - cth->cth_typeoff); +/* Set up the ctl hashes in a ctf_dict_t. Called by both writable and + non-writable dictionary initialization. */ +void ctf_set_ctl_hashes (ctf_dict_t *fp) +{ + /* Initialize the ctf_lookup_by_name top-level dictionary. We keep an + array of type name prefixes and the corresponding ctf_hash to use. */ + fp->ctf_lookups[0].ctl_prefix = "struct"; + fp->ctf_lookups[0].ctl_len = strlen (fp->ctf_lookups[0].ctl_prefix); + fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs; + fp->ctf_lookups[1].ctl_prefix = "union"; + fp->ctf_lookups[1].ctl_len = strlen (fp->ctf_lookups[1].ctl_prefix); + fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions; + fp->ctf_lookups[2].ctl_prefix = "enum"; + fp->ctf_lookups[2].ctl_len = strlen (fp->ctf_lookups[2].ctl_prefix); + fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums; + fp->ctf_lookups[3].ctl_prefix = _CTF_NULLSTR; + fp->ctf_lookups[3].ctl_len = strlen (fp->ctf_lookups[3].ctl_prefix); + fp->ctf_lookups[3].ctl_hash = &fp->ctf_names; + fp->ctf_lookups[4].ctl_prefix = NULL; + fp->ctf_lookups[4].ctl_len = 0; + fp->ctf_lookups[4].ctl_hash = NULL; } /* Open a CTF file, mocking up a suitable ctf_sect. */ -ctf_file_t *ctf_simple_open (const char *ctfsect, size_t ctfsect_size, + +ctf_dict_t *ctf_simple_open (const char *ctfsect, size_t ctfsect_size, const char *symsect, size_t symsect_size, size_t symsect_entsize, const char *strsect, size_t strsect_size, int *errp) +{ + return ctf_simple_open_internal (ctfsect, ctfsect_size, symsect, symsect_size, + symsect_entsize, strsect, strsect_size, NULL, + 0, errp); +} + +/* Open a CTF file, mocking up a suitable ctf_sect and overriding the external + strtab with a synthetic one. */ + +ctf_dict_t *ctf_simple_open_internal (const char *ctfsect, size_t ctfsect_size, + const char *symsect, size_t symsect_size, + size_t symsect_entsize, + const char *strsect, size_t strsect_size, + ctf_dynhash_t *syn_strtab, int writable, + int *errp) { ctf_sect_t skeleton; @@ -1170,10 +1281,7 @@ ctf_file_t *ctf_simple_open (const char *ctfsect, size_t ctfsect_size, ctf_sect_t *strsectp = NULL; skeleton.cts_name = _CTF_SECTION; - skeleton.cts_type = SHT_PROGBITS; - skeleton.cts_flags = 0; skeleton.cts_entsize = 1; - skeleton.cts_offset = 0; if (ctfsect) { @@ -1200,29 +1308,40 @@ ctf_file_t *ctf_simple_open (const char *ctfsect, size_t ctfsect_size, strsectp = &str_sect; } - return ctf_bufopen (ctfsectp, symsectp, strsectp, errp); + return ctf_bufopen_internal (ctfsectp, symsectp, strsectp, syn_strtab, + writable, errp); } /* Decode the specified CTF buffer and optional symbol table, and create a new - CTF container representing the symbolic debugging information. This code can + CTF dict representing the symbolic debugging information. This code can be used directly by the debugger, or it can be used as the engine for ctf_fdopen() or ctf_open(), below. */ -ctf_file_t * +ctf_dict_t * ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, const ctf_sect_t *strsect, int *errp) +{ + return ctf_bufopen_internal (ctfsect, symsect, strsect, NULL, 0, errp); +} + +/* Like ctf_bufopen, but overriding the external strtab with a synthetic one. */ + +ctf_dict_t * +ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, + const ctf_sect_t *strsect, ctf_dynhash_t *syn_strtab, + int writable, int *errp) { const ctf_preamble_t *pp; - ctf_header_t hp; - ctf_file_t *fp; - void *buf, *base; - size_t size, hdrsz; + size_t hdrsz = sizeof (ctf_header_t); + ctf_header_t *hp; + ctf_dict_t *fp; int foreign_endian = 0; int err; libctf_init_debug(); - if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL))) + if ((ctfsect == NULL) || ((symsect != NULL) && + ((strsect == NULL) && syn_strtab == NULL))) return (ctf_set_open_errno (errp, EINVAL)); if (symsect != NULL && symsect->cts_entsize != sizeof (Elf32_Sym) && @@ -1273,39 +1392,109 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, info. We do not support dynamically upgrading such entries (none should exist in any case, since dwarf2ctf does not create them). */ - ctf_dprintf ("ctf_bufopen: CTF version %d symsect not " - "supported\n", pp->ctp_version); + ctf_err_warn (NULL, 0, ECTF_NOTSUP, _("ctf_bufopen: CTF version %d " + "symsect not supported"), + pp->ctp_version); return (ctf_set_open_errno (errp, ECTF_NOTSUP)); } - if (ctfsect->cts_size < sizeof (ctf_header_t)) + if (pp->ctp_version < CTF_VERSION_3) + hdrsz = sizeof (ctf_header_v2_t); + + if (_libctf_unlikely_ (pp->ctp_flags > CTF_F_MAX)) + { + ctf_err_warn (NULL, 0, ECTF_FLAGS, _("ctf_bufopen: invalid header " + "flags: %x"), + (unsigned int) pp->ctp_flags); + return (ctf_set_open_errno (errp, ECTF_FLAGS)); + } + + if (ctfsect->cts_size < hdrsz) return (ctf_set_open_errno (errp, ECTF_NOCTFBUF)); - memcpy (&hp, ctfsect->cts_data, sizeof (hp)); + if ((fp = malloc (sizeof (ctf_dict_t))) == NULL) + return (ctf_set_open_errno (errp, ENOMEM)); + + memset (fp, 0, sizeof (ctf_dict_t)); + + if (writable) + fp->ctf_flags |= LCTF_RDWR; + + if ((fp->ctf_header = malloc (sizeof (struct ctf_header))) == NULL) + { + free (fp); + return (ctf_set_open_errno (errp, ENOMEM)); + } + hp = fp->ctf_header; + memcpy (hp, ctfsect->cts_data, hdrsz); + if (pp->ctp_version < CTF_VERSION_3) + upgrade_header (hp); if (foreign_endian) - flip_header (&hp); + ctf_flip_header (hp); + fp->ctf_openflags = hp->cth_flags; + fp->ctf_size = hp->cth_stroff + hp->cth_strlen; - hdrsz = sizeof (ctf_header_t); + ctf_dprintf ("ctf_bufopen: uncompressed size=%lu\n", + (unsigned long) fp->ctf_size); + + if (hp->cth_lbloff > fp->ctf_size || hp->cth_objtoff > fp->ctf_size + || hp->cth_funcoff > fp->ctf_size || hp->cth_objtidxoff > fp->ctf_size + || hp->cth_funcidxoff > fp->ctf_size || hp->cth_typeoff > fp->ctf_size + || hp->cth_stroff > fp->ctf_size) + { + ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("header offset exceeds CTF size")); + return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + } - size = hp.cth_stroff + hp.cth_strlen; + if (hp->cth_lbloff > hp->cth_objtoff + || hp->cth_objtoff > hp->cth_funcoff + || hp->cth_funcoff > hp->cth_typeoff + || hp->cth_funcoff > hp->cth_objtidxoff + || hp->cth_objtidxoff > hp->cth_funcidxoff + || hp->cth_funcidxoff > hp->cth_varoff + || hp->cth_varoff > hp->cth_typeoff || hp->cth_typeoff > hp->cth_stroff) + { + ctf_err_warn (NULL, 0, ECTF_CORRUPT, _("overlapping CTF sections")); + return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + } - ctf_dprintf ("ctf_bufopen: uncompressed size=%lu\n", (unsigned long) size); + if ((hp->cth_lbloff & 3) || (hp->cth_objtoff & 2) + || (hp->cth_funcoff & 2) || (hp->cth_objtidxoff & 2) + || (hp->cth_funcidxoff & 2) || (hp->cth_varoff & 3) + || (hp->cth_typeoff & 3)) + { + ctf_err_warn (NULL, 0, ECTF_CORRUPT, + _("CTF sections not properly aligned")); + return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + } - if (hp.cth_lbloff > size || hp.cth_objtoff > size - || hp.cth_funcoff > size || hp.cth_typeoff > size || hp.cth_stroff > size) - return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + /* This invariant will be lifted in v4, but for now it is true. */ - if (hp.cth_lbloff > hp.cth_objtoff - || hp.cth_objtoff > hp.cth_funcoff - || hp.cth_funcoff > hp.cth_typeoff - || hp.cth_funcoff > hp.cth_varoff - || hp.cth_varoff > hp.cth_typeoff || hp.cth_typeoff > hp.cth_stroff) - return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + if ((hp->cth_funcidxoff - hp->cth_objtidxoff != 0) && + (hp->cth_funcidxoff - hp->cth_objtidxoff + != hp->cth_funcoff - hp->cth_objtoff)) + { + ctf_err_warn (NULL, 0, ECTF_CORRUPT, + _("Object index section is neither empty nor the " + "same length as the object section: %u versus %u " + "bytes"), hp->cth_funcoff - hp->cth_objtoff, + hp->cth_funcidxoff - hp->cth_objtidxoff); + return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + } - if ((hp.cth_lbloff & 3) || (hp.cth_objtoff & 1) - || (hp.cth_funcoff & 1) || (hp.cth_varoff & 3) || (hp.cth_typeoff & 3)) - return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + if ((hp->cth_varoff - hp->cth_funcidxoff != 0) && + (hp->cth_varoff - hp->cth_funcidxoff + != hp->cth_objtidxoff - hp->cth_funcoff) && + (hp->cth_flags & CTF_F_NEWFUNCINFO)) + { + ctf_err_warn (NULL, 0, ECTF_CORRUPT, + _("Function index section is neither empty nor the " + "same length as the function section: %u versus %u " + "bytes"), hp->cth_objtidxoff - hp->cth_funcoff, + hp->cth_varoff - hp->cth_funcidxoff); + return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + } /* Once everything is determined to be valid, attempt to decompress the CTF data buffer if it is compressed, or copy it into new storage if it is not @@ -1315,69 +1504,97 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, /* Note: if this is a v1 buffer, it will be reallocated and expanded by init_types(). */ - if (hp.cth_flags & CTF_F_COMPRESS) + if (hp->cth_flags & CTF_F_COMPRESS) { - size_t srclen, dstlen; + size_t srclen; + uLongf dstlen; const void *src; int rc = Z_OK; - if ((base = ctf_data_alloc (size + hdrsz)) == NULL) - return (ctf_set_open_errno (errp, ECTF_ZALLOC)); + /* We are allocating this ourselves, so we can drop the ctf header + copy in favour of ctf->ctf_header. */ - memcpy (base, ctfsect->cts_data, hdrsz); - ((ctf_preamble_t *) base)->ctp_flags &= ~CTF_F_COMPRESS; - buf = (unsigned char *) base + hdrsz; + if ((fp->ctf_base = malloc (fp->ctf_size)) == NULL) + { + err = ECTF_ZALLOC; + goto bad; + } + fp->ctf_dynbase = fp->ctf_base; + hp->cth_flags &= ~CTF_F_COMPRESS; src = (unsigned char *) ctfsect->cts_data + hdrsz; srclen = ctfsect->cts_size - hdrsz; - dstlen = size; + dstlen = fp->ctf_size; + fp->ctf_buf = fp->ctf_base; - if ((rc = uncompress (buf, &dstlen, src, srclen)) != Z_OK) + if ((rc = uncompress (fp->ctf_base, &dstlen, src, srclen)) != Z_OK) { - ctf_dprintf ("zlib inflate err: %s\n", zError (rc)); - ctf_data_free (base, size + hdrsz); - return (ctf_set_open_errno (errp, ECTF_DECOMPRESS)); + ctf_err_warn (NULL, 0, ECTF_DECOMPRESS, _("zlib inflate err: %s"), + zError (rc)); + err = ECTF_DECOMPRESS; + goto bad; } - if (dstlen != size) + if ((size_t) dstlen != fp->ctf_size) { - ctf_dprintf ("zlib inflate short -- got %lu of %lu " - "bytes\n", (unsigned long) dstlen, (unsigned long) size); - ctf_data_free (base, size + hdrsz); - return (ctf_set_open_errno (errp, ECTF_CORRUPT)); + ctf_err_warn (NULL, 0, ECTF_CORRUPT, + _("zlib inflate short: got %lu of %lu bytes"), + (unsigned long) dstlen, (unsigned long) fp->ctf_size); + err = ECTF_CORRUPT; + goto bad; } - - } - else if (foreign_endian) - { - if ((base = ctf_data_alloc (size + hdrsz)) == NULL) - return (ctf_set_open_errno (errp, ECTF_ZALLOC)); } else { - base = (void *) ctfsect->cts_data; - buf = (unsigned char *) base + hdrsz; + if (_libctf_unlikely_ (ctfsect->cts_size < hdrsz + fp->ctf_size)) + { + ctf_err_warn (NULL, 0, ECTF_CORRUPT, + _("%lu byte long CTF dictionary overruns %lu byte long CTF section"), + (unsigned long) ctfsect->cts_size, + (unsigned long) (hdrsz + fp->ctf_size)); + err = ECTF_CORRUPT; + goto bad; + } + + if (foreign_endian) + { + if ((fp->ctf_base = malloc (fp->ctf_size)) == NULL) + { + err = ECTF_ZALLOC; + goto bad; + } + fp->ctf_dynbase = fp->ctf_base; + memcpy (fp->ctf_base, ((unsigned char *) ctfsect->cts_data) + hdrsz, + fp->ctf_size); + fp->ctf_buf = fp->ctf_base; + } + else + { + /* We are just using the section passed in -- but its header may + be an old version. Point ctf_buf past the old header, and + never touch it again. */ + fp->ctf_base = (unsigned char *) ctfsect->cts_data; + fp->ctf_dynbase = NULL; + fp->ctf_buf = fp->ctf_base + hdrsz; + } } /* Once we have uncompressed and validated the CTF data buffer, we can - proceed with allocating a ctf_file_t and initializing it. + proceed with initializing the ctf_dict_t we allocated above. Nothing that depends on buf or base should be set directly in this function before the init_types() call, because it may be reallocated during transparent upgrade if this recension of libctf is so configured: see - ctf_set_base() and ctf_realloc_base(). */ - - if ((fp = ctf_alloc (sizeof (ctf_file_t))) == NULL) - return (ctf_set_open_errno (errp, ENOMEM)); - - memset (fp, 0, sizeof (ctf_file_t)); - ctf_set_version (fp, &hp, hp.cth_version); + ctf_set_base(). */ - if (_libctf_unlikely_ (hp.cth_version < CTF_VERSION_2)) - fp->ctf_parmax = CTF_MAX_PTYPE_V1; - else - fp->ctf_parmax = CTF_MAX_PTYPE; + ctf_set_version (fp, hp, hp->cth_version); + if (ctf_str_create_atoms (fp) < 0) + { + err = ENOMEM; + goto bad; + } + fp->ctf_parmax = CTF_MAX_PTYPE; memcpy (&fp->ctf_data, ctfsect, sizeof (ctf_sect_t)); if (symsect != NULL) @@ -1387,11 +1604,23 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, } if (fp->ctf_data.cts_name != NULL) - fp->ctf_data.cts_name = ctf_strdup (fp->ctf_data.cts_name); + if ((fp->ctf_data.cts_name = strdup (fp->ctf_data.cts_name)) == NULL) + { + err = ENOMEM; + goto bad; + } if (fp->ctf_symtab.cts_name != NULL) - fp->ctf_symtab.cts_name = ctf_strdup (fp->ctf_symtab.cts_name); + if ((fp->ctf_symtab.cts_name = strdup (fp->ctf_symtab.cts_name)) == NULL) + { + err = ENOMEM; + goto bad; + } if (fp->ctf_strtab.cts_name != NULL) - fp->ctf_strtab.cts_name = ctf_strdup (fp->ctf_strtab.cts_name); + if ((fp->ctf_strtab.cts_name = strdup (fp->ctf_strtab.cts_name)) == NULL) + { + err = ENOMEM; + goto bad; + } if (fp->ctf_data.cts_name == NULL) fp->ctf_data.cts_name = _CTF_NULLSTR; @@ -1405,75 +1634,61 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, fp->ctf_str[CTF_STRTAB_1].cts_strs = strsect->cts_data; fp->ctf_str[CTF_STRTAB_1].cts_len = strsect->cts_size; } + fp->ctf_syn_ext_strtab = syn_strtab; if (foreign_endian && - (err = flip_ctf (&hp, base)) != 0) + (err = ctf_flip (fp, hp, fp->ctf_buf, 0)) != 0) { - /* We can be certain that flip_ctf() will have endian-flipped everything - other than the types table when we return. In particular the header - is fine, so set it, to allow freeing to use the usual code path. */ + /* We can be certain that ctf_flip() will have endian-flipped everything + other than the types table when we return. In particular the header + is fine, so set it, to allow freeing to use the usual code path. */ - (void) ctf_set_open_errno (errp, err); - ctf_set_base (fp, &hp, base); + ctf_set_base (fp, hp, fp->ctf_base); goto bad; } - ctf_set_base (fp, &hp, base); - fp->ctf_size = size + hdrsz; + ctf_set_base (fp, hp, fp->ctf_base); - if ((err = init_types (fp, &hp)) != 0) + /* No need to do anything else for dynamic dicts: they do not support symbol + lookups, and the type table is maintained in the dthashes. */ + if (fp->ctf_flags & LCTF_RDWR) { - (void) ctf_set_open_errno (errp, err); - goto bad; + fp->ctf_refcnt = 1; + return fp; } - /* The ctf region may have been reallocated by init_types(), but now - that is done, it will not move again, so we can protect it, as long - as it didn't come from the ctfsect, which might have been allocated - with malloc(). */ - - if (fp->ctf_base != (void *) ctfsect->cts_data) - ctf_data_protect ((void *) fp->ctf_base, fp->ctf_size); - - /* If we have a symbol table section, allocate and initialize - the symtab translation table, pointed to by ctf_sxlate. */ + if ((err = init_types (fp, hp)) != 0) + goto bad; + + /* Allocate and initialize the symtab translation table, pointed to by + ctf_sxlate, and the corresponding index sections. This table may be too + large for the actual size of the object and function info sections: if so, + ctf_nsyms will be adjusted and the excess will never be used. It's + possible to do indexed symbol lookups even without a symbol table, so check + even in that case. Initially, we assume the symtab is native-endian: if it + isn't, the caller will inform us later by calling ctf_symsect_endianness. */ +#ifdef WORDS_BIGENDIAN + fp->ctf_symsect_little_endian = 0; +#else + fp->ctf_symsect_little_endian = 1; +#endif if (symsect != NULL) { fp->ctf_nsyms = symsect->cts_size / symsect->cts_entsize; - fp->ctf_sxlate = ctf_alloc (fp->ctf_nsyms * sizeof (uint32_t)); + fp->ctf_sxlate = malloc (fp->ctf_nsyms * sizeof (uint32_t)); if (fp->ctf_sxlate == NULL) { - (void) ctf_set_open_errno (errp, ENOMEM); - goto bad; - } - - if ((err = init_symtab (fp, &hp, symsect, strsect)) != 0) - { - (void) ctf_set_open_errno (errp, err); + err = ENOMEM; goto bad; } } - /* Initialize the ctf_lookup_by_name top-level dictionary. We keep an - array of type name prefixes and the corresponding ctf_hash to use. - NOTE: This code must be kept in sync with the code in ctf_update(). */ - fp->ctf_lookups[0].ctl_prefix = "struct"; - fp->ctf_lookups[0].ctl_len = strlen (fp->ctf_lookups[0].ctl_prefix); - fp->ctf_lookups[0].ctl_hash = fp->ctf_structs; - fp->ctf_lookups[1].ctl_prefix = "union"; - fp->ctf_lookups[1].ctl_len = strlen (fp->ctf_lookups[1].ctl_prefix); - fp->ctf_lookups[1].ctl_hash = fp->ctf_unions; - fp->ctf_lookups[2].ctl_prefix = "enum"; - fp->ctf_lookups[2].ctl_len = strlen (fp->ctf_lookups[2].ctl_prefix); - fp->ctf_lookups[2].ctl_hash = fp->ctf_enums; - fp->ctf_lookups[3].ctl_prefix = _CTF_NULLSTR; - fp->ctf_lookups[3].ctl_len = strlen (fp->ctf_lookups[3].ctl_prefix); - fp->ctf_lookups[3].ctl_hash = fp->ctf_names; - fp->ctf_lookups[4].ctl_prefix = NULL; - fp->ctf_lookups[4].ctl_len = 0; - fp->ctf_lookups[4].ctl_hash = NULL; + if ((err = init_symtab (fp, hp, symsect)) != 0) + goto bad; + + ctf_set_ctl_hashes (fp); if (symsect != NULL) { @@ -1489,25 +1704,40 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, return fp; bad: - ctf_file_close (fp); + ctf_set_open_errno (errp, err); + ctf_err_warn_to_open (fp); + ctf_dict_close (fp); return NULL; } -/* Close the specified CTF container and free associated data structures. Note - that ctf_file_close() is a reference counted operation: if the specified file - is the parent of other active containers, its reference count will be greater - than one and it will be freed later when no active children exist. */ +/* Bump the refcount on the specified CTF dict, to allow export of ctf_dict_t's + from iterators that open and close the ctf_dict_t around the loop. (This + does not extend their lifetime beyond that of the ctf_archive_t in which they + are contained.) */ void -ctf_file_close (ctf_file_t *fp) +ctf_ref (ctf_dict_t *fp) +{ + fp->ctf_refcnt++; +} + +/* Close the specified CTF dict and free associated data structures. Note that + ctf_dict_close() is a reference counted operation: if the specified file is + the parent of other active dict, its reference count will be greater than one + and it will be freed later when no active children exist. */ + +void +ctf_dict_close (ctf_dict_t *fp) { ctf_dtdef_t *dtd, *ntd; ctf_dvdef_t *dvd, *nvd; + ctf_in_flight_dynsym_t *did, *nid; + ctf_err_warning_t *err, *nerr; if (fp == NULL) - return; /* Allow ctf_file_close(NULL) to simplify caller code. */ + return; /* Allow ctf_dict_close(NULL) to simplify caller code. */ - ctf_dprintf ("ctf_file_close(%p) refcnt=%u\n", (void *) fp, fp->ctf_refcnt); + ctf_dprintf ("ctf_dict_close(%p) refcnt=%u\n", (void *) fp, fp->ctf_refcnt); if (fp->ctf_refcnt > 1) { @@ -1515,11 +1745,17 @@ ctf_file_close (ctf_file_t *fp) return; } - if (fp->ctf_dynparname != NULL) - ctf_free (fp->ctf_dynparname); + /* It is possible to recurse back in here, notably if dicts in the + ctf_link_inputs or ctf_link_outputs cite this dict as a parent without + using ctf_import_unref. Do nothing in that case. */ + if (fp->ctf_refcnt == 0) + return; - if (fp->ctf_parent != NULL) - ctf_file_close (fp->ctf_parent); + fp->ctf_refcnt--; + free (fp->ctf_dyncuname); + free (fp->ctf_dynparname); + if (fp->ctf_parent && !fp->ctf_parent_unreffed) + ctf_dict_close (fp->ctf_parent); for (dtd = ctf_list_next (&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { @@ -1527,7 +1763,20 @@ ctf_file_close (ctf_file_t *fp) ctf_dtd_delete (fp, dtd); } ctf_dynhash_destroy (fp->ctf_dthash); - ctf_dynhash_destroy (fp->ctf_dtbyname); + if (fp->ctf_flags & LCTF_RDWR) + { + ctf_dynhash_destroy (fp->ctf_structs.ctn_writable); + ctf_dynhash_destroy (fp->ctf_unions.ctn_writable); + ctf_dynhash_destroy (fp->ctf_enums.ctn_writable); + ctf_dynhash_destroy (fp->ctf_names.ctn_writable); + } + else + { + ctf_hash_destroy (fp->ctf_structs.ctn_readonly); + ctf_hash_destroy (fp->ctf_unions.ctn_readonly); + ctf_hash_destroy (fp->ctf_enums.ctn_readonly); + ctf_hash_destroy (fp->ctf_names.ctn_readonly); + } for (dvd = ctf_list_next (&fp->ctf_dvdefs); dvd != NULL; dvd = nvd) { @@ -1536,40 +1785,68 @@ ctf_file_close (ctf_file_t *fp) } ctf_dynhash_destroy (fp->ctf_dvhash); - ctf_free (fp->ctf_tmp_typeslice); + ctf_dynhash_destroy (fp->ctf_symhash); + free (fp->ctf_funcidx_sxlate); + free (fp->ctf_objtidx_sxlate); + ctf_dynhash_destroy (fp->ctf_objthash); + ctf_dynhash_destroy (fp->ctf_funchash); + free (fp->ctf_dynsymidx); + ctf_dynhash_destroy (fp->ctf_dynsyms); + for (did = ctf_list_next (&fp->ctf_in_flight_dynsyms); did != NULL; did = nid) + { + nid = ctf_list_next (did); + ctf_list_delete (&fp->ctf_in_flight_dynsyms, did); + free (did); + } - if (fp->ctf_data.cts_name != _CTF_NULLSTR && - fp->ctf_data.cts_name != NULL) - ctf_free ((char *) fp->ctf_data.cts_name); + ctf_str_free_atoms (fp); + free (fp->ctf_tmp_typeslice); - if (fp->ctf_symtab.cts_name != _CTF_NULLSTR && - fp->ctf_symtab.cts_name != NULL) - ctf_free ((char *) fp->ctf_symtab.cts_name); + if (fp->ctf_data.cts_name != _CTF_NULLSTR) + free ((char *) fp->ctf_data.cts_name); - if (fp->ctf_strtab.cts_name != _CTF_NULLSTR && - fp->ctf_strtab.cts_name != NULL) - ctf_free ((char *) fp->ctf_strtab.cts_name); + if (fp->ctf_symtab.cts_name != _CTF_NULLSTR) + free ((char *) fp->ctf_symtab.cts_name); + if (fp->ctf_strtab.cts_name != _CTF_NULLSTR) + free ((char *) fp->ctf_strtab.cts_name); else if (fp->ctf_data_mmapped) ctf_munmap (fp->ctf_data_mmapped, fp->ctf_data_mmapped_len); - ctf_free_base (fp, NULL, 0); + free (fp->ctf_dynbase); - if (fp->ctf_sxlate != NULL) - ctf_free (fp->ctf_sxlate); + ctf_dynhash_destroy (fp->ctf_syn_ext_strtab); + ctf_dynhash_destroy (fp->ctf_link_inputs); + ctf_dynhash_destroy (fp->ctf_link_outputs); + ctf_dynhash_destroy (fp->ctf_link_type_mapping); + ctf_dynhash_destroy (fp->ctf_link_in_cu_mapping); + ctf_dynhash_destroy (fp->ctf_link_out_cu_mapping); + ctf_dynhash_destroy (fp->ctf_add_processing); + ctf_dedup_fini (fp, NULL, 0); + ctf_dynset_destroy (fp->ctf_dedup_atoms_alloc); - if (fp->ctf_txlate != NULL) - ctf_free (fp->ctf_txlate); + for (err = ctf_list_next (&fp->ctf_errs_warnings); err != NULL; err = nerr) + { + nerr = ctf_list_next (err); + ctf_list_delete (&fp->ctf_errs_warnings, err); + free (err->cew_text); + free (err); + } - if (fp->ctf_ptrtab != NULL) - ctf_free (fp->ctf_ptrtab); + free (fp->ctf_sxlate); + free (fp->ctf_txlate); + free (fp->ctf_ptrtab); + free (fp->ctf_pptrtab); - ctf_hash_destroy (fp->ctf_structs); - ctf_hash_destroy (fp->ctf_unions); - ctf_hash_destroy (fp->ctf_enums); - ctf_hash_destroy (fp->ctf_names); + free (fp->ctf_header); + free (fp); +} - ctf_free (fp); +/* Backward compatibility. */ +void +ctf_file_close (ctf_file_t *fp) +{ + ctf_dict_close (fp); } /* The converse of ctf_open(). ctf_open() disguises whatever it opens as an @@ -1580,57 +1857,115 @@ ctf_close (ctf_archive_t *arc) ctf_arc_close (arc); } -/* Get the CTF archive from which this ctf_file_t is derived. */ +/* Get the CTF archive from which this ctf_dict_t is derived. */ ctf_archive_t * -ctf_get_arc (const ctf_file_t *fp) +ctf_get_arc (const ctf_dict_t *fp) { return fp->ctf_archive; } /* Return the ctfsect out of the core ctf_impl. Useful for freeing the - ctfsect's data * after ctf_file_close(), which is why we return the actual + ctfsect's data * after ctf_dict_close(), which is why we return the actual structure, not a pointer to it, since that is likely to become a pointer to freed data before the return value is used under the expected use case of - ctf_getsect()/ ctf_file_close()/free(). */ -extern ctf_sect_t -ctf_getdatasect (const ctf_file_t *fp) + ctf_getsect()/ ctf_dict_close()/free(). */ +ctf_sect_t +ctf_getdatasect (const ctf_dict_t *fp) { return fp->ctf_data; } -/* Return the CTF handle for the parent CTF container, if one exists. - Otherwise return NULL to indicate this container has no imported parent. */ -ctf_file_t * -ctf_parent_file (ctf_file_t *fp) +ctf_sect_t +ctf_getsymsect (const ctf_dict_t *fp) +{ + return fp->ctf_symtab; +} + +ctf_sect_t +ctf_getstrsect (const ctf_dict_t *fp) +{ + return fp->ctf_strtab; +} + +/* Set the endianness of the symbol table attached to FP. */ +void +ctf_symsect_endianness (ctf_dict_t *fp, int little_endian) +{ + int old_endianness = fp->ctf_symsect_little_endian; + + fp->ctf_symsect_little_endian = !!little_endian; + + /* If we already have a symtab translation table, we need to repopulate it if + our idea of the endianness has changed. */ + + if (old_endianness != fp->ctf_symsect_little_endian + && fp->ctf_sxlate != NULL && fp->ctf_symtab.cts_data != NULL) + assert (init_symtab (fp, fp->ctf_header, &fp->ctf_symtab) == 0); +} + +/* Return the CTF handle for the parent CTF dict, if one exists. Otherwise + return NULL to indicate this dict has no imported parent. */ +ctf_dict_t * +ctf_parent_dict (ctf_dict_t *fp) { return fp->ctf_parent; } -/* Return the name of the parent CTF container, if one exists. Otherwise - return NULL to indicate this container is a root container. */ +/* Backward compatibility. */ +ctf_dict_t * +ctf_parent_file (ctf_dict_t *fp) +{ + return ctf_parent_dict (fp); +} + +/* Return the name of the parent CTF dict, if one exists, or NULL otherwise. */ const char * -ctf_parent_name (ctf_file_t *fp) +ctf_parent_name (ctf_dict_t *fp) { return fp->ctf_parname; } /* Set the parent name. It is an error to call this routine without calling ctf_import() at some point. */ -void -ctf_parent_name_set (ctf_file_t *fp, const char *name) +int +ctf_parent_name_set (ctf_dict_t *fp, const char *name) { if (fp->ctf_dynparname != NULL) - ctf_free (fp->ctf_dynparname); + free (fp->ctf_dynparname); - fp->ctf_dynparname = ctf_strdup (name); + if ((fp->ctf_dynparname = strdup (name)) == NULL) + return (ctf_set_errno (fp, ENOMEM)); fp->ctf_parname = fp->ctf_dynparname; + return 0; +} + +/* Return the name of the compilation unit this CTF file applies to. Usually + non-NULL only for non-parent dicts. */ +const char * +ctf_cuname (ctf_dict_t *fp) +{ + return fp->ctf_cuname; +} + +/* Set the compilation unit name. */ +int +ctf_cuname_set (ctf_dict_t *fp, const char *name) +{ + if (fp->ctf_dyncuname != NULL) + free (fp->ctf_dyncuname); + + if ((fp->ctf_dyncuname = strdup (name)) == NULL) + return (ctf_set_errno (fp, ENOMEM)); + fp->ctf_cuname = fp->ctf_dyncuname; + return 0; } -/* Import the types from the specified parent container by storing a pointer - to it in ctf_parent and incrementing its reference count. Only one parent - is allowed: if a parent already exists, it is replaced by the new parent. */ +/* Import the types from the specified parent dict by storing a pointer to it in + ctf_parent and incrementing its reference count. Only one parent is allowed: + if a parent already exists, it is replaced by the new parent. The pptrtab + is wiped, and will be refreshed by the next ctf_lookup_by_name call. */ int -ctf_import (ctf_file_t *fp, ctf_file_t *pfp) +ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp) { if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0)) return (ctf_set_errno (fp, EINVAL)); @@ -1638,24 +1973,72 @@ ctf_import (ctf_file_t *fp, ctf_file_t *pfp) if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel) return (ctf_set_errno (fp, ECTF_DMODEL)); - if (fp->ctf_parent != NULL) - ctf_file_close (fp->ctf_parent); + if (fp->ctf_parent && !fp->ctf_parent_unreffed) + ctf_dict_close (fp->ctf_parent); + fp->ctf_parent = NULL; + + free (fp->ctf_pptrtab); + fp->ctf_pptrtab = NULL; + fp->ctf_pptrtab_len = 0; + fp->ctf_pptrtab_typemax = 0; if (pfp != NULL) { + int err; + + if (fp->ctf_parname == NULL) + if ((err = ctf_parent_name_set (fp, "PARENT")) < 0) + return err; + fp->ctf_flags |= LCTF_CHILD; pfp->ctf_refcnt++; + fp->ctf_parent_unreffed = 0; + } + + fp->ctf_parent = pfp; + return 0; +} + +/* Like ctf_import, but does not increment the refcount on the imported parent + or close it at any point: as a result it can go away at any time and the + caller must do all freeing itself. Used internally to avoid refcount + loops. */ +int +ctf_import_unref (ctf_dict_t *fp, ctf_dict_t *pfp) +{ + if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0)) + return (ctf_set_errno (fp, EINVAL)); + + if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel) + return (ctf_set_errno (fp, ECTF_DMODEL)); + + if (fp->ctf_parent && !fp->ctf_parent_unreffed) + ctf_dict_close (fp->ctf_parent); + fp->ctf_parent = NULL; + + free (fp->ctf_pptrtab); + fp->ctf_pptrtab = NULL; + fp->ctf_pptrtab_len = 0; + fp->ctf_pptrtab_typemax = 0; + if (pfp != NULL) + { + int err; if (fp->ctf_parname == NULL) - ctf_parent_name_set (fp, "PARENT"); + if ((err = ctf_parent_name_set (fp, "PARENT")) < 0) + return err; + + fp->ctf_flags |= LCTF_CHILD; + fp->ctf_parent_unreffed = 1; } + fp->ctf_parent = pfp; return 0; } -/* Set the data model constant for the CTF container. */ +/* Set the data model constant for the CTF dict. */ int -ctf_setmodel (ctf_file_t *fp, int model) +ctf_setmodel (ctf_dict_t *fp, int model) { const ctf_dmodel_t *dp; @@ -1671,21 +2054,24 @@ ctf_setmodel (ctf_file_t *fp, int model) return (ctf_set_errno (fp, EINVAL)); } -/* Return the data model constant for the CTF container. */ +/* Return the data model constant for the CTF dict. */ int -ctf_getmodel (ctf_file_t *fp) +ctf_getmodel (ctf_dict_t *fp) { return fp->ctf_dmodel->ctd_code; } +/* The caller can hang an arbitrary pointer off each ctf_dict_t using this + function. */ void -ctf_setspecific (ctf_file_t *fp, void *data) +ctf_setspecific (ctf_dict_t *fp, void *data) { fp->ctf_specific = data; } +/* Retrieve the arbitrary pointer again. */ void * -ctf_getspecific (ctf_file_t *fp) +ctf_getspecific (ctf_dict_t *fp) { return fp->ctf_specific; }