libctf: add LIBCTF_WRITE_FOREIGN_ENDIAN debugging option

libctf has always handled endianness differences by detecting
foreign-endian CTF dicts on the input and endian-flipping them: dicts
are always written in native endianness.  This makes endian-awareness
very low overhead, but it means that the foreign-endian code paths
almost never get routinely tested, since "make check" usually reads in
dicts ld has just written out: only a few corrupted-CTF tests are
actually in fixed endianness, and even they only test the foreign-
endian code paths when you run make check on a big-endian machine.
(And the fix is surely not to add more .s-based tests like that, because
they are a nightmare to maintain compared to the C-code-based ones.)

To improve on this, add a new environment variable,
LIBCTF_WRITE_FOREIGN_ENDIAN, which causes libctf to unconditionally
endian-flip at ctf_write time, so the output is always in the wrong
endianness.  This then tests the foreign-endian read paths properly
at open time.

Make this easier by restructuring the writeout code in ctf-serialize.c,
which duplicates the maybe-gzip-and-write-out code three times (once
for ctf_write_mem, with thresholding, and once each for
ctf_compress_write and ctf_write just so those can avoid thresholding
and/or compression).  Instead, have the latter two call the former
with thresholds of 0 or (size_t) -1, respectively.

The endian-flipping code itself gains a bit of complexity, because
one single endian-flipper (flip_types) was assuming the input to be
in foreign-endian form and assuming it could pull things out of the
input once they had been flipped and make sense of them. At the
cost of a few lines of duplicated initializations, teach it to
read before flipping if we're flipping to foreign-endianness instead
of away from it.

libctf/
	* ctf-impl.h (ctf_flip_header): No longer static.
	(ctf_flip): Likewise.
	* ctf-open.c (flip_header): Rename to...
	(ctf_flip_header): ... this, now it is not private to one file.
	(flip_ctf): Rename...
	(ctf_flip): ... this too.  Add FOREIGN_ENDIAN arg.
	(flip_types): Likewise.  Use it.
	(ctf_bufopen_internal): Adjust calls.
	* ctf-serialize.c (ctf_write_mem): Add flip_endian path via
	a newly-allocated bounce buffer.
	(ctf_compress_write): Move below ctf_write_mem and reimplement
	in terms of it.
	(ctf_write): Likewise.
	(ctf_gzwrite): Note that this obscure writeout function does not
	support endian-flipping.
This commit is contained in:
Nick Alcock 2022-03-18 13:20:29 +00:00
parent 84f5c557a4
commit faf5e6ace8
3 changed files with 147 additions and 114 deletions

View file

@ -965,8 +965,8 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth)
/* Flip the endianness of the CTF header. */
static void
flip_header (ctf_header_t *cth)
void
ctf_flip_header (ctf_header_t *cth)
{
swap_thing (cth->cth_preamble.ctp_magic);
swap_thing (cth->cth_preamble.ctp_version);
@ -1031,26 +1031,48 @@ flip_vars (void *start, size_t len)
ctf_stype followed by variable data. */
static int
flip_types (ctf_dict_t *fp, void *start, size_t len)
flip_types (ctf_dict_t *fp, void *start, size_t len, int to_foreign)
{
ctf_type_t *t = start;
while ((uintptr_t) t < ((uintptr_t) start) + len)
{
uint32_t kind;
size_t size;
uint32_t vlen;
size_t vbytes;
if (to_foreign)
{
kind = CTF_V2_INFO_KIND (t->ctt_info);
size = t->ctt_size;
vlen = CTF_V2_INFO_VLEN (t->ctt_info);
vbytes = get_vbytes_v2 (fp, kind, size, vlen);
}
swap_thing (t->ctt_name);
swap_thing (t->ctt_info);
swap_thing (t->ctt_size);
uint32_t kind = CTF_V2_INFO_KIND (t->ctt_info);
size_t size = t->ctt_size;
uint32_t vlen = CTF_V2_INFO_VLEN (t->ctt_info);
size_t vbytes = get_vbytes_v2 (fp, kind, size, vlen);
if (!to_foreign)
{
kind = CTF_V2_INFO_KIND (t->ctt_info);
size = t->ctt_size;
vlen = CTF_V2_INFO_VLEN (t->ctt_info);
vbytes = get_vbytes_v2 (fp, kind, size, vlen);
}
if (_libctf_unlikely_ (size == CTF_LSIZE_SENT))
{
if (to_foreign)
size = CTF_TYPE_LSIZE (t);
swap_thing (t->ctt_lsizehi);
swap_thing (t->ctt_lsizelo);
size = CTF_TYPE_LSIZE (t);
if (!to_foreign)
size = CTF_TYPE_LSIZE (t);
t = (ctf_type_t *) ((uintptr_t) t + sizeof (ctf_type_t));
}
else
@ -1182,22 +1204,27 @@ flip_types (ctf_dict_t *fp, void *start, size_t len)
}
/* Flip the endianness of BUF, given the offsets in the (already endian-
converted) CTH.
converted) CTH. If TO_FOREIGN is set, flip to foreign-endianness; if not,
flip away.
All of this stuff happens before the header is fully initialized, so the
LCTF_*() macros cannot be used yet. Since we do not try to endian-convert v1
data, this is no real loss. */
static int
flip_ctf (ctf_dict_t *fp, ctf_header_t *cth, unsigned char *buf)
int
ctf_flip (ctf_dict_t *fp, ctf_header_t *cth, unsigned char *buf,
int to_foreign)
{
ctf_dprintf("flipping endianness\n");
flip_lbls (buf + cth->cth_lbloff, cth->cth_objtoff - cth->cth_lbloff);
flip_objts (buf + cth->cth_objtoff, cth->cth_funcoff - cth->cth_objtoff);
flip_objts (buf + cth->cth_funcoff, cth->cth_objtidxoff - cth->cth_funcoff);
flip_objts (buf + cth->cth_objtidxoff, cth->cth_funcidxoff - cth->cth_objtidxoff);
flip_objts (buf + cth->cth_funcidxoff, cth->cth_varoff - cth->cth_funcidxoff);
flip_vars (buf + cth->cth_varoff, cth->cth_typeoff - cth->cth_varoff);
return flip_types (fp, buf + cth->cth_typeoff, cth->cth_stroff - cth->cth_typeoff);
return flip_types (fp, buf + cth->cth_typeoff,
cth->cth_stroff - cth->cth_typeoff, to_foreign);
}
/* Set up the ctl hashes in a ctf_dict_t. Called by both writable and
@ -1404,7 +1431,7 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
upgrade_header (hp);
if (foreign_endian)
flip_header (hp);
ctf_flip_header (hp);
fp->ctf_openflags = hp->cth_flags;
fp->ctf_size = hp->cth_stroff + hp->cth_strlen;
@ -1610,9 +1637,9 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
fp->ctf_syn_ext_strtab = syn_strtab;
if (foreign_endian &&
(err = flip_ctf (fp, hp, fp->ctf_buf)) != 0)
(err = ctf_flip (fp, hp, fp->ctf_buf, 0)) != 0)
{
/* We can be certain that flip_ctf() will have endian-flipped everything
/* We can be certain that ctf_flip() will have endian-flipped everything
other than the types table when we return. In particular the header
is fine, so set it, to allow freeing to use the usual code path. */