]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
libctf: create, types: variables and datasecs (REVIEW NEEDED)
authorNick Alcock <nick.alcock@oracle.com>
Thu, 24 Apr 2025 16:42:16 +0000 (17:42 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 25 Apr 2025 17:07:43 +0000 (18:07 +0100)
This is an area of significant difference from CTFv3.  The API changes
significantly, with quite a few additions to allow creation and querying of
these new datasec entities:

-typedef int ctf_variable_f (const char *name, ctf_id_t type, void *arg);
+typedef int ctf_variable_f (ctf_dict_t *, const char *name, ctf_id_t type,
+     void *arg);
+typedef int ctf_datasec_var_f (ctf_dict_t *fp, ctf_id_t type, size_t offset,
+        size_t datasec_size, void *arg);

+/* Search a datasec for a variable covering a given offset.
+
+   Errors with ECTF_NODATASEC if not found.  */
+
+ctf_id_t ctf_datasec_var_offset (ctf_dict_t *fp, ctf_id_t datasec,
+  uint32_t offset);
+
+/* Return the datasec that a given variable appears in, or ECTF_NODATASEC if
+   none.  */
+
+ctf_id_t ctf_variable_datasec (ctf_dict_t *fp, ctf_id_t var);

+int ctf_datasec_var_iter (ctf_dict_t *, ctf_id_t, ctf_datasec_var_f *,
+   void *);
+ctf_id_t ctf_datasec_var_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
+        size_t *size, size_t *offset);

-int ctf_add_variable (ctf_dict_t *, const char *, ctf_id_t);
+/* ctf_add_variable adds variables to no datasec at all;
+   ctf_add_section_variable adds them to the given datasec, or to no datasec at
+   all if the datasec is NULL.  */
+
+ctf_id_t ctf_add_variable (ctf_dict_t *, const char *, int linkage, ctf_id_t);
+ctf_id_t ctf_add_section_variable (ctf_dict_t *, uint32_t,
+    const char *datasec, const char *name,
+    int linkage, ctf_id_t type,
+    size_t size, size_t offset);

We tie datasecs quite closely to variables at addition (and, as should
become clear later, dedup) time: you never create datasecs, you only create
variables *in* datasecs, and the datasec springs into existence when you do
so: datasecs are always found in the same dict as the variables they contain
(the variables are never in the parent if the datasec is in a child or
anything).  We keep track of the variable->datasec mapping in
ctf_var_datasecs (populating it at addition and open time), to allow
ctf_variable_datasec to work at reasonable speed.  (But, as yet, there are
no tests of this function at all.)

The datasecs are created unsorted (to avoid variable addition becoming
O(n^2)) and sorted at serialization time, and when ctf_datasec_var_offset is
invoked.

We reuse the natural-alignment code from struct addition to get a plausible
offset in datasecs if an alignment of -1 is specified: maybe this is
unnecessary now (it was originally added when ctf_add_variable added
variables to a "default datasec", while now it just leaves them out of
all datasecs, like externs are).

One constraint of this is that we currently prohibit the addition of
nonrepresentable-typed variables, because we can't tell what their natural
alignment is: if we dropped the whole "align" and just required everyone
adding a variable to a datasec to specify an offset, we could drop that
restriction. WDYT?

One additional caveat: right now, ctf_lookup_variable() looks up the type of
a variable (because when it was invented, variables were not entities in
themselves that you could look up).  This name is confusing as hell as a
result.  It might be less confusing to make it return the CTF_K_VAR, but
that would be awful to adapt callers to, since both are represented with
ctf_id_t's, so the compiler wouldn't warn about the needed change at all...
I've vacillated on this three or four times now.

include/ctf-api.h
libctf/ctf-create.c
libctf/ctf-decl.c
libctf/ctf-impl.h
libctf/ctf-lookup.c
libctf/ctf-types.c
libctf/libctf.ver

index f0769a67fed2df65dba04eae6e4d574a6858dade..ac8965fef066a695b9a3a7fc108718820c1ebd74 100644 (file)
@@ -229,7 +229,7 @@ typedef struct ctf_snapshot_id
   _CTF_ITEM (ECTF_RDONLY, "CTF container is read-only.") \
   _CTF_ITEM (ECTF_DTFULL, "CTF type is full (no more members allowed).") \
   _CTF_ITEM (ECTF_FULL, "CTF container is full.") \
-  _CTF_ITEM (ECTF_DUPLICATE, "Duplicate member, enumerator, or variable name.") \
+  _CTF_ITEM (ECTF_DUPLICATE, "Duplicate member, enumerator, datasec, or variable name.") \
   _CTF_ITEM (ECTF_CONFLICT, "Conflicting type is already defined.") \
   _CTF_ITEM (ECTF_OVERROLLBACK, "Attempt to roll back past a ctf_update.") \
   _CTF_ITEM (ECTF_COMPRESS, "Failed to compress CTF data.") \
