]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
libctf: create: structure and union member addition
authorNick Alcock <nick.alcock@oracle.com>
Thu, 24 Apr 2025 15:47:14 +0000 (16:47 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 25 Apr 2025 17:07:42 +0000 (18:07 +0100)
There is one API addition here:

int ctf_add_member_bitfield (ctf_dict_t *, ctf_id_t souid,
                             const char *, ctf_id_t type,
                             unsigned long bit_offset,
                             int bit_width);

SoU addition handles the representational changes for bitfields and for
CTF_K_BIG structs (i.e. all structs you can add members to), errors out if
you add bitfields to structs that aren't created with the
CTF_ADD_STRUCT_BITFIELDS flag, and arranges to add padding as needed if
there is too much of a gap for the offsets to encode in one hop (that
part is still untested).

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

index 03711f7a213de24c040e057643c84f3780b99adf..71bc6da0e62d748beede32dbdf9d76e0b14663d7 100644 (file)
@@ -871,7 +871,9 @@ extern int ctf_add_enumerator (ctf_dict_t *, ctf_id_t, const char *, int);
 /* Add a member to a struct or union, either at the next available offset (with
    suitable padding for the alignment) or at a specific offset, and possibly
    with a specific encoding (creating a slice for you).  Offsets need not be
-   unique, and need not be added in ascending order.  */
+   unique, and need not be added in ascending order.  ctf_add_member_bitfield
+   with a nonzero bit_width will fail unless the struct was created with
+   CTF_ADD_STRUCT_BITFIELDS.  */
 
 extern int ctf_add_member (ctf_dict_t *, ctf_id_t, const char *, ctf_id_t);
 extern int ctf_add_member_offset (ctf_dict_t *, ctf_id_t, const char *,
@@ -879,6 +881,10 @@ extern int ctf_add_member_offset (ctf_dict_t *, ctf_id_t, const char *,
 extern int ctf_add_member_encoded (ctf_dict_t *, ctf_id_t, const char *,
                                   ctf_id_t, unsigned long,
                                   const ctf_encoding_t);
+extern int ctf_add_member_bitfield (ctf_dict_t *, ctf_id_t souid,
+                                    const char *, ctf_id_t type,
+                                    unsigned long bit_offset,
+                                    int bit_width);
 
 extern int ctf_add_variable (ctf_dict_t *, const char *, ctf_id_t);
 
index a0ffb8780339ae143b36387091bff5191410d3dc..287f4cee04feabf5422fe103f154479fbb3b6e76 100644 (file)
@@ -1276,18 +1276,21 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
 }
 
 int
-ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
-                      ctf_id_t type, unsigned long bit_offset)
+ctf_add_member_bitfield (ctf_dict_t *fp, ctf_id_t souid, const char *name,
+                        ctf_id_t type, unsigned long bit_offset,
+                        int bit_width)
 {
   ctf_dict_t *ofp = fp;
   ctf_dict_t *tmp = fp;
-  ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, souid);
+  ctf_dtdef_t *dtd;
+  ctf_type_t *prefix;
 
-  ssize_t msize, malign, ssize;
-  uint32_t kind, vlen, root;
+  ssize_t msize, ssize;
+  uint32_t kind, kflag;
+  size_t vlen;
   size_t i;
   int is_incomplete = 0;
-  ctf_lmember_t *memb;
+  ctf_member_t *memb;
 
   if (fp->ctf_flags & LCTF_NO_STR)
     return (ctf_set_errno (fp, ECTF_NOPARENT));
@@ -1306,53 +1309,71 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
       fp = fp->ctf_parent;
     }
 
+  dtd = ctf_dtd_lookup (fp, souid);
+
   if (souid < fp->ctf_stypes)
     return (ctf_set_errno (ofp, ECTF_RDONLY));
 
   if (dtd == NULL)
     return (ctf_set_errno (ofp, ECTF_BADID));
 
-  if ((ctf_lookup_by_id (&tmp, type)) == NULL)
+  if ((ctf_lookup_by_id (&tmp, type, NULL)) == NULL)
     return -1;                 /* errno is set for us.  */
 
   if (name != NULL && name[0] == '\0')
     name = NULL;
 
