mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-09 04:21:49 +08:00
149ce5c263
A few years ago we introduced a 'pending refs' abstraction to fix one problem: serializing a dict, then changing it would tend to corrupt the dict because the strtab sort we do on strtab writeout (to improve compression efficiency) would modify the offset of any strings that sorted lexicographically earlier in the strtab: so we added a new restriction that all strings are added only at serialization time, and maintained a set of 'pending' refs that were added earlier, whose offsets we could update (like other refs) at writeout time. This was in hindsight seriously problematic for maintenance (because serialization has to traverse all strings in all datatypes in the entire dict), and has become impossible to sustain now that we can read in existing dicts, modify them, and reserialize them again. We really don't want to have to dig through the entire dict we jut read in just in order to dig out all its strtab offsets, then *change* it, just for the sake of a sort that adds a frankly trivial amount of compression efficiency. Sorting *is* still worthwhile -- but it sacrifices very little to only sort newly-added portions of the strtab, reusing older portions as necessary. As a first stage in this, discard the whole "pending refs" abstraction and replace it with "movable" refs, which are exactly like all other refs (addresses containing the strtab offset of some string, which are updated wiht the final strtab offset on serialization) except that we track them in a reverse dict so that we can move the refs around (which we do whenever we realloc() a buffer containing a bunch of structure members or something when we add members to the structure). libctf/ * ctf-create.c (ctf_add_enumerator): Call ctf_str_move_refs; add a movable ref. (ctf_add_member_offset): Likewise. * ctf-util.c (ctf_realloc): Delete. * ctf-serialize.c (ctf_serialize): No longer use it. Adjust to new fields. * ctf-string.c (ctf_str_purge_atom_refs): Purge movable refs. (ctf_str_free_atom): Free freeable atoms' strings. (ctf_str_create_atoms): Create the movable refs dynhash if needed. (ctf_str_free_atoms): Destroy it. (CTF_STR_MOVABLE): Switch (back) from ints to flags (see previous reversion). Add new flag. (aref_create): New, populate movable refs if need be. (ctf_str_add_ref_internal): Switch back to flags, update refs directly for nonprovisional strings (with already-known fixed offsets); create refs via aref_create. Allocate strings only if not within an mmapped strtab. (ctf_str_add_movable_ref): New. (ctf_str_add): Adjust to CTF_STR_* reintroduction. (ctf_str_add_external): LIkewise. (ctf_str_move_refs): New, move refs via ctf_str_movable_refs backpointer. (ctf_str_purge_refs): Drop ctf_str_num_refs. (ctf_str_update_refs): Fix indentation. * ctf-impl.h (struct ctf_str_atom_movable): New. (struct ctf_dict.ctf_str_num_refs): Drop. (struct ctf_dict.ctf_str_movable_refs): New. (ctf_str_add_movable_ref): Declare. (ctf_str_move_refs): Likewise. (ctf_realloc): Drop.
292 lines
6.9 KiB
C
292 lines
6.9 KiB
C
/* Miscellaneous utilities.
|
|
Copyright (C) 2019-2024 Free Software Foundation, Inc.
|
|
|
|
This file is part of libctf.
|
|
|
|
libctf is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <ctf-impl.h>
|
|
#include <string.h>
|
|
#include "ctf-endian.h"
|
|
|
|
/* Simple doubly-linked list append routine. This implementation assumes that
|
|
each list element contains an embedded ctf_list_t as the first member.
|
|
An additional ctf_list_t is used to store the head (l_next) and tail
|
|
(l_prev) pointers. The current head and tail list elements have their
|
|
previous and next pointers set to NULL, respectively. */
|
|
|
|
void
|
|
ctf_list_append (ctf_list_t *lp, void *newp)
|
|
{
|
|
ctf_list_t *p = lp->l_prev; /* p = tail list element. */
|
|
ctf_list_t *q = newp; /* q = new list element. */
|
|
|
|
lp->l_prev = q;
|
|
q->l_prev = p;
|
|
q->l_next = NULL;
|
|
|
|
if (p != NULL)
|
|
p->l_next = q;
|
|
else
|
|
lp->l_next = q;
|
|
}
|
|
|
|
/* Prepend the specified existing element to the given ctf_list_t. The
|
|
existing pointer should be pointing at a struct with embedded ctf_list_t. */
|
|
|
|
void
|
|
ctf_list_prepend (ctf_list_t * lp, void *newp)
|
|
{
|
|
ctf_list_t *p = newp; /* p = new list element. */
|
|
ctf_list_t *q = lp->l_next; /* q = head list element. */
|
|
|
|
lp->l_next = p;
|
|
p->l_prev = NULL;
|
|
p->l_next = q;
|
|
|
|
if (q != NULL)
|
|
q->l_prev = p;
|
|
else
|
|
lp->l_prev = p;
|
|
}
|
|
|
|
/* Delete the specified existing element from the given ctf_list_t. The
|
|
existing pointer should be pointing at a struct with embedded ctf_list_t. */
|
|
|
|
void
|
|
ctf_list_delete (ctf_list_t *lp, void *existing)
|
|
{
|
|
ctf_list_t *p = existing;
|
|
|
|
if (p->l_prev != NULL)
|
|
p->l_prev->l_next = p->l_next;
|
|
else
|
|
lp->l_next = p->l_next;
|
|
|
|
if (p->l_next != NULL)
|
|
p->l_next->l_prev = p->l_prev;
|
|
else
|
|
lp->l_prev = p->l_prev;
|
|
}
|
|
|
|
/* Return 1 if the list is empty. */
|
|
|
|
int
|
|
ctf_list_empty_p (ctf_list_t *lp)
|
|
{
|
|
return (lp->l_next == NULL && lp->l_prev == NULL);
|
|
}
|
|
|
|
/* Splice one entire list onto the end of another one. The existing list is
|
|
emptied. */
|
|
|
|
void
|
|
ctf_list_splice (ctf_list_t *lp, ctf_list_t *append)
|
|
{
|
|
if (ctf_list_empty_p (append))
|
|
return;
|
|
|
|
if (lp->l_prev != NULL)
|
|
lp->l_prev->l_next = append->l_next;
|
|
else
|
|
lp->l_next = append->l_next;
|
|
|
|
append->l_next->l_prev = lp->l_prev;
|
|
lp->l_prev = append->l_prev;
|
|
append->l_next = NULL;
|
|
append->l_prev = NULL;
|
|
}
|
|
|
|
/* Convert a 32-bit ELF symbol to a ctf_link_sym_t. */
|
|
|
|
ctf_link_sym_t *
|
|
ctf_elf32_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *dst, const Elf32_Sym *src,
|
|
uint32_t symidx)
|
|
{
|
|
Elf32_Sym tmp;
|
|
int needs_flipping = 0;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (fp->ctf_symsect_little_endian)
|
|
needs_flipping = 1;
|
|
#else
|
|
if (!fp->ctf_symsect_little_endian)
|
|
needs_flipping = 1;
|
|
#endif
|
|
|
|
memcpy (&tmp, src, sizeof (Elf32_Sym));
|
|
if (needs_flipping)
|
|
{
|
|
swap_thing (tmp.st_name);
|
|
swap_thing (tmp.st_size);
|
|
swap_thing (tmp.st_shndx);
|
|
swap_thing (tmp.st_value);
|
|
}
|
|
/* The name must be in the external string table. */
|
|
if (tmp.st_name < fp->ctf_str[CTF_STRTAB_1].cts_len)
|
|
dst->st_name = (const char *) fp->ctf_str[CTF_STRTAB_1].cts_strs + tmp.st_name;
|
|
else
|
|
dst->st_name = _CTF_NULLSTR;
|
|
dst->st_nameidx_set = 0;
|
|
dst->st_symidx = symidx;
|
|
dst->st_shndx = tmp.st_shndx;
|
|
dst->st_type = ELF32_ST_TYPE (tmp.st_info);
|
|
dst->st_value = tmp.st_value;
|
|
|
|
return dst;
|
|
}
|
|
|
|
/* Convert a 64-bit ELF symbol to a ctf_link_sym_t. */
|
|
|
|
ctf_link_sym_t *
|
|
ctf_elf64_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *dst, const Elf64_Sym *src,
|
|
uint32_t symidx)
|
|
{
|
|
Elf64_Sym tmp;
|
|
int needs_flipping = 0;
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (fp->ctf_symsect_little_endian)
|
|
needs_flipping = 1;
|
|
#else
|
|
if (!fp->ctf_symsect_little_endian)
|
|
needs_flipping = 1;
|
|
#endif
|
|
|
|
memcpy (&tmp, src, sizeof (Elf64_Sym));
|
|
if (needs_flipping)
|
|
{
|
|
swap_thing (tmp.st_name);
|
|
swap_thing (tmp.st_size);
|
|
swap_thing (tmp.st_shndx);
|
|
swap_thing (tmp.st_value);
|
|
}
|
|
|
|
/* The name must be in the external string table. */
|
|
if (tmp.st_name < fp->ctf_str[CTF_STRTAB_1].cts_len)
|
|
dst->st_name = (const char *) fp->ctf_str[CTF_STRTAB_1].cts_strs + tmp.st_name;
|
|
else
|
|
dst->st_name = _CTF_NULLSTR;
|
|
dst->st_nameidx_set = 0;
|
|
dst->st_symidx = symidx;
|
|
dst->st_shndx = tmp.st_shndx;
|
|
dst->st_type = ELF32_ST_TYPE (tmp.st_info);
|
|
|
|
/* We only care if the value is zero, so avoid nonzeroes turning into
|
|
zeroes. */
|
|
if (_libctf_unlikely_ (tmp.st_value != 0 && ((uint32_t) tmp.st_value == 0)))
|
|
dst->st_value = 1;
|
|
else
|
|
dst->st_value = (uint32_t) tmp.st_value;
|
|
|
|
return dst;
|
|
}
|
|
|
|
/* A string appender working on dynamic strings. Returns NULL on OOM. */
|
|
|
|
char *
|
|
ctf_str_append (char *s, const char *append)
|
|
{
|
|
size_t s_len = 0;
|
|
|
|
if (append == NULL)
|
|
return s;
|
|
|
|
if (s != NULL)
|
|
s_len = strlen (s);
|
|
|
|
size_t append_len = strlen (append);
|
|
|
|
if ((s = realloc (s, s_len + append_len + 1)) == NULL)
|
|
return NULL;
|
|
|
|
memcpy (s + s_len, append, append_len);
|
|
s[s_len + append_len] = '\0';
|
|
|
|
return s;
|
|
}
|
|
|
|
/* A version of ctf_str_append that returns the old string on OOM. */
|
|
|
|
char *
|
|
ctf_str_append_noerr (char *s, const char *append)
|
|
{
|
|
char *new_s;
|
|
|
|
new_s = ctf_str_append (s, append);
|
|
if (!new_s)
|
|
return s;
|
|
return new_s;
|
|
}
|
|
|
|
/* Store the specified error code into errp if it is non-NULL, and then
|
|
return NULL for the benefit of the caller. */
|
|
|
|
void *
|
|
ctf_set_open_errno (int *errp, int error)
|
|
{
|
|
if (errp != NULL)
|
|
*errp = error;
|
|
return NULL;
|
|
}
|
|
|
|
/* Create a ctf_next_t. */
|
|
|
|
ctf_next_t *
|
|
ctf_next_create (void)
|
|
{
|
|
return calloc (1, sizeof (struct ctf_next));
|
|
}
|
|
|
|
/* Destroy a ctf_next_t, for early exit from iterators. */
|
|
|
|
void
|
|
ctf_next_destroy (ctf_next_t *i)
|
|
{
|
|
if (i == NULL)
|
|
return;
|
|
|
|
if (i->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
|
|
free (i->u.ctn_sorted_hkv);
|
|
if (i->ctn_next)
|
|
ctf_next_destroy (i->ctn_next);
|
|
free (i);
|
|
}
|
|
|
|
/* Copy a ctf_next_t. */
|
|
|
|
ctf_next_t *
|
|
ctf_next_copy (ctf_next_t *i)
|
|
{
|
|
ctf_next_t *i2;
|
|
|
|
if ((i2 = ctf_next_create()) == NULL)
|
|
return NULL;
|
|
memcpy (i2, i, sizeof (struct ctf_next));
|
|
|
|
if (i2->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
|
|
{
|
|
size_t els = ctf_dynhash_elements ((ctf_dynhash_t *) i->cu.ctn_h);
|
|
if ((i2->u.ctn_sorted_hkv = calloc (els, sizeof (ctf_next_hkv_t))) == NULL)
|
|
{
|
|
free (i2);
|
|
return NULL;
|
|
}
|
|
memcpy (i2->u.ctn_sorted_hkv, i->u.ctn_sorted_hkv,
|
|
els * sizeof (ctf_next_hkv_t));
|
|
}
|
|
return i2;
|
|
}
|