@@ -255,6 +255,10 @@ typedef struct ctf_snapshot_id
   _CTF_ITEM (ECTF_NOTSERIALIZED, "CTF dict must be serialized first.") \
   _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_NOTDATASEC, "This function requires a datasec.") \
+  _CTF_ITEM (ECTF_NOTVAR, "This function requires a variable.") \
+  _CTF_ITEM (ECTF_NODATASEC, "Variable not found in datasec.") \
 
 #define        ECTF_BASE       1000    /* Base value for libctf errnos.  */
 
@@ -306,7 +310,6 @@ _CTF_ERRORS
 
 typedef int ctf_visit_f (const char *name, ctf_id_t type, unsigned long offset,
                         int depth, void *arg);
-typedef int ctf_variable_f (const char *name, ctf_id_t type, void *arg);
 typedef int ctf_type_f (ctf_id_t type, void *arg);
 typedef int ctf_type_all_f (ctf_id_t type, int flag, void *arg);
                         void *arg);
@@ -314,6 +317,10 @@ typedef int ctf_member_f (ctf_dict_t *, const char *name, ctf_id_t membtype,
                          size_t offset, int bit_width, void *arg);
 typedef int ctf_enum_f (const char *name, int64_t val, void *arg);
 typedef int ctf_unsigned_enum_f (const char *name, uint64_t val, void *arg);
+typedef int ctf_variable_f (ctf_dict_t *, const char *name, ctf_id_t type,
+                           void *arg);
+typedef int ctf_datasec_var_f (ctf_dict_t *fp, ctf_id_t type, size_t offset,
+                              size_t datasec_size, void *arg);
 typedef int ctf_archive_member_f (ctf_dict_t *fp, const char *name, void *arg);
 typedef int ctf_archive_raw_member_f (const char *name, const void *content,
                                      size_t len, void *arg);
@@ -563,7 +570,10 @@ extern ctf_id_t ctf_lookup_by_name (ctf_dict_t *, const char *);
    relationship to a symbol table.  Before linking, everything with types in the
    symbol table will be in the variable table as well; after linking, only those
    typed functions and data objects that are not asssigned to symbols by the
-   linker are left in the variable table here.  */
+   linker are left in the variable table here.
+
+   Note: this looks up a variable's *type*, not the variable itself.
+   For that, use ctf_lookup_by_kind, below.  */
 
 extern ctf_id_t ctf_lookup_variable (ctf_dict_t *, const char *);
 
@@ -713,6 +723,18 @@ extern int ctf_member_info (ctf_dict_t *, ctf_id_t, const char *,
                            ctf_membinfo_t *);
 extern ssize_t ctf_member_count (ctf_dict_t *, ctf_id_t);
 
+/* Search a datasec for a variable covering a given offset.
+
+   Errors with ECTF_NODATASEC if not found.  */
+
+extern ctf_id_t ctf_datasec_var_offset (ctf_dict_t *fp, ctf_id_t datasec,
+                                       uint32_t offset);
+
+/* Return the datasec that a given variable appears in, or ECTF_NODATASEC if
+   none.  */
+
+extern ctf_id_t ctf_variable_datasec (ctf_dict_t *fp, ctf_id_t var);
+
 /* Iterators.  */
 
 /* ctf_member_next is a _next-style iterator that can additionally traverse into
@@ -764,7 +786,10 @@ extern ctf_id_t ctf_arc_lookup_enumerator_next (ctf_archive_t *, const char *nam
    of CTF_ADD_ROOT for such types.  ctf_type_next allows you to choose whether
    to see non-root types or not with the want_hidden arg: if set, the flag (if
    passed) returns the non-root state of each type in turn.  Types in parent
-   dictionaries are not returned.  */
+   dictionaries are not returned.
+
+   These days, even variables are included in the things returned by ctf_type*()
+   (type kind CTF_K_VAR).  */
 
 extern int ctf_type_iter (ctf_dict_t *, ctf_type_f *, void *);
 extern int ctf_type_iter_all (ctf_dict_t *, ctf_type_all_f *, void *);
@@ -775,6 +800,11 @@ extern int ctf_variable_iter (ctf_dict_t *, ctf_variable_f *, void *);
 extern ctf_id_t ctf_variable_next (ctf_dict_t *, ctf_next_t **,
                                   const char **);
 
+extern int ctf_datasec_var_iter (ctf_dict_t *, ctf_id_t, ctf_datasec_var_f *,
+                                void *);
+extern ctf_id_t ctf_datasec_var_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
+                                     size_t *size, size_t *offset);
+
 /* 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
@@ -915,7 +945,16 @@ extern int ctf_add_member_bitfield (ctf_dict_t *, ctf_id_t souid,
                                     unsigned long bit_offset,
                                     int bit_width);
 
-extern int ctf_add_variable (ctf_dict_t *, const char *, ctf_id_t);
+/* ctf_add_variable adds variables to no datasec at all;
+   ctf_add_section_variable adds them to the given datasec, or to no datasec at
+   all if the datasec is NULL.  */
+
+extern ctf_id_t ctf_add_variable (ctf_dict_t *, const char *, int linkage,
+                                 ctf_id_t);
+extern ctf_id_t ctf_add_section_variable (ctf_dict_t *, uint32_t,
+                                         const char *datasec, const char *name,
+                                         int linkage, ctf_id_t type,
+                                         size_t size, size_t offset);
 
 /* Set the size and member and index types of an array.  */
 
