From: Nick Alcock Date: Fri, 25 Apr 2025 16:28:37 +0000 (+0100) Subject: libctf: serialize: size and emit the type section X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=585f569a2dc390524607a8c12b7c4716b92abd12;p=thirdparty%2Fbinutils-gdb.git libctf: serialize: size and emit the type section As with sizing, this needs to support type suppression and CTF_K_BIG elision, and adapt to the DTD representation changes. Those changes cause a general complexity reduction because we no longer have to memcpy the vlen into place separately for every type kind, but can do it all at once using shared code above the per-kind switch statement. That statement's only job now is generating refs out of type IDs and string offsets, and translating the struct offset from gap- into non-gap representation for non-big structs. We do three distinct things: - check whether all the types in a section are BTF-compatible, after suppression of unwanted type kinds (including types with unwanted prefixes), and elision of unneeded struct/union CTF_K_BIGs - size the type section, taking suppression and CTF_K_BIG elision into account - actually emit it, again taking all the above into account These all have to come to the same conclusions for every type: if the first one gets things wrong we might try to emit something as BTF when we can't; if the latter two are inconsistent, we might have a buffer overrun. So the type emission code double-checks BTF-compatibility and raises ECTF_NOTBTF if necessary; we also aggressively check for potential overruns before every memcpy() into the buffer and raise an ECTF_INTERNAL assertion failure if need be. Thankfully there are a lot fewer memcpy()s than there used to be: there are only four places we need to check, all close to each other, which is pretty maintainable. We add a bit of debugging when --enable-libctf-hash-debugging is on, printing the translation from provisional to final type ID so that you can use it to map back to the provisional ID again when trying to track down deduplicator problems, since the IDs the deduplicator will report at its emission time are only provisional (the final parent-relative IDs are not assigned until now). --- diff --git a/libctf/ctf-serialize.c b/libctf/ctf-serialize.c index e0f86c85372..2cbaafc4e17 100644 --- a/libctf/ctf-serialize.c +++ b/libctf/ctf-serialize.c @@ -789,6 +789,42 @@ ctf_write_suppress_kind (ctf_dict_t *fp, int kind, int prohibited) return 0; } +/* Figure out whether we can elide a given type prefix as unnecessary. + + Prefixes can be elided iff they are BIG and zero-size and zero-vlen and (if + structs) the type as a whole is smallifiable. If not elided, this prefix is + included. */ +static int +ctf_prefix_elidable (ctf_dict_t *fp, uint32_t kind, ctf_dtdef_t *dtd, + ctf_type_t *prefix) +{ + int prefix_kind = LCTF_INFO_UNPREFIXED_KIND (fp, prefix->ctt_info); + + if ((prefix_kind == CTF_K_BIG) && prefix->ctt_size == 0 + && LCTF_INFO_UNPREFIXED_VLEN (fp, prefix->ctt_info) == 0) + { + ssize_t size; + + if (kind != CTF_K_STRUCT) + return 1; + + /* For bitfields, we must check if the individual member offsets will + still fit if they are encoded as offsets rather than offset-since-last. + We can check this for both cases without checking individual fields by + looking at the ctt_size. We need not check for nameless padding + members, because these can only be produced at all if the struct would + require CTF_K_BIG in the first place. */ + + size = ctf_get_ctt_size (fp, dtd->dtd_buf, NULL, NULL); + + if (CTF_INFO_KFLAG (dtd->dtd_data->ctt_info)) + return (size <= CTF_MAX_BIT_OFFSET); + else + return (size <= CTF_MAX_SIZE); + } + return 0; +} + /* Determine whether newly-defined or modified types indicate that we will be able to emit a BTF type section or must emit a CTF one. Only called if we have already verified that the CTF-specific sections (typetabs, etc) will be @@ -843,19 +879,19 @@ ctf_type_sect_is_btf (ctf_dict_t *fp, int force_ctf) dtd != NULL; dtd = ctf_list_next (dtd)) { ctf_type_t *tp = dtd->dtd_buf; - int kind; + uint32_t kind = LCTF_KIND (fp, dtd->dtd_buf); + uint32_t prefix_kind; /* Any un-suppressed prefixes other than an empty/redundant CTF_K_BIG must be CTF. (Redundant CTF_K_BIGs will be elided instead.) */ - while (kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), - LCTF_IS_PREFIXED_KIND (kind)) + while (prefix_kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), + LCTF_IS_PREFIXED_KIND (prefix_kind)) { - if ((kind != CTF_K_BIG) || tp->ctt_size != 0 - || LCTF_INFO_UNPREFIXED_VLEN (fp, tp->ctt_info) != 0) + if (!ctf_prefix_elidable (fp, kind, dtd, tp)) if (!fp->ctf_write_suppressions || ctf_dynset_lookup (fp->ctf_write_suppressions, - (const void *) (uintptr_t) kind) == NULL) + (const void *) (uintptr_t) prefix_kind) == NULL) return 0; tp++; @@ -864,8 +900,6 @@ ctf_type_sect_is_btf (ctf_dict_t *fp, int force_ctf) /* Prefixes checked. If this kind is suppressed, it won't influence the result. */ - kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info); - if (fp->ctf_write_suppressions && ctf_dynset_lookup (fp->ctf_write_suppressions, (const void *) (uintptr_t) kind)) @@ -873,33 +907,6 @@ ctf_type_sect_is_btf (ctf_dict_t *fp, int force_ctf) if (kind == CTF_K_FLOAT || kind == CTF_K_SLICE) return 0; - - if (kind == CTF_K_STRUCT) - { - ctf_member_t *memb = (ctf_member_t *) dtd->dtd_vlen; - size_t off = 0; - size_t j; - - for (j = 0; j < LCTF_VLEN (fp, dtd->dtd_buf); j++) - { - /* For bitfields, we must check if the individual members will - still fit into a BTF type if they are encoded as offsets rather - than offset-since-last. (For non-bitfields, this is satisfied - by a check of the ctt_size, which is equivalent to checking - that the CTF_K_BIG's ctt_size is zero, done above.) */ - - if (CTF_INFO_KFLAG (tp->ctt_info)) - { - off += CTF_MEMBER_BIT_OFFSET (memb[j].ctm_offset); - if (off > CTF_MAX_BIT_OFFSET) - return 0; - } - - /* Check for nameless padding members. */ - if (memb[j].ctm_name == 0 && memb[j].ctm_type == CTF_K_UNKNOWN) - return 0; - } - } } return 1; @@ -921,27 +928,27 @@ ctf_type_sect_size (ctf_dict_t *fp) dtd != NULL; dtd = ctf_list_next (dtd)) { uint32_t kind = LCTF_KIND (fp, dtd->dtd_buf); + uint32_t prefix_kind; ctf_type_t *tp = dtd->dtd_buf; /* Check for suppressions: a suppression consumes precisely one ctf_type_t - record of space. */ + record of space. A suppressed prefix suppresses the whole type. */ if (fp->ctf_write_suppressions) { int suppress = 0; - while (kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), - LCTF_IS_PREFIXED_KIND (kind)) + while (prefix_kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), + LCTF_IS_PREFIXED_KIND (prefix_kind)) { - if ((kind != CTF_K_BIG) || tp->ctt_size != 0 - || LCTF_INFO_UNPREFIXED_VLEN (fp, tp->ctt_info) != 0) - if (ctf_dynset_lookup (fp->ctf_write_suppressions, - (const void *) (uintptr_t) kind) != NULL) - { - type_size += sizeof (ctf_type_t); - suppress = 1; - break; - } + if (!ctf_prefix_elidable (fp, kind, dtd, tp) + && (ctf_dynset_lookup (fp->ctf_write_suppressions, + (const void *) (uintptr_t) prefix_kind) != NULL)) + { + type_size += sizeof (ctf_type_t); + suppress = 1; + break; + } tp++; } @@ -949,15 +956,13 @@ ctf_type_sect_size (ctf_dict_t *fp) continue; } - /* Type headers: elide CTF_K_BIG from types if possible. Must match - corresponding elision in ctf_emit_type_sect. */ + /* Type headers: elide CTF_K_BIG from types if possible. */ tp = dtd->dtd_buf; - while (kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), - LCTF_IS_PREFIXED_KIND (kind)) + while (prefix_kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), + LCTF_IS_PREFIXED_KIND (prefix_kind)) { - if ((kind != CTF_K_BIG) || tp->ctt_size != 0 - || LCTF_INFO_UNPREFIXED_VLEN (fp, tp->ctt_info) != 0) + if (!ctf_prefix_elidable (fp, kind, dtd, tp)) type_size += sizeof (ctf_type_t); tp++; } @@ -970,29 +975,35 @@ ctf_type_sect_size (ctf_dict_t *fp) /* Take a final lap through the dynamic type definition list and copy the appropriate type records to the output buffer, noting down the strings - and type IDs as we go. */ + and type IDs as we go. + + By this stage we no longer need to worry about CTF-versus-BTF, only about + whether a type has been suppressed or not. */ static int -ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr) +ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr, + size_t expected_size) { unsigned char *t = *tptr; ctf_dtdef_t *dtd; ctf_id_t id; - if (!(fp->ctf_flags & LCTF_CHILD)) - id = fp->ctf_stypes + 1; - else - id = fp->ctf_header->cth_parent_typemax + 1; + id = fp->ctf_stypes + 1; + + if (fp->ctf_flags & LCTF_CHILD) + id += fp->ctf_parent->ctf_typemax; for (dtd = ctf_list_next (&fp->ctf_dtdefs); dtd != NULL; dtd = ctf_list_next (dtd), id++) { - uint32_t kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info); - uint32_t vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info); - size_t type_ctt_size = dtd->dtd_data.ctt_size; - size_t len; - ctf_stype_t *copied; + uint32_t prefix_kind; + uint32_t kind = LCTF_KIND (fp, dtd->dtd_buf); + size_t vlen = LCTF_VLEN (fp, dtd->dtd_buf); + ctf_type_t *tp = dtd->dtd_buf; + ctf_type_t *copied; const char *name; + int suppress = 0; + int big = 0, big_elided = 0; size_t i; /* Make sure the ID hasn't changed, if already assigned by a previous @@ -1002,36 +1013,120 @@ ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr) && !ctf_assert (fp, dtd->dtd_final_type == id)) return -1; /* errno is set for us. */ - /* Shrink ctf_type_t-using types from a ctf_type_t to a ctf_stype_t - if possible. */ + /* Suppress everything if this kind is suppressed. */ + + while (prefix_kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), + LCTF_IS_PREFIXED_KIND (prefix_kind)) + { + /* Don't worry about BIGs that will be elided. */ + if (ctf_prefix_elidable (fp, kind, dtd, tp)) + { + tp++; + continue; + } + + if (!fp->ctf_write_suppressions + || ctf_dynset_lookup (fp->ctf_write_suppressions, + (const void *) (uintptr_t) prefix_kind) == NULL) + { + if (_libctf_btf_mode == LIBCTF_BTM_BTF) + { + ctf_err_warn (fp, 0, ECTF_NOTBTF, _("type %lx: Attempt to write out CTF-specific kind %i as BTF"), + id, prefix_kind); + return (ctf_set_errno (fp, ECTF_NOTBTF)); + } + } + else + { + suppress = 1; + break; + } + tp++; + } + + if (fp->ctf_write_suppressions + && ctf_dynset_lookup (fp->ctf_write_suppressions, + (const void *) (uintptr_t) kind) != NULL) + suppress = 1; - if (kind == CTF_K_STRUCT || kind == CTF_K_UNION) + if (suppress) { - size_t lsize = CTF_TYPE_LSIZE (&dtd->dtd_data); + ctf_type_t suppressed = { 0 }; - if (lsize <= CTF_MAX_SIZE) - type_ctt_size = lsize; + if (!ctf_assert (fp, t - *tptr + sizeof (ctf_type_t) <= expected_size)) + return -1; /* errno is set for us. */ + + suppressed.ctt_info = CTF_TYPE_INFO (CTF_K_UNKNOWN, 0, 0); + memcpy (t, &suppressed, sizeof (ctf_type_t)); + t += sizeof (ctf_type_t); + + continue; } - if (type_ctt_size != CTF_LSIZE_SENT) - len = sizeof (ctf_stype_t); - else - len = sizeof (ctf_type_t); + /* Write out all the type headers, eliding empty CTF_K_BIG, and noting if + this type is BIG. */ + + tp = dtd->dtd_buf; + while (prefix_kind = LCTF_INFO_UNPREFIXED_KIND (fp, tp->ctt_info), + LCTF_IS_PREFIXED_KIND (prefix_kind)) + { + if (prefix_kind == CTF_K_BIG) + { + big = 1; + + if (ctf_prefix_elidable (fp, kind, dtd, tp)) + { + big_elided = 1; + tp++; + continue; + } + } + + if (!ctf_assert (fp, t - *tptr + sizeof (ctf_type_t) <= expected_size)) + return -1; /* errno is set for us. */ + + memcpy (t, tp, sizeof (ctf_type_t)); + copied = (ctf_type_t *) t; + + /* CTF_K_CONFLICTING has a name to keep track of. */ + + if (copied->ctt_name + && (name = ctf_strraw (fp, copied->ctt_name)) != NULL) + ctf_str_add_ref (fp, name, &copied->ctt_name); + + /* No prefixed kinds have any ctt_types to deal with. */ - memcpy (t, &dtd->dtd_data, len); - copied = (ctf_stype_t *) t; /* name is at the start: constant offset. */ + tp++; + t += sizeof (ctf_type_t); + } + + if (!ctf_assert (fp, t - *tptr + sizeof (ctf_type_t) <= expected_size)) + return -1; /* errno is set for us. */ + + memcpy (t, tp, sizeof (ctf_type_t)); + copied = (ctf_type_t *) t; if (copied->ctt_name && (name = ctf_strraw (fp, copied->ctt_name)) != NULL) ctf_str_add_ref (fp, name, &copied->ctt_name); - copied->ctt_size = type_ctt_size; - t += len; + t += sizeof (ctf_type_t); + + if (!ctf_assert (fp, t - *tptr + dtd->dtd_vlen_size <= expected_size)) + return -1; /* errno is set for us. */ + + memcpy (t, dtd->dtd_vlen, dtd->dtd_vlen_size); switch (kind) { - case CTF_K_INTEGER: - case CTF_K_FLOAT: - memcpy (t, dtd->dtd_vlen, sizeof (uint32_t)); - t += sizeof (uint32_t); + case CTF_K_ARRAY: + { + ctf_array_t *array = (ctf_array_t *) t; + + if (ctf_type_add_ref (fp, &array->cta_contents) < 0) + return -1; /* errno is set for us. */ + + if (ctf_type_add_ref (fp, &array->cta_index) < 0) + return -1; /* errno is set for us. */ + } break; case CTF_K_POINTER: @@ -1039,6 +1134,10 @@ ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr) case CTF_K_CONST: case CTF_K_RESTRICT: case CTF_K_TYPEDEF: + case CTF_K_FUNC_LINKAGE: + case CTF_K_TYPE_TAG: + case CTF_K_DECL_TAG: + case CTF_K_VAR: if (ctf_type_add_ref (fp, &copied->ctt_type) < 0) return -1; /* errno is set for us. */ break; @@ -1047,8 +1146,6 @@ ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr) { ctf_slice_t *slice = (ctf_slice_t *) t; - memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_slice)); - if (ctf_type_add_ref (fp, &slice->cts_type) < 0) return -1; /* errno is set for us. */ } @@ -1057,102 +1154,132 @@ ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr) break; - case CTF_K_ARRAY: - { - ctf_array_t *array = (ctf_array_t *) t; - - memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_array)); - - if (ctf_type_add_ref (fp, &array->cta_contents) < 0) - return -1; /* errno is set for us. */ - - if (ctf_type_add_ref (fp, &array->cta_index) < 0) - return -1; /* errno is set for us. */ - } - t += sizeof (struct ctf_array); - break; - case CTF_K_FUNCTION: { - uint32_t *args = (uint32_t *) t; - - /* Functions with no args also have no vlen. */ - - if (dtd->dtd_vlen) - memcpy (t, dtd->dtd_vlen, sizeof (uint32_t) * (vlen + (vlen & 1))); + ctf_param_t *args = (ctf_param_t *) t; + ctf_param_t *dtd_args = (ctf_param_t *) dtd->dtd_vlen; if (ctf_type_add_ref (fp, &copied->ctt_type) < 0) return -1; /* errno is set for us. */ for (i = 0; i < vlen; i++) { - if (ctf_type_add_ref (fp, &args[i]) < 0) + const char *name = ctf_strraw (fp, args[i].cfp_name); + + ctf_str_add_ref (fp, name, &args[i].cfp_name); + ctf_str_add_ref (fp, name, &dtd_args[i].cfp_name); + + if (ctf_type_add_ref (fp, &args[i].cfp_type) < 0) return -1; /* errno is set for us. */ } - - t += sizeof (uint32_t) * (vlen + (vlen & 1)); - break; + break; } - /* These need to be copied across element by element, depending on - their ctt_size. */ + /* These may need all their offsets adjusting. */ case CTF_K_STRUCT: case CTF_K_UNION: { - ctf_lmember_t *dtd_vlen = (ctf_lmember_t *) dtd->dtd_vlen; - ctf_lmember_t *t_lvlen = (ctf_lmember_t *) t; - ctf_member_t *t_vlen = (ctf_member_t *) t; + size_t offset = 0; + int bitwise = CTF_INFO_KFLAG (tp->ctt_info); + int struct_is_prefixed_big = big; + + ctf_member_t *memb = (ctf_member_t *) t; + ctf_member_t *dtd_memb = (ctf_member_t *) dtd->dtd_vlen; + + /* All structs and unions in a DTD must always be BIG. */ + + if (!ctf_assert (fp, struct_is_prefixed_big)) + return -1; /* errno is set for us. */ for (i = 0; i < vlen; i++) { - const char *name = ctf_strraw (fp, dtd_vlen[i].ctlm_name); + const char *name = ctf_strraw (fp, memb[i].ctm_name); - ctf_str_add_ref (fp, name, &dtd_vlen[i].ctlm_name); + ctf_str_add_ref (fp, name, &memb[i].ctm_name); + ctf_str_add_ref (fp, name, &dtd_memb[i].ctm_name); - if (type_ctt_size < CTF_LSTRUCT_THRESH) - { - t_vlen[i].ctm_name = dtd_vlen[i].ctlm_name; - t_vlen[i].ctm_type = dtd_vlen[i].ctlm_type; - t_vlen[i].ctm_offset = CTF_LMEM_OFFSET (&dtd_vlen[i]); + /* If we are reducing a struct to non-big, we must convert its + offsets back to offset-from-start. */ - ctf_str_add_ref (fp, name, &t_vlen[i].ctm_name); - if (ctf_type_add_ref (fp, &t_vlen[i].ctm_type) < 0) - return -1; /* errno is set for us. */ - } - else + if (big_elided && kind == CTF_K_STRUCT) { - t_lvlen[i] = dtd_vlen[i]; - - ctf_str_add_ref (fp, name, &t_lvlen[i].ctlm_name); - if (ctf_type_add_ref (fp, &t_lvlen[i].ctlm_type) < 0) - return -1; /* errno is set for us. */ + size_t this_offset, this_size; + + if (bitwise) + { + this_offset = CTF_MEMBER_BIT_OFFSET (memb[i].ctm_offset); + this_size = CTF_MEMBER_BIT_SIZE (memb[i].ctm_offset); + offset += this_offset; + memb[i].ctm_offset = CTF_MEMBER_MAKE_BIT_OFFSET (this_size, + offset); + } + else + { + offset += memb[i].ctm_offset; + memb[i].ctm_offset = offset; + } } + + if (ctf_type_add_ref (fp, &memb[i].ctm_type) < 0) + return -1; /* errno is set for us. */ } } - - if (type_ctt_size < CTF_LSTRUCT_THRESH) - t += sizeof (ctf_member_t) * vlen; - else - t += sizeof (ctf_lmember_t) * vlen; break; case CTF_K_ENUM: { - ctf_enum_t *dtd_vlen = (struct ctf_enum *) dtd->dtd_vlen; - ctf_enum_t *t_vlen = (struct ctf_enum *) t; + ctf_enum_t *dtd_vlen = (ctf_enum_t *) dtd->dtd_vlen; + ctf_enum_t *t_vlen = (ctf_enum_t *) t; + + for (i = 0; i < vlen; i++) + { + const char *name = ctf_strraw (fp, dtd_vlen[i].cte_name); + + ctf_str_add_ref (fp, name, &dtd_vlen[i].cte_name); + ctf_str_add_ref (fp, name, &t_vlen[i].cte_name); + } + + break; + } + + case CTF_K_ENUM64: + { + ctf_enum64_t *dtd_vlen = (ctf_enum64_t *) dtd->dtd_vlen; + ctf_enum64_t *t_vlen = (ctf_enum64_t *) t; - memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_enum) * vlen); for (i = 0; i < vlen; i++) { const char *name = ctf_strraw (fp, dtd_vlen[i].cte_name); + ctf_str_add_ref (fp, name, &dtd_vlen[i].cte_name); ctf_str_add_ref (fp, name, &t_vlen[i].cte_name); } - t += sizeof (struct ctf_enum) * vlen; break; } + case CTF_K_DATASEC: + { + ctf_var_secinfo_t *sec = (ctf_var_secinfo_t *) t; + + if (dtd->dtd_flags & DTD_F_UNSORTED) + ctf_datasec_sort (fp, dtd); + + for (i = 0; i < vlen; i++) + { + if (ctf_type_add_ref (fp, &sec[i].cvs_type) < 0) + return -1; /* errno is set for us. */ + } + break; + } } + +#ifdef ENABLE_LIBCTF_HASH_DEBUGGING + if (dtd->dtd_type != id) + ctf_dprintf ("%p: provisional ID assignment: %lx -> %lx\n", (void *) fp, + dtd->dtd_type, id); +#endif + + t += dtd->dtd_vlen_size; dtd->dtd_final_type = id; }