diff --git a/include/ctf-api.h b/include/ctf-api.h index 92b602df4f2..e986cadee09 100644 --- a/include/ctf-api.h +++ b/include/ctf-api.h @@ -253,13 +253,17 @@ typedef struct ctf_snapshot_id _CTF_ITEM (ECTF_HASPARENT, "Cannot ctf_import: dict already has a parent.") \ _CTF_ITEM (ECTF_WRONGPARENT, "Cannot ctf_import: incorrect parent provided.") \ _CTF_ITEM (ECTF_NOTSERIALIZED, "CTF dict must be serialized first.") \ + _CTF_ITEM (ECTF_BADCOMPONENT, "Declaration tag component_idx is invalid.") \ _CTF_ITEM (ECTF_NOTBITSOU, "Type is not a bitfield-capable struct or union.") \ _CTF_ITEM (ECTF_DESCENDING, "Structure offsets may not descend.") \ _CTF_ITEM (ECTF_LINKAGE, "Invalid linkage.") \ _CTF_ITEM (ECTF_LINKKIND, "Only functions and variables have linkage.") \ + _CTF_ITEM (ECTF_NEVERTAG, "Cannot call this function with a tag kind.") \ _CTF_ITEM (ECTF_NOTDATASEC, "This function requires a datasec.") \ _CTF_ITEM (ECTF_NOTVAR, "This function requires a variable.") \ _CTF_ITEM (ECTF_NODATASEC, "Variable not found in datasec.") \ + _CTF_ITEM (ECTF_NOTDECLTAG, "This function requires a decl tag.") \ + _CTF_ITEM (ECTF_NOTTAG, "This function requires a type or decl tag.") \ #define ECTF_BASE 1000 /* Base value for libctf errnos. */ @@ -745,6 +749,20 @@ extern ctf_id_t ctf_datasec_var_offset (ctf_dict_t *fp, ctf_id_t datasec, extern ctf_id_t ctf_variable_datasec (ctf_dict_t *fp, ctf_id_t var); +/* Type and decl tags. */ + +/* Return the type ID of the type to which a given type tag is attached, or of + the type of the declaration to which a decl tag is attached (so a decl tag on + a function parameter would return the type ID of the parameter's type). */ + +extern ctf_id_t ctf_tag (ctf_dict_t *, ctf_id_t tag); + +/* Return the component ID and declaration to which a decl tag is attached. + -1 means "whole type". */ + +extern ctf_id_t ctf_decl_tag (ctf_dict_t *, ctf_id_t decl_tag, + int64_t *component_idx); + /* Iterators. */ /* ctf_member_next is a _next-style iterator that can additionally traverse into @@ -817,6 +835,9 @@ extern int ctf_datasec_var_iter (ctf_dict_t *, ctf_id_t, ctf_datasec_var_f *, extern ctf_id_t ctf_datasec_var_next (ctf_dict_t *, ctf_id_t, ctf_next_t **, size_t *size, size_t *offset); +/* Iterate over all tags with the given TAG, returning the ID of each tag. */ +extern ctf_id_t ctf_tag_next (ctf_dict_t *, const char *tag, ctf_next_t **); + /* ctf_archive_iter and ctf_archive_next open each member dict for you, automatically importing any parent dict as usual: ctf_archive_iter closes the dict on return from ctf_archive_member_f, but for ctf_archive_next the caller @@ -920,6 +941,14 @@ extern ctf_id_t ctf_add_typedef (ctf_dict_t *, uint32_t, const char *, ctf_id_t); extern ctf_id_t ctf_add_restrict (ctf_dict_t *, uint32_t, ctf_id_t); +/* Add type and decl tags to whole types or (for decl tags) specific + components of types (parameter count for functions, member count for structs + and unions). */ +extern ctf_id_t ctf_add_type_tag (ctf_dict_t *, uint32_t, ctf_id_t, const char *); +extern ctf_id_t ctf_add_decl_type_tag (ctf_dict_t *, uint32_t, ctf_id_t, const char *); +extern ctf_id_t ctf_add_decl_tag (ctf_dict_t *, uint32_t, ctf_id_t, const char *, + int component_idx); + /* Struct and union addition. Straight addition uses possibly-confusing rules to guess the final size of the struct/union given its members: to explicitly state the size of the struct or union (to report compiler-generated padding, diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c index 25876bbd583..6a22aca1d67 100644 --- a/libctf/ctf-create.c +++ b/libctf/ctf-create.c @@ -272,6 +272,9 @@ ctf_name_table (ctf_dict_t *fp, int kind) case CTF_K_ENUM: case CTF_K_ENUM64: return fp->ctf_enums; + case CTF_K_TYPE_TAG: + case CTF_K_DECL_TAG: + return fp->ctf_tags; case CTF_K_DATASEC: return fp->ctf_datasecs; default: @@ -279,6 +282,37 @@ ctf_name_table (ctf_dict_t *fp, int kind) } } +int +ctf_insert_type_decl_tag (ctf_dict_t *fp, ctf_id_t type, const char *name, + int kind) +{ + ctf_dynset_t *types; + ctf_dynhash_t *h = ctf_name_table (fp, kind); + int err; + + if ((types = ctf_dynhash_lookup (h, name)) == NULL) + { + types = ctf_dynset_create (htab_hash_pointer, htab_eq_pointer, NULL); + + if (!types) + return (ctf_set_errno (fp, ENOMEM)); + + err = ctf_dynhash_cinsert (h, name, types); + if (err != 0) + { + err *= -1; + return (ctf_set_errno (fp, err)); + } + } + + if ((err = ctf_dynset_insert (types, (void *) (uintptr_t) type)) != 0) + { + err *= -1; + return (ctf_set_errno (fp, err)); + } + return 0; +} + int ctf_dtd_insert (ctf_dict_t *fp, ctf_dtdef_t *dtd, int flag, int kind) { @@ -287,17 +321,25 @@ ctf_dtd_insert (ctf_dict_t *fp, ctf_dtdef_t *dtd, int flag, int kind) dtd) < 0) return ctf_set_errno (fp, ENOMEM); - if (flag == CTF_ADD_ROOT && dtd->dtd_data.ctt_name - && (name = ctf_strraw (fp, dtd->dtd_data.ctt_name)) != NULL) + if (flag == CTF_ADD_ROOT && dtd->dtd_data->ctt_name + && (name = ctf_strraw (fp, dtd->dtd_data->ctt_name)) != NULL) { - if (ctf_dynhash_insert (ctf_name_table (fp, kind), - (char *) name, (void *) (uintptr_t) - dtd->dtd_type) < 0) + /* Type and decl tags have unusual name tables, since their names are not + unique. */ + + if (kind != CTF_K_TYPE_TAG && kind != CTF_K_DECL_TAG) { - ctf_dynhash_remove (fp->ctf_dthash, (void *) (uintptr_t) - dtd->dtd_type); - return ctf_set_errno (fp, ENOMEM); + if (ctf_dynhash_insert (ctf_name_table (fp, kind), + (char *) name, (void *) (uintptr_t) + dtd->dtd_type) < 0) + { + ctf_dynhash_remove (fp->ctf_dthash, (void *) (uintptr_t) + dtd->dtd_type); + return ctf_set_errno (fp, ENOMEM); + } } + else if (ctf_insert_type_decl_tag (fp, dtd->dtd_type, name, kind) < 0) + return -1; /* errno is set for us. */ } ctf_list_append (&fp->ctf_dtdefs, dtd); return 0; @@ -886,6 +928,125 @@ ctf_set_array (ctf_dict_t *fp, ctf_id_t type, const ctf_arinfo_t *arp) return 0; } +/* Add a type or decl tag applying to some whole type, or to some + component of a type. Component -1 is a whole type. */ + +static ctf_id_t +ctf_add_tag (ctf_dict_t *fp, uint32_t flag, ctf_id_t type, const char *tag, + int is_decl, int32_t component_idx) +{ + ctf_dtdef_t *dtd; + size_t vlen_size = 0; + int kind = is_decl ? CTF_K_DECL_TAG : CTF_K_TYPE_TAG; + int ref_kind = ctf_type_kind (fp, type); + + if (component_idx < -1) + return (ctf_set_typed_errno (fp, ECTF_BADCOMPONENT)); + + if (is_decl) + { + vlen_size = sizeof (ctf_decl_tag_t); + + /* Whole-type declarations. */ + + if (component_idx == 0) + { + switch (ref_kind) + { + case CTF_K_TYPEDEF: + case CTF_K_STRUCT: + case CTF_K_UNION: + case CTF_K_VAR: + /* TODO: support addition and querying on CTF_K_FUNCTION too, chasing back + to relevant CTF_K_FUNC_LINKAGEs. */ + case CTF_K_FUNC_LINKAGE: + break; + default: + return (ctf_set_typed_errno (fp, ECTF_BADID)); + } + } + } + else if (component_idx != -1) + { + ctf_id_t func_type; + + /* Within-type declarations. */ + + switch (ref_kind) + { + case CTF_K_STRUCT: + case CTF_K_UNION: + + if (component_idx >= ctf_member_count (fp, type)) + return (ctf_set_typed_errno (fp, ECTF_BADCOMPONENT)); + break; + + case CTF_K_FUNC_LINKAGE: + if ((func_type = ctf_type_reference (fp, type)) == CTF_ERR) + return (ctf_set_typed_errno (fp, ECTF_BADID)); + /* FALLTHRU */ + + case CTF_K_FUNCTION: + { + ctf_funcinfo_t fi; + + if (ctf_func_type_info (fp, func_type, &fi) < 0) + return -1; /* errno is set for us. */ + + if ((size_t) component_idx >= fi.ctc_argc) + return (ctf_set_typed_errno (fp, ECTF_BADCOMPONENT)); + + break; + } + default: + return (ctf_set_typed_errno (fp, ECTF_BADCOMPONENT)); + } + } + + if (tag == NULL || tag[0] == '\0') + return (ctf_set_typed_errno (fp, ECTF_NONAME)); + + if ((dtd = ctf_add_generic (fp, flag, tag, kind, 0, vlen_size, + 0, NULL)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + dtd->dtd_data->ctt_info = CTF_TYPE_INFO (kind, flag, 0); + dtd->dtd_data->ctt_type = (uint32_t) type; + + if (is_decl) + { + ctf_decl_tag_t *vlen = (ctf_decl_tag_t *) dtd->dtd_vlen; + vlen->cdt_component_idx = component_idx; + } + + return dtd->dtd_type; +} + +/* Create a type tag. */ + +ctf_id_t +ctf_add_type_tag (ctf_dict_t *fp, uint32_t flag, ctf_id_t type, const char *tag) +{ + return ctf_add_tag (fp, flag, type, tag, 0, -1); +} + +/* Create a decl tag applied to an entire type. */ + +ctf_id_t +ctf_add_decl_type_tag (ctf_dict_t *fp, uint32_t flag, ctf_id_t type, const char *tag) +{ + return ctf_add_tag (fp, flag, type, tag, 1, -1); +} + +/* Create a decl tag applied to one element of a type. + component_idx must be >= 0. */ +ctf_id_t +ctf_add_decl_tag (ctf_dict_t *fp, uint32_t flag, ctf_id_t type, const char *tag, + int component_idx) +{ + return ctf_add_tag (fp, flag, type, tag, 1, component_idx); +} + ctf_id_t ctf_add_function (ctf_dict_t *fp, uint32_t flag, const ctf_funcinfo_t *ctc, const ctf_id_t *argv, diff --git a/libctf/ctf-decl.c b/libctf/ctf-decl.c index e2b046d7325..4c1007f5a67 100644 --- a/libctf/ctf-decl.c +++ b/libctf/ctf-decl.c @@ -127,6 +127,7 @@ ctf_decl_push (ctf_decl_t *cd, ctf_dict_t *fp, ctf_id_t type) prec = CTF_PREC_POINTER; break; + case CTF_K_DECL_TAG: case CTF_K_VAR: ctf_decl_push (cd, fp, suffix->ctt_type); prec = CTF_PREC_BASE; /* UPTODO probably wrong */ diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index f0422c05970..a90a672826c 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -745,6 +745,8 @@ extern ctf_id_t ctf_add_reftype (ctf_dict_t *, uint32_t, ctf_id_t, extern int ctf_add_funcobjt_sym_forced (ctf_dict_t *, int is_function, const char *, ctf_id_t); +extern int ctf_insert_type_decl_tag (ctf_dict_t *, ctf_id_t, const char *, + int kind); extern int ctf_track_enumerator (ctf_dict_t *, ctf_id_t, const char *); extern int ctf_dedup_atoms_init (ctf_dict_t *); diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c index 8f19b566313..dac1ec50468 100644 --- a/libctf/ctf-types.c +++ b/libctf/ctf-types.c @@ -853,6 +853,45 @@ ctf_datasec_var_next (ctf_dict_t *fp, ctf_id_t datasec, ctf_next_t **it, return (ctf_set_typed_errno (fp, ECTF_NEXT_END)); } +/* Iterate over all tags with the given TAG, returning the ID of each tag. */ + +ctf_id_t +ctf_tag_next (ctf_dict_t *fp, const char *tag, ctf_next_t **it) +{ + ctf_next_t *i = *it; + int err; + void *type; + + if (!i) + { + if ((i = ctf_next_create ()) == NULL) + return (ctf_set_typed_errno (fp, ENOMEM)); + i->cu.ctn_fp = fp; + i->ctn_iter_fun = (void (*) (void)) ctf_tag_next; + + i->cu.ctn_s = ctf_dynhash_lookup (fp->ctf_tags, tag); + + *it = i; + } + + if ((void (*) (void)) ctf_tag_next != i->ctn_iter_fun) + return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFUN)); + + if (fp != i->cu.ctn_fp) + return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP)); + + err = ctf_dynset_next (i->cu.ctn_s, &i->ctn_next, &type); + if (err != 0 && err != ECTF_NEXT_END) + return ctf_set_typed_errno (fp, err); + + if (err == 0) + return (ctf_id_t) (uintptr_t) type; + + ctf_next_destroy (i); + *it = NULL; + return (ctf_set_typed_errno (fp, ECTF_NEXT_END)); +} + /* 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 @@ -1149,6 +1188,14 @@ ctf_type_aname (ctf_dict_t *fp, ctf_id_t type) case CTF_K_ENUM64: ctf_decl_sprintf (&cd, "enum %s", name); break; + case CTF_K_TYPE_TAG: + ctf_decl_sprintf (&cd, "%s", name); + break; + /* UPTODO: decl tags... I guess we print them when we print the + associated variable, somehow? For now, just this... */ + case CTF_K_DECL_TAG: + ctf_decl_sprintf (&cd, "btf_decl_tag (\"%s\")", name); + break; case CTF_K_FUNC_LINKAGE: case CTF_K_VAR: { @@ -1603,6 +1650,8 @@ ctf_type_reference (ctf_dict_t *fp, ctf_id_t type) case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: + case CTF_K_TYPE_TAG: + case CTF_K_DECL_TAG: case CTF_K_FUNCTION: case CTF_K_FUNC_LINKAGE: case CTF_K_VAR: @@ -1630,6 +1679,119 @@ ctf_type_reference (ctf_dict_t *fp, ctf_id_t type) } } +/* Return the component ID and declaration to which a decl tag is attached. */ + +ctf_id_t +ctf_decl_tag (ctf_dict_t *fp, ctf_id_t decl_tag, int64_t *component_idx) +{ + ctf_dict_t *ofp = fp; + const ctf_type_t *tp, *suffix; + unsigned char *vlen; + ctf_decl_tag_t *cdt; + + if ((tp = ctf_lookup_by_id (&fp, decl_tag, &suffix)) == NULL) + return CTF_ERR; /* errno is set for us. */ + + if (LCTF_KIND (fp, tp) != CTF_K_DECL_TAG) + return (ctf_set_typed_errno (ofp, ECTF_NOTDECLTAG)); + + vlen = ctf_vlen (fp, decl_tag, tp, NULL); + cdt = (ctf_decl_tag_t *) vlen; + + *component_idx = cdt->cdt_component_idx; + + return suffix->ctt_type; +} + +/* Return the type ID of the type to which a given type tag is attached, or of + the type of the declaration to which a decl tag is attached (so a decl tag on + a function parameter would return the type ID of the parameter's type). */ + +ctf_id_t +ctf_tag (ctf_dict_t *fp, ctf_id_t tag) +{ + int kind = ctf_type_kind (fp, tag); + int64_t component_idx; + ctf_id_t ref; + + if (kind != CTF_K_TYPE_TAG && kind != CTF_K_DECL_TAG) + return (ctf_set_typed_errno (fp, ECTF_NOTTAG)); + + if ((ref = ctf_type_reference (fp, tag)) == CTF_ERR) + return CTF_ERR; /* errno is set for us. */ + + if (kind == CTF_K_TYPE_TAG) + return ref; + + if (ctf_decl_tag (fp, tag, &component_idx) == CTF_ERR) + return CTF_ERR; /* errno is set for us. */ + + if (component_idx == -1) + return ref; + + /* See ctf_add_tag. */ + + switch (ctf_type_kind (fp, ref)) + { + case CTF_K_STRUCT: + case CTF_K_UNION: + { + ctf_next_t *i = NULL; + int64_t j = 0; + ctf_id_t type; + + while (ctf_member_next (fp, ref, &i, NULL, &type, NULL, 0) >= 0) + { + if (j++ == component_idx) + { + ctf_next_destroy (i); + return type; + } + } + if (ctf_errno (fp) != ECTF_NEXT_END) + { + ctf_next_destroy (i); + return CTF_ERR; /* errno is set for us. */ + } + } + break; + case CTF_K_FUNC_LINKAGE: + case CTF_K_FUNCTION: + { + ctf_funcinfo_t fi; + ctf_id_t *args; + ctf_id_t argtype; + + if (ctf_func_type_info (fp, ref, &fi) < 0) + return CTF_ERR; /* errno is set for us. */ + + if (component_idx + 1 > (ssize_t) fi.ctc_argc) + break; + + if ((args = malloc ((component_idx + 1) * sizeof (ctf_id_t))) == NULL) + return (ctf_set_typed_errno (fp, ENOMEM)); + + if (ctf_func_type_args (fp, ref, component_idx + 1, args)) + { + free (args); + return CTF_ERR; /* errno is set for us. */ + } + argtype = args[component_idx]; + free (args); + return argtype; + } + + default: + return CTF_ERR; /* errno is set for us. */ + } + + ctf_err_warn (fp, 0, ECTF_NOTREF, _("decl tag %lx refers to type %lx, " + "component %" PRIi64 ", which does not exist"), + tag, (long) ref, component_idx); + + return (ctf_set_typed_errno (fp, ECTF_NOTREF)); +} + /* Find a pointer to type by looking in fp->ctf_ptrtab and fp->ctf_pptrtab. 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 diff --git a/libctf/libctf.ver b/libctf/libctf.ver index daa8375a9ed..66ddb530a1b 100644 --- a/libctf/libctf.ver +++ b/libctf/libctf.ver @@ -84,6 +84,8 @@ LIBCTF_2.0 { ctf_type_visit; ctf_type_cmp; ctf_type_compat; + ctf_tag; + ctf_decl_tag; ctf_member_info; ctf_member_next; @@ -111,6 +113,7 @@ LIBCTF_2.0 { ctf_variable_next; ctf_datasec_iter; ctf_datasec_next; + ctf_tag_next; ctf_next_create; ctf_next_destroy; @@ -118,6 +121,8 @@ LIBCTF_2.0 { ctf_add_array; ctf_add_const; + ctf_add_decl_tag; + ctf_add_decl_type_tag; ctf_add_enum; ctf_add_enum_encoded; ctf_add_enum64; @@ -131,6 +136,7 @@ LIBCTF_2.0 { ctf_add_pointer; ctf_add_type; ctf_add_typedef; + ctf_add_type_tag; ctf_add_restrict; ctf_add_section_variable; ctf_add_slice;