libctf: generalize the ref system

Despite the removal of the separate movable ref list, the ref system as
a whole is more than complex enough to be worth generalizing now that
we are adding different kinds of ref.

Refs now are lists of uint32_t * which can be updated through the
pointer for all entries in the list and moved to new sites for all
pointers in a given range: they are no longer references to string
offsets in particular and can be references to other uint32_t-sized
things instead (note that ctf_id_t is a typedef to a uint32_t).

ctf-string.c has been adjusted accordingly (the adjustments are tiny,
more or less just turning a bunch of references to atom into
&atom->csa_refs).
This commit is contained in:
Nick Alcock
2025-01-13 15:04:17 +00:00
parent 69d4f6d74c
commit 9835747b21
5 changed files with 158 additions and 130 deletions

View File

@@ -1118,7 +1118,7 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
/* Remove refs in the old vlen region and reapply them. */ /* Remove refs in the old vlen region and reapply them. */
ctf_str_move_refs (fp, old_vlen, sizeof (ctf_enum_t) * vlen, dtd->dtd_vlen); ctf_move_refs (fp, old_vlen, sizeof (ctf_enum_t) * vlen, dtd->dtd_vlen);
/* Check for constant duplication within any given enum: only needed for /* Check for constant duplication within any given enum: only needed for
non-root-visible types, since the duplicate detection above does the job non-root-visible types, since the duplicate detection above does the job
@@ -1209,7 +1209,7 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
/* Remove refs in the old vlen region and reapply them. */ /* Remove refs in the old vlen region and reapply them. */
ctf_str_move_refs (fp, old_vlen, sizeof (ctf_lmember_t) * vlen, dtd->dtd_vlen); ctf_move_refs (fp, old_vlen, sizeof (ctf_lmember_t) * vlen, dtd->dtd_vlen);
if (name != NULL) if (name != NULL)
{ {

View File

@@ -228,13 +228,13 @@ typedef struct ctf_str_atom
int csa_flags; /* CTF_STR_ATOM_* flags. */ int csa_flags; /* CTF_STR_ATOM_* flags. */
} ctf_str_atom_t; } ctf_str_atom_t;
/* The refs of a single string in the atoms table. */ /* A single ref. */
typedef struct ctf_str_atom_ref typedef struct ctf_ref
{ {
ctf_list_t caf_list; /* List forward/back pointers. */ ctf_list_t cre_list; /* List forward/back pointers. */
uint32_t *caf_ref; /* A single ref to this string. */ uint32_t *cre_ref; /* A single ref to this string. */
} ctf_str_atom_ref_t; } ctf_ref_t;
/* A single linker-provided symbol, during symbol addition, possibly before we /* A single linker-provided symbol, during symbol addition, possibly before we
have been given external strtab refs. */ have been given external strtab refs. */
@@ -392,7 +392,7 @@ struct ctf_dict
ctf_strs_t ctf_str[2]; /* Array of string table base and bounds. */ ctf_strs_t ctf_str[2]; /* Array of string table base and bounds. */
ctf_strs_writable_t *ctf_dynstrtab; /* Dynamically allocated string table, if any. */ ctf_strs_writable_t *ctf_dynstrtab; /* Dynamically allocated string table, if any. */
ctf_dynhash_t *ctf_str_atoms; /* Hash table of ctf_str_atoms_t. */ ctf_dynhash_t *ctf_str_atoms; /* Hash table of ctf_str_atoms_t. */
ctf_dynhash_t *ctf_str_movable_refs; /* Hash table of void * -> ctf_str_atom_ref_movable_t. */ ctf_dynhash_t *ctf_movable_refs; /* Hash table of void * -> ctf_ref_t. */
uint32_t ctf_str_prov_offset; /* Latest provisional offset assigned so far. */ uint32_t ctf_str_prov_offset; /* Latest provisional offset assigned so far. */
unsigned char *ctf_base; /* CTF file pointer. */ unsigned char *ctf_base; /* CTF file pointer. */
unsigned char *ctf_dynbase; /* Freeable CTF file pointer. */ unsigned char *ctf_dynbase; /* Freeable CTF file pointer. */
@@ -765,13 +765,21 @@ extern uint32_t ctf_str_add_no_dedup_ref (ctf_dict_t *, const char *,
uint32_t *ref); uint32_t *ref);
extern uint32_t ctf_str_add_movable_ref (ctf_dict_t *, const char *, extern uint32_t ctf_str_add_movable_ref (ctf_dict_t *, const char *,
uint32_t *ref); uint32_t *ref);
extern int ctf_str_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest);
extern int ctf_str_add_external (ctf_dict_t *, const char *, uint32_t offset); extern int ctf_str_add_external (ctf_dict_t *, const char *, uint32_t offset);
extern void ctf_str_remove_ref (ctf_dict_t *, const char *, uint32_t *ref); extern void ctf_str_remove_ref (ctf_dict_t *, const char *, uint32_t *ref);
extern void ctf_str_purge_refs (ctf_dict_t *fp); extern void ctf_str_purge_refs (ctf_dict_t *fp);
extern void ctf_str_rollback (ctf_dict_t *, ctf_snapshot_id_t); extern void ctf_str_rollback (ctf_dict_t *, ctf_snapshot_id_t);
extern const ctf_strs_writable_t *ctf_str_write_strtab (ctf_dict_t *); extern const ctf_strs_writable_t *ctf_str_write_strtab (ctf_dict_t *);
extern int ctf_init_refs (ctf_dict_t *);
extern void ctf_free_refs (ctf_dict_t *);
extern ctf_ref_t *ctf_create_ref (ctf_dict_t *, ctf_list_t *, uint32_t *ref,
int movable);
extern void ctf_remove_ref (ctf_dict_t *fp, ctf_list_t *, uint32_t *ref);
extern int ctf_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest);
extern void ctf_purge_ref_list (ctf_dict_t *, ctf_list_t *);
extern void ctf_update_refs (ctf_list_t *, uint32_t value);
extern int ctf_preserialize (ctf_dict_t *fp); extern int ctf_preserialize (ctf_dict_t *fp);
extern void ctf_depreserialize (ctf_dict_t *fp); extern void ctf_depreserialize (ctf_dict_t *fp);

