From: Nick Alcock Date: Thu, 24 Apr 2025 15:47:14 +0000 (+0100) Subject: libctf: create: structure and union member addition X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=20e6f72dc7a799b39f259a5138e307937969099b;p=thirdparty%2Fbinutils-gdb.git libctf: create: structure and union member addition 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). --- diff --git a/include/ctf-api.h b/include/ctf-api.h index 03711f7a213..71bc6da0e62 100644 --- a/include/ctf-api.h +++ b/include/ctf-api.h @@ -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); diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c index a0ffb878033..287f4cee04f 100644 --- a/libctf/ctf-create.c +++ b/libctf/ctf-create.c @@ -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 diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index 76615b337f7..25617f50b04 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -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 *, ...); diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c index 6b8ff8659d8..6a714ceb850 100644 --- a/libctf/ctf-types.c +++ b/libctf/ctf-types.c @@ -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. diff --git a/libctf/libctf.ver b/libctf/libctf.ver index 2dc2c561fa3..77d532f2941 100644 --- a/libctf/libctf.ver +++ b/libctf/libctf.ver @@ -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;