-  kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
-  root = LCTF_INFO_ISROOT (fp, dtd->dtd_data.ctt_info);
-  vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
+  if ((prefix = (ctf_type_t *) ctf_find_prefix (fp, dtd->dtd_buf, CTF_K_BIG)) == NULL)
+    return (ctf_set_errno (ofp, ECTF_CORRUPT));
+
+  kind = LCTF_KIND (fp, prefix);
+  kflag = CTF_INFO_KFLAG (dtd->dtd_data->ctt_info);
+  vlen = LCTF_VLEN (fp, prefix);
 
   if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
     return (ctf_set_errno (ofp, ECTF_NOTSOU));
 
+  if (!kflag && bit_width != 0)
+    return (ctf_set_errno (ofp, ECTF_NOTBITSOU));
+
   if (vlen == CTF_MAX_VLEN)
     return (ctf_set_errno (ofp, ECTF_DTFULL));
 
-  if (ctf_grow_vlen (fp, dtd, sizeof (ctf_lmember_t) * (vlen + 1)) < 0)
-    return (ctf_set_errno (ofp, ctf_errno (fp)));
-  memb = (ctf_lmember_t *) dtd->dtd_vlen;
+  /* Figure out the offset of this field: all structures in DTDs
+     are CTF_K_BIG, which means their offsets are all encoded as
+     distances from the last field's.  */
+
+  if (bit_offset != (unsigned long) -1)
+    {
+      if (bit_offset < dtd->dtd_last_offset)
+       return (ctf_set_errno (ofp, ECTF_DESCENDING));
+
+      bit_offset -= dtd->dtd_last_offset;
+    }
+
+  memb = (ctf_member_t *) dtd->dtd_vlen;
 
   if (name != NULL)
     {
       for (i = 0; i < vlen; i++)
-       if (strcmp (ctf_strptr (fp, memb[i].ctlm_name), name) == 0)
+       if (strcmp (ctf_strptr (fp, memb[i].ctm_name), name) == 0)
          return (ctf_set_errno (ofp, ECTF_DUPLICATE));
     }
 
   if ((msize = ctf_type_size (fp, type)) < 0 ||
-      (malign = ctf_type_align (fp, type)) < 0)
+      (ctf_type_align (fp, type)) < 0)
     {
       /* The unimplemented type, and any type that resolves to it, has no size
         and no alignment: it can correspond to any number of compiler-inserted
         types.  We allow incomplete types through since they are routinely
         added to the ends of structures, and can even be added elsewhere in
-        structures by the deduplicator.  They are assumed to be zero-size with
-        no alignment: this is often wrong, but problems can be avoided in this
-        case by explicitly specifying the size of the structure via the _sized
-        functions.  The deduplicator always does this.  */
+        structures by the deduplicator and by the padding inserter below.  They
+        are assumed to be zero-size with no alignment: this is often wrong, but
+        problems can be avoided in this case by explicitly specifying the size
+        of the structure via the _sized functions.  The deduplicator always
+        does this.  */
 
       msize = 0;
-      malign = 0;
       if (ctf_errno (fp) == ECTF_NONREPRESENTABLE)
        ctf_set_errno (fp, 0);
       else if (ctf_errno (fp) == ECTF_INCOMPLETE)
@@ -1361,95 +1382,130 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
        return -1;              /* errno is set for us.  */
     }
 
-  memb[vlen].ctlm_name = ctf_str_add (fp, name);
-  memb[vlen].ctlm_type = type;
-  if (memb[vlen].ctlm_name == 0 && name != NULL && name[0] != '\0')
-    return -1;                 /* errno is set for us.  */
+  /* Figure out the right offset for naturally-aligned types, if need be,
+     and insert additional unnamed members as needed.  */
 