View File

@@ -1798,7 +1798,7 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *) fp->ctf_buf fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *) fp->ctf_buf
+ hp->cth_stroff; + hp->cth_stroff;
fp->ctf_str[CTF_STRTAB_0].cts_len = hp->cth_strlen; fp->ctf_str[CTF_STRTAB_0].cts_len = hp->cth_strlen;
if (ctf_str_create_atoms (fp) < 0) if (ctf_init_refs (fp) < 0 || ctf_str_create_atoms (fp) < 0)
{ {
err = ENOMEM; err = ENOMEM;
goto bad; goto bad;
@@ -2032,6 +2032,7 @@ ctf_dict_close (ctf_dict_t *fp)
ctf_dynhash_destroy (fp->ctf_link_out_cu_mapping); ctf_dynhash_destroy (fp->ctf_link_out_cu_mapping);
ctf_str_free_atoms (fp); ctf_str_free_atoms (fp);
ctf_free_refs (fp);
free (fp->ctf_tmp_typeslice); free (fp->ctf_tmp_typeslice);
if (fp->ctf_data.cts_name != _CTF_NULLSTR) if (fp->ctf_data.cts_name != _CTF_NULLSTR)

View File

@@ -154,21 +154,6 @@ ctf_strptr_validate (ctf_dict_t *fp, uint32_t name)
return str; return str;
} }
/* Remove all refs to a given atom. */
static void
ctf_str_purge_atom_refs (ctf_dict_t *fp, ctf_str_atom_t *atom)
{
ctf_str_atom_ref_t *ref, *next;
for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next)
{
next = ctf_list_next (ref);
ctf_list_delete (&atom->csa_refs, ref);
ctf_dynhash_remove (fp->ctf_str_movable_refs, ref);
free (ref);
}
}
/* Free an atom. */ /* Free an atom. */
static void static void
ctf_str_free_atom (void *a, void *fp_) ctf_str_free_atom (void *a, void *fp_)
@@ -176,7 +161,7 @@ ctf_str_free_atom (void *a, void *fp_)
ctf_str_atom_t *atom = a; ctf_str_atom_t *atom = a;
ctf_dict_t *fp = fp_; ctf_dict_t *fp = fp_;
ctf_str_purge_atom_refs (fp, atom); ctf_purge_ref_list (fp, &atom->csa_refs);
if (atom->csa_flags & CTF_STR_ATOM_FREEABLE) if (atom->csa_flags & CTF_STR_ATOM_FREEABLE)
free (atom->csa_str); free (atom->csa_str);
@@ -212,12 +197,6 @@ ctf_str_create_atoms (ctf_dict_t *fp)
if (!fp->ctf_prov_strtab) if (!fp->ctf_prov_strtab)
goto oom_prov_strtab; goto oom_prov_strtab;
fp->ctf_str_movable_refs = ctf_dynhash_create (ctf_hash_integer,
ctf_hash_eq_integer,
NULL, NULL);
if (!fp->ctf_str_movable_refs)
goto oom_movable_refs;
errno = 0; errno = 0;
ctf_str_add (fp, ""); ctf_str_add (fp, "");
if (errno == ENOMEM) if (errno == ENOMEM)
@@ -252,9 +231,6 @@ ctf_str_create_atoms (ctf_dict_t *fp)
return 0; return 0;
oom_str_add: oom_str_add:
ctf_dynhash_destroy (fp->ctf_str_movable_refs);
fp->ctf_str_movable_refs = NULL;
oom_movable_refs:
ctf_dynhash_destroy (fp->ctf_prov_strtab); ctf_dynhash_destroy (fp->ctf_prov_strtab);
fp->ctf_prov_strtab = NULL; fp->ctf_prov_strtab = NULL;
oom_prov_strtab: oom_prov_strtab:
@@ -269,7 +245,6 @@ ctf_str_free_atoms (ctf_dict_t *fp)
{ {
ctf_dynhash_destroy (fp->ctf_prov_strtab); ctf_dynhash_destroy (fp->ctf_prov_strtab);
ctf_dynhash_destroy (fp->ctf_str_atoms); ctf_dynhash_destroy (fp->ctf_str_atoms);
ctf_dynhash_destroy (fp->ctf_str_movable_refs);
if (fp->ctf_dynstrtab) if (fp->ctf_dynstrtab)
{ {
free (fp->ctf_dynstrtab->cts_strs); free (fp->ctf_dynstrtab->cts_strs);
@@ -283,37 +258,6 @@ ctf_str_free_atoms (ctf_dict_t *fp)
#define CTF_STR_COPY 0x8 #define CTF_STR_COPY 0x8
#define CTF_STR_NO_DEDUP 0x10 #define CTF_STR_NO_DEDUP 0x10
/* Allocate a ref and bind it into a ref list. */
static ctf_str_atom_ref_t *
aref_create (ctf_dict_t *fp, ctf_str_atom_t *atom, uint32_t *ref, int flags)
{
ctf_str_atom_ref_t *aref;
aref = malloc (sizeof (struct ctf_str_atom_ref));
if (!aref)
return NULL;
aref->caf_ref = ref;
/* Movable refs get a backpointer to them in ctf_str_movable_refs: they can be
moved later in batches via a call to ctf_str_move_refs. */
if (flags & CTF_STR_MOVABLE)
{
if (ctf_dynhash_insert (fp->ctf_str_movable_refs, ref, aref) < 0)
{
free (aref);
return NULL;
}
}
ctf_list_append (&atom->csa_refs, aref);
return aref;
}
/* Add a string to the atoms table, copying the passed-in string if /* Add a string to the atoms table, copying the passed-in string if
necessary. Return the atom added. Return NULL only when out of memory necessary. Return the atom added. Return NULL only when out of memory
(and do not touch the passed-in string in that case). (and do not touch the passed-in string in that case).
@@ -357,7 +301,8 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
if (flags & CTF_STR_ADD_REF) if (flags & CTF_STR_ADD_REF)
{ {
if (!aref_create (fp, atom, ref, flags)) if (!ctf_create_ref (fp, &atom->csa_refs, ref,
flags & CTF_STR_MOVABLE))
{ {
ctf_set_errno (fp, ENOMEM); ctf_set_errno (fp, ENOMEM);
return NULL; return NULL;
@@ -429,7 +374,7 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
if (flags & CTF_STR_ADD_REF) if (flags & CTF_STR_ADD_REF)
{ {
if (!aref_create (fp, atom, ref, flags)) if (!ctf_create_ref (fp, &atom->csa_refs, ref, flags & CTF_STR_MOVABLE))
goto oom; goto oom;
} }
} }
@@ -559,62 +504,17 @@ ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
return 1; return 1;
} }
/* Note that refs have moved from (SRC, LEN) to DEST. We use the movable /* Remove a single ref to a string. */
refs backpointer for this, because it is done an amortized-constant
number of times during structure member and enumerand addition, and if we
did a linear search this would turn such addition into an O(n^2)
operation. */
int
ctf_str_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest)
{
uintptr_t p;
if (src == dest)
return 0;
for (p = (uintptr_t) src; p - (uintptr_t) src < len; p++)
{
ctf_str_atom_ref_t *ref;
if ((ref = ctf_dynhash_lookup (fp->ctf_str_movable_refs,
(ctf_str_atom_ref_t *) p)) != NULL)
{
int out_of_memory;
ref->caf_ref = (uint32_t *) (((uintptr_t) ref->caf_ref +
(uintptr_t) dest - (uintptr_t) src));
ctf_dynhash_remove (fp->ctf_str_movable_refs,
(ctf_str_atom_ref_t *) p);
out_of_memory = ctf_dynhash_insert (fp->ctf_str_movable_refs,
ref->caf_ref, ref);
assert (out_of_memory == 0);
}
}
return 0;
}
/* Remove a single ref. */
void void
ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref) ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
{ {
ctf_str_atom_ref_t *aref, *anext;
ctf_str_atom_t *atom = NULL; ctf_str_atom_t *atom = NULL;
atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str); atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
if (!atom) if (!atom)
return; return;
for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext) ctf_remove_ref (fp, &atom->csa_refs, ref);
{
anext = ctf_list_next (aref);
if (aref->caf_ref == ref)
{
ctf_list_delete (&atom->csa_refs, aref);
ctf_dynhash_remove (fp->ctf_str_movable_refs, ref);
free (aref);
}
}
} }
/* A ctf_dynhash_iter_remove() callback that removes atoms later than a given /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
@@ -638,7 +538,7 @@ ctf_str_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id); ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id);
} }
/* An adaptor around ctf_purge_atom_refs. */ /* An adaptor around ctf_purge_ref_list. */
static void static void
ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value, ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
void *arg) void *arg)
@@ -646,7 +546,7 @@ ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
ctf_str_atom_t *atom = (ctf_str_atom_t *) value; ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
ctf_dict_t *fp = (ctf_dict_t *) arg; ctf_dict_t *fp = (ctf_dict_t *) arg;
ctf_str_purge_atom_refs (fp, atom); ctf_purge_ref_list (fp, &atom->csa_refs);
} }
/* Remove all the recorded refs from the atoms table. */ /* Remove all the recorded refs from the atoms table. */
@@ -656,17 +556,6 @@ ctf_str_purge_refs (ctf_dict_t *fp)
ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, fp); ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, fp);
} }
/* Update a list of refs to the specified value. */
static void
ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value)
{
ctf_str_atom_ref_t *ref;
for (ref = ctf_list_next (&refs->csa_refs); ref != NULL;
ref = ctf_list_next (ref))
*(ref->caf_ref) = value;
}
/* Sort the strtab. */ /* Sort the strtab. */
static int static int
ctf_str_sort_strtab (const void *a, const void *b) ctf_str_sort_strtab (const void *a, const void *b)
@@ -893,7 +782,7 @@ ctf_str_write_strtab (ctf_dict_t *fp)
offset = atom->csa_offset + fp->ctf_header->cth_parent_strlen; offset = atom->csa_offset + fp->ctf_header->cth_parent_strlen;
} }
ctf_str_update_refs (atom, offset); ctf_update_refs (&atom->csa_refs, offset);
} }
if (err != ECTF_NEXT_END) if (err != ECTF_NEXT_END)
{ {

View File

@@ -146,6 +146,136 @@ ctf_str_append_noerr (char *s, const char *append)
return new_s; return new_s;
} }
/* Initialize the ref system. */
int
ctf_init_refs (ctf_dict_t *fp)
{
fp->ctf_movable_refs = ctf_dynhash_create (ctf_hash_integer,
ctf_hash_eq_integer,
NULL, NULL);
if (!fp->ctf_movable_refs)
return -ENOMEM;
return 0;
}
/* Destroy the ref system. */
void
ctf_free_refs (ctf_dict_t *fp)
{
ctf_dynhash_destroy (fp->ctf_movable_refs);
}
/* Allocate a ref and bind it into a ref list. Does not actually
initialize anything through the ref: the caller must do that. */
ctf_ref_t *
ctf_create_ref (ctf_dict_t *fp, ctf_list_t *reflist, uint32_t *ref, int movable)
{
ctf_ref_t *aref;
aref = malloc (sizeof (struct ctf_ref));
if (!aref)
return NULL;
aref->cre_ref = ref;
/* Movable refs get a backpointer to them in ctf_movable_refs: they can be
moved later in batches via a call to ctf_move_refs. */
if (movable)
{
if (ctf_dynhash_insert (fp->ctf_movable_refs, ref, aref) < 0)
{
free (aref);
return NULL;
}
}
ctf_list_append (reflist, aref);
return aref;
}
/* Note that refs have moved from (SRC, LEN) to DEST. We use the movable
refs backpointer for this, because it is done an amortized-constant
number of times during structure member and enumerand addition, and if we
did a linear search this would turn such addition into an O(n^2)
operation. */
int
ctf_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest)
{
uintptr_t p;
if (src == dest)
return 0;
for (p = (uintptr_t) src; p - (uintptr_t) src < len; p++)
{
ctf_ref_t *ref;
if ((ref = ctf_dynhash_lookup (fp->ctf_movable_refs,
(ctf_ref_t *) p)) != NULL)
{
int out_of_memory;
ref->cre_ref = (uint32_t *) (((uintptr_t) ref->cre_ref +
(uintptr_t) dest - (uintptr_t) src));
ctf_dynhash_remove (fp->ctf_movable_refs, (ctf_ref_t *) p);
out_of_memory = ctf_dynhash_insert (fp->ctf_movable_refs,
ref->cre_ref, ref);
assert (out_of_memory == 0);
}
}
return 0;
}
/* Remove a single ref. */
void
ctf_remove_ref (ctf_dict_t *fp, ctf_list_t *reflist, uint32_t *ref)
{
ctf_ref_t *aref, *anext;
for (aref = ctf_list_next (reflist); aref != NULL; aref = anext)
{
anext = ctf_list_next (aref);
if (aref->cre_ref == ref)
{
ctf_list_delete (reflist, aref);
ctf_dynhash_remove (fp->ctf_movable_refs, ref);
free (aref);
}
}
}
/* Remove all refs to a given entity. */
void
ctf_purge_ref_list (ctf_dict_t *fp, ctf_list_t *reflist)
{
ctf_ref_t *ref, *next;
for (ref = ctf_list_next (reflist); ref != NULL; ref = next)
{
next = ctf_list_next (ref);
ctf_list_delete (reflist, ref);
ctf_dynhash_remove (fp->ctf_movable_refs, ref);
free (ref);
}
}
/* Update a list of refs to the specified value. */
void
ctf_update_refs (ctf_list_t *reflist, uint32_t value)
{
ctf_ref_t *ref;
for (ref = ctf_list_next (reflist); ref != NULL;
ref = ctf_list_next (ref))
*(ref->cre_ref) = value;
}
/* Create a ctf_next_t. */ /* Create a ctf_next_t. */
ctf_next_t * ctf_next_t *