/*-
 * Copyright (c) 2006 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/types.h>

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

#include "elf32.h"
#include "elf64.h"
#include "libelf.h"
#include "_libelf.h"

/* WARNING: GENERATED FROM __file__. */

/*
 * Macros to swap various integral quantities.
 */

#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)

/*
 * Write out various integral values.  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 {						\
		unsigned char *const _p = (unsigned char *) (P);	\
		_p[0]		= (unsigned char) (X);			\
		(P)		= _p + 1;				\
	} while (0)
#define	WRITE_HALF(P,X)	do {						\
		uint16_t _t	= (X);					\
		unsigned char *const _p	= (unsigned char *) (P);	\
		unsigned const char *const _q = (unsigned 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);					\
		unsigned char *const _p	= (unsigned char *) (P);	\
		unsigned const char *const _q = (unsigned 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);					\
		unsigned char *const _p	= (unsigned char *) (P);	\
		unsigned const char *const _q = (unsigned 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)

/*
 * Read in various integral values.  The source pointer could be
 * unaligned.  Values are read in in native byte order.  The source
 * pointer is incremented appropriately.
 */

#define	READ_BYTE(P,X)	do {						\
		const unsigned char *const _p =				\
			(const unsigned char *) (P);			\
		(X)		= _p[0];				\
		(P)		= (P) + 1;				\
	} while (0)