-  if (kind == CTF_K_STRUCT && vlen != 0)
+  if (kind == CTF_K_UNION || vlen == 0)
     {
+      bit_offset = 0;
+      ssize = ctf_get_ctt_size (fp, prefix, NULL, NULL);
+      ssize = MAX (ssize, msize);
+    }
+  else                                 /* Subsequent struct member. */
+    {
+      size_t bound;
+      ssize_t off;
+      int added_padding = 0;
+
       if (bit_offset == (unsigned long) - 1)
        {
          /* Natural alignment.  */
 
-         ctf_id_t ltype = ctf_type_resolve (fp, memb[vlen - 1].ctlm_type);
-         size_t off = CTF_LMEM_OFFSET(&memb[vlen - 1]);
-
-         ctf_encoding_t linfo;
-         ssize_t lsize;
-
-         /* Propagate any error from ctf_type_resolve.  If the last member was
-            of unimplemented type, this may be -ECTF_NONREPRESENTABLE: we
-            cannot insert right after such a member without explicit offset
-            specification, because its alignment and size is not known.  */
-         if (ltype == CTF_ERR)
-           return -1;                          /* errno is set for us.  */
-
          if (is_incomplete)
            {
              ctf_err_warn (ofp, 1, ECTF_INCOMPLETE,
-                           _("ctf_add_member_offset: cannot add member %s of "
+                           _("ctf_add_member: cannot add member %s of "
                              "incomplete type %lx to struct %lx without "
                              "specifying explicit offset\n"),
                            name ? name : _("(unnamed member)"), type, souid);
              return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
            }
 
-         if (ctf_type_encoding (fp, ltype, &linfo) == 0)
-           off += linfo.cte_bits;
-         else if ((lsize = ctf_type_size (fp, ltype)) > 0)
-           off += lsize * CHAR_BIT;
-         else if (lsize == -1 && ctf_errno (fp) == ECTF_INCOMPLETE)
+         if ((off = ctf_type_align_natural (fp, memb[vlen - 1].ctm_type,
+                                            type, dtd->dtd_last_offset)) < 0)
            {
-             const char *lname = ctf_strraw (fp, memb[vlen - 1].ctlm_name);
-
-             ctf_err_warn (ofp, 1, ECTF_INCOMPLETE,
-                           _("ctf_add_member_offset: cannot add member %s of "
-                             "type %lx to struct %lx without specifying "
-                             "explicit offset after member %s of type %lx, "
-                             "which is an incomplete type\n"),
-                           name ? name : _("(unnamed member)"), type, souid,
-                           lname ? lname : _("(unnamed member)"), ltype);
-             return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
+             if (ctf_errno (fp) == ECTF_INCOMPLETE)
+               {
+                 const char *lname = ctf_strraw (fp, memb[vlen - 1].ctm_name);
+
+                 ctf_err_warn (ofp, 1, ECTF_INCOMPLETE,
+                               _("ctf_add_member_offset: cannot add member %s "
+                                 "of type %lx to struct %lx without "
+                                 "specifying explicit offset after member %s"
+                                 "of type %x, which is an incomplete type\n"),
+                               name ? name : _("(unnamed member)"), type, souid,
+                               lname ? lname : _("(unnamed member)"),
+                               memb[vlen -1].ctm_type);
+               }
+             return (ctf_set_errno (ofp, ctf_errno (fp)));
            }
 
-         /* Round up the offset of the end of the last member to
-            the next byte boundary, convert 'off' to bytes, and
-            then round it up again to the next multiple of the
-            alignment required by the new member.  Finally,
-            convert back to bits and store the result in
-            dmd_offset.  Technically we could do more efficient
-            packing if the new member is a bit-field, but we're
-            the "compiler" and ANSI says we can do as we choose.  */
-
-         off = roundup (off, CHAR_BIT) / CHAR_BIT;
-         off = roundup (off, MAX (malign, 1));
-         memb[vlen].ctlm_offsethi = CTF_OFFSET_TO_LMEMHI (off * CHAR_BIT);
-         memb[vlen].ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO (off * CHAR_BIT);
-         ssize = off + msize;
+         /* Convert the offset to a gap-since-the-last.  */
+         off -= dtd->dtd_last_offset;
+         bit_offset = off;
        }
+
+      /* Insert as many nameless members as needed.  */
+
+      if (kflag)
+       bound = CTF_MAX_BIT_OFFSET;
       else
+       bound = CTF_MAX_SIZE;
+
+      while (bit_offset > bound)
        {
-         /* Specified offset in bits.  */
+         added_padding = 1;
+
+         off = bound;
+         if (kflag)
+           off = CTF_MEMBER_BIT_OFFSET (bound);
 
-         memb[vlen].ctlm_offsethi = CTF_OFFSET_TO_LMEMHI (bit_offset);
-         memb[vlen].ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO (bit_offset);
-         ssize = ctf_get_ctt_size (fp, &dtd->dtd_data, NULL, NULL);
-         ssize = MAX (ssize, ((signed) bit_offset / CHAR_BIT) + msize);
+         if (ctf_add_member_bitfield (fp, souid, "", 0, off, 0) < 0)
+           return -1;          /* errno is set for us.  */
+
+         bit_offset =- off;
        }
+
+      off = bit_offset;
+      if (kflag)
+       off = CTF_MEMBER_BIT_OFFSET (off);
+
+      /* Possibly hunt down the prefix and member list again: they may have been
+        moved by the realloc()s involved in field additions.  */
+
+      if (added_padding
+         && (prefix = (ctf_type_t *) ctf_find_prefix (fp, dtd->dtd_buf, CTF_K_BIG)) == NULL)
+       return (ctf_set_errno (ofp, ECTF_CORRUPT));
+
+      vlen = LCTF_VLEN (fp, prefix);
+      memb = (ctf_member_t *) dtd->dtd_vlen;
+      bit_offset = off;
+
+      ssize = ctf_get_ctt_size (fp, prefix, NULL, NULL);
+      ssize = MAX (ssize, ((signed) ((bit_offset + dtd->dtd_last_offset)) / CHAR_BIT) + msize);
     }
+
+  if (kflag)
+    memb[vlen].ctm_offset = CTF_MEMBER_MAKE_BIT_OFFSET (bit_width, bit_offset);
   else
-    {
-      memb[vlen].ctlm_offsethi = 0;
-      memb[vlen].ctlm_offsetlo = 0;
-      ssize = ctf_get_ctt_size (fp, &dtd->dtd_data, NULL, NULL);
-      ssize = MAX (ssize, msize);
-    }
+    memb[vlen].ctm_offset = bit_offset;
 
-  dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
-  dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI (ssize);
-  dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO (ssize);
-  dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, root, vlen + 1);
+  vlen = LCTF_VLEN (fp, prefix);
+
+  if (ctf_grow_vlen (fp, dtd, sizeof (ctf_member_t) * (vlen + 1)) < 0)
+    return (ctf_set_errno (ofp, ctf_errno (fp)));
+
+  dtd->dtd_vlen_size += sizeof (ctf_member_t);
+
+  /* Hunt down the prefix and member list yet again, since they may have been
+     reallocated by ctf_grow_vlen.  */
+
+  if ((prefix = (ctf_type_t *) ctf_find_prefix (fp, dtd->dtd_buf, CTF_K_BIG)) == NULL)
+    return (ctf_set_errno (ofp, ECTF_CORRUPT));
+  memb = (ctf_member_t *) dtd->dtd_vlen;
+
+  memb[vlen].ctm_name = ctf_str_add (fp, name);
+  memb[vlen].ctm_type = type;
+  if (memb[vlen].ctm_name == 0 && name != NULL && name[0] != '\0')
+    return -1;                 /* errno is set for us.  */
+
+  dtd->dtd_data->ctt_size = CTF_SIZE_TO_LSIZE_LO (ssize);
+  prefix->ctt_size = CTF_SIZE_TO_LSIZE_HI (ssize);
+
+  dtd->dtd_data->ctt_info = CTF_TYPE_INFO (kind, kflag, CTF_VLEN_TO_VLEN_LO(vlen + 1));
+  prefix->ctt_info = CTF_TYPE_INFO (CTF_K_BIG, 0, CTF_VLEN_TO_VLEN_HI(vlen + 1));
+
+  dtd->dtd_last_offset += bit_offset;
 
   return 0;
 }
