/* CTF string table management.
- Copyright (C) 2019-2020 Free Software Foundation, Inc.
+ Copyright (C) 2019-2021 Free Software Foundation, Inc.
This file is part of libctf.
#include <ctf-impl.h>
#include <string.h>
+#include <assert.h>
/* Convert an encoded CTF string name into a pointer to a C string, using an
explicit internal strtab rather than the fp-based one. */
const char *
-ctf_strraw_explicit (ctf_file_t *fp, uint32_t name, ctf_strs_t *strtab)
+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)];
/* Convert an encoded CTF string name into a pointer to a C string by looking
up the appropriate string table buffer and then adding the offset. */
const char *
-ctf_strraw (ctf_file_t *fp, uint32_t name)
+ctf_strraw (ctf_dict_t *fp, uint32_t name)
{
return ctf_strraw_explicit (fp, name, NULL);
}
/* Return a guaranteed-non-NULL pointer to the string with the given CTF
name. */
const char *
-ctf_strptr (ctf_file_t *fp, uint32_t name)
+ctf_strptr (ctf_dict_t *fp, uint32_t name)
{
const char *s = ctf_strraw (fp, name);
return (s != NULL ? s : "(?)");
/* Create the atoms table. There is always at least one atom in it, the null
string. */
int
-ctf_str_create_atoms (ctf_file_t *fp)
+ctf_str_create_atoms (ctf_dict_t *fp)
{
fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
free, ctf_str_free_atom);
- if (fp->ctf_str_atoms == NULL)
+ if (!fp->ctf_str_atoms)
return -ENOMEM;
if (!fp->ctf_prov_strtab)
if (!fp->ctf_prov_strtab)
goto oom_prov_strtab;
+ if (!fp->ctf_str_pending_ref)
+ fp->ctf_str_pending_ref = ctf_dynset_create (htab_hash_pointer,
+ htab_eq_pointer,
+ NULL);
+ if (!fp->ctf_str_pending_ref)
+ goto oom_str_pending_ref;
+
errno = 0;
ctf_str_add (fp, "");
if (errno == ENOMEM)
oom_str_add:
ctf_dynhash_destroy (fp->ctf_prov_strtab);
fp->ctf_prov_strtab = NULL;
+ oom_str_pending_ref:
+ ctf_dynset_destroy (fp->ctf_str_pending_ref);
+ fp->ctf_str_pending_ref = NULL;
oom_prov_strtab:
ctf_dynhash_destroy (fp->ctf_str_atoms);
fp->ctf_str_atoms = NULL;
/* Destroy the atoms table. */
void
-ctf_str_free_atoms (ctf_file_t *fp)
+ctf_str_free_atoms (ctf_dict_t *fp)
{
ctf_dynhash_destroy (fp->ctf_prov_strtab);
ctf_dynhash_destroy (fp->ctf_str_atoms);
+ ctf_dynset_destroy (fp->ctf_str_pending_ref);
}
+#define CTF_STR_ADD_REF 0x1
+#define CTF_STR_MAKE_PROVISIONAL 0x2
+#define CTF_STR_PENDING_REF 0x4
+
/* Add a string to the atoms table, copying the passed-in string. Return the
atom added. Return NULL only when out of memory (and do not touch the
passed-in string in that case). Possibly augment the ref list with the
passed-in ref. Possibly add a provisional entry for this string to the
provisional strtab. */
static ctf_str_atom_t *
-ctf_str_add_ref_internal (ctf_file_t *fp, const char *str,
- int add_ref, int make_provisional, uint32_t *ref)
+ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
+ int flags, uint32_t *ref)
{
char *newstr = NULL;
ctf_str_atom_t *atom = NULL;
atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
- if (add_ref)
+ if (flags & CTF_STR_ADD_REF)
{
if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL)
return NULL;
if (atom)
{
- if (add_ref)
+ if (flags & CTF_STR_ADD_REF)
{
+ ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
ctf_list_append (&atom->csa_refs, aref);
fp->ctf_str_num_refs++;
}
atom->csa_str = newstr;
atom->csa_snapshot_id = fp->ctf_snapshots;
- if (make_provisional)
+ if (flags & CTF_STR_MAKE_PROVISIONAL)
{
atom->csa_offset = fp->ctf_str_prov_offset;
fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1;
}
- if (add_ref)
+ if (flags & CTF_STR_PENDING_REF)
{
+ if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) ref) < 0)
+ goto oom;
+ }
+ else if (flags & CTF_STR_ADD_REF)
+ {
+ ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
ctf_list_append (&atom->csa_refs, aref);
fp->ctf_str_num_refs++;
}
free (atom);
free (aref);
free (newstr);
+ ctf_set_errno (fp, ENOMEM);
return NULL;
}
provisional offset is assigned to should be added as a ref using
ctf_str_add_ref() as well.) */
uint32_t
-ctf_str_add (ctf_file_t *fp, const char *str)
+ctf_str_add (ctf_dict_t *fp, const char *str)
{
ctf_str_atom_t *atom;
+
if (!str)
- return 0;
+ str = "";
- atom = ctf_str_add_ref_internal (fp, str, FALSE, TRUE, 0);
+ atom = ctf_str_add_ref_internal (fp, str, CTF_STR_MAKE_PROVISIONAL, 0);
if (!atom)
return 0;
passed-in ref, whether or not the string is already present. There is no
attempt to deduplicate the refs list (but duplicates are harmless). */
uint32_t
-ctf_str_add_ref (ctf_file_t *fp, const char *str, uint32_t *ref)
+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_MAKE_PROVISIONAL, ref);
+ if (!atom)
return 0;
- atom = ctf_str_add_ref_internal (fp, str, TRUE, TRUE, ref);
+ return atom->csa_offset;
+}
+
+/* Like ctf_str_add_ref(), but notes that this memory location must be added as
+ a ref by a later serialization phase, rather than adding it itself. */
+uint32_t
+ctf_str_add_pending (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_PENDING_REF
+ | CTF_STR_MAKE_PROVISIONAL, ref);
if (!atom)
return 0;
return atom->csa_offset;
}
+/* Note that a pending ref now located at NEW_REF has moved by BYTES bytes. */
+int
+ctf_str_move_pending (ctf_dict_t *fp, uint32_t *new_ref, ptrdiff_t bytes)
+{
+ if (bytes == 0)
+ return 0;
+
+ if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) new_ref) < 0)
+ return (ctf_set_errno (fp, ENOMEM));
+
+ ctf_dynset_remove (fp->ctf_str_pending_ref,
+ (void *) ((signed char *) new_ref - bytes));
+ return 0;
+}
+
/* Add an external strtab reference at OFFSET. Returns zero if the addition
failed, nonzero otherwise. */
int
-ctf_str_add_external (ctf_file_t *fp, const char *str, uint32_t offset)
+ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
{
ctf_str_atom_t *atom;
+
if (!str)
- return 0;
+ str = "";
- atom = ctf_str_add_ref_internal (fp, str, FALSE, FALSE, 0);
+ atom = ctf_str_add_ref_internal (fp, str, 0, 0);
if (!atom)
return 0;
atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1);
+
+ if (!fp->ctf_syn_ext_strtab)
+ fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
+ ctf_hash_eq_integer,
+ NULL, NULL);
+ if (!fp->ctf_syn_ext_strtab)
+ {
+ ctf_set_errno (fp, ENOMEM);
+ return 0;
+ }
+
+ if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
+ (void *) (uintptr_t)
+ atom->csa_external_offset,
+ (void *) atom->csa_str) < 0)
+ {
+ /* No need to bother freeing the syn_ext_strtab: it will get freed at
+ ctf_str_write_strtab time if unreferenced. */
+ ctf_set_errno (fp, ENOMEM);
+ return 0;
+ }
+
return 1;
}
/* Remove a single ref. */
void
-ctf_str_remove_ref (ctf_file_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;
free (aref);
}
}
+
+ ctf_dynset_remove (fp->ctf_str_pending_ref, (void *) ref);
}
/* A ctf_dynhash_iter_remove() callback that removes atoms later than a given
- snapshot ID. */
+ snapshot ID. External atoms are never removed, because they came from the
+ linker string table and are still present even if you roll back type
+ additions. */
static int
ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg)
{
ctf_str_atom_t *atom = (ctf_str_atom_t *) value;
ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg;
- return (atom->csa_snapshot_id > id->snapshot_id);
+ return (atom->csa_snapshot_id > id->snapshot_id)
+ && (atom->csa_external_offset == 0);
}
-/* Roll back, deleting all atoms created after a particular ID. */
+/* Roll back, deleting all (internal) atoms created after a particular ID. */
void
-ctf_str_rollback (ctf_file_t *fp, ctf_snapshot_id_t id)
+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);
}
/* Remove all the recorded refs from the atoms table. */
void
-ctf_str_purge_refs (ctf_file_t *fp)
+ctf_str_purge_refs (ctf_dict_t *fp)
{
if (fp->ctf_str_num_refs > 0)
ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
external strtab offsets to names, so we can look them up with ctf_strptr().
Only external strtab offsets with references are added. */
ctf_strs_writable_t
-ctf_str_write_strtab (ctf_file_t *fp)
+ctf_str_write_strtab (ctf_dict_t *fp)
{
ctf_strs_writable_t strtab;
ctf_str_atom_t *nullstr;
nullstr = ctf_dynhash_lookup (fp->ctf_str_atoms, "");
if (!nullstr)
{
- ctf_dprintf ("Internal error: null string not found in strtab.\n");
+ ctf_err_warn (fp, 0, ECTF_INTERNAL, _("null string not found in strtab"));
strtab.cts_strs = NULL;
return strtab;
}
if ((strtab.cts_strs = malloc (strtab.cts_len)) == NULL)
goto oom_sorttab;
- if (!fp->ctf_syn_ext_strtab)
- fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer,
- ctf_hash_eq_integer,
- NULL, NULL);
- if (!fp->ctf_syn_ext_strtab)
- goto oom_strtab;
-
/* Update all refs: also update the strtab appropriately. */
for (i = 0; i < s.strtab_count; i++)
{
if (sorttab[i]->csa_external_offset)
{
- /* External strtab entry: populate the synthetic external strtab.
-
- This is safe because you cannot ctf_rollback to before the point
- when a ctf_update is done, and the strtab is written at ctf_update
- time. So any atoms we reference here are sure to stick around
- until ctf_file_close. */
+ /* External strtab entry. */
any_external = 1;
ctf_str_update_refs (sorttab[i], sorttab[i]->csa_external_offset);
- if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab,
- (void *) (uintptr_t)
- sorttab[i]->csa_external_offset,
- (void *) sorttab[i]->csa_str) < 0)
- goto oom_strtab;
sorttab[i]->csa_offset = sorttab[i]->csa_external_offset;
}
else
fp->ctf_str_prov_offset = strtab.cts_len + 1;
return strtab;
- oom_strtab:
- free (strtab.cts_strs);
- strtab.cts_strs = NULL;
oom_sorttab:
free (sorttab);
oom: