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.
This commit is contained in:
Nick Alcock 2020-11-23 21:17:44 +00:00
parent cbfa382abb
commit 53651de80f
11 changed files with 197 additions and 56 deletions

View file

@ -36,7 +36,8 @@ static off_t arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold);
static ctf_dict_t *ctf_dict_open_by_offset (const struct ctf_archive *arc,
const ctf_sect_t *symsect,
const ctf_sect_t *strsect,
size_t offset, int *errp);
size_t offset, int little_endian,
int *errp);
static int sort_modent_by_name (const void *one, const void *two, void *n);
static void *arc_mmap_header (int fd, size_t headersz);
static void *arc_mmap_file (int fd, size_t size);
@ -378,10 +379,21 @@ ctf_new_archive_internal (int is_archive, int unmap_on_close,
arci->ctfi_free_symsect = 0;
arci->ctfi_free_strsect = 0;
arci->ctfi_unmap_on_close = unmap_on_close;
arci->ctfi_symsect_little_endian = -1;
return arci;
}
/* Set the symbol-table endianness of an archive (defaulting the symtab
endianness of all ctf_file_t's opened from that archive). */
void
ctf_arc_symsect_endianness (ctf_archive_t *arc, int little_endian)
{
arc->ctfi_symsect_little_endian = !!little_endian;
if (!arc->ctfi_is_archive)
ctf_symsect_endianness (arc->ctfi_dict, arc->ctfi_symsect_little_endian);
}
/* Get the CTF preamble from data in a buffer, which may be either an archive or
a CTF dict. If multiple dicts are present in an archive, the preamble comes
from an arbitrary dict. The preamble is a pointer into the ctfsect passed
@ -536,7 +548,8 @@ static ctf_dict_t *
ctf_dict_open_internal (const struct ctf_archive *arc,
const ctf_sect_t *symsect,
const ctf_sect_t *strsect,
const char *name, int *errp)
const char *name, int little_endian,
int *errp)
{
struct ctf_archive_modent *modent;
const char *search_nametbl;
@ -564,7 +577,8 @@ ctf_dict_open_internal (const struct ctf_archive *arc,
}
return ctf_dict_open_by_offset (arc, symsect, strsect,
le64toh (modent->ctf_offset), errp);
le64toh (modent->ctf_offset),
little_endian, errp);
}
/* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
@ -584,7 +598,8 @@ ctf_dict_open_sections (const ctf_archive_t *arc,
{
ctf_dict_t *ret;
ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect,
name, errp);
name, arc->ctfi_symsect_little_endian,
errp);
if (ret)
{
ret->ctf_archive = (ctf_archive_t *) arc;
@ -691,7 +706,7 @@ static ctf_dict_t *
ctf_dict_open_by_offset (const struct ctf_archive *arc,
const ctf_sect_t *symsect,
const ctf_sect_t *strsect, size_t offset,
int *errp)
int little_endian, int *errp)
{
ctf_sect_t ctfsect;
ctf_dict_t *fp;
@ -708,7 +723,11 @@ ctf_dict_open_by_offset (const struct ctf_archive *arc,
ctfsect.cts_data = (void *) ((char *) arc + offset + sizeof (uint64_t));
fp = ctf_bufopen (&ctfsect, symsect, strsect, errp);
if (fp)
ctf_setmodel (fp, le64toh (arc->ctfa_model));
{
ctf_setmodel (fp, le64toh (arc->ctfa_model));
if (little_endian >= 0)
ctf_symsect_endianness (fp, little_endian);
}
return fp;
}
@ -961,7 +980,9 @@ ctf_archive_iter_internal (const ctf_archive_t *wrapper,
name = &nametbl[le64toh (modent[i].name_offset)];
if ((f = ctf_dict_open_internal (arc, symsect, strsect,
name, &rc)) == NULL)
name,
wrapper->ctfi_symsect_little_endian,
&rc)) == NULL)
return rc;
f->ctf_archive = (ctf_archive_t *) wrapper;