diff options
Diffstat (limited to 'libctf')
-rw-r--r-- | libctf/ChangeLog | 6 | ||||
-rw-r--r-- | libctf/ctf-decl.c | 195 | ||||
-rw-r--r-- | libctf/ctf-impl.h | 8 | ||||
-rw-r--r-- | libctf/ctf-types.c | 1023 |
4 files changed, 1232 insertions, 0 deletions
diff --git a/libctf/ChangeLog b/libctf/ChangeLog index 23a8b89..56d6016 100644 --- a/libctf/ChangeLog +++ b/libctf/ChangeLog @@ -1,5 +1,11 @@ 2019-05-28 Nick Alcock <nick.alcock@oracle.com> + * ctf-decl.c: New file. + * ctf-types.c: Likewise. + * ctf-impl.h: New declarations. + +2019-05-28 Nick Alcock <nick.alcock@oracle.com> + * ctf-open-bfd.c: New file. * ctf-open.c (ctf_close): New. * ctf-impl.h: Include bfd.h. diff --git a/libctf/ctf-decl.c b/libctf/ctf-decl.c new file mode 100644 index 0000000..c85982e --- /dev/null +++ b/libctf/ctf-decl.c @@ -0,0 +1,195 @@ +/* C declarator syntax glue. + Copyright (C) 2019 Free Software Foundation, Inc. + + This file is part of libctf. + + libctf is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. */ + +/* CTF Declaration Stack + + In order to implement ctf_type_name(), we must convert a type graph back + into a C type declaration. Unfortunately, a type graph represents a storage + class ordering of the type whereas a type declaration must obey the C rules + for operator precedence, and the two orderings are frequently in conflict. + For example, consider these CTF type graphs and their C declarations: + + CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)() + CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[] + + In each case, parentheses are used to raise operator * to higher lexical + precedence, so the string form of the C declaration cannot be constructed by + walking the type graph links and forming the string from left to right. + + The functions in this file build a set of stacks from the type graph nodes + corresponding to the C operator precedence levels in the appropriate order. + The code in ctf_type_name() can then iterate over the levels and nodes in + lexical precedence order and construct the final C declaration string. */ + +#include <ctf-impl.h> +#include <string.h> + +void +ctf_decl_init (ctf_decl_t *cd) +{ + int i; + + memset (cd, 0, sizeof (ctf_decl_t)); + + for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) + cd->cd_order[i] = CTF_PREC_BASE - 1; + + cd->cd_qualp = CTF_PREC_BASE; + cd->cd_ordp = CTF_PREC_BASE; +} + +void +ctf_decl_fini (ctf_decl_t *cd) +{ + ctf_decl_node_t *cdp, *ndp; + int i; + + for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) + { + for (cdp = ctf_list_next (&cd->cd_nodes[i]); cdp != NULL; cdp = ndp) + { + ndp = ctf_list_next (cdp); + ctf_free (cdp); + } + } +} + +void +ctf_decl_push (ctf_decl_t *cd, ctf_file_t *fp, ctf_id_t type) +{ + ctf_decl_node_t *cdp; + ctf_decl_prec_t prec; + uint32_t kind, n = 1; + int is_qual = 0; + + const ctf_type_t *tp; + ctf_arinfo_t ar; + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + { + cd->cd_err = fp->ctf_errno; + return; + } + + switch (kind = LCTF_INFO_KIND (fp, tp->ctt_info)) + { + case CTF_K_ARRAY: + (void) ctf_array_info (fp, type, &ar); + ctf_decl_push (cd, fp, ar.ctr_contents); + n = ar.ctr_nelems; + prec = CTF_PREC_ARRAY; + break; + + case CTF_K_TYPEDEF: + if (ctf_strptr (fp, tp->ctt_name)[0] == '\0') + { + ctf_decl_push (cd, fp, tp->ctt_type); + return; + } + prec = CTF_PREC_BASE; + break; + + case CTF_K_FUNCTION: + ctf_decl_push (cd, fp, tp->ctt_type); + prec = CTF_PREC_FUNCTION; + break; + + case CTF_K_POINTER: + ctf_decl_push (cd, fp, tp->ctt_type); + prec = CTF_PREC_POINTER; + break; + + case CTF_K_SLICE: + ctf_decl_push (cd, fp, ctf_type_reference (fp, type)); + prec = CTF_PREC_BASE; + break; + + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + ctf_decl_push (cd, fp, tp->ctt_type); + prec = cd->cd_qualp; + is_qual++; + break; + + default: + prec = CTF_PREC_BASE; + } + + if ((cdp = ctf_alloc (sizeof (ctf_decl_node_t))) == NULL) + { + cd->cd_err = EAGAIN; + return; + } + + cdp->cd_type = type; + cdp->cd_kind = kind; + cdp->cd_n = n; + + if (ctf_list_next (&cd->cd_nodes[prec]) == NULL) + cd->cd_order[prec] = cd->cd_ordp++; + + /* Reset cd_qualp to the highest precedence level that we've seen so + far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER). */ + + if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY) + cd->cd_qualp = prec; + + /* C array declarators are ordered inside out so prepend them. Also by + convention qualifiers of base types precede the type specifier (e.g. + const int vs. int const) even though the two forms are equivalent. */ + + if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE)) + ctf_list_prepend (&cd->cd_nodes[prec], cdp); + else + ctf_list_append (&cd->cd_nodes[prec], cdp); +} + +_libctf_printflike_ (2, 3) +void ctf_decl_sprintf (ctf_decl_t *cd, const char *format, ...) +{ + va_list ap; + char *str; + int n; + + if (cd->cd_enomem) + return; + + va_start (ap, format); + n = vasprintf (&str, format, ap); + va_end (ap); + + if (n > 0) + cd->cd_buf = ctf_str_append (cd->cd_buf, str); + + /* Sticky error condition. */ + if (n < 0) + { + free (cd->cd_buf); + cd->cd_buf = NULL; + cd->cd_enomem = 1; + } + + free (str); +} + +char *ctf_decl_buf (ctf_decl_t *cd) +{ + return cd->cd_buf; +} diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index 8522a03..cd84505 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -324,6 +324,14 @@ extern void ctf_dvd_insert (ctf_file_t *, ctf_dvdef_t *); extern void ctf_dvd_delete (ctf_file_t *, ctf_dvdef_t *); extern ctf_dvdef_t *ctf_dvd_lookup (const ctf_file_t *, const char *); +extern void ctf_decl_init (ctf_decl_t *); +extern void ctf_decl_fini (ctf_decl_t *); +extern void ctf_decl_push (ctf_decl_t *, ctf_file_t *, ctf_id_t); + +_libctf_printflike_ (2, 3) +extern void ctf_decl_sprintf (ctf_decl_t *, const char *, ...); +extern char *ctf_decl_buf (ctf_decl_t *cd); + extern const char *ctf_strraw (ctf_file_t *, uint32_t); extern const char *ctf_strptr (ctf_file_t *, uint32_t); diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c new file mode 100644 index 0000000..a7fe5d0 --- /dev/null +++ b/libctf/ctf-types.c @@ -0,0 +1,1023 @@ +/* Type handling functions. + Copyright (C) 2019 Free Software Foundation, Inc. + + This file is part of libctf. + + libctf is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. */ + +#include <ctf-impl.h> +#include <string.h> + +/* Determine whether a type is a parent or a child. */ + +int +ctf_type_isparent (ctf_file_t *fp, ctf_id_t id) +{ + return (LCTF_TYPE_ISPARENT (fp, id)); +} + +int +ctf_type_ischild (ctf_file_t * fp, ctf_id_t id) +{ + return (LCTF_TYPE_ISCHILD (fp, id)); +} + +/* Iterate over the members of a STRUCT or UNION. We pass the name, member + type, and offset of each member to the specified callback function. */ + +int +ctf_member_iter (ctf_file_t *fp, ctf_id_t type, ctf_member_f *func, void *arg) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + ssize_t size, increment; + uint32_t kind, n; + int rc; + + if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) + return CTF_ERR; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + (void) ctf_get_ctt_size (fp, tp, &size, &increment); + kind = LCTF_INFO_KIND (fp, tp->ctt_info); + + if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) + return (ctf_set_errno (ofp, ECTF_NOTSOU)); + + if (size < CTF_LSTRUCT_THRESH) + { + const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t) tp + + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++) + { + const char *name = ctf_strptr (fp, mp->ctm_name); + if ((rc = func (name, mp->ctm_type, mp->ctm_offset, arg)) != 0) + return rc; + } + + } + else + { + const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t) tp + + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++) + { + const char *name = ctf_strptr (fp, lmp->ctlm_name); + if ((rc = func (name, lmp->ctlm_type, + (unsigned long) CTF_LMEM_OFFSET (lmp), arg)) != 0) + return rc; + } + } + + return 0; +} + +/* Iterate over the members of an ENUM. We pass the string name and associated + integer value of each enum element to the specified callback function. */ + +int +ctf_enum_iter (ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + const ctf_enum_t *ep; + ssize_t increment; + uint32_t n; + int rc; + + if ((type = ctf_type_resolve_unsliced (fp, type)) == CTF_ERR) + return CTF_ERR; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ENUM) + return (ctf_set_errno (ofp, ECTF_NOTENUM)); + + (void) ctf_get_ctt_size (fp, tp, NULL, &increment); + + ep = (const ctf_enum_t *) ((uintptr_t) tp + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++) + { + const char *name = ctf_strptr (fp, ep->cte_name); + if ((rc = func (name, ep->cte_value, arg)) != 0) + return rc; + } + + return 0; +} + +/* Iterate over every root (user-visible) type in the given CTF container. + We pass the type ID of each type to the specified callback function. */ + +int +ctf_type_iter (ctf_file_t *fp, ctf_type_f *func, void *arg) +{ + ctf_id_t id, max = fp->ctf_typemax; + int rc, child = (fp->ctf_flags & LCTF_CHILD); + + for (id = 1; id <= max; id++) + { + const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR (fp, id); + if (LCTF_INFO_ISROOT (fp, tp->ctt_info) + && (rc = func (LCTF_INDEX_TO_TYPE (fp, id, child), arg)) != 0) + return rc; + } + + return 0; +} + +/* Iterate over every variable in the given CTF container, in arbitrary order. + We pass the name of each variable to the specified callback function. */ + +int +ctf_variable_iter (ctf_file_t *fp, ctf_variable_f *func, void *arg) +{ + unsigned long i; + int rc; + + if ((fp->ctf_flags & LCTF_CHILD) && (fp->ctf_parent == NULL)) + return ECTF_NOPARENT; + + for (i = 0; i < fp->ctf_nvars; i++) + if ((rc = func (ctf_strptr (fp, fp->ctf_vars[i].ctv_name), + fp->ctf_vars[i].ctv_type, arg)) != 0) + return rc; + + return 0; +} + +/* Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and + RESTRICT nodes until we reach a "base" type node. This is useful when + we want to follow a type ID to a node that has members or a size. To guard + against infinite loops, we implement simplified cycle detection and check + each link against itself, the previous node, and the topmost node. + + Does not drill down through slices to their contained type. */ + +ctf_id_t +ctf_type_resolve (ctf_file_t *fp, ctf_id_t type) +{ + ctf_id_t prev = type, otype = type; + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + + while ((tp = ctf_lookup_by_id (&fp, type)) != NULL) + { + switch (LCTF_INFO_KIND (fp, tp->ctt_info)) + { + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + if (tp->ctt_type == type || tp->ctt_type == otype + || tp->ctt_type == prev) + { + ctf_dprintf ("type %ld cycle detected\n", otype); + return (ctf_set_errno (ofp, ECTF_CORRUPT)); + } + prev = type; + type = tp->ctt_type; + break; + default: + return type; + } + } + + return CTF_ERR; /* errno is set for us. */ +} + +/* Like ctf_type_resolve(), but traverse down through slices to their contained + type. */ + +ctf_id_t +ctf_type_resolve_unsliced (ctf_file_t *fp, ctf_id_t type) +{ + const ctf_type_t *tp; + + if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) + return -1; + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if ((LCTF_INFO_KIND (fp, tp->ctt_info)) == CTF_K_SLICE) + return ctf_type_reference (fp, type); + return type; +} + +/* Lookup the given type ID and return its name as a new dynamcally-allocated + string. */ + +char * +ctf_type_aname (ctf_file_t *fp, ctf_id_t type) +{ + ctf_decl_t cd; + ctf_decl_node_t *cdp; + ctf_decl_prec_t prec, lp, rp; + int ptr, arr; + uint32_t k; + char *buf; + + if (fp == NULL && type == CTF_ERR) + return NULL; /* Simplify caller code by permitting CTF_ERR. */ + + ctf_decl_init (&cd); + ctf_decl_push (&cd, fp, type); + + if (cd.cd_err != 0) + { + ctf_decl_fini (&cd); + ctf_set_errno (fp, cd.cd_err); + return NULL; + } + + /* If the type graph's order conflicts with lexical precedence order + for pointers or arrays, then we need to surround the declarations at + the corresponding lexical precedence with parentheses. This can + result in either a parenthesized pointer (*) as in int (*)() or + int (*)[], or in a parenthesized pointer and array as in int (*[])(). */ + + ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER; + arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY; + + rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1; + lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1; + + k = CTF_K_POINTER; /* Avoid leading whitespace (see below). */ + + for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) + { + for (cdp = ctf_list_next (&cd.cd_nodes[prec]); + cdp != NULL; cdp = ctf_list_next (cdp)) + { + ctf_file_t *rfp = fp; + const ctf_type_t *tp = ctf_lookup_by_id (&rfp, cdp->cd_type); + const char *name = ctf_strptr (rfp, tp->ctt_name); + + if (k != CTF_K_POINTER && k != CTF_K_ARRAY) + ctf_decl_sprintf (&cd, " "); + + if (lp == prec) + { + ctf_decl_sprintf (&cd, "("); + lp = -1; + } + + switch (cdp->cd_kind) + { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + case CTF_K_TYPEDEF: + ctf_decl_sprintf (&cd, "%s", name); + break; + case CTF_K_POINTER: + ctf_decl_sprintf (&cd, "*"); + break; + case CTF_K_ARRAY: + ctf_decl_sprintf (&cd, "[%u]", cdp->cd_n); + break; + case CTF_K_FUNCTION: + ctf_decl_sprintf (&cd, "()"); + break; + case CTF_K_STRUCT: + case CTF_K_FORWARD: + ctf_decl_sprintf (&cd, "struct %s", name); + break; + case CTF_K_UNION: + ctf_decl_sprintf (&cd, "union %s", name); + break; + case CTF_K_ENUM: + ctf_decl_sprintf (&cd, "enum %s", name); + break; + case CTF_K_VOLATILE: + ctf_decl_sprintf (&cd, "volatile"); + break; + case CTF_K_CONST: + ctf_decl_sprintf (&cd, "const"); + break; + case CTF_K_RESTRICT: + ctf_decl_sprintf (&cd, "restrict"); + break; + case CTF_K_SLICE: + /* No representation: just changes encoding of contained type, + which is not in any case printed. Skip it. */ + break; + } + + k = cdp->cd_kind; + } + + if (rp == prec) + ctf_decl_sprintf (&cd, ")"); + } + + if (cd.cd_enomem) + (void) ctf_set_errno (fp, ENOMEM); + + buf = ctf_decl_buf (&cd); + + ctf_decl_fini (&cd); + return buf; +} + +/* Lookup the given type ID and print a string name for it into buf. Return + the actual number of bytes (not including \0) needed to format the name. */ + +ssize_t +ctf_type_lname (ctf_file_t *fp, ctf_id_t type, char *buf, size_t len) +{ + char *str = ctf_type_aname (fp, type); + size_t slen = strlen (str); + + if (str == NULL) + return CTF_ERR; /* errno is set for us */ + + snprintf (buf, len, "%s", str); + free (str); + + if (slen >= len) + (void) ctf_set_errno (fp, ECTF_NAMELEN); + + return slen; +} + +/* Lookup the given type ID and print a string name for it into buf. If buf + is too small, return NULL: the ECTF_NAMELEN error is set on 'fp' for us. */ + +char * +ctf_type_name (ctf_file_t *fp, ctf_id_t type, char *buf, size_t len) +{ + ssize_t rv = ctf_type_lname (fp, type, buf, len); + return (rv >= 0 && (size_t) rv < len ? buf : NULL); +} + +/* Resolve the type down to a base type node, and then return the size + of the type storage in bytes. */ + +ssize_t +ctf_type_size (ctf_file_t *fp, ctf_id_t type) +{ + const ctf_type_t *tp; + ssize_t size; + ctf_arinfo_t ar; + + if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) + return -1; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return -1; /* errno is set for us. */ + + switch (LCTF_INFO_KIND (fp, tp->ctt_info)) + { + case CTF_K_POINTER: + return fp->ctf_dmodel->ctd_pointer; + + case CTF_K_FUNCTION: + return 0; /* Function size is only known by symtab. */ + + case CTF_K_ENUM: + return fp->ctf_dmodel->ctd_int; + + case CTF_K_ARRAY: + /* ctf_add_array() does not directly encode the element size, but + requires the user to multiply to determine the element size. + + If ctf_get_ctt_size() returns nonzero, then use the recorded + size instead. */ + + if ((size = ctf_get_ctt_size (fp, tp, NULL, NULL)) > 0) + return size; + + if (ctf_array_info (fp, type, &ar) == CTF_ERR + || (size = ctf_type_size (fp, ar.ctr_contents)) == CTF_ERR) + return -1; /* errno is set for us. */ + + return size * ar.ctr_nelems; + + default: /* including slices of enums, etc */ + return (ctf_get_ctt_size (fp, tp, NULL, NULL)); + } +} + +/* Resolve the type down to a base type node, and then return the alignment + needed for the type storage in bytes. + + XXX may need arch-dependent attention. */ + +ssize_t +ctf_type_align (ctf_file_t *fp, ctf_id_t type) +{ + const ctf_type_t *tp; + ctf_file_t *ofp = fp; + int kind; + + if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) + return -1; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return -1; /* errno is set for us. */ + + kind = LCTF_INFO_KIND (fp, tp->ctt_info); + switch (kind) + { + case CTF_K_POINTER: + case CTF_K_FUNCTION: + return fp->ctf_dmodel->ctd_pointer; + + case CTF_K_ARRAY: + { + ctf_arinfo_t r; + if (ctf_array_info (fp, type, &r) == CTF_ERR) + return -1; /* errno is set for us. */ + return (ctf_type_align (fp, r.ctr_contents)); + } + + case CTF_K_STRUCT: + case CTF_K_UNION: + { + size_t align = 0; + ctf_dtdef_t *dtd; + + if ((dtd = ctf_dynamic_type (ofp, type)) == NULL) + { + uint32_t n = LCTF_INFO_VLEN (fp, tp->ctt_info); + ssize_t size, increment; + const void *vmp; + + (void) ctf_get_ctt_size (fp, tp, &size, &increment); + vmp = (unsigned char *) tp + increment; + + if (kind == CTF_K_STRUCT) + n = MIN (n, 1); /* Only use first member for structs. */ + + if (size < CTF_LSTRUCT_THRESH) + { + const ctf_member_t *mp = vmp; + for (; n != 0; n--, mp++) + { + ssize_t am = ctf_type_align (fp, mp->ctm_type); + align = MAX (align, am); + } + } + else + { + const ctf_lmember_t *lmp = vmp; + for (; n != 0; n--, lmp++) + { + ssize_t am = ctf_type_align (fp, lmp->ctlm_type); + align = MAX (align, am); + } + } + } + else + { + ctf_dmdef_t *dmd; + + for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members); + dmd != NULL; dmd = ctf_list_next (dmd)) + { + ssize_t am = ctf_type_align (fp, dmd->dmd_type); + align = MAX (align, am); + if (kind == CTF_K_STRUCT) + break; + } + } + + return align; + } + + case CTF_K_ENUM: + return fp->ctf_dmodel->ctd_int; + + default: /* including slices of enums, etc */ + return (ctf_get_ctt_size (fp, tp, NULL, NULL)); + } +} + +/* Return the kind (CTF_K_* constant) for the specified type ID. */ + +int +ctf_type_kind_unsliced (ctf_file_t *fp, ctf_id_t type) +{ + const ctf_type_t *tp; + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + return (LCTF_INFO_KIND (fp, tp->ctt_info)); +} + +/* Return the kind (CTF_K_* constant) for the specified type ID. + Slices are considered to be of the same kind as the type sliced. */ + +int +ctf_type_kind (ctf_file_t *fp, ctf_id_t type) +{ + int kind; + + if ((kind = ctf_type_kind_unsliced (fp, type)) == CTF_ERR) + return CTF_ERR; + + if (kind == CTF_K_SLICE) + { + if ((type = ctf_type_reference (fp, type)) == CTF_ERR) + return CTF_ERR; + kind = ctf_type_kind_unsliced (fp, type); + } + + return kind; +} + +/* If the type is one that directly references another type (such as POINTER), + then return the ID of the type to which it refers. */ + +ctf_id_t +ctf_type_reference (ctf_file_t *fp, ctf_id_t type) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + switch (LCTF_INFO_KIND (fp, tp->ctt_info)) + { + case CTF_K_POINTER: + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + return tp->ctt_type; + /* Slices store their type in an unusual place. */ + case CTF_K_SLICE: + { + const ctf_slice_t *sp; + ssize_t increment; + (void) ctf_get_ctt_size (fp, tp, NULL, &increment); + sp = (const ctf_slice_t *) ((uintptr_t) tp + increment); + return sp->cts_type; + } + default: + return (ctf_set_errno (ofp, ECTF_NOTREF)); + } +} + +/* Find a pointer to type by looking in fp->ctf_ptrtab. If we can't find a + pointer to the given type, see if we can compute a pointer to the type + resulting from resolving the type down to its base type and use that + instead. This helps with cases where the CTF data includes "struct foo *" + but not "foo_t *" and the user accesses "foo_t *" in the debugger. + + XXX what about parent containers? */ + +ctf_id_t +ctf_type_pointer (ctf_file_t *fp, ctf_id_t type) +{ + ctf_file_t *ofp = fp; + ctf_id_t ntype; + + if (ctf_lookup_by_id (&fp, type) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if ((ntype = fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, type)]) != 0) + return (LCTF_INDEX_TO_TYPE (fp, ntype, (fp->ctf_flags & LCTF_CHILD))); + + if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) + return (ctf_set_errno (ofp, ECTF_NOTYPE)); + + if (ctf_lookup_by_id (&fp, type) == NULL) + return (ctf_set_errno (ofp, ECTF_NOTYPE)); + + if ((ntype = fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, type)]) != 0) + return (LCTF_INDEX_TO_TYPE (fp, ntype, (fp->ctf_flags & LCTF_CHILD))); + + return (ctf_set_errno (ofp, ECTF_NOTYPE)); +} + +/* Return the encoding for the specified INTEGER or FLOAT. */ + +int +ctf_type_encoding (ctf_file_t *fp, ctf_id_t type, ctf_encoding_t *ep) +{ + ctf_file_t *ofp = fp; + ctf_dtdef_t *dtd; + const ctf_type_t *tp; + ssize_t increment; + uint32_t data; + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if ((dtd = ctf_dynamic_type (ofp, type)) != NULL) + { + *ep = dtd->dtd_u.dtu_enc; + return 0; + } + + (void) ctf_get_ctt_size (fp, tp, NULL, &increment); + + switch (LCTF_INFO_KIND (fp, tp->ctt_info)) + { + case CTF_K_INTEGER: + data = *(const uint32_t *) ((uintptr_t) tp + increment); + ep->cte_format = CTF_INT_ENCODING (data); + ep->cte_offset = CTF_INT_OFFSET (data); + ep->cte_bits = CTF_INT_BITS (data); + break; + case CTF_K_FLOAT: + data = *(const uint32_t *) ((uintptr_t) tp + increment); + ep->cte_format = CTF_FP_ENCODING (data); + ep->cte_offset = CTF_FP_OFFSET (data); + ep->cte_bits = CTF_FP_BITS (data); + break; + case CTF_K_SLICE: + { + const ctf_slice_t *slice; + ctf_encoding_t underlying_en; + + slice = (ctf_slice_t *) ((uintptr_t) tp + increment); + data = ctf_type_encoding (fp, slice->cts_type, &underlying_en); + + ep->cte_format = underlying_en.cte_format; + ep->cte_offset = slice->cts_offset; + ep->cte_bits = slice->cts_bits; + break; + } + default: + return (ctf_set_errno (ofp, ECTF_NOTINTFP)); + } + + return 0; +} + +int +ctf_type_cmp (ctf_file_t *lfp, ctf_id_t ltype, ctf_file_t *rfp, + ctf_id_t rtype) +{ + int rval; + + if (ltype < rtype) + rval = -1; + else if (ltype > rtype) + rval = 1; + else + rval = 0; + + if (lfp == rfp) + return rval; + + if (LCTF_TYPE_ISPARENT (lfp, ltype) && lfp->ctf_parent != NULL) + lfp = lfp->ctf_parent; + + if (LCTF_TYPE_ISPARENT (rfp, rtype) && rfp->ctf_parent != NULL) + rfp = rfp->ctf_parent; + + if (lfp < rfp) + return -1; + + if (lfp > rfp) + return 1; + + return rval; +} + +/* Return a boolean value indicating if two types are compatible. This function + returns true if the two types are the same, or if they (or their ultimate + base type) have the same encoding properties, or (for structs / unions / + enums / forward declarations) if they have the same name and (for structs / + unions) member count. */ + +int +ctf_type_compat (ctf_file_t *lfp, ctf_id_t ltype, + ctf_file_t *rfp, ctf_id_t rtype) +{ + const ctf_type_t *ltp, *rtp; + ctf_encoding_t le, re; + ctf_arinfo_t la, ra; + uint32_t lkind, rkind; + int same_names = 0; + + if (ctf_type_cmp (lfp, ltype, rfp, rtype) == 0) + return 1; + + ltype = ctf_type_resolve (lfp, ltype); + lkind = ctf_type_kind (lfp, ltype); + + rtype = ctf_type_resolve (rfp, rtype); + rkind = ctf_type_kind (rfp, rtype); + + ltp = ctf_lookup_by_id (&lfp, ltype); + rtp = ctf_lookup_by_id (&rfp, rtype); + + if (ltp != NULL && rtp != NULL) + same_names = (strcmp (ctf_strptr (lfp, ltp->ctt_name), + ctf_strptr (rfp, rtp->ctt_name)) == 0); + + if (((lkind == CTF_K_ENUM) && (rkind == CTF_K_INTEGER)) || + ((rkind == CTF_K_ENUM) && (lkind == CTF_K_INTEGER))) + return 1; + + if (lkind != rkind) + return 0; + + switch (lkind) + { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + memset (&le, 0, sizeof (le)); + memset (&re, 0, sizeof (re)); + return (ctf_type_encoding (lfp, ltype, &le) == 0 + && ctf_type_encoding (rfp, rtype, &re) == 0 + && memcmp (&le, &re, sizeof (ctf_encoding_t)) == 0); + case CTF_K_POINTER: + return (ctf_type_compat (lfp, ctf_type_reference (lfp, ltype), + rfp, ctf_type_reference (rfp, rtype))); + case CTF_K_ARRAY: + return (ctf_array_info (lfp, ltype, &la) == 0 + && ctf_array_info (rfp, rtype, &ra) == 0 + && la.ctr_nelems == ra.ctr_nelems + && ctf_type_compat (lfp, la.ctr_contents, rfp, ra.ctr_contents) + && ctf_type_compat (lfp, la.ctr_index, rfp, ra.ctr_index)); + case CTF_K_STRUCT: + case CTF_K_UNION: + return (same_names && (ctf_type_size (lfp, ltype) + == ctf_type_size (rfp, rtype))); + case CTF_K_ENUM: + { + int lencoded, rencoded; + lencoded = ctf_type_encoding (lfp, ltype, &le); + rencoded = ctf_type_encoding (rfp, rtype, &re); + + if ((lencoded != rencoded) || + ((lencoded == 0) && memcmp (&le, &re, sizeof (ctf_encoding_t)) != 0)) + return 0; + } + /* FALLTHRU */ + case CTF_K_FORWARD: + return same_names; /* No other checks required for these type kinds. */ + default: + return 0; /* Should not get here since we did a resolve. */ + } +} + +/* Return the type and offset for a given member of a STRUCT or UNION. */ + +int +ctf_member_info (ctf_file_t *fp, ctf_id_t type, const char *name, + ctf_membinfo_t *mip) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + ssize_t size, increment; + uint32_t kind, n; + + if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) + return CTF_ERR; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + (void) ctf_get_ctt_size (fp, tp, &size, &increment); + kind = LCTF_INFO_KIND (fp, tp->ctt_info); + + if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) + return (ctf_set_errno (ofp, ECTF_NOTSOU)); + + if (size < CTF_LSTRUCT_THRESH) + { + const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t) tp + + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++) + { + if (strcmp (ctf_strptr (fp, mp->ctm_name), name) == 0) + { + mip->ctm_type = mp->ctm_type; + mip->ctm_offset = mp->ctm_offset; + return 0; + } + } + } + else + { + const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t) tp + + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++) + { + if (strcmp (ctf_strptr (fp, lmp->ctlm_name), name) == 0) + { + mip->ctm_type = lmp->ctlm_type; + mip->ctm_offset = (unsigned long) CTF_LMEM_OFFSET (lmp); + return 0; + } + } + } + + return (ctf_set_errno (ofp, ECTF_NOMEMBNAM)); +} + +/* Return the array type, index, and size information for the specified ARRAY. */ + +int +ctf_array_info (ctf_file_t *fp, ctf_id_t type, ctf_arinfo_t *arp) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + const ctf_array_t *ap; + const ctf_dtdef_t *dtd; + ssize_t increment; + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ARRAY) + return (ctf_set_errno (ofp, ECTF_NOTARRAY)); + + if ((dtd = ctf_dynamic_type (ofp, type)) != NULL) + { + *arp = dtd->dtd_u.dtu_arr; + return 0; + } + + (void) ctf_get_ctt_size (fp, tp, NULL, &increment); + + ap = (const ctf_array_t *) ((uintptr_t) tp + increment); + arp->ctr_contents = ap->cta_contents; + arp->ctr_index = ap->cta_index; + arp->ctr_nelems = ap->cta_nelems; + + return 0; +} + +/* Convert the specified value to the corresponding enum tag name, if a + matching name can be found. Otherwise NULL is returned. */ + +const char * +ctf_enum_name (ctf_file_t *fp, ctf_id_t type, int value) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + const ctf_enum_t *ep; + ssize_t increment; + uint32_t n; + + if ((type = ctf_type_resolve_unsliced (fp, type)) == CTF_ERR) + return NULL; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return NULL; /* errno is set for us. */ + + if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ENUM) + { + (void) ctf_set_errno (ofp, ECTF_NOTENUM); + return NULL; + } + + (void) ctf_get_ctt_size (fp, tp, NULL, &increment); + + ep = (const ctf_enum_t *) ((uintptr_t) tp + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++) + { + if (ep->cte_value == value) + return (ctf_strptr (fp, ep->cte_name)); + } + + (void) ctf_set_errno (ofp, ECTF_NOENUMNAM); + return NULL; +} + +/* Convert the specified enum tag name to the corresponding value, if a + matching name can be found. Otherwise CTF_ERR is returned. */ + +int +ctf_enum_value (ctf_file_t * fp, ctf_id_t type, const char *name, int *valp) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + const ctf_enum_t *ep; + ssize_t increment; + uint32_t n; + + if ((type = ctf_type_resolve_unsliced (fp, type)) == CTF_ERR) + return CTF_ERR; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ENUM) + { + (void) ctf_set_errno (ofp, ECTF_NOTENUM); + return CTF_ERR; + } + + (void) ctf_get_ctt_size (fp, tp, NULL, &increment); + + ep = (const ctf_enum_t *) ((uintptr_t) tp + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++) + { + if (strcmp (ctf_strptr (fp, ep->cte_name), name) == 0) + { + if (valp != NULL) + *valp = ep->cte_value; + return 0; + } + } + + (void) ctf_set_errno (ofp, ECTF_NOENUMNAM); + return CTF_ERR; +} + +/* Recursively visit the members of any type. This function is used as the + engine for ctf_type_visit, below. We resolve the input type, recursively + invoke ourself for each type member if the type is a struct or union, and + then invoke the callback function on the current type. If any callback + returns non-zero, we abort and percolate the error code back up to the top. */ + +static int +ctf_type_rvisit (ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, + void *arg, const char *name, unsigned long offset, int depth) +{ + ctf_id_t otype = type; + const ctf_type_t *tp; + ssize_t size, increment; + uint32_t kind, n; + int rc; + + if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) + return CTF_ERR; /* errno is set for us. */ + + if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if ((rc = func (name, otype, offset, depth, arg)) != 0) + return rc; + + kind = LCTF_INFO_KIND (fp, tp->ctt_info); + + if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) + return 0; + + (void) ctf_get_ctt_size (fp, tp, &size, &increment); + + if (size < CTF_LSTRUCT_THRESH) + { + const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t) tp + + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++) + { + if ((rc = ctf_type_rvisit (fp, mp->ctm_type, + func, arg, ctf_strptr (fp, mp->ctm_name), + offset + mp->ctm_offset, + depth + 1)) != 0) + return rc; + } + + } + else + { + const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t) tp + + increment); + + for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++) + { + if ((rc = ctf_type_rvisit (fp, lmp->ctlm_type, + func, arg, ctf_strptr (fp, + lmp->ctlm_name), + offset + (unsigned long) CTF_LMEM_OFFSET (lmp), + depth + 1)) != 0) + return rc; + } + } + + return 0; +} + +/* Recursively visit the members of any type. We pass the name, member + type, and offset of each member to the specified callback function. */ +int +ctf_type_visit (ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg) +{ + return (ctf_type_rvisit (fp, type, func, arg, "", 0, 0)); +} |