binutils-gdb/libctf/ctf-util.c
Nick Alcock 53651de80f libctf, include: support foreign-endianness symtabs with CTF
The CTF symbol lookup machinery added recently has one deficit: it
assumes the symtab is in the machine's native endianness.  This is
always true when the linker is writing out symtabs (because cross
linkers byteswap symbols only after libctf has been called on them), but
may be untrue in the cross case when the linker or another tool
(objdump, etc) is reading them.

Unfortunately the easy way to model this to the caller, as an endianness
field in the ctf_sect_t, is precluded because doing so would change the
size of the ctf_sect_t, which would be an ABI break.  So, instead, allow
the endianness of the symtab to be set after open time, by calling one
of the two new API functions ctf_symsect_endianness (for ctf_dict_t's)
or ctf_arc_symsect_endianness (for entire ctf_archive_t's).  libctf
calls these functions automatically for objects opened via any of the
BFD-aware mechanisms (ctf_bfdopen, ctf_bfdopen_ctfsect, ctf_fdopen,
ctf_open, or ctf_arc_open), but the various mechanisms that just take
raw ctf_sect_t's will assume the symtab is in native endianness and need
a later call to ctf_*symsect_endianness to adjust it if needed.  (This
call is basically free if the endianness is actually native: it only
costs anything if the symtab endianness was previously guessed wrong,
and there is a symtab, and we are using it directly rather than using
symtab indexing.)

Obviously, calling ctf_lookup_by_symbol or ctf_symbol_next before the
symtab endianness is correctly set will probably give wrong answers --
but you can set it at any time as long as it is before then.

include/ChangeLog
2020-11-23  Nick Alcock  <nick.alcock@oracle.com>

	* ctf-api.h: Style nit: remove () on function names in comments.
	(ctf_sect_t): Mention endianness concerns.
	(ctf_symsect_endianness): New declaration.
	(ctf_arc_symsect_endianness): Likewise.

libctf/ChangeLog
2020-11-23  Nick Alcock  <nick.alcock@oracle.com>

	* ctf-impl.h (ctf_dict_t) <ctf_symtab_little_endian>: New.
	(struct ctf_archive_internal) <ctfi_symsect_little_endian>: Likewise.
	* ctf-create.c (ctf_serialize): Adjust for new field.
	* ctf-open.c (init_symtab): Note the semantics of repeated calls.
	(ctf_symsect_endianness): New.
	(ctf_bufopen_internal): Set ctf_symtab_little_endian suitably for
	the native endianness.
	(_Static_assert): Moved...
	(swap_thing): ... with this...
	* swap.h: ... to here.
	* ctf-util.c (ctf_elf32_to_link_sym): Use it, byteswapping the
	Elf32_Sym if the ctf_symtab_little_endian demands it.
	(ctf_elf64_to_link_sym): Likewise swap the Elf64_Sym if needed.
	* ctf-archive.c (ctf_arc_symsect_endianness): New, set the
	endianness of the symtab used by the dicts in an archive.
	(ctf_archive_iter_internal): Initialize to unknown (assumed native,
	do not call ctf_symsect_endianness).
	(ctf_dict_open_by_offset): Call ctf_symsect_endianness if need be.
	(ctf_dict_open_internal): Propagate the endianness down.
	(ctf_dict_open_sections): Likewise.
	* ctf-open-bfd.c (ctf_bfdopen_ctfsect): Get the endianness from the
	struct bfd and pass it down to the archive.
	* libctf.ver: Add ctf_symsect_endianness and
	ctf_arc_symsect_endianness.
2020-11-25 19:11:35 +00:00

316 lines
7.6 KiB
C

/* Miscellaneous utilities.
Copyright (C) 2019-2020 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_iter_fun == (void (*) (void)) ctf_symbol_next
&& i->cu.ctn_fp->ctf_flags & LCTF_RDWR)
ctf_next_destroy (i->u.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;
}