@@ -1466,15 +1522,31 @@ ctf_add_member_encoded (ctf_dict_t *fp, ctf_id_t souid, const char *name,
   if (dtd == NULL)
     return (ctf_set_errno (fp, ECTF_BADID));
 
-  kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
+  kind = LCTF_KIND (fp, dtd->dtd_buf);
 
   if ((kind != CTF_K_INTEGER) && (kind != CTF_K_FLOAT) && (kind != CTF_K_ENUM))
     return (ctf_set_errno (fp, ECTF_NOTINTFP));
 
-  if ((type = ctf_add_slice (fp, CTF_ADD_NONROOT, otype, &encoding)) == CTF_ERR)
-    return -1;                 /* errno is set for us.  */
+  /* Create a slice if need be.  */
+
+  if (encoding.cte_offset != 0 ||
+      encoding.cte_format != 0 ||
+      (encoding.cte_bits != 0 && CTF_INFO_KFLAG (dtd->dtd_data->ctt_info) == 0))
+    {
+      if ((type = ctf_add_slice (fp, CTF_ADD_NONROOT, otype, &encoding)) == CTF_ERR)
+       return -1;                      /* errno is set for us.  */
+    }
+  else
+    type = otype;
+
+  return ctf_add_member_bitfield (fp, souid, name, type, bit_offset, 0);
+}
 