index 2ab8472be2c3ce491c46b4c87128f22f7d56006f..5b558d5491640635a51f2756da4f0d9f6f95875e 100644 (file)
@@ -272,6 +272,8 @@ ctf_name_table (ctf_dict_t *fp, int kind)
     case CTF_K_ENUM:
     case CTF_K_ENUM64:
       return fp->ctf_enums;
+    case CTF_K_DATASEC:
+      return fp->ctf_datasecs;
     default:
       return fp->ctf_names;
     }
@@ -1656,60 +1658,200 @@ ctf_add_member (ctf_dict_t *fp, ctf_id_t souid, const char *name,
   return ctf_add_member_offset (fp, souid, name, type, (unsigned long) - 1);
 }
 
-/* Add a variable regardless of whether or not it is already present.
+/* Add a DATASEC to hang variables off of.  */
 
-   Internal use only.  */
-int
-ctf_add_variable_forced (ctf_dict_t *fp, const char *name, ctf_id_t ref)
+static ctf_id_t
+ctf_add_datasec (ctf_dict_t *fp, uint32_t flag, const char *datasec)
+{
+  ctf_dtdef_t *dtd;
+  size_t initial_vlen = sizeof (ctf_var_secinfo_t) * INITIAL_VLEN;
+
+  if ((dtd = ctf_add_generic (fp, flag, datasec, CTF_K_DATASEC,
+                             0, 0, initial_vlen, NULL)) == NULL)
+    return CTF_ERR;            /* errno is set for us. */
+
+  dtd->dtd_data->ctt_info = CTF_TYPE_INFO (CTF_K_DATASEC, 0, 0);
+  dtd->dtd_data->ctt_size = 0;
+
+  return dtd->dtd_type;
+}
+
+ctf_id_t
+ctf_add_variable (ctf_dict_t *fp, const char *name, int linkage, ctf_id_t ref)
 {
-  ctf_dvdef_t *dvd;
+  return ctf_add_section_variable (fp, CTF_ADD_ROOT, NULL, name, linkage, ref,
+                                  0, (unsigned long) -1);
+}
+
+/* Add variable, interning it in the specified DATASEC (which must be in the
+   same dict, but which may be NULL, meaning "no datasec").  As with structs, an
+   offset of -1 means "next natural alignment".  A size of zero means "get it
+   from the type" and is the common case.  */
+ctf_id_t
+ctf_add_section_variable (ctf_dict_t *fp, uint32_t flag, const char *datasec,
+                         const char *name, int linkage, ctf_id_t type,
+                         size_t size, size_t offset)
+{
+  ctf_dtdef_t *sec_dtd = NULL;
+  ctf_dtdef_t *var_dtd = NULL;
+
+  uint32_t kind, kflag;
+  size_t vlen;
+
+  ctf_linkage_t *l;
+  ctf_var_secinfo_t *sec;
+
   ctf_dict_t *tmp = fp;
+  ctf_id_t datasec_id = 0;
+  int is_incomplete = 0;
+  ctf_snapshot_id_t err_snap = ctf_snapshot (fp);
 
-  if (ctf_lookup_by_id (&tmp, ref) == NULL)
-    return -1;                 /* errno is set for us.  */
+  if (fp->ctf_flags & LCTF_NO_STR)
+    return (ctf_set_typed_errno (fp, ECTF_NOPARENT));
 
-  /* Make sure this type is representable.  */
-  if ((ctf_type_resolve (fp, ref) == CTF_ERR)
+  if (name == NULL || name[0] == '\0')
+    return (ctf_set_typed_errno (fp, ECTF_NONAME));
+
+  if (linkage < 0 || linkage > 2)
+    return (ctf_set_typed_errno (fp, ECTF_LINKAGE));
+
+  if (flag == CTF_ADD_ROOT && ctf_lookup_by_rawname (fp, CTF_K_VAR, name) != 0)
+    return (ctf_set_typed_errno (fp, ECTF_DUPLICATE));
+
+  /* First, create the variable.  Make sure its type exists.  */
+
+  if (ctf_lookup_by_id (&tmp, type, NULL) == NULL)
+    return CTF_ERR;                    /* errno is set for us.  */
+
+  /* Make sure this type is representable: if a variable is nonrepresentable
+     there's nothing the end-user can do with it even if they know it's there.
+     Allow type 0: this is used for const void variables in BTF input.  */
+
+  if ((ctf_type_resolve_nonrepresentable (fp, type, 1) == CTF_ERR)
       && (ctf_errno (fp) == ECTF_NONREPRESENTABLE))
-    return -1;
+    return CTF_ERR;
+
+  if ((var_dtd = ctf_add_generic (fp, flag, name, CTF_K_VAR, 0,
+                                 sizeof (ctf_linkage_t), 0, NULL)) == NULL)
+    return CTF_ERR;                    /* errno is set for us.  */
+
+  l = (ctf_linkage_t *) var_dtd->dtd_vlen;
+  var_dtd->dtd_data->ctt_info = CTF_TYPE_INFO (CTF_K_VAR, 0, 0);
+  var_dtd->dtd_data->ctt_type = type;
+  l->ctl_linkage = linkage;
+
+  /* Add it to the datasec, if requested, creating the datasec if need be.  */
 
-  if ((dvd = malloc (sizeof (ctf_dvdef_t))) == NULL)
-    return (ctf_set_errno (fp, EAGAIN));
+  if (!datasec)
+    return var_dtd->dtd_type;
 
-  if (name != NULL && (dvd->dvd_name = strdup (name)) == NULL)
+  if ((datasec_id = ctf_lookup_by_rawname (fp, CTF_K_DATASEC,
+                                          datasec)) == 0)
     {
-      free (dvd);
-      return (ctf_set_errno (fp, EAGAIN));
+      if ((datasec_id = ctf_add_datasec (fp, CTF_ADD_ROOT,
+                                        datasec)) == CTF_ERR)
+       goto err;                               /* errno is set for us.  */
     }
-  dvd->dvd_type = ref;
-  dvd->dvd_snapshots = fp->ctf_snapshots;
 
-  if (ctf_dvd_insert (fp, dvd) < 0)
+  sec_dtd = ctf_dtd_lookup (fp, datasec_id);
+
+  kind = LCTF_KIND (fp, sec_dtd->dtd_buf);
+  kflag = CTF_INFO_KFLAG (sec_dtd->dtd_data->ctt_info);
+  vlen = LCTF_VLEN (fp, sec_dtd->dtd_buf);
+
+  if (vlen == CTF_MAX_RAW_VLEN)
     {
-      free (dvd->dvd_name);
-      free (dvd);
-      return -1;                       /* errno is set for us.  */
+      ctf_set_typed_errno (fp, ECTF_DTFULL);
+      goto err;
     }
 
-  return 0;
-}
+  /* DATASECs do not support CTF_K_BIG (yet).  */
+  if (vlen == CTF_MAX_RAW_VLEN)
+    {
+      ctf_set_typed_errno (fp, ECTF_DTFULL);
+      goto err;
+    }
 
-int
-ctf_add_variable (ctf_dict_t *fp, const char *name, ctf_id_t ref)
-{
-  if (fp->ctf_flags & LCTF_NO_STR)
-    return (ctf_set_errno (fp, ECTF_NOPARENT));
+  /* Allow for variables of void-like types.  */
+  if (type == 0)
+    is_incomplete = 1;
+  else if (ctf_type_align (fp, type) < 0)
+    {
+      /* See the comment in ctf_add_member_bitfield.  We don't need to worry
+        about norepresentable types, because we just added this one: we know
+        it's representable.  We do not know it's complete.  */
 
-  if (fp->ctf_flags & LCTF_NO_TYPE)
-    return (ctf_set_errno (fp, ECTF_NOTSERIALIZED));
+      if (ctf_errno (fp) == ECTF_INCOMPLETE)
+       is_incomplete = 1;
+      else
+       goto err;                       /* errno is set for us.  */
+    }
 
-  if (ctf_lookup_variable_here (fp, name) != CTF_ERR)
-    return (ctf_set_errno (fp, ECTF_DUPLICATE));
+  /* Here we just add the var info to the end of the datasec, naturally aligning
+     the offset as with struct/union membership addition if no offset is
+     specified.  */
 
-  if (ctf_errno (fp) != ECTF_NOTYPEDAT)
-    return -1;                         /* errno is set for us.  */
+  sec = (ctf_var_secinfo_t *) sec_dtd->dtd_vlen;
+
+  if (vlen != 0)
+    {
+      /* To avoid causing trouble with existing code promiscuously adding
+        variables without caring about their types, if natural alignment fails
+        due to incomplete types, just set the next offset to something aligned
+        mod 8.  It might be a waste of space but it'll avoid an error and
+        should suffice for a long time to come.  */
+
+      if (offset == (unsigned long) -1 && is_incomplete)
+       offset = roundup (offset, 8);
+      else if (offset == (unsigned long) -1)
+       {
+         /* Natural alignment.  */
+
+         ssize_t bit_offset = offset * 8;
+
+         if ((bit_offset = ctf_type_align_natural (fp, sec[vlen - 1].cvs_type,
+                                                   type, sec[vlen -1].cvs_offset)) < 0)
+           offset = roundup (offset, 8);
+         else
+           offset = bit_offset / CHAR_BIT;
+       }
+
+      /* This DTD may need sorting.  */
+
+      if (offset < sec[vlen - 1].cvs_offset)
+       sec_dtd->dtd_flags |= ~DTD_F_UNSORTED;
+
+    } else if (offset == (unsigned long) -1)
+       offset = 0;
+
+  /* Remember the variable -> datasec mapping.  */
+
+  if (ctf_dynhash_insert (fp->ctf_var_datasecs,
+                         (void *) (ptrdiff_t) var_dtd->dtd_type,
+                         (void *) (ptrdiff_t) datasec_id) != 0)
+    {
+      ctf_set_typed_errno (fp, ENOMEM);
+      goto err;
+    }
 
-  return ctf_add_variable_forced (fp, name, ref);
+  if (ctf_grow_vlen (fp, sec_dtd, sizeof (ctf_var_secinfo_t) * (vlen + 1)) < 0)
+    goto err;
+
+  sec_dtd->dtd_vlen_size += sizeof (ctf_var_secinfo_t);
+  sec = (ctf_var_secinfo_t *) sec_dtd->dtd_vlen;
+
+  sec[vlen].cvs_type = (uint32_t) var_dtd->dtd_type;
+  sec[vlen].cvs_offset = (uint32_t) offset;
+  sec[vlen].cvs_size = (uint32_t) size;
+  sec_dtd->dtd_data->ctt_info = CTF_TYPE_INFO (kind, kflag, vlen + 1);
+
+  return var_dtd->dtd_type;
+
+err:
+  ctf_dynhash_remove (fp->ctf_var_datasecs,
+                     (void *) (ptrdiff_t) var_dtd->dtd_type);
+  ctf_rollback (fp, err_snap);
+  return CTF_ERR;
 }
 
 /* Add a function or object symbol regardless of whether or not it is already
@@ -1767,6 +1909,39 @@ ctf_add_func_sym (ctf_dict_t *fp, const char *name, ctf_id_t id)
   return (ctf_add_funcobjt_sym (fp, 1, name, id));
 }
 
+/* Sort function used by ctf_datasec_sort.  */
+
+static int
+ctf_datasec_sort_ascending (const void *one_, const void *two_)
+{
+  ctf_var_secinfo_t *one = (ctf_var_secinfo_t *) one_;
+  ctf_var_secinfo_t *two = (ctf_var_secinfo_t *) two_;
+
+  if (one->cvs_offset < two->cvs_offset)
+    return -1;
+  else if (one->cvs_offset > two->cvs_offset)
+    return 1;
+  return 0;
+}
+
+/* Sort a datasec into order.  Needed before serialization or query
+   operations.  */
+
+void
+ctf_datasec_sort (ctf_dict_t *fp, ctf_dtdef_t *dtd)
+{
+  size_t vlen;
+
+  if (!(dtd->dtd_flags & DTD_F_UNSORTED))
+    return;
+
+  vlen = LCTF_VLEN (fp, dtd->dtd_buf);
+
+  qsort (dtd->dtd_vlen, vlen, sizeof (ctf_var_secinfo_t),
+        ctf_datasec_sort_ascending);
+  dtd->dtd_flags &= ~DTD_F_UNSORTED;
+}
+
 /* Add an enumeration constant observed in a given enum type as an identifier.
    They appear as names that cite the enum type.
 
index f021130b2b8c999691678d9d5fb079ac53a9269c..4b1450d2ff13ab1058cb1b5e1dcd6ff79781e254 100644 (file)
@@ -122,6 +122,11 @@ ctf_decl_push (ctf_decl_t *cd, ctf_dict_t *fp, ctf_id_t type)
       prec = CTF_PREC_POINTER;
       break;
 
+    case CTF_K_VAR:
+      ctf_decl_push (cd, fp, suffix->ctt_type);
+      prec = CTF_PREC_BASE; /* UPTODO probably wrong */
+      break;
+
     case CTF_K_SLICE:
       /* Slices themselves have no print representation and should not appear in
         the decl stack.  */
index c9d1c8cf8ba322529aa2044fb036e785ec6d41e7..49f677b0d65f3f5c5065f27dfa47bc1c8c7cd12c 100644 (file)
@@ -176,6 +176,8 @@ typedef struct ctf_decl
   int cd_enomem;                    /* Nonzero if OOM during printing.  */
 } ctf_decl_t;
 
