/*-
 * Copyright (c) 2006-2011 Joseph Koshy
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>

#include <assert.h>
#include <libelf.h>
#include <string.h>

#include "_libelf.h"

ELFTC_VCSID("$Id: libelf_convert.m4 2361 2011-12-28 12:03:05Z jkoshy $");

/* WARNING: GENERATED FROM __file__. */

divert(-1)

# Generate conversion routines for converting between in-memory and
# file representations of Elf data structures.
#
# These conversions use the type information defined in `elf_types.m4'.

include(SRCDIR`/elf_types.m4')

# For the purposes of generating conversion code, ELF types may be
# classified according to the following characteristics:
#
# 1. Whether the ELF type can be directly mapped to an integral C
#    language type.  For example, the ELF_T_WORD type maps directly to
#    a 'uint32_t', but ELF_T_GNUHASH lacks a matching C type.
#
# 2. Whether the type has word size dependent variants.  For example,
#    ELT_T_EHDR is represented using C types Elf32_Ehdr and El64_Ehdr,
#    and the ELF_T_ADDR and ELF_T_OFF types have integral C types that
#    can be 32- or 64- bit wide.
#
# 3. Whether the ELF types has a fixed representation or not.  For
#    example, the ELF_T_SYM type has a fixed size file representation,
#    some types like ELF_T_NOTE and ELF_T_GNUHASH use a variable size
#    representation.
#
# We use m4 macros to generate conversion code for ELF types that have
# a fixed size representation.  Conversion functions for the remaining
# types are coded by hand.
#
#* Handling File and Memory Representations
#
# `In-memory' representations of an Elf data structure use natural
# alignments and native byte ordering.  This allows pointer arithmetic
# and casting to work as expected.  On the other hand, the `file'
# representation of an ELF data structure could possibly be packed
# tighter than its `in-memory' representation, and could be of a
# differing byte order.  Reading ELF objects that are members of `ar'
# archives present an additional complication: `ar' pads file data to
# even addresses, so file data structures in an archive member
# residing inside an `ar' archive could be at misaligned memory
# addresses when brought into memory.
#
# In summary, casting the `char *' pointers that point to memory
# representations (i.e., source pointers for the *_tof() functions and
# the destination pointers for the *_tom() functions), is safe, as
# these pointers should be correctly aligned for the memory type
# already.  However, pointers to file representations have to be
# treated as being potentially unaligned and no casting can be done.

# NOCVT(TYPE) -- Do not generate the cvt[] structure entry for TYPE
define(`NOCVT',`define(`NOCVT_'$1,1)')

# NOFUNC(TYPE) -- Do not generate a conversion function for TYPE
define(`NOFUNC',`define(`NOFUNC_'$1,1)')

# IGNORE(TYPE) -- Completely ignore the type.
define(`IGNORE',`NOCVT($1)NOFUNC($1)')

# Mark ELF types that should not be processed by the M4 macros below.

# Types for which we use functions with non-standard names.
IGNORE(`BYTE')			# Uses a wrapper around memcpy().
IGNORE(`NOTE')			# Not a fixed size type.

# Types for which we supply hand-coded functions.
NOFUNC(`GNUHASH')		# A type with complex internal structure.
NOFUNC(`VDEF')			# See MAKE_VERSION_CONVERTERS below.
NOFUNC(`VNEED')			# ..

# Unimplemented types.
IGNORE(`MOVEP')

# ELF types that don't exist in a 32-bit world.
NOFUNC(`XWORD32')
NOFUNC(`SXWORD32')

# `Primitive' ELF types are those that are an alias for an integral
# type.  As they have no internal structure, they can be copied using
# a `memcpy()', and byteswapped in straightforward way.
#
# Mark all ELF types that directly map to integral C types.
define(`PRIM_ADDR',	1)
define(`PRIM_BYTE',	1)
define(`PRIM_HALF',	1)
define(`PRIM_LWORD',	1)
define(`PRIM_OFF',	1)
define(`PRIM_SWORD',	1)
define(`PRIM_SXWORD',	1)
define(`PRIM_WORD',	1)
define(`PRIM_XWORD',	1)

# Note the primitive types that are size-dependent.
define(`SIZEDEP_ADDR',	1)
define(`SIZEDEP_OFF',	1)

# Generate conversion functions for primitive types.
#
# Macro use: MAKEPRIMFUNCS(ELFTYPE,CTYPE,TYPESIZE,SYMSIZE)
# `$1': Name of the ELF type.
# `$2': C structure name suffix.
# `$3': ELF class specifier for types, one of [`32', `64'].
# `$4': Additional ELF class specifier, one of [`', `32', `64'].
#
# Generates a pair of conversion functions.
define(`MAKEPRIMFUNCS',`
static int
_libelf_cvt_$1$4_tof(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	Elf$3_$2 t, *s = (Elf$3_$2 *) (uintptr_t) src;
	size_t c;

	(void) dsz;

	if (!byteswap) {
		(void) memcpy(dst, src, count * sizeof(*s));
		return (1);
	}

	for (c = 0; c < count; c++) {
		t = *s++;
		SWAP_$1$4(t);
		WRITE_$1$4(dst,t);
	}

	return (1);
}

static int
_libelf_cvt_$1$4_tom(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	Elf$3_$2 t, *d = (Elf$3_$2 *) (uintptr_t) dst;
	size_t c;

	if (dsz < count * sizeof(Elf$3_$2))
		return (0);

	if (!byteswap) {
		(void) memcpy(dst, src, count * sizeof(*d));
		return (1);
	}

	for (c = 0; c < count; c++) {
		READ_$1$4(src,t);
		SWAP_$1$4(t);
		*d++ = t;
	}

	return (1);
}
')

#
# Handling composite ELF types
#

# SWAP_FIELD(FIELDNAME,ELFTYPE) -- Generate code to swap one field.
define(`SWAP_FIELD',
  `ifdef(`SIZEDEP_'$2,
    `SWAP_$2'SZ()`(t.$1);
			',
    `SWAP_$2(t.$1);
			')')

# SWAP_MEMBERS(STRUCT) -- Iterate over a structure definition.
define(`SWAP_MEMBERS',
  `ifelse($#,1,`/**/',
     `SWAP_FIELD($1)SWAP_MEMBERS(shift($@))')')

# SWAP_STRUCT(CTYPE,SIZE) -- Generate code to swap an ELF structure.
define(`SWAP_STRUCT',
  `pushdef(`SZ',$2)/* Swap an Elf$2_$1 */
			SWAP_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')

# WRITE_FIELD(ELFTYPE,FIELDNAME) -- Generate code to write one field.
define(`WRITE_FIELD',
  `ifdef(`SIZEDEP_'$2,
    `WRITE_$2'SZ()`(dst,t.$1);
		',
    `WRITE_$2(dst,t.$1);
		')')

# WRITE_MEMBERS(ELFTYPELIST) -- Iterate over a structure definition.
define(`WRITE_MEMBERS',
  `ifelse($#,1,`/**/',
    `WRITE_FIELD($1)WRITE_MEMBERS(shift($@))')')

# WRITE_STRUCT(CTYPE,SIZE) -- Generate code to write out an ELF structure.
define(`WRITE_STRUCT',
  `pushdef(`SZ',$2)/* Write an Elf$2_$1 */
		WRITE_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')

# READ_FIELD(ELFTYPE,CTYPE) -- Generate code to read one field.
define(`READ_FIELD',
  `ifdef(`SIZEDEP_'$2,
    `READ_$2'SZ()`(s,t.$1);
		',
    `READ_$2(s,t.$1);
		')')

# READ_MEMBERS(ELFTYPELIST) -- Iterate over a structure definition.
define(`READ_MEMBERS',
  `ifelse($#,1,`/**/',
    `READ_FIELD($1)READ_MEMBERS(shift($@))')')

# READ_STRUCT(CTYPE,SIZE) -- Generate code to read an ELF structure.
define(`READ_STRUCT',
  `pushdef(`SZ',$2)/* Read an Elf$2_$1 */
		READ_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')


# MAKECOMPFUNCS -- Generate converters for composite ELF structures.
#
# When converting data to file representation, the source pointer will
# be naturally aligned for a data structure's in-memory
# representation.  When converting data to memory, the destination
# pointer will be similarly aligned.
#
# For in-place conversions, when converting to file representations,
# the source buffer is large enough to hold `file' data.  When
# converting from file to memory, we need to be careful to work
# `backwards', to avoid overwriting unconverted data.
#
# Macro use:
# `$1': Name of the ELF type.
# `$2': C structure name suffix.
# `$3': ELF class specifier, one of [`', `32', `64']
define(`MAKECOMPFUNCS', `ifdef(`NOFUNC_'$1$3,`',`
static int
_libelf_cvt_$1$3_tof(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	Elf$3_$2	t, *s;
	size_t c;

	(void) dsz;

	s = (Elf$3_$2 *) (uintptr_t) src;
	for (c = 0; c < count; c++) {
		t = *s++;
		if (byteswap) {
			SWAP_STRUCT($2,$3)
		}
		WRITE_STRUCT($2,$3)
	}

	return (1);
}

static int
_libelf_cvt_$1$3_tom(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	Elf$3_$2	 t, *d;
	char		*s,*s0;
	size_t		fsz;

	fsz = elf$3_fsize(ELF_T_$1, (size_t) 1, EV_CURRENT);
	d   = ((Elf$3_$2 *) (uintptr_t) dst) + (count - 1);
	s0  = (char *) src + (count - 1) * fsz;

	if (dsz < count * sizeof(Elf$3_$2))
		return (0);

	while (count--) {
		s = s0;
		READ_STRUCT($2,$3)
		if (byteswap) {
			SWAP_STRUCT($2,$3)
		}
		*d-- = t; s0 -= fsz;
	}

	return (1);
}
')')

# MAKE_TYPE_CONVERTER(ELFTYPE,CTYPE)
#
# Make type convertor functions from the type definition
# of the ELF type:
# - Skip convertors marked as `NOFUNC'.
# - Invoke `MAKEPRIMFUNCS' or `MAKECOMPFUNCS' as appropriate.
define(`MAKE_TYPE_CONVERTER',
  `ifdef(`NOFUNC_'$1,`',
    `ifdef(`PRIM_'$1,
      `ifdef(`SIZEDEP_'$1,
	`MAKEPRIMFUNCS($1,$2,32,32)dnl
	 MAKEPRIMFUNCS($1,$2,64,64)',
	`MAKEPRIMFUNCS($1,$2,64)')',
      `MAKECOMPFUNCS($1,$2,32)dnl
       MAKECOMPFUNCS($1,$2,64)')')')

# MAKE_TYPE_CONVERTERS(ELFTYPELIST) -- Generate conversion functions.
define(`MAKE_TYPE_CONVERTERS',
  `ifelse($#,1,`',
    `MAKE_TYPE_CONVERTER($1)MAKE_TYPE_CONVERTERS(shift($@))')')


#
# Macros to generate entries for the table of convertors.
#

# CONV(ELFTYPE,SIZE,DIRECTION)
#
# Generate the name of a convertor function.
define(`CONV',
  `ifdef(`NOFUNC_'$1$2,
    `.$3$2 = NULL',
    `ifdef(`PRIM_'$1,
      `ifdef(`SIZEDEP_'$1,
	`.$3$2 = _libelf_cvt_$1$2_$3',
	`.$3$2 = _libelf_cvt_$1_$3')',
      `.$3$2 = _libelf_cvt_$1$2_$3')')')

# CONVERTER_NAME(ELFTYPE)
#
# Generate the contents of one `struct cvt' instance.
define(`CONVERTER_NAME',
  `ifdef(`NOCVT_'$1,`',
    `	[ELF_T_$1] = {
		CONV($1,32,tof),
		CONV($1,32,tom),
		CONV($1,64,tof),
		CONV($1,64,tom)
	},

')')

# CONVERTER_NAMES(ELFTYPELIST)
#
# Generate the `struct cvt[]' array.
define(`CONVERTER_NAMES',
  `ifelse($#,1,`',
    `CONVERTER_NAME($1)CONVERTER_NAMES(shift($@))')')

#
# Handling ELF version sections.
#

# _FSZ(FIELD,BASETYPE) - return the file size for a field.
define(`_FSZ',
  `ifelse($2,`HALF',2,
     $2,`WORD',4)')

# FSZ(STRUCT) - determine the file size of a structure.
define(`FSZ',
  `ifelse($#,1,0,
    `eval(_FSZ($1) + FSZ(shift($@)))')')

# MAKE_VERSION_CONVERTERS(TYPE,BASE,AUX,PFX) -- Generate conversion
# functions for versioning structures.
define(`MAKE_VERSION_CONVERTERS',
  `MAKE_VERSION_CONVERTER($1,$2,$3,$4,32)
   MAKE_VERSION_CONVERTER($1,$2,$3,$4,64)')

# MAKE_VERSION_CONVERTOR(TYPE,CBASE,CAUX,PFX,SIZE) -- Generate a
# conversion function.
define(`MAKE_VERSION_CONVERTER',`
static int
_libelf_cvt_$1$5_tof(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	Elf$5_$2	t;
	Elf$5_$3	a;
	const size_t	verfsz = FSZ(Elf$5_$2_DEF);
	const size_t	auxfsz = FSZ(Elf$5_$3_DEF);
	const size_t	vermsz = sizeof(Elf$5_$2);
	const size_t	auxmsz = sizeof(Elf$5_$3);
	char * const	dstend = dst + dsz;
	char * const	srcend = src + count;
	char		*dtmp, *dstaux, *srcaux;
	Elf$5_Word	aux, anext, cnt, vnext;

	for (dtmp = dst, vnext = ~0;
	     vnext != 0 && dtmp + verfsz <= dstend && src + vermsz <= srcend;
	     dtmp += vnext, src += vnext) {

		/* Read in an Elf$5_$2 structure. */
		t = *((Elf$5_$2 *) (uintptr_t) src);

		aux = t.$4_aux;
		cnt = t.$4_cnt;
		vnext = t.$4_next;

		if (byteswap) {
			SWAP_STRUCT($2, $5)
		}

		dst = dtmp;
		WRITE_STRUCT($2, $5)

		if (aux < verfsz)
			return (0);

		/* Process AUX entries. */
		for (anext = ~0, dstaux = dtmp + aux, srcaux = src + aux;
		     cnt != 0 && anext != 0 && dstaux + auxfsz <= dstend &&
			srcaux + auxmsz <= srcend;
		     dstaux += anext, srcaux += anext, cnt--) {

			/* Read in an Elf$5_$3 structure. */
			a = *((Elf$5_$3 *) (uintptr_t) srcaux);
			anext = a.$4a_next;

			if (byteswap) {
				pushdef(`t',`a')SWAP_STRUCT($3, $5)popdef(`t')
			}

			dst = dstaux;
			pushdef(`t',`a')WRITE_STRUCT($3, $5)popdef(`t')
		}

		if (anext || cnt)
			return (0);
	}

	if (vnext)
		return (0);

	return (1);
}

static int
_libelf_cvt_$1$5_tom(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	Elf$5_$2	t, *dp;
	Elf$5_$3	a, *ap;
	const size_t	verfsz = FSZ(Elf$5_$2_DEF);
	const size_t	auxfsz = FSZ(Elf$5_$3_DEF);
	const size_t	vermsz = sizeof(Elf$5_$2);
	const size_t	auxmsz = sizeof(Elf$5_$3);
	char * const	dstend = dst + dsz;
	char * const	srcend = src + count;
	char		*dstaux, *s, *srcaux, *stmp;
	Elf$5_Word	aux, anext, cnt, vnext;

	for (stmp = src, vnext = ~0;
	     vnext != 0 && stmp + verfsz <= srcend && dst + vermsz <= dstend;
	     stmp += vnext, dst += vnext) {

		/* Read in a $1 structure. */
		s = stmp;
		READ_STRUCT($2, $5)
		if (byteswap) {
			SWAP_STRUCT($2, $5)
		}

		dp = (Elf$5_$2 *) (uintptr_t) dst;
		*dp = t;

		aux = t.$4_aux;
		cnt = t.$4_cnt;
		vnext = t.$4_next;

		if (aux < vermsz)
			return (0);

		/* Process AUX entries. */
		for (anext = ~0, dstaux = dst + aux, srcaux = stmp + aux;
		     cnt != 0 && anext != 0 && dstaux + auxmsz <= dstend &&
			srcaux + auxfsz <= srcend;
		     dstaux += anext, srcaux += anext, cnt--) {

			s = srcaux;
			pushdef(`t',`a')READ_STRUCT($3, $5)popdef(`t')

			if (byteswap) {
				pushdef(`t',`a')SWAP_STRUCT($3, $5)popdef(`t')
			}

			anext = a.$4a_next;

			ap = ((Elf$5_$3 *) (uintptr_t) dstaux);
			*ap = a;
		}

		if (anext || cnt)
			return (0);
	}

	if (vnext)
		return (0);

	return (1);
}')

divert(0)

/*
 * C macros to byte swap integral quantities.
 */

#define	SWAP_BYTE(X)	do { (void) (X); } while (0)
#define	SWAP_IDENT(X)	do { (void) (X); } while (0)
#define	SWAP_HALF(X)	do {						\
		uint16_t _x = (uint16_t) (X);				\
		uint16_t _t = _x & 0xFF;				\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		(X) = _t;						\
	} while (0)
#define	SWAP_WORD(X)	do {						\
		uint32_t _x = (uint32_t) (X);				\
		uint32_t _t = _x & 0xFF;				\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		(X) = _t;						\
	} while (0)
#define	SWAP_ADDR32(X)	SWAP_WORD(X)
#define	SWAP_OFF32(X)	SWAP_WORD(X)
#define	SWAP_SWORD(X)	SWAP_WORD(X)
#define	SWAP_WORD64(X)	do {						\
		uint64_t _x = (uint64_t) (X);				\
		uint64_t _t = _x & 0xFF;				\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		_t <<= 8; _x >>= 8; _t |= _x & 0xFF;			\
		(X) = _t;						\
	} while (0)
#define	SWAP_ADDR64(X)	SWAP_WORD64(X)
#define	SWAP_LWORD(X)	SWAP_WORD64(X)
#define	SWAP_OFF64(X)	SWAP_WORD64(X)
#define	SWAP_SXWORD(X)	SWAP_WORD64(X)
#define	SWAP_XWORD(X)	SWAP_WORD64(X)

/*
 * C macros to write out various integral values.
 *
 * Note:
 * - The destination pointer could be unaligned.
 * - Values are written out in native byte order.
 * - The destination pointer is incremented after the write.
 */
#define	WRITE_BYTE(P,X) do {						\
		char *const _p = (char *) (P);	\
		_p[0]		= (char) (X);			\
		(P)		= _p + 1;				\
	} while (0)
#define	WRITE_HALF(P,X)	do {						\
		uint16_t _t	= (X);					\
		char *const _p	= (char *) (P);	\
		const char *const _q = (char *) &_t;	\
		_p[0]		= _q[0];				\
		_p[1]		= _q[1];				\
		(P)		= _p + 2;				\
	} while (0)
#define	WRITE_WORD(P,X)	do {						\
		uint32_t _t	= (X);					\
		char *const _p	= (char *) (P);	\
		const char *const _q = (char *) &_t;	\
		_p[0]		= _q[0];				\
		_p[1]		= _q[1];				\
		_p[2]		= _q[2];				\
		_p[3]		= _q[3];				\
		(P)		= _p + 4;				\
	} while (0)
#define	WRITE_ADDR32(P,X)	WRITE_WORD(P,X)
#define	WRITE_OFF32(P,X)	WRITE_WORD(P,X)
#define	WRITE_SWORD(P,X)	WRITE_WORD(P,X)
#define	WRITE_WORD64(P,X)	do {					\
		uint64_t _t	= (X);					\
		char *const _p	= (char *) (P);	\
		const char *const _q = (char *) &_t;	\
		_p[0]		= _q[0];				\
		_p[1]		= _q[1];				\
		_p[2]		= _q[2];				\
		_p[3]		= _q[3];				\
		_p[4]		= _q[4];				\
		_p[5]		= _q[5];				\
		_p[6]		= _q[6];				\
		_p[7]		= _q[7];				\
		(P)		= _p + 8;				\
	} while (0)
#define	WRITE_ADDR64(P,X)	WRITE_WORD64(P,X)
#define	WRITE_LWORD(P,X)	WRITE_WORD64(P,X)
#define	WRITE_OFF64(P,X)	WRITE_WORD64(P,X)
#define	WRITE_SXWORD(P,X)	WRITE_WORD64(P,X)
#define	WRITE_XWORD(P,X)	WRITE_WORD64(P,X)
#define	WRITE_IDENT(P,X)	do {					\
		(void) memcpy((P), (X), sizeof((X)));			\
		(P)		= (P) + EI_NIDENT;			\
	} while (0)

/*
 * C macros to read in various integral values.
 *
 * Note:
 * - The source pointer could be unaligned.
 * - Values are read in native byte order.
 * - The source pointer is incremented appropriately.
 */

#define	READ_BYTE(P,X)	do {						\
		const char *const _p =				\
			(const char *) (P);			\
		(X)		= _p[0];				\
		(P)		= (P) + 1;				\
	} while (0)
#define	READ_HALF(P,X)	do {						\
		uint16_t _t;						\
		char *const _q = (char *) &_t;	\
		const char *const _p =				\
			(const char *) (P);			\
		_q[0]		= _p[0];				\
		_q[1]		= _p[1];				\
		(P)		= (P) + 2;				\
		(X)		= _t;					\
	} while (0)
#define	READ_WORD(P,X)	do {						\
		uint32_t _t;						\
		char *const _q = (char *) &_t;	\
		const char *const _p =				\
			(const char *) (P);			\
		_q[0]		= _p[0];				\
		_q[1]		= _p[1];				\
		_q[2]		= _p[2];				\
		_q[3]		= _p[3];				\
		(P)		= (P) + 4;				\
		(X)		= _t;					\
	} while (0)
#define	READ_ADDR32(P,X)	READ_WORD(P,X)
#define	READ_OFF32(P,X)		READ_WORD(P,X)
#define	READ_SWORD(P,X)		READ_WORD(P,X)
#define	READ_WORD64(P,X)	do {					\
		uint64_t _t;						\
		char *const _q = (char *) &_t;	\
		const char *const _p =				\
			(const char *) (P);			\
		_q[0]		= _p[0];				\
		_q[1]		= _p[1];				\
		_q[2]		= _p[2];				\
		_q[3]		= _p[3];				\
		_q[4]		= _p[4];				\
		_q[5]		= _p[5];				\
		_q[6]		= _p[6];				\
		_q[7]		= _p[7];				\
		(P)		= (P) + 8;				\
		(X)		= _t;					\
	} while (0)
#define	READ_ADDR64(P,X)	READ_WORD64(P,X)
#define	READ_LWORD(P,X)		READ_WORD64(P,X)
#define	READ_OFF64(P,X)		READ_WORD64(P,X)
#define	READ_SXWORD(P,X)	READ_WORD64(P,X)
#define	READ_XWORD(P,X)		READ_WORD64(P,X)
#define	READ_IDENT(P,X)		do {					\
		(void) memcpy((X), (P), sizeof((X)));			\
		(P)		= (P) + EI_NIDENT;			\
	} while (0)

#define	ROUNDUP2(V,N)	(V) = ((((V) + (N) - 1)) & ~((N) - 1))

/*[*/
MAKE_TYPE_CONVERTERS(ELF_TYPE_LIST)
MAKE_VERSION_CONVERTERS(VDEF,Verdef,Verdaux,vd)
MAKE_VERSION_CONVERTERS(VNEED,Verneed,Vernaux,vn)
/*]*/

/*
 * Sections of type ELF_T_BYTE are never byteswapped, consequently a
 * simple memcpy suffices for both directions of conversion.
 */

static int
_libelf_cvt_BYTE_tox(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	(void) byteswap;
	if (dsz < count)
		return (0);
	if (dst != src)
		(void) memcpy(dst, src, count);
	return (1);
}

/*
 * Sections of type ELF_T_GNUHASH start with a header containing 4 32-bit
 * words.  Bloom filter data comes next, followed by hash buckets and the
 * hash chain.
 *
 * Bloom filter words are 64 bit wide on ELFCLASS64 objects and are 32 bit
 * wide on ELFCLASS32 objects.  The other objects in this section are 32
 * bits wide.
 *
 * Argument `srcsz' denotes the number of bytes to be converted.  In the
 * 32-bit case we need to translate `srcsz' to a count of 32-bit words.
 */

static int
_libelf_cvt_GNUHASH32_tom(char *dst, size_t dsz, char *src, size_t srcsz,
    int byteswap)
{
	return (_libelf_cvt_WORD_tom(dst, dsz, src, srcsz / sizeof(uint32_t),
		byteswap));
}

static int
_libelf_cvt_GNUHASH32_tof(char *dst, size_t dsz, char *src, size_t srcsz,
    int byteswap)
{
	return (_libelf_cvt_WORD_tof(dst, dsz, src, srcsz / sizeof(uint32_t),
		byteswap));
}

static int
_libelf_cvt_GNUHASH64_tom(char *dst, size_t dsz, char *src, size_t srcsz,
    int byteswap)
{
	size_t sz;
	uint64_t t64, *bloom64;
	Elf_GNU_Hash_Header *gh;
	uint32_t n, nbuckets, nchains, maskwords, shift2, symndx, t32;
	uint32_t *buckets, *chains;

	sz = 4 * sizeof(uint32_t);	/* File header is 4 words long. */
	if (dsz < sizeof(Elf_GNU_Hash_Header) || srcsz < sz)
		return (0);

	/* Read in the section header and byteswap if needed. */
	READ_WORD(src, nbuckets);
	READ_WORD(src, symndx);
	READ_WORD(src, maskwords);
	READ_WORD(src, shift2);

	srcsz -= sz;

	if (byteswap) {
		SWAP_WORD(nbuckets);
		SWAP_WORD(symndx);
		SWAP_WORD(maskwords);
		SWAP_WORD(shift2);
	}

	/* Check source buffer and destination buffer sizes. */
	sz = nbuckets * sizeof(uint32_t) + maskwords * sizeof(uint64_t);
	if (srcsz < sz || dsz < sz + sizeof(Elf_GNU_Hash_Header))
		return (0);

	gh = (Elf_GNU_Hash_Header *) (uintptr_t) dst;
	gh->gh_nbuckets  = nbuckets;
	gh->gh_symndx    = symndx;
	gh->gh_maskwords = maskwords;
	gh->gh_shift2    = shift2;

	dsz -= sizeof(Elf_GNU_Hash_Header);
	dst += sizeof(Elf_GNU_Hash_Header);

	bloom64 = (uint64_t *) (uintptr_t) dst;

	/* Copy bloom filter data. */
	for (n = 0; n < maskwords; n++) {
		READ_XWORD(src, t64);
		if (byteswap)
			SWAP_XWORD(t64);
		bloom64[n] = t64;
	}

	/* The hash buckets follows the bloom filter. */
	dst += maskwords * sizeof(uint64_t);
	buckets = (uint32_t *) (uintptr_t) dst;

	for (n = 0; n < nbuckets; n++) {
		READ_WORD(src, t32);
		if (byteswap)
			SWAP_WORD(t32);
		buckets[n] = t32;
	}

	dst += nbuckets * sizeof(uint32_t);

	/* The hash chain follows the hash buckets. */
	dsz -= sz;
	srcsz -= sz;

	if (dsz < srcsz)	/* Destination lacks space. */
		return (0);

	nchains = srcsz / sizeof(uint32_t);
	chains = (uint32_t *) (uintptr_t) dst;

	for (n = 0; n < nchains; n++) {
		READ_WORD(src, t32);
		if (byteswap)
			SWAP_WORD(t32);
		*chains++ = t32;
	}

	return (1);
}

static int
_libelf_cvt_GNUHASH64_tof(char *dst, size_t dsz, char *src, size_t srcsz,
    int byteswap)
{
	uint32_t *s32;
	size_t sz, hdrsz;
	uint64_t *s64, t64;
	Elf_GNU_Hash_Header *gh;
	uint32_t maskwords, n, nbuckets, nchains, t0, t1, t2, t3, t32;

	hdrsz = 4 * sizeof(uint32_t);	/* Header is 4x32 bits. */
	if (dsz < hdrsz || srcsz < sizeof(Elf_GNU_Hash_Header))
		return (0);

	gh = (Elf_GNU_Hash_Header *) (uintptr_t) src;

	t0 = nbuckets = gh->gh_nbuckets;
	t1 = gh->gh_symndx;
	t2 = maskwords = gh->gh_maskwords;
	t3 = gh->gh_shift2;

	src   += sizeof(Elf_GNU_Hash_Header);
	srcsz -= sizeof(Elf_GNU_Hash_Header);
	dsz   -= hdrsz;

	sz = gh->gh_nbuckets * sizeof(uint32_t) + gh->gh_maskwords *
	    sizeof(uint64_t);

	if (srcsz < sz || dsz < sz)
		return (0);

	/* Write out the header. */
	if (byteswap) {
		SWAP_WORD(t0);
		SWAP_WORD(t1);
		SWAP_WORD(t2);
		SWAP_WORD(t3);
	}

	WRITE_WORD(dst, t0);
	WRITE_WORD(dst, t1);
	WRITE_WORD(dst, t2);
	WRITE_WORD(dst, t3);

	/* Copy the bloom filter and the hash table. */
	s64 = (uint64_t *) (uintptr_t) src;
	for (n = 0; n < maskwords; n++) {
		t64 = *s64++;
		if (byteswap)
			SWAP_XWORD(t64);
		WRITE_WORD64(dst, t64);
	}

	s32 = (uint32_t *) s64;
	for (n = 0; n < nbuckets; n++) {
		t32 = *s32++;
		if (byteswap)
			SWAP_WORD(t32);
		WRITE_WORD(dst, t32);
	}

	srcsz -= sz;
	dsz   -= sz;

	/* Copy out the hash chains. */
	if (dsz < srcsz)
		return (0);

	nchains = srcsz / sizeof(uint32_t);
	for (n = 0; n < nchains; n++) {
		t32 = *s32++;
		if (byteswap)
			SWAP_WORD(t32);
		WRITE_WORD(dst, t32);
	}

	return (1);
}

/*
 * Elf_Note structures comprise a fixed size header followed by variable
 * length strings.  The fixed size header needs to be byte swapped, but
 * not the strings.
 *
 * Argument `count' denotes the total number of bytes to be converted.
 * The destination buffer needs to be at least `count' bytes in size.
 */
static int
_libelf_cvt_NOTE_tom(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	uint32_t namesz, descsz, type;
	Elf_Note *en;
	size_t sz, hdrsz;

	if (dsz < count)	/* Destination buffer is too small. */
		return (0);

	hdrsz = 3 * sizeof(uint32_t);
	if (count < hdrsz)		/* Source too small. */
		return (0);

	if (!byteswap) {
		(void) memcpy(dst, src, count);
		return (1);
	}

	/* Process all notes in the section. */
	while (count > hdrsz) {
		/* Read the note header. */
		READ_WORD(src, namesz);
		READ_WORD(src, descsz);
		READ_WORD(src, type);

		/* Translate. */
		SWAP_WORD(namesz);
		SWAP_WORD(descsz);
		SWAP_WORD(type);

		/* Copy out the translated note header. */
		en = (Elf_Note *) (uintptr_t) dst;
		en->n_namesz = namesz;
		en->n_descsz = descsz;
		en->n_type = type;

		dsz -= sizeof(Elf_Note);
		dst += sizeof(Elf_Note);
		count -= hdrsz;

		ROUNDUP2(namesz, 4);
		ROUNDUP2(descsz, 4);

		sz = namesz + descsz;

		if (count < sz || dsz < sz)	/* Buffers are too small. */
			return (0);

		(void) memcpy(dst, src, sz);

		src += sz;
		dst += sz;

		count -= sz;
		dsz -= sz;
	}

	return (1);
}

static int
_libelf_cvt_NOTE_tof(char *dst, size_t dsz, char *src, size_t count,
    int byteswap)
{
	uint32_t namesz, descsz, type;
	Elf_Note *en;
	size_t sz;

	if (dsz < count)
		return (0);

	if (!byteswap) {
		(void) memcpy(dst, src, count);
		return (1);
	}

	while (count > sizeof(Elf_Note)) {

		en = (Elf_Note *) (uintptr_t) src;
		namesz = en->n_namesz;
		descsz = en->n_descsz;
		type = en->n_type;

		SWAP_WORD(namesz);
		SWAP_WORD(descsz);
		SWAP_WORD(type);

		WRITE_WORD(dst, namesz);
		WRITE_WORD(dst, descsz);
		WRITE_WORD(dst, type);

		src += sizeof(Elf_Note);

		ROUNDUP2(namesz, 4);
		ROUNDUP2(descsz, 4);

		sz = namesz + descsz;

		if (count < sz)
			sz = count;

		(void) memcpy(dst, src, sz);

		src += sz;
		dst += sz;
		count -= sz;
	}

	return (1);
}

struct converters {
	int	(*tof32)(char *dst, size_t dsz, char *src, size_t cnt,
		    int byteswap);
	int	(*tom32)(char *dst, size_t dsz, char *src, size_t cnt,
		    int byteswap);
	int	(*tof64)(char *dst, size_t dsz, char *src, size_t cnt,
		    int byteswap);
	int	(*tom64)(char *dst, size_t dsz, char *src, size_t cnt,
		    int byteswap);
};


static struct converters cvt[ELF_T_NUM] = {
	/*[*/
CONVERTER_NAMES(ELF_TYPE_LIST)
	/*]*/

	/*
	 * Types that need hand-coded converters follow.
	 */

	[ELF_T_BYTE] = {
		.tof32 = _libelf_cvt_BYTE_tox,
		.tom32 = _libelf_cvt_BYTE_tox,
		.tof64 = _libelf_cvt_BYTE_tox,
		.tom64 = _libelf_cvt_BYTE_tox
	},

	[ELF_T_NOTE] = {
		.tof32 = _libelf_cvt_NOTE_tof,
		.tom32 = _libelf_cvt_NOTE_tom,
		.tof64 = _libelf_cvt_NOTE_tof,
		.tom64 = _libelf_cvt_NOTE_tom
	}
};

int (*_libelf_get_translator(Elf_Type t, int direction, int elfclass))
 (char *_dst, size_t dsz, char *_src, size_t _cnt, int _byteswap)
{
	assert(elfclass == ELFCLASS32 || elfclass == ELFCLASS64);
	assert(direction == ELF_TOFILE || direction == ELF_TOMEMORY);

	if (t >= ELF_T_NUM ||
	    (elfclass != ELFCLASS32 && elfclass != ELFCLASS64) ||
	    (direction != ELF_TOFILE && direction != ELF_TOMEMORY))
		return (NULL);

	return ((elfclass == ELFCLASS32) ?
	    (direction == ELF_TOFILE ? cvt[t].tof32 : cvt[t].tom32) :
	    (direction == ELF_TOFILE ? cvt[t].tof64 : cvt[t].tom64));
}