-  return ctf_add_member_offset (fp, souid, name, type, bit_offset);
+int
+ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
+                      ctf_id_t type, unsigned long bit_offset)
+{
+  return ctf_add_member_bitfield (fp, souid, name, type, bit_offset, 0);
 }
 
 int
index 76615b337f7919038cae7527d17940b378ea1f3c..25617f50b04438aeb66808be09dea8dc4ebd48af 100644 (file)
@@ -812,6 +812,8 @@ extern char *ctf_str_append_noerr (char *, const char *);
 
 extern ctf_id_t ctf_type_resolve_unsliced (ctf_dict_t *, ctf_id_t);
 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);
 
 _libctf_printflike_ (1, 2)
 extern void ctf_dprintf (const char *, ...);
index 6b8ff8659d8e67c32674f50c0d7fe8cd58d0a29d..6a714ceb850f3bf3ab330509d2483b603dd4e411 100644 (file)
@@ -1094,6 +1094,60 @@ ctf_type_size (ctf_dict_t *fp, ctf_id_t type)
     }
 }
 
+/* Determine the natural alignment (in bits) for some type, given the previous
+   TYPE at BIT_OFFSET.
+
+   Not public because doing this entirely right requires arch-dependent
+   attention: this is just to reduce code repetition in ctf-create.c.
+
+   Errors if the TYPE or PREV_TYPE are unsuitable for automatic alignment
+   determination: in particular, you can insert incomplete or nonrepresentable
+   TYPEs, but PREV_TYPE cannot be incomplete or nonrepresentable.  */
+
+ssize_t
+ctf_type_align_natural (ctf_dict_t *fp, ctf_id_t prev_type,
+                       ctf_id_t type, ssize_t bit_offset)
+{
+  ctf_encoding_t info;
+  ssize_t size;
+  ssize_t align;
+
+  if ((prev_type = ctf_type_resolve (fp, prev_type)) == CTF_ERR)
+    return -1;                 /* errno is set for us.  */
+
+  if ((align = ctf_type_align (fp, type)) < 0)
+    {
+      /* Ignore incompleteness and nonrepresentability of the type we're
+        inserting: just assume such a type has no alignment constraints of its
+        own.  */
+      if (ctf_errno (fp) == ECTF_NONREPRESENTABLE
+         || ctf_errno (fp) == ECTF_INCOMPLETE)
+       align = 0;
+      else
+       return -1;              /* errno is set for us.  */
+    }
+
+  if (ctf_type_encoding (fp, prev_type, &info) == 0)
+    bit_offset += info.cte_bits;
+  else if ((size = ctf_type_size (fp, prev_type)) > 0)
+    bit_offset += size * CHAR_BIT;
+  else if (size < 0)
+    return -1;                 /* errno is set for us.  */
+
+  /* Round up the offset of the end of the last member to the next byte
+     boundary, convert 'off' to bytes, and then round it up again to the next
+     multiple of the alignment required by the new member.  Finally, convert
+     back to bits and store the result.  Technically we could do more efficient
+     packing within structs if the new member is a bit-field, but we're the
+     "compiler" and the Standard says we can do as we choose.  */
+
+  bit_offset = roundup (bit_offset, CHAR_BIT) / CHAR_BIT;
+  bit_offset = roundup (bit_offset, MAX (align, 1));
+  bit_offset *= CHAR_BIT;
+
+  return bit_offset;
+}
+
 /* Resolve the type down to a base type node, and then return the alignment
    needed for the type storage in bytes.
 
index 2dc2c561fa30a2f5a034372358b7c30bd2427030..77d532f294136e0235026ba3e579e854706ba461 100644 (file)
@@ -132,6 +132,7 @@ LIBCTF_2.0 {
        ctf_add_member_offset;
        ctf_add_member_encoded;
        ctf_add_variable;
+       ctf_add_member_bitfield;
 
        ctf_set_array;