diff options
author | Nick Alcock <nick.alcock@oracle.com> | 2024-07-15 21:56:15 +0100 |
---|---|---|
committer | Nick Alcock <nick.alcock@oracle.com> | 2024-11-18 11:50:08 +0000 |
commit | 5d0de5db96bf59bd5411d5029938fc9d673ea9d5 (patch) | |
tree | f0ad3fadfc1e7071d617e1831c15921a83ec5099 | |
parent | dc0867e89e6cf46158e3fb6da34eccc42b47eca6 (diff) | |
download | binutils-5d0de5db96bf59bd5411d5029938fc9d673ea9d5.zip binutils-5d0de5db96bf59bd5411d5029938fc9d673ea9d5.tar.gz binutils-5d0de5db96bf59bd5411d5029938fc9d673ea9d5.tar.bz2 |
include, libctf: string lookup and writeout of a parent-shared strtab
The next stage of strtab sharing is actual lookup of strings in such
strtabs, interning of strings in such strtabs and writing out of
such strtabs (but not actually figuring out which strings should
be shared: that's next).
We introduce several new internal ctf_str_* API functions to augment the
existing rather large set: ctf_str_add_copy, which adds a string and always
makes a copy of it (used when deduplicating to stop dedupped strings holding
permanent references on the input dicts), and ctf_str_no_dedup_ref (which
adds a ref to a string while preventing it from ever being deduplicated,
used for header fields like the parent name, which is the same for almost
all child dicts but had still better not be stored in the parent!).
ctf_strraw_explicit, the ultimate underlying "look up a string" function
that backs ctf_strptr et al, gains the ability to automatically find strings
in the parent if the offset is < cth_parent_strlen, and generally make all
offsets parent-relative (so something at offset 1 in the child strlen will
need to be looked up at offset 257 if cth_parent_strlen is 256). This
suffices to paste together the parent and child from the perspective
of lookup.
We do quite a lot of new checks in here, simply because it's called all over
the place and it's preferable to emit a nice error into the ctf_err_warning
stream if things go wrong. Among other things this traps cases where you
accidentally added a string to the parent, throwing off all the offsets.
Completely invalid offsets also now add a message to the err_warning
stream.
Insertion of new atoms (the deduplicated entities underlying strings in a
given dict), already a flag-heavy operation, gains more flags, corresponding
to the new ctf_str_add_copy and ctf_str_no_dedup_ref functions: atom
addition also checks the ctf_max_children set by ctf_import and prevents
addition of new atoms to any dicts with ctf_imported children and an
already-serialized strtab.
strtab writeout gains more checks as well: you can't write out a strtab for
a child dict whose parent hasn't been serialized yet (and thus doesn't have
a serialized strtab itself); you can't write it out if the child already
depended on a shared parent strtab and that strtab has changed length. The
null atom at offset 0 is only written to the parent strtab; and ref updating
changes to look up offsets in the parent's atoms table iff a new
CTF_STR_ATOM_IN_PARENT flag is set on the atom (this will be set by
deduplication to ensure that serializing a dict will update all its refs
properly even though a bunch of them have moved to the parent dict).
None of this actually has any *effect* yet because no string deduplication
is being carried out, and the cth_parent_strlen is still locked at 0.
include/
* ctf-api.h (_CTF_ERRORS) [ECTF_NOTSERIALIZED]: New.
(ECTF_NERR): Updated.
libctf/
* ctf-impl.h (CTF_STR_ATOM_IN_PARENT): New.
(CTF_STR_ATOM_NO_DEDUP): Likewise.
(ctf_str_add_no_dedup_ref): New.
(ctf_str_add_copy): New.
* ctf-string.c (ctf_strraw_explicit): Look in parents if necessary:
use parent-relative offsets.
(ctf_strptr_validate): Avoid duplicating errors.
(ctf_str_create_atoms): Update comment.
(CTF_STR_COPY): New.
(CTF_STR_NO_DEDUP): Likewise.
(ctf_str_add_ref_internal): Use them, setting the corresponding
csa_flags, prohibiting addition to serialized parents, and copying
strings if so requested.
(ctf_str_add): Turn into a wrapper around...
(ctf_str_add_flagged): ... this new function. The offset is now
parent-relative.
(ctf_str_add_ref): Likewise.
(ctf_str_add_movable_ref): Likewise.
(ctf_str_add_copy): New.
(ctf_str_add_no_dedup_ref): New.
(ctf_str_write_strtab): Prohibit writes when the parent has
changed length or is not serialized. Only write the null atom
to parent strtabs. Chase refs to the parent if necessary.
-rw-r--r-- | include/ctf-api.h | 5 | ||||
-rw-r--r-- | libctf/ctf-impl.h | 16 | ||||
-rw-r--r-- | libctf/ctf-string.c | 284 |
3 files changed, 232 insertions, 73 deletions
diff --git a/include/ctf-api.h b/include/ctf-api.h index 29d404c..1032145 100644 --- a/include/ctf-api.h +++ b/include/ctf-api.h @@ -248,7 +248,8 @@ typedef struct ctf_snapshot_id _CTF_ITEM (ECTF_CTFVERS_NO_SERIALIZE, "CTFv1 dicts are too old to serialize.") \ _CTF_ITEM (ECTF_UNSTABLE, "Attempt to write unstable file format version: set I_KNOW_LIBCTF_IS_UNSTABLE in the environment.") \ _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_WRONGPARENT, "Cannot ctf_import: incorrect parent provided.") \ + _CTF_ITEM (ECTF_NOTSERIALIZED, "CTF dict must be serialized first.") #define ECTF_BASE 1000 /* Base value for libctf errnos. */ @@ -261,7 +262,7 @@ _CTF_ERRORS #undef _CTF_FIRST }; -#define ECTF_NERR (ECTF_WRONGPARENT - ECTF_BASE + 1) /* Count of CTF errors. */ +#define ECTF_NERR (ECTF_NOTSERIALIZED - ECTF_BASE + 1) /* Count of CTF errors. */ /* The CTF data model is inferred to be the caller's data model or the data model of the given object, unless ctf_setmodel is explicitly called. */ diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index 9b7d848..5e1c081 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -202,12 +202,21 @@ typedef struct ctf_err_warning string, so that ctf_serialize() can instantiate all the strings using the ctf_str_atoms and then reassociate them with the real string later. + The csa_offset is the offset within *this particular strtab*: no matter + how many strings the parent has, the childrens' csa_offsets are unchanged. + So csa_offset may not be the value actually returned as the offset of this + string. + Strings can be interned into ctf_str_atom without having refs associated with them, for values that are returned to callers, etc. Items are only removed from this table on ctf_close(), but on every ctf_serialize(), all - the csa_refs in all entries are purged. */ + the csa_refs in all entries are purged. Refs may also be removed if they are + migrated from one atoms table to another as a consequence of strtab + deduplication. */ #define CTF_STR_ATOM_FREEABLE 0x1 +#define CTF_STR_ATOM_IN_PARENT 0x2 +#define CTF_STR_ATOM_NO_DEDUP 0x4 typedef struct ctf_str_atom { @@ -228,7 +237,7 @@ typedef struct ctf_str_atom_ref uint32_t *caf_ref; /* A single ref to this string. */ } ctf_str_atom_ref_t; - /* Like a ctf_str_atom_ref_t, but specific to movable refs. */ +/* Like a ctf_str_atom_ref_t, but specific to movable refs. */ typedef struct ctf_str_atom_ref_movable { @@ -750,7 +759,10 @@ extern const char *ctf_strptr_validate (ctf_dict_t *, uint32_t); extern int ctf_str_create_atoms (ctf_dict_t *); extern void ctf_str_free_atoms (ctf_dict_t *); extern uint32_t ctf_str_add (ctf_dict_t *, const char *); +extern uint32_t ctf_str_add_copy (ctf_dict_t *, const char *); extern uint32_t ctf_str_add_ref (ctf_dict_t *, const char *, uint32_t *ref); +extern uint32_t ctf_str_add_no_dedup_ref (ctf_dict_t *, const char *, + uint32_t *ref); extern uint32_t ctf_str_add_movable_ref (ctf_dict_t *, const char *, uint32_t *ref); extern int ctf_str_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest); diff --git a/libctf/ctf-string.c b/libctf/ctf-string.c index 4e1fd62..2b1ce89 100644 --- a/libctf/ctf-string.c +++ b/libctf/ctf-string.c @@ -26,32 +26,74 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str, int flags, uint32_t *ref); /* Convert an encoded CTF string name into a pointer to a C string, possibly - using an explicit internal provisional strtab rather than the fp-based - one. */ + using an explicit internal provisional strtab rather than the fp-based + one. */ const char * ctf_strraw_explicit (ctf_dict_t *fp, uint32_t name, ctf_strs_t *strtab) { - ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)]; + int stid_tab = CTF_NAME_STID (name); + ctf_strs_t *ctsp = &fp->ctf_str[stid_tab]; + + /* For dicts in a parent/child relationship, there are two phases to string + lookup: before writeout, fp->ctf_parent->cts_len is 0, and the parent and + child are uncorrelated and lookups start at offset 0; and after writeout, + the parent's strings are incorporated into the child and further + modification of the parent's strtab (even the addition of new strings) is + prohibited. This prohibition means that ctf_prov_strtab is safe to use: + the "start" of the child strtab will never be observed changing. */ + + if (stid_tab == CTF_STRTAB_0) + { + if (_libctf_unlikely_ (fp->ctf_flags & LCTF_NO_STR)) + { + ctf_set_errno (fp, ECTF_NOPARENT); + ctf_err_warn (fp, 0, 0, _("internal error: attempt to look up strings in child before parent is imported")); + return NULL; + } + + if (fp->ctf_parent + && _libctf_unlikely_ (fp->ctf_header->cth_parent_strlen != 0 + && fp->ctf_header->cth_parent_strlen != + fp->ctf_parent->ctf_str[CTF_STRTAB_0].cts_len)) + { + ctf_set_errno (fp, ECTF_BADNAME); + ctf_err_warn (fp, 0, 0, _("lookup of string in child with wrongly-associated parent: " + "child dict's parent strtab offset: %x; " + "actual parent strtab offset: %zx"), + fp->ctf_header->cth_parent_strlen, + fp->ctf_parent->ctf_str[CTF_STRTAB_0].cts_len); + return NULL; + } + + if (name < fp->ctf_header->cth_parent_strlen) + ctsp = &fp->ctf_parent->ctf_str[CTF_STRTAB_0]; + else + { + name -= fp->ctf_header->cth_parent_strlen; - if ((CTF_NAME_STID (name) == CTF_STRTAB_0) && (strtab != NULL)) - ctsp = strtab; + if (strtab != NULL) + ctsp = strtab; + else + ctsp = &fp->ctf_str[CTF_STRTAB_0]; + } + } - /* If this name is in the external strtab, and there is a synthetic - strtab, use it in preference. (This is used to add the set of strings - -- symbol names, etc -- the linker knows about before the strtab is - written out.) */ + /* If this name is in the external strtab, and there is a synthetic strtab, + use it in preference. (This is used to add the set of strings -- symbol + names, etc -- the linker knows about before the strtab is written out. + The set is added to every dict, so we don't need to scan the parent.) */ - if (CTF_NAME_STID (name) == CTF_STRTAB_1 - && fp->ctf_syn_ext_strtab != NULL) + if (stid_tab == CTF_STRTAB_1 && fp->ctf_syn_ext_strtab != NULL) return ctf_dynhash_lookup (fp->ctf_syn_ext_strtab, (void *) (uintptr_t) name); - /* If the name is in the internal strtab, and the name offset is beyond - the end of the ctsp->cts_len but below the ctf_str_prov_offset, this is - a provisional string added by ctf_str_add*() but not yet built into a - real strtab: get the value out of the ctf_prov_strtab. */ + /* If the name (adjusted to allow for names in the parent) is in the internal + strtab, and the name offset is beyond the end of the ctsp->cts_len but + below the ctf_str_prov_offset, this is a provisional string added by + ctf_str_add*() but not yet built into a real strtab: get the value out of + the ctf_prov_strtab. */ - if (CTF_NAME_STID (name) == CTF_STRTAB_0 + if (stid_tab == CTF_STRTAB_0 && name >= ctsp->cts_len && name < fp->ctf_str_prov_offset) return ctf_dynhash_lookup (fp->ctf_prov_strtab, (void *) (uintptr_t) name); @@ -59,6 +101,9 @@ ctf_strraw_explicit (ctf_dict_t *fp, uint32_t name, ctf_strs_t *strtab) if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len) return (ctsp->cts_strs + CTF_NAME_OFFSET (name)); + ctf_err_warn (fp, 1, 0, _("offset %x: strtab not found or corrupt offset: cts_len is %zx, parent strlen is %u, cts_strs is %p"), + CTF_NAME_OFFSET (name), ctsp->cts_len, fp->ctf_header->cth_parent_strlen, ctsp->cts_strs); + /* String table not loaded or corrupt offset. */ return NULL; } @@ -86,9 +131,14 @@ ctf_strptr (ctf_dict_t *fp, uint32_t name) const char * ctf_strptr_validate (ctf_dict_t *fp, uint32_t name) { - const char *str = ctf_strraw (fp, name); + const char *str; - if (str == NULL) + ctf_set_errno (fp, 0); + str = ctf_strraw (fp, name); + + /* Only report errors if ctf_strraw() didn't already. */ + + if (str == NULL && ctf_errno (fp) == 0) { if (CTF_NAME_STID (name) == CTF_STRTAB_1 && fp->ctf_syn_ext_strtab == NULL @@ -149,7 +199,10 @@ ctf_str_free_atom (void *a) calls to ctf_str_add_external to populate external strtab entries, since these are often not quite the same as what appears in any external strtab, and the external strtab is often huge and best not aggressively - pulled in.) */ + pulled in.) + + Note that the *final strtab* may be entirely empty, if all its strings are + shared with the parent: the atoms table is a superset. */ int ctf_str_create_atoms (ctf_dict_t *fp) { @@ -181,7 +234,9 @@ ctf_str_create_atoms (ctf_dict_t *fp) /* Pull in all the strings in the strtab as new atoms. The provisional strtab must be empty at this point, so there is no need to populate atoms from it as well. Types in this subset are frozen and readonly, - so the refs list and movable refs list need not be populated. */ + so the refs list and movable refs list need not be populated. The + offsets are not parent-relative, so we don't need to have imported any + dicts at this stage, and the parent need not be considered. */ for (i = 0; i < fp->ctf_str[CTF_STRTAB_0].cts_len; i += strlen (&fp->ctf_str[CTF_STRTAB_0].cts_strs[i]) + 1) @@ -230,9 +285,11 @@ ctf_str_free_atoms (ctf_dict_t *fp) } } -#define CTF_STR_ADD_REF 0x1 -#define CTF_STR_PROVISIONAL 0x2 -#define CTF_STR_MOVABLE 0x4 +#define CTF_STR_ADD_REF 0x1 +#define CTF_STR_PROVISIONAL 0x2 +#define CTF_STR_MOVABLE 0x4 +#define CTF_STR_COPY 0x8 +#define CTF_STR_NO_DEDUP 0x10 /* Allocate a ref and bind it into a ref list. */ @@ -301,6 +358,9 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str, if (atom) { + if (flags & CTF_STR_NO_DEDUP) + atom->csa_flags |= CTF_STR_ATOM_NO_DEDUP; + if (!ctf_dynhash_lookup (fp->ctf_prov_strtab, (void *) (uintptr_t) atom->csa_offset)) { @@ -326,17 +386,36 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str, return atom; } - /* New atom. */ + /* New atom. Prohibited if this is a parent dict with children and a + non-empty existing strtab. */ + + if (fp->ctf_str[CTF_STRTAB_0].cts_len != 0 + && fp->ctf_max_children != 0) + { + ctf_set_errno (fp, ECTF_RDONLY); + ctf_err_warn (fp, 0, 0, _("attempt to add strings to a serialized parent dict")); + return NULL; + } if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL) goto oom; memset (atom, 0, sizeof (struct ctf_str_atom)); + if (flags & CTF_STR_NO_DEDUP) + atom->csa_flags |= CTF_STR_ATOM_NO_DEDUP; + + /* Special case: there is always only one "", and it is always in the parent + if there is a parent/child relationship in force (even though it is + explicitly skipped in the deduplicator; see ctf_dedup_strings). */ + if (str[0] == 0) + atom->csa_flags |= CTF_STR_ATOM_IN_PARENT; + /* Don't allocate new strings if this string is within an mmapped - strtab. */ + strtab, unless forced. */ - if ((unsigned char *) str < (unsigned char *) fp->ctf_data_mmapped - || (unsigned char *) str > (unsigned char *) fp->ctf_data_mmapped + fp->ctf_data_mmapped_len) + if (flags & CTF_STR_COPY + || ((unsigned char *) str < (unsigned char *) fp->ctf_data_mmapped + || (unsigned char *) str > (unsigned char *) fp->ctf_data_mmapped + fp->ctf_data_mmapped_len)) { if ((newstr = strdup (str)) == NULL) goto oom; @@ -352,8 +431,10 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str, atom->csa_snapshot_id = fp->ctf_snapshots; - /* New atoms marked provisional go into the provisional strtab, and get a - ref added. */ + /* New atoms marked provisional go into the provisional strtab, and get a ref + added. The offset starts at 1, so may overlap with values in the parent: + offsets are always adjusted by the size of the parent strtab before lookup + to compensate for this. */ if (flags & CTF_STR_PROVISIONAL) { @@ -383,24 +464,50 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str, return NULL; } -/* Add a string to the atoms table, without augmenting the ref list for this - string: return a 'provisional offset' which can be used to return this string - until ctf_str_write_strtab is called, or 0 on failure. (Everywhere the - provisional offset is assigned to should be added as a ref using - ctf_str_add_ref() as well.) */ -uint32_t -ctf_str_add (ctf_dict_t *fp, const char *str) +static uint32_t +ctf_str_add_flagged (ctf_dict_t *fp, const char *str, uint32_t *ref, + int flags) { ctf_str_atom_t *atom; + uint32_t offset; if (!str) str = ""; - atom = ctf_str_add_ref_internal (fp, str, CTF_STR_PROVISIONAL, 0); + atom = ctf_str_add_ref_internal (fp, str, flags, ref); if (!atom) return 0; - return atom->csa_offset; + offset = atom->csa_offset + fp->ctf_header->cth_parent_strlen; + + if (atom->csa_external_offset) + offset = atom->csa_external_offset; + + return offset; +} + + +/* Add a string to the atoms table, without augmenting the ref list for this + string: return a 'provisional offset' which can be used to return this string + until ctf_str_write_strtab is called, or 0 on failure. (Everywhere the + provisional offset is assigned to should be added as a ref using + ctf_str_add_ref() as well.) + + If this atom is already known to have an external offset, the external offset + is simply returned unchanged. */ +uint32_t +ctf_str_add (ctf_dict_t *fp, const char *str) +{ + return ctf_str_add_flagged (fp, str, 0, CTF_STR_PROVISIONAL); +} + +/* Like ctf_str_add, but always take a copy of the string rather than using a + reference into an mmapped region where possible. Useful only when sharing + strings between dicts (which is rare indeed). */ +uint32_t +ctf_str_add_copy (ctf_dict_t *fp, const char *str) +{ + return ctf_str_add_flagged (fp, str, 0, CTF_STR_PROVISIONAL | CTF_STR_COPY); } /* Like ctf_str_add(), but additionally augment the atom's refs list with the @@ -409,35 +516,26 @@ ctf_str_add (ctf_dict_t *fp, const char *str) uint32_t ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref) { - ctf_str_atom_t *atom; - - if (!str) - str = ""; - - atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF - | CTF_STR_PROVISIONAL, ref); - if (!atom) - return 0; + return ctf_str_add_flagged (fp, str, ref, + CTF_STR_ADD_REF | CTF_STR_PROVISIONAL); +} - return atom->csa_offset; +/* Like ctf_str_add_ref(), but prevent this string from being deduplicated. */ +uint32_t +ctf_str_add_no_dedup_ref (ctf_dict_t *fp, const char *str, uint32_t *ref) +{ + return ctf_str_add_flagged (fp, str, ref, + CTF_STR_ADD_REF | CTF_STR_PROVISIONAL + | CTF_STR_NO_DEDUP); } /* Like ctf_str_add_ref(), but note that the ref may be moved later on. */ uint32_t ctf_str_add_movable_ref (ctf_dict_t *fp, const char *str, uint32_t *ref) { - ctf_str_atom_t *atom; - - if (!str) - str = ""; - - atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF - | CTF_STR_PROVISIONAL - | CTF_STR_MOVABLE, ref); - if (!atom) - return 0; - - return atom->csa_offset; + return ctf_str_add_flagged (fp, str, ref, + CTF_STR_ADD_REF | CTF_STR_PROVISIONAL + | CTF_STR_MOVABLE); } /* Add an external strtab reference at OFFSET. Returns zero if the addition @@ -658,19 +756,46 @@ ctf_str_write_strtab (ctf_dict_t *fp) int new_strtab = 0; int any_external = 0; + /* Writing a full v4 shared-with-parent child strtab is possible only if the + parent has already been written out. */ + + if (fp->ctf_parent && fp->ctf_header->cth_parent_strlen != 0) + { + if (ctf_dynhash_elements (fp->ctf_parent->ctf_prov_strtab) != 0) + { + ctf_set_errno (fp, ECTF_NOTSERIALIZED); + ctf_err_warn (fp, 0, 0, _("attempt to write strtab with unserialized parent")); + return NULL; + } + + /* Writing such a child strtab is possible only if the parent strtab has not + changed length. */ + + if (fp->ctf_header->cth_parent_strlen != fp->ctf_parent->ctf_str[CTF_STRTAB_0].cts_len) + { + ctf_set_errno (fp, ECTF_WRONGPARENT); + ctf_err_warn (fp, 0, 0, _("cannot serialize child strtab: " + "parent strtab has changed length from %x to %zx\n"), + fp->ctf_header->cth_parent_strlen, + fp->ctf_parent->ctf_str[CTF_STRTAB_0].cts_len); + return NULL; + } + } + strtab = calloc (1, sizeof (ctf_strs_writable_t)); if (!strtab) return NULL; - /* The strtab contains the existing string table at its start: figure out - how many new strings we need to add. We only need to add new strings - that have no external offset, that have refs, and that are found in the - provisional strtab. If the existing strtab is empty we also need to - add the null string at its start. */ + /* The strtab contains the existing string table at its start: figure out how + many new strings we need to add. We only need to add new strings that have + no external offset, that have refs, and that are found in the provisional + strtab. If the existing strtab is empty and has no parent strings, we also + need to add the null string at its start. (Dicts promoted from CTFv3 and + below always have no parent strings in this sense.) */ strtab->cts_len = fp->ctf_str[CTF_STRTAB_0].cts_len; - if (strtab->cts_len == 0) + if (strtab->cts_len == 0 && fp->ctf_header->cth_parent_strlen == 0) { new_strtab = 1; strtab->cts_len++; /* For the \0. */ @@ -766,8 +891,11 @@ ctf_str_write_strtab (ctf_dict_t *fp) free (sorttab); sorttab = NULL; - /* Update all refs, then purge them as no longer necessary: also update - the strtab appropriately. */ + /* Update all refs (incorporating any parent strtab offset adjustment), then + purge them as no longer necessary: also update the strtab appropriately. + Some atoms (with refs updated after the parent was serialized) may be in + the parent: use the parent's csa_offset instead -- but not its ref list, + which will already have been updated and emptied. */ while ((err = ctf_dynhash_next (fp->ctf_str_atoms, &it, NULL, &v)) == 0) { @@ -784,7 +912,25 @@ ctf_str_write_strtab (ctf_dict_t *fp) offset = atom->csa_external_offset; } else - offset = atom->csa_offset; + { + if (atom->csa_flags & CTF_STR_ATOM_IN_PARENT + && fp->ctf_parent) + { + ctf_str_atom_t *parent_atom; + + parent_atom = ctf_dynhash_lookup (fp->ctf_parent->ctf_str_atoms, + atom->csa_str); + if (parent_atom) + offset = parent_atom->csa_offset; + else + offset = atom->csa_offset + fp->ctf_header->cth_parent_strlen; + + atom->csa_flags &= ~CTF_STR_ATOM_IN_PARENT; + } + else + offset = atom->csa_offset + fp->ctf_header->cth_parent_strlen; + } + ctf_str_update_refs (atom, offset); } if (err != ECTF_NEXT_END) |