#define	READ_HALF(P,X)	do {						\
		uint16_t _t;						\
		unsigned char *const _q = (unsigned char *) &_t;	\
		const unsigned char *const _p =				\
			(const unsigned 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;						\
		unsigned char *const _q = (unsigned char *) &_t;	\
		const unsigned char *const _p =				\
			(const unsigned 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;						\
		unsigned char *const _q = (unsigned char *) &_t;	\
		const unsigned char *const _p =				\
			(const unsigned 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))

divert(-1)

/*
 * Generate conversion routines for converting between in-memory and
 * file representations of Elf data structures.
 *
 * `In-memory' representations of an Elf data structure use natural
 * alignments and native byte ordering.  This allows arithmetic and
 * casting to work as expected.  On the other hand the `file'
 * representation of an ELF data structure could be packed tighter
 * than its `in-memory' representation, and could be of a differing
 * byte order.  An additional complication is that `ar' only pads data
 * to even addresses and so ELF archive member data being read from
 * inside an `ar' archive could end up at misaligned memory addresses.
 *
 * Consequently, 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.
 */

include(SRCDIR`/elf_types.m4')

/*
 * `IGNORE'_* flags turn off generation of template code.
 */

define(`IGNORE',
  `define(IGNORE_$1`'32,	1)
   define(IGNORE_$1`'64,	1)')

IGNORE(MOVEP)
IGNORE(NOTE)

define(IGNORE_BYTE,		1)	/* 'lator, leave 'em bytes alone */
define(IGNORE_NOTE,		1)
define(IGNORE_SXWORD32,		1)
define(IGNORE_XWORD32,		1)

/*
 * `BASE'_XXX flags cause class agnostic template functions
 * to be generated.
 */

define(`BASE_BYTE',	1)
define(`BASE_HALF',	1)
define(`BASE_NOTE',	1)
define(`BASE_WORD',	1)
define(`BASE_LWORD',	1)
define(`BASE_SWORD',	1)
define(`BASE_XWORD',	1)
define(`BASE_SXWORD',	1)

/*
 * `SIZEDEP'_XXX flags cause 32/64 bit variants to be generated
 * for each primitive type.
 */

define(`SIZEDEP_ADDR',	1)
define(`SIZEDEP_OFF',	1)

/*
 * `Primitive' ELF types are those that are an alias for an integral
 * type.  They have no internal structure. These can be copied using
 * a `memcpy()', and byteswapped in straightforward way.
 *
 * Macro use:
 * `$1': Name of the ELF type.
 * `$2': C structure name suffix
 * `$3': ELF class specifier for symbols, one of [`', `32', `64']
 * `$4': ELF class specifier for types, one of [`32', `64']
 */
define(`MAKEPRIM_TO_F',`
static void
libelf_cvt_$1$3_tof(char *dst, char *src, size_t count, int byteswap)
{
	Elf$4_$2 t, *s = (Elf$4_$2 *) (uintptr_t) src;
	size_t c;

	if (dst == src && !byteswap)
		return;

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

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

define(`MAKEPRIM_TO_M',`
static void
libelf_cvt_$1$3_tom(char *dst, char *src, size_t count, int byteswap)
{
	Elf$4_$2 t, *d = (Elf$4_$2 *) (uintptr_t) dst;
	size_t c;

	if (dst == src && !byteswap)
		return;

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

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

define(`SWAP_FIELD',
  `ifdef(`IGNORE_'$2,`',
    `ifelse(BASE_$2,1,
      `SWAP_$2(t.$1);
			',
      `ifelse($2,BYTE,`',
        `ifelse($2,IDENT,`',
          `SWAP_$2'SZ()`(t.$1);
			')')')')')
define(`SWAP_MEMBERS',
  `ifelse($#,1,`/**/',
     `SWAP_FIELD($1)SWAP_MEMBERS(shift($@))')')

define(`SWAP_STRUCT',
  `pushdef(`SZ',$2)/* Swap an Elf$2_$1 */
			SWAP_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')

define(`WRITE_FIELD',
  `ifelse(BASE_$2,1,
    `WRITE_$2(dst,t.$1);
		',
    `ifelse($2,IDENT,
      `WRITE_$2(dst,t.$1);
		',
      `WRITE_$2'SZ()`(dst,t.$1);
		')')')
define(`WRITE_MEMBERS',
  `ifelse($#,1,`/**/',
    `WRITE_FIELD($1)WRITE_MEMBERS(shift($@))')')

define(`WRITE_STRUCT',
  `pushdef(`SZ',$2)/* Write an Elf$2_$1 */
		WRITE_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')

define(`READ_FIELD',
  `ifelse(BASE_$2,1,
    `READ_$2(s,t.$1);
		',
    `ifelse($2,IDENT,
      `READ_$2(s,t.$1);
		',
      `READ_$2'SZ()`(s,t.$1);
		')')')

define(`READ_MEMBERS',
  `ifelse($#,1,`/**/',
    `READ_FIELD($1)READ_MEMBERS(shift($@))')')

define(`READ_STRUCT',
  `pushdef(`SZ',$2)/* Read an Elf$2_$1 */
		READ_MEMBERS(Elf$2_$1_DEF)popdef(`SZ')')

/*
 * Converters for non-integral ELF data 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(`MAKE_TO_F',
  `ifdef(`IGNORE_'$1$3,`',`
static void
libelf_cvt$3_$1_tof(char *dst, char *src, size_t count, int byteswap)
{
	Elf$3_$2	t, *s;
	size_t c;

	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)
	}
}
')')

define(`MAKE_TO_M',
  `ifdef(`IGNORE_'$1$3,`',`
static void
libelf_cvt$3_$1_tom(char *dst, char *src, size_t count, int byteswap)
{
	Elf$3_$2	 t, *d;
	unsigned 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  = (unsigned char *) src + (count - 1) * fsz;

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

/*
 * Make type convertor functions from the type definition
 * of the ELF type:
 * - if the type is a base (i.e., `primitive') type:
 *   - if it is marked as to be ignored (i.e., `IGNORE_'TYPE)
 *     is defined, we skip the code generation step.
 *   - if the type is declared as `SIZEDEP', then 32 and 64 bit
 *     variants of the conversion functions are generated.
 *   - otherwise a 32 bit variant is generated.
 * - if the type is a structure type, we generate 32 and 64 bit
 *   variants of the conversion functions.
 */

define(`MAKE_TYPE_CONVERTER',
  `ifdef(`BASE'_$1,
    `ifdef(`IGNORE_'$1,`',
      `MAKEPRIM_TO_F($1,$2,`',64)
       MAKEPRIM_TO_M($1,$2,`',64)')',
    `ifdef(`SIZEDEP_'$1,
      `MAKEPRIM_TO_F($1,$2,32,32)dnl
       MAKEPRIM_TO_M($1,$2,32,32)dnl
       MAKEPRIM_TO_F($1,$2,64,64)dnl
       MAKEPRIM_TO_M($1,$2,64,64)',
      `MAKE_TO_F($1,$2,32)dnl
       MAKE_TO_F($1,$2,64)dnl
       MAKE_TO_M($1,$2,32)dnl
       MAKE_TO_M($1,$2,64)')')
')

define(`MAKE_TYPE_CONVERTERS',
  `ifelse($#,1,`',
    `MAKE_TYPE_CONVERTER($1)MAKE_TYPE_CONVERTERS(shift($@))')')

divert(0)

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

static void
libelf_cvt_BYTE_tox(char *dst, char *src, size_t count, int byteswap)
{
	(void) byteswap;
	if (dst != src)
		(void) memcpy(dst, src, count);
}

/*
 * 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.
 */
static void
libelf_cvt_NOTE_tom(char *dst, char *src, size_t count, int byteswap)
{
	uint32_t namesz, descsz, type;
	Elf_Note *en;
	size_t sz;

	if (dst == src && !byteswap)
		return;

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

	while (count > sizeof(Elf_Note)) {

		READ_WORD(src, namesz);
		READ_WORD(src, descsz);
		READ_WORD(src, type);

		if (byteswap) {
			SWAP_WORD(namesz);
			SWAP_WORD(descsz);
			SWAP_WORD(type);
		}

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

		dst += 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;
	}
}

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

	if (dst == src && !byteswap)
		return;

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

	while (count > sizeof(Elf_Note)) {

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

		if (byteswap) {
			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;
	}
}

MAKE_TYPE_CONVERTERS(ELF_TYPE_LIST)

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

divert(-1)
define(`CONV',
  `ifdef(`IGNORE_'$1$2,
    `.$3$2 = NULL',
    `ifdef(`BASE_'$1,
      `ifdef(`IGNORE_'$1,
	`.$3$2 = NULL',
        `.$3$2 = libelf_cvt_$1_$3')',
      `ifdef(`SIZEDEP_'$1,
        `.$3$2 = libelf_cvt_$1$2_$3',
        `.$3$2 = libelf_cvt$2_$1_$3')')')')

define(`CONVERTER_NAME',
  `[ELF_T_$1] = {
        CONV($1,32,tof), CONV($1,32,tom),
        CONV($1,64,tof), CONV($1,64,tom) },')')

define(`CONVERTER_NAMES',
  `ifelse($#,1,`',
    `CONVERTER_NAME($1)CONVERTER_NAMES(shift($@))')')

undefine(`IGNORE_BYTE32')
undefine(`IGNORE_BYTE64')
divert(0)

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

	/*
	 * Types that needs 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
	}
};

void (*_libelf_get_translator(Elf_Type t, int direction, int elfclass))
 (char *_dst, 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));
}
