mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
6c3a38777b
libctf has no intrinsic support for the GCC unnamed structure member extension. This principally means that you can't look up named members inside unnamed struct or union members via ctf_member_info: you have to tiresomely find out the type ID of the unnamed members via iteration, then look in each of these. This is ridiculous. Fix it by extending ctf_member_info so that it recurses into unnamed members for you: this is still unambiguous because GCC won't let you create ambiguously-named members even in the presence of this extension. For consistency, and because the release hasn't happened and we can still do this, break the ctf_member_next API and add flags: we specify one flag, CTF_MN_RECURSE, which if set causes ctf_member_next to automatically recurse into unnamed members for you, returning not only the members themselves but all their contained members, so that you can use ctf_member_next to identify every member that it would be valid to call ctf_member_info with. New lookup tests are added for all of this. include/ChangeLog 2021-01-05 Nick Alcock <nick.alcock@oracle.com> * ctf-api.h (CTF_MN_RECURSE): New. (ctf_member_next): Add flags argument. libctf/ChangeLog 2021-01-05 Nick Alcock <nick.alcock@oracle.com> * ctf-impl.h (struct ctf_next) <u.ctn_next>: Move to... <ctn_next>: ... here. * ctf-util.c (ctf_next_destroy): Unconditionally destroy it. * ctf-lookup.c (ctf_symbol_next): Adjust accordingly. * ctf-types.c (ctf_member_iter): Reimplement in terms of... (ctf_member_next): ... this. Support recursive unnamed member iteration (off by default). (ctf_member_info): Look up members in unnamed sub-structs. * ctf-dedup.c (ctf_dedup_rhash_type): Adjust ctf_member_next call. (ctf_dedup_emit_struct_members): Likewise. * testsuite/libctf-lookup/struct-iteration-ctf.c: Test empty unnamed members, and a normal member after the end. * testsuite/libctf-lookup/struct-iteration.c: Verify that ctf_member_count is consistent with the number of successful returns from a non-recursive ctf_member_next. * testsuite/libctf-lookup/struct-iteration-*: New, test iteration over struct members. * testsuite/libctf-lookup/struct-lookup.c: New test. * testsuite/libctf-lookup/struct-lookup.lk: New test.
315 lines
7.5 KiB
C
315 lines
7.5 KiB
C
/* Miscellaneous utilities.
|
|
Copyright (C) 2019-2021 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;
|
|
}
|
|
|
|
/* A realloc() that fails noisily if called with any ctf_str_num_users. */
|
|
void *
|
|
ctf_realloc (ctf_dict_t *fp, void *ptr, size_t size)
|
|
{
|
|
if (fp->ctf_str_num_refs > 0)
|
|
{
|
|
ctf_dprintf ("%p: attempt to realloc() string table with %lu active refs\n",
|
|
(void *) fp, (unsigned long) fp->ctf_str_num_refs);
|
|
return NULL;
|
|
}
|
|
return realloc (ptr, size);
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Store the specified error code into the CTF dict, and then return CTF_ERR /
|
|
-1 for the benefit of the caller. */
|
|
|
|
unsigned long
|
|
ctf_set_errno (ctf_dict_t *fp, int err)
|
|
{
|
|
fp->ctf_errno = err;
|
|
return CTF_ERR;
|
|
}
|
|
|
|
/* 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;
|
|
}
|