+#define DTD_F_UNSORTED 0x0001  /* set on a datasec if it needs sorting.  */
+
 typedef struct ctf_dtdef
 {
   ctf_list_t dtd_list;         /* List forward/back pointers.  */
@@ -431,8 +433,6 @@ struct ctf_dict
   ctf_link_sym_t **ctf_dynsymidx;  /* Indexes ctf_dynsyms by symidx.  */
   uint32_t ctf_dynsymmax;        /* Maximum ctf_dynsym index.  */
   ctf_list_t ctf_in_flight_dynsyms; /* Dynsyms during accumulation.  */
-  struct ctf_varent *ctf_vars;   /* Sorted variable->type mapping.  */
-  unsigned long ctf_nvars;       /* Number of variables in ctf_vars.  */
   uint32_t ctf_typemax;                  /* Maximum valid type index.  */
   uint32_t ctf_idmax;            /* Maximum valid non-provisional type ID.  */
   uint32_t ctf_stypes;           /* Number of static (non-dynamic) types.  */
@@ -630,7 +630,6 @@ extern ctf_id_t ctf_index_to_type (const ctf_dict_t *, uint32_t);
 #define LCTF_PRESERIALIZED     0x0020  /* Already serialized all but the strtab.  */
 
 extern ctf_dynhash_t *ctf_name_table (ctf_dict_t *, int);
-extern ctf_id_t ctf_lookup_variable_here (ctf_dict_t *fp, const char *name);
 extern const ctf_type_t *ctf_lookup_by_id (ctf_dict_t **, ctf_id_t,
                                           const ctf_type_t **suffix);
 extern const ctf_type_t *ctf_find_prefix (ctf_dict_t *, const ctf_type_t *,
@@ -743,7 +742,6 @@ extern ctf_id_t ctf_add_encoded (ctf_dict_t *, uint32_t, const char *,
                                 const ctf_encoding_t *, uint32_t kind);
 extern ctf_id_t ctf_add_reftype (ctf_dict_t *, uint32_t, ctf_id_t,
                                 uint32_t kind);
-extern int ctf_add_variable_forced (ctf_dict_t *, const char *, ctf_id_t);
 extern int ctf_add_funcobjt_sym_forced (ctf_dict_t *, int is_function,
                                        const char *, ctf_id_t);
 
@@ -815,6 +813,9 @@ extern ctf_id_t ctf_type_resolve_nonrepresentable (ctf_dict_t *, ctf_id_t, int a
 extern int ctf_type_kind_unsliced (ctf_dict_t *, ctf_id_t);
 extern ssize_t ctf_type_align_natural (ctf_dict_t *fp, ctf_id_t prev_type,
                                       ctf_id_t type, ssize_t bit_offset);
+extern ctf_var_secinfo_t *ctf_datasec_entry (ctf_dict_t *, ctf_id_t,
+                                            int component_idx);
+extern void ctf_datasec_sort (ctf_dict_t *, ctf_dtdef_t *);
 
 _libctf_printflike_ (1, 2)
 extern void ctf_dprintf (const char *, ...);
index 1095c8d0f0a86da770191824fc4e4fa03c9a407c..e06ecd78f67e0310436c968ef36370788f87faff 100644 (file)
@@ -471,6 +471,11 @@ ctf_lookup_by_kind (ctf_dict_t *fp, int kind, const char *name)
   return ctf_set_typed_errno (fp, ECTF_NOTYPE);
 }
 
+/* Look up a variable by name, in this dict or the parent.  */
+ctf_id_t
+ctf_lookup_variable (ctf_dict_t *fp, const char *name)
+{
+  return ctf_lookup_by_kind (fp, CTF_K_VAR, name);
 }
 
 /* Look up a single enumerator by enumeration constant name.  Returns the ID of
index 00cbd96f0d83e07801a9bad7d9d4f3a103a9840f..94ecdbeadf6ff924edb2a158e8b8807648756ccc 100644 (file)
@@ -608,7 +608,7 @@ ctf_variable_iter (ctf_dict_t *fp, ctf_variable_f *func, void *arg)
   while ((type = ctf_variable_next (fp, &i, &name)) != CTF_ERR)
     {
       int rc;
-      if ((rc = func (name, type, arg)) != 0)
+      if ((rc = func (fp, name, type, arg)) != 0)
        {
          ctf_next_destroy (i);
          return rc;
@@ -643,7 +643,7 @@ ctf_variable_next (ctf_dict_t *fp, ctf_next_t **it, const char **name)
 
       i->cu.ctn_fp = fp;
       i->ctn_iter_fun = (void (*) (void)) ctf_variable_next;
-      i->u.ctn_dvd = ctf_list_next (&fp->ctf_dvdefs);
+      i->ctn_next = NULL;
       *it = i;
     }
 
@@ -653,25 +653,110 @@ ctf_variable_next (ctf_dict_t *fp, ctf_next_t **it, const char **name)
   if (fp != i->cu.ctn_fp)
     return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP));
 
-  if (i->ctn_n < fp->ctf_nvars)
+  if ((id = ctf_type_kind_next (fp, &i->ctn_next, CTF_K_VAR)) == CTF_ERR)
     {
-      *name = ctf_strptr (fp, fp->ctf_vars[i->ctn_n].ctv_name);
-      return fp->ctf_vars[i->ctn_n++].ctv_type;
+      if (ctf_errno (fp) == ECTF_NEXT_END)
+       ctf_next_destroy (i);
+    }
+
+  if (name)
+    *name = ctf_type_name_raw (fp, id);
+
+  return id;
+}
+
+/* Iterate over every variable in the given DATASEC, in arbitrary order.  We
+   pass the type ID, datasec-recorded size (usually 0), and offset of each
+   variable to the specified callback function.  */
+
+int
+ctf_datasec_var_iter (ctf_dict_t *fp, ctf_id_t datasec,
+                     ctf_datasec_var_f *func, void *arg)
+{
+  ctf_next_t *i = NULL;
+  ctf_id_t type;
+  size_t size, offset;
 
+  while ((type = ctf_datasec_var_next (fp, datasec, &i, &size, &offset)) != CTF_ERR)
+    {
+      int rc;
+      if ((rc = func (fp, type, offset, size, arg)) != 0)
+       {
+         ctf_next_destroy (i);
+         return rc;
+       }
     }
+  if (ctf_errno (fp) != ECTF_NEXT_END)
+    return -1;                                 /* errno is set for us.  */
+
+  return 0;
+}
+
+/* Iterate over every variable in the given CTF datasec, in arbitrary order,
+   returning the name and type of each variable in turn.  Returns CTF_ERR on end
+   of iteration or error.
 
-  if (i->u.ctn_dvd == NULL)
+   (The order is arbitrary so we don't need to worry about sorting unsorted
+   datasecs.)  */
+
+ctf_id_t
+ctf_datasec_var_next (ctf_dict_t *fp, ctf_id_t datasec, ctf_next_t **it,
+                     size_t *size, size_t *offset)
+{
+  ctf_next_t *i = *it;
+  ctf_id_t type;
+
+  if (!i)
+    {
+      const ctf_type_t *tp;
+      unsigned char *vlen;
+      ctf_dict_t *ofp = fp;
+
+      if ((datasec = ctf_type_resolve_unsliced (fp, datasec)) == CTF_ERR)
+       return CTF_ERR;                 /* errno is set for us.  */
+
+      if (ctf_type_kind (fp, datasec) != CTF_K_DATASEC)
+       return (ctf_set_typed_errno (ofp, ECTF_NOTDATASEC));
+
+      if ((tp = ctf_lookup_by_id (&fp, datasec, NULL)) == NULL)
+       return CTF_ERR;                 /* errno is set for us.  */
+
+      if ((i = ctf_next_create ()) == NULL)
+       return (ctf_set_typed_errno (ofp, ENOMEM));
+
+      i->cu.ctn_fp = ofp;
+      i->ctn_iter_fun = (void (*) (void)) ctf_datasec_var_next;
+      vlen = ctf_vlen (fp, datasec, tp, &i->ctn_n);
+
+      i->u.ctn_datasec = (const ctf_var_secinfo_t *) vlen;
+
+      *it = i;
+    }
+
+  if ((void (*) (void)) ctf_datasec_var_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));
+
+  if (i->ctn_n == 0)
     goto end_iter;
 
-  *name = i->u.ctn_dvd->dvd_name;
-  id = i->u.ctn_dvd->dvd_type;
-  i->u.ctn_dvd = ctf_list_next (i->u.ctn_dvd);
-  return id;
+  if (size)
+    *size = i->u.ctn_datasec->cvs_size;
+  if (offset)
+    *offset = i->u.ctn_datasec->cvs_offset;
+  type = i->u.ctn_datasec->cvs_type;
+
+  i->u.ctn_datasec++;
+  i->ctn_n--;
+
+  return type;
 
  end_iter:
   ctf_next_destroy (i);
   *it = NULL;
-  return ctf_set_typed_errno (fp, ECTF_NEXT_END);
+  return (ctf_set_typed_errno (fp, ECTF_NEXT_END));
 }
 
 /* Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and
@@ -874,8 +959,10 @@ ctf_type_aname (ctf_dict_t *fp, ctf_id_t type)
            case CTF_K_INTEGER:
            case CTF_K_FLOAT:
            case CTF_K_TYPEDEF:
-             /* Integers, floats, and typedefs must always be named types.  */
            case CTF_K_BTF_FLOAT:
+           case CTF_K_DATASEC:
+             /* Integers, floats, typedefs, and datasecs must always be named
+                types.  */
 
              if (name[0] == '\0')
                {
@@ -884,7 +971,11 @@ ctf_type_aname (ctf_dict_t *fp, ctf_id_t type)
                  return NULL;
                }
 
-             ctf_decl_sprintf (&cd, "%s", name);
+             if (cdp->cd_kind != CTF_K_DATASEC)
+               ctf_decl_sprintf (&cd, "%s", name);
+             else
+               ctf_decl_sprintf (&cd, "DATASEC (\"%s\", %i)", name,
+                                 LCTF_VLEN (rfp, tp));
              break;
            case CTF_K_POINTER:
              ctf_decl_sprintf (&cd, "*");
@@ -1967,6 +2058,110 @@ ctf_func_type_args (ctf_dict_t *fp, ctf_id_t type, uint32_t argc, ctf_id_t *argv
   return 0;
 }
 
+/* bsearch_r comparison function for datasec searches.  */
+static int
+search_datasec_by_offset (const void *key_, const void *arr_)
+{
+  uint32_t *key = (uint32_t *) key_;
+  ctf_var_secinfo_t *arr = (ctf_var_secinfo_t *) arr_;
+
+  if (*key < arr->cvs_offset)
+    return -1;
+  else if (*key > arr->cvs_offset)
+    return 1;
+
+  return 0;
+}
+
+/* Search a datasec for a variable covering a given offset.
+
+   Errors with ECTF_NODATASEC if not found.  */
+
+ctf_id_t
+ctf_datasec_var_offset (ctf_dict_t *fp, ctf_id_t datasec, uint32_t offset)
+{
+  ctf_dtdef_t *dtd;
+  const ctf_type_t *tp;
+  unsigned char *vlen;
+  size_t vlen_len;
+  ctf_var_secinfo_t *sec;
+  ctf_var_secinfo_t *el;
+  ssize_t size;
+
+  if ((tp = ctf_lookup_by_id (&fp, datasec, NULL)) == NULL)
+    return -1;                 /* errno is set for us.  */
+
+  if (ctf_type_kind (fp, datasec) != CTF_K_DATASEC)
+    return ctf_set_typed_errno (fp, ECTF_NOTDATASEC);
+
+  if ((dtd = ctf_dynamic_type (fp, datasec)) != NULL)
+    {
+      if (dtd->dtd_flags & DTD_F_UNSORTED)
+       ctf_datasec_sort (fp, dtd);
+    }
+
+  vlen = ctf_vlen (fp, datasec, tp, &vlen_len);
+  sec = (ctf_var_secinfo_t *) vlen;
+
+  if ((el = bsearch (&offset, sec, vlen_len, sizeof (ctf_var_secinfo_t),
+                    search_datasec_by_offset)) == NULL)
+    return ctf_set_typed_errno (fp, ECTF_NODATASEC);
+
+  if (el->cvs_offset == offset)
+    return el->cvs_type;
+
+  if ((size = ctf_type_size (fp, el->cvs_type)) >= 0)
+    if (el->cvs_offset < offset && el->cvs_offset + size > offset)
+      return el->cvs_type;
+
+  return ctf_set_typed_errno (fp, ECTF_NODATASEC);
+}
+
+/* Return the entry corresponding to a given component_idx in a datasec.
+
+   Not currently public API.  */
+
+ctf_var_secinfo_t *
+ctf_datasec_entry (ctf_dict_t *fp, ctf_id_t datasec, int component_idx)
+{
+  const ctf_type_t *tp;
+  unsigned char *vlen;
+  size_t vlen_len;
+  ctf_var_secinfo_t *sec;
+
+  if ((tp = ctf_lookup_by_id (&fp, datasec, NULL)) == NULL)
+    return NULL;               /* errno is set for us.  */
+
+  /* No type kind check: internal function.  */
+  vlen = ctf_vlen (fp, datasec, tp, &vlen_len);
+  sec = (ctf_var_secinfo_t *) vlen;
+
+  if (component_idx < 0 || (size_t) component_idx > vlen_len)
+    {
+      ctf_set_errno (fp, EOVERFLOW);
+      return NULL;
+    }
+
+  return &sec[component_idx];
+}
+
+/* Return the datasec that a given variable appears in, or ECTF_NODATASEC if
+   none.  */
+
+ctf_id_t ctf_variable_datasec (ctf_dict_t *fp, ctf_id_t var)
+{
+  void *sec;
+
+  if (ctf_type_kind (fp, var) != CTF_K_VAR)
+    return (ctf_set_typed_errno (fp, ECTF_NOTVAR));
+
+  if (ctf_dynhash_lookup_kv (fp->ctf_var_datasecs, (void *) (ptrdiff_t) var,
+                            NULL, &sec))
+    return (ctf_id_t) sec;
+
+  return (ctf_set_typed_errno (fp, ECTF_NODATASEC));
+}
+
 /* 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
index a4dfa5d5aae6390583fa7a84563d03c266615f1e..421bf40945b424c7a71d18aa3620ca6f7c81e5b2 100644 (file)
@@ -104,6 +104,8 @@ LIBCTF_2.0 {
        ctf_type_iter_all;
        ctf_variable_iter;
        ctf_variable_next;
+       ctf_datasec_iter;
+       ctf_datasec_next;
 
        ctf_next_create;
        ctf_next_destroy;
@@ -124,19 +126,20 @@ LIBCTF_2.0 {
        ctf_add_type;
        ctf_add_typedef;
        ctf_add_restrict;
+       ctf_add_section_variable;
        ctf_add_slice;
        ctf_add_struct;
        ctf_add_union;
        ctf_add_struct_sized;
        ctf_add_union_sized;
        ctf_add_unknown;
+       ctf_add_variable;
        ctf_add_volatile;
 
        ctf_add_enumerator;
        ctf_add_member;
        ctf_add_member_offset;
        ctf_add_member_encoded;
-       ctf_add_variable;
        ctf_add_member_bitfield;
 
        ctf_set_array;