/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * String conversion routines for ELF header attributes.
 */
#include	<stdio.h>
#include	<string.h>
#include	"_conv.h"
#include	"elf_msg.h"
#include	<sys/elf_SPARC.h>



/* Instantiate a local copy of conv_map2str() from _conv.h */
DEFINE_conv_map2str



const char *
conv_ehdr_class(uchar_t class, Conv_fmt_flags_t fmt_flags,
    Conv_inv_buf_t *inv_buf)
{
	static const Msg	classes[] = {
		MSG_ELFCLASSNONE, MSG_ELFCLASS32, MSG_ELFCLASS64
	};
	static const Msg	classes_alt[] = {
		MSG_ELFCLASSNONE_ALT, MSG_ELFCLASS32_ALT, MSG_ELFCLASS64_ALT
	};

	/* Use alternative strings? */
	switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
	case CONV_FMT_ALT_DUMP:
	case CONV_FMT_ALT_FILE:
		return (conv_map2str(inv_buf, class, fmt_flags,
		    ARRAY_NELTS(classes_alt), classes_alt));
	}

	/* Use default strings */
	return (conv_map2str(inv_buf, class, fmt_flags,
	    ARRAY_NELTS(classes), classes));
}

const char *
conv_ehdr_data(uchar_t data, Conv_fmt_flags_t fmt_flags,
    Conv_inv_buf_t *inv_buf)
{
	static const Msg	datas[] = {
		MSG_ELFDATANONE, MSG_ELFDATA2LSB, MSG_ELFDATA2MSB
	};
	static const Msg	datas_dump[] = {
		MSG_ELFDATANONE_ALT, MSG_ELFDATA2LSB_ALT1, MSG_ELFDATA2MSB_ALT1
	};
	static const Msg	datas_file[] = {
		MSG_ELFDATANONE_ALT, MSG_ELFDATA2LSB_ALT2, MSG_ELFDATA2MSB_ALT2
	};

	/* Use alternative strings? */
	switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
	case CONV_FMT_ALT_DUMP:
		return (conv_map2str(inv_buf, data, fmt_flags,
		    ARRAY_NELTS(datas_dump), datas_dump));
	case CONV_FMT_ALT_FILE:
		return (conv_map2str(inv_buf, data, fmt_flags,
		    ARRAY_NELTS(datas_file), datas_file));
	}

	/* Use default strings */
	return (conv_map2str(inv_buf, data, fmt_flags,
	    ARRAY_NELTS(datas), datas));
}

static const Msg machines[EM_NUM] = {
	MSG_EM_NONE,		MSG_EM_M32,		MSG_EM_SPARC,
	MSG_EM_386,		MSG_EM_68K,		MSG_EM_88K,
	MSG_EM_486,		MSG_EM_860,		MSG_EM_MIPS,
	MSG_EM_S370,		MSG_EM_MIPS_RS3_LE,	MSG_EM_RS6000,
	MSG_EM_UNKNOWN12,	MSG_EM_UNKNOWN13,	MSG_EM_UNKNOWN14,
	MSG_EM_PA_RISC,		MSG_EM_nCUBE,		MSG_EM_VPP500,
	MSG_EM_SPARC32PLUS,	MSG_EM_960,		MSG_EM_PPC,
	MSG_EM_PPC64,		MSG_EM_S390,		MSG_EM_UNKNOWN23,
	MSG_EM_UNKNOWN24,	MSG_EM_UNKNOWN25,	MSG_EM_UNKNOWN26,
	MSG_EM_UNKNOWN27,	MSG_EM_UNKNOWN28,	MSG_EM_UNKNOWN29,
	MSG_EM_UNKNOWN30,	MSG_EM_UNKNOWN31,	MSG_EM_UNKNOWN32,
	MSG_EM_UNKNOWN33,	MSG_EM_UNKNOWN34,	MSG_EM_UNKNOWN35,
	MSG_EM_V800,		MSG_EM_FR20,		MSG_EM_RH32,
	MSG_EM_RCE,		MSG_EM_ARM,		MSG_EM_ALPHA,
	MSG_EM_SH,		MSG_EM_SPARCV9,		MSG_EM_TRICORE,
	MSG_EM_ARC,		MSG_EM_H8_300,		MSG_EM_H8_300H,
	MSG_EM_H8S,		MSG_EM_H8_500,		MSG_EM_IA_64,
	MSG_EM_MIPS_X,		MSG_EM_COLDFIRE,	MSG_EM_68HC12,
	MSG_EM_MMA,		MSG_EM_PCP,		MSG_EM_NCPU,
	MSG_EM_NDR1,		MSG_EM_STARCORE,	MSG_EM_ME16,
	MSG_EM_ST100,		MSG_EM_TINYJ,		MSG_EM_AMD64,
	MSG_EM_PDSP,		MSG_EM_UNKNOWN64,	MSG_EM_UNKNOWN65,
	MSG_EM_FX66,		MSG_EM_ST9PLUS,		MSG_EM_ST7,
	MSG_EM_68HC16,		MSG_EM_68HC11,		MSG_EM_68HC08,
	MSG_EM_68HC05,		MSG_EM_SVX,		MSG_EM_ST19,
	MSG_EM_VAX,		MSG_EM_CRIS,		MSG_EM_JAVELIN,
	MSG_EM_FIREPATH,	MSG_EM_ZSP,		MSG_EM_MMIX,
	MSG_EM_HUANY,		MSG_EM_PRISM,		MSG_EM_AVR,
	MSG_EM_FR30,		MSG_EM_D10V,		MSG_EM_D30V,
	MSG_EM_V850,		MSG_EM_M32R,		MSG_EM_MN10300,
	MSG_EM_MN10200,		MSG_EM_PJ,		MSG_EM_OPENRISC,
	MSG_EM_ARC_A5,		MSG_EM_XTENSA
};
static const Msg machines_alt[EM_NUM] = {
	MSG_EM_NONE_ALT,	MSG_EM_M32_ALT,		MSG_EM_SPARC_ALT,
	MSG_EM_386_ALT,		MSG_EM_68K_ALT,		MSG_EM_88K_ALT,
	MSG_EM_486_ALT,		MSG_EM_860_ALT,		MSG_EM_MIPS_ALT,
	MSG_EM_S370,		MSG_EM_MIPS_RS3_LE_ALT,	MSG_EM_RS6000_ALT,
	MSG_EM_UNKNOWN12,	MSG_EM_UNKNOWN13,	MSG_EM_UNKNOWN14,
	MSG_EM_PA_RISC_ALT,	MSG_EM_nCUBE_ALT,	MSG_EM_VPP500_ALT,
	MSG_EM_SPARC32PLUS_ALT,	MSG_EM_960,		MSG_EM_PPC_ALT,
	MSG_EM_PPC64_ALT,	MSG_EM_S390,		MSG_EM_UNKNOWN23,
	MSG_EM_UNKNOWN24,	MSG_EM_UNKNOWN25,	MSG_EM_UNKNOWN26,
	MSG_EM_UNKNOWN27,	MSG_EM_UNKNOWN28,	MSG_EM_UNKNOWN29,
	MSG_EM_UNKNOWN30,	MSG_EM_UNKNOWN31,	MSG_EM_UNKNOWN32,
	MSG_EM_UNKNOWN33,	MSG_EM_UNKNOWN34,	MSG_EM_UNKNOWN35,
	MSG_EM_V800,		MSG_EM_FR20,		MSG_EM_RH32,
	MSG_EM_RCE,		MSG_EM_ARM_ALT,		MSG_EM_ALPHA_ALT,
	MSG_EM_SH,		MSG_EM_SPARCV9_ALT,	MSG_EM_TRICORE,
	MSG_EM_ARC,		MSG_EM_H8_300,		MSG_EM_H8_300H,
	MSG_EM_H8S,		MSG_EM_H8_500,		MSG_EM_IA_64_ALT,
	MSG_EM_MIPS_X,		MSG_EM_COLDFIRE,	MSG_EM_68HC12,
	MSG_EM_MMA,		MSG_EM_PCP,		MSG_EM_NCPU,
	MSG_EM_NDR1,		MSG_EM_STARCORE,	MSG_EM_ME16,
	MSG_EM_ST100,		MSG_EM_TINYJ,		MSG_EM_AMD64_ALT,
	MSG_EM_PDSP,		MSG_EM_UNKNOWN64,	MSG_EM_UNKNOWN65,
	MSG_EM_FX66,		MSG_EM_ST9PLUS,		MSG_EM_ST7,
	MSG_EM_68HC16,		MSG_EM_68HC11,		MSG_EM_68HC08,
	MSG_EM_68HC05,		MSG_EM_SVX,		MSG_EM_ST19,
	MSG_EM_VAX_ALT,		MSG_EM_CRIS,		MSG_EM_JAVELIN,
	MSG_EM_FIREPATH,	MSG_EM_ZSP,		MSG_EM_MMIX,
	MSG_EM_HUANY,		MSG_EM_PRISM,		MSG_EM_AVR,
	MSG_EM_FR30,		MSG_EM_D10V,		MSG_EM_D30V,
	MSG_EM_V850,		MSG_EM_M32R,		MSG_EM_MN10300,
	MSG_EM_MN10200,		MSG_EM_PJ,		MSG_EM_OPENRISC,
	MSG_EM_ARC_A5,		MSG_EM_XTENSA
};
#if	(EM_NUM != (EM_XTENSA + 1))
#error	"EM_NUM has grown"
#endif

const char *
conv_ehdr_mach(Half machine, Conv_fmt_flags_t fmt_flags,
    Conv_inv_buf_t *inv_buf)
{
	/* Use alternative strings? */
	switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
	case CONV_FMT_ALT_DUMP:
	case CONV_FMT_ALT_FILE:
		return (conv_map2str(inv_buf, machine, fmt_flags,
		    ARRAY_NELTS(machines_alt), machines_alt));
	}

	/* Use default strings */
	return (conv_map2str(inv_buf, machine, fmt_flags,
	    ARRAY_NELTS(machines), machines));
}


const char *
conv_ehdr_type(Half etype, Conv_fmt_flags_t fmt_flags, Conv_inv_buf_t *inv_buf)
{
	static const Msg	etypes[] = {
		MSG_ET_NONE,		MSG_ET_REL,		MSG_ET_EXEC,
		MSG_ET_DYN,		MSG_ET_CORE
	};
	static const Msg	etypes_alt[] = {
		MSG_ET_NONE_ALT,	MSG_ET_REL_ALT,		MSG_ET_EXEC_ALT,
		MSG_ET_DYN_ALT,		MSG_ET_CORE_ALT
	};

	if (etype == ET_SUNWPSEUDO) {
		switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
		case CONV_FMT_ALT_DUMP:
		case CONV_FMT_ALT_FILE:
			return (MSG_ORIG(MSG_ET_SUNWPSEUDO_ALT));
		default:
			return (MSG_ORIG(MSG_ET_SUNWPSEUDO));
		}
	}

	/* Use alternative strings? */
	switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
	case CONV_FMT_ALT_DUMP:
	case CONV_FMT_ALT_FILE:
		return (conv_map2str(inv_buf, etype, fmt_flags,
		    ARRAY_NELTS(etypes_alt), etypes_alt));
	}

	/* Use default strings */
	return (conv_map2str(inv_buf, etype, fmt_flags,
	    ARRAY_NELTS(etypes), etypes));

}

const char *
conv_ehdr_vers(Word version, Conv_fmt_flags_t fmt_flags,
    Conv_inv_buf_t *inv_buf)
{
	static const Msg	versions[] = {
		MSG_EV_NONE,		MSG_EV_CURRENT
	};
	static const Msg	versions_alt[] = {
		MSG_EV_NONE_ALT,	MSG_EV_CURRENT_ALT
	};

	/* Use alternative strings? */
	switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
	case CONV_FMT_ALT_DUMP:
	case CONV_FMT_ALT_FILE:
		return (conv_map2str(inv_buf, version, fmt_flags,
		    ARRAY_NELTS(versions_alt), versions_alt));
	}

	/* Use default strings */
	return (conv_map2str(inv_buf, version, fmt_flags,
	    ARRAY_NELTS(versions), versions));
}

#define	EFLAGSZ	CONV_EXPN_FIELD_DEF_PREFIX_SIZE + \
		MSG_EF_SPARCV9_TSO_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE +  \
		MSG_EF_SPARC_SUN_US1_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE +  \
		MSG_EF_SPARC_HAL_R1_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE +  \
		MSG_EF_SPARC_SUN_US3_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE +  \
		CONV_INV_BUFSIZE + CONV_EXPN_FIELD_DEF_SUFFIX_SIZE

/*
 * Ensure that Conv_ehdr_flags_buf_t is large enough:
 *
 * EFLAGSZ is the real minimum size of the buffer required by conv_ehdr_flags().
 * However, Conv_ehdr_flags_buf_t uses CONV_EHDR_FLAG_BUFSIZE to set the
 * buffer size. We do things this way because the definition of EFLAGSZ uses
 * information that is not available in the environment of other programs
 * that include the conv.h header file.
 */
#if (CONV_EHDR_FLAGS_BUFSIZE != EFLAGSZ) && !defined(__lint)
#define	REPORT_BUFSIZE EFLAGSZ
#include "report_bufsize.h"
#error "CONV_EHDR_FLAGS_BUFSIZE does not match EFLAGSZ"
#endif

/*
 * Make a string representation of the e_flags field.
 */
const char *
conv_ehdr_flags(Half mach, Word flags, Conv_fmt_flags_t fmt_flags,
    Conv_ehdr_flags_buf_t *flags_buf)
{
	static Val_desc vda[] = {
		{ EF_SPARC_32PLUS,	MSG_ORIG(MSG_EF_SPARC_32PLUS) },
		{ EF_SPARC_SUN_US1,	MSG_ORIG(MSG_EF_SPARC_SUN_US1) },
		{ EF_SPARC_HAL_R1,	MSG_ORIG(MSG_EF_SPARC_HAL_R1) },
		{ EF_SPARC_SUN_US3,	MSG_ORIG(MSG_EF_SPARC_SUN_US3) },
		{ 0,			0 }
	};
	static const Msg mm_flags[] = {
		MSG_EF_SPARCV9_TSO,	MSG_EF_SPARCV9_PSO,
		MSG_EF_SPARCV9_RMO
	};
	static const char *leading_str_arr[2];
	static CONV_EXPN_FIELD_ARG conv_arg = {
	    NULL, sizeof (flags_buf->buf), vda, leading_str_arr };

	const char **lstr = leading_str_arr;

	conv_arg.buf = flags_buf->buf;

	/*
	 * Non-SPARC architectures presently provide no known flags.
	 */
	if ((mach == EM_SPARCV9) || (((mach == EM_SPARC) ||
	    (mach == EM_SPARC32PLUS)) && flags)) {
		/*
		 * Valid vendor extension bits for SPARCV9.  These must be
		 * updated along with elf_SPARC.h.
		 */

		conv_arg.oflags = conv_arg.rflags = flags;
		if ((mach == EM_SPARCV9) && (flags <= EF_SPARCV9_RMO)) {
			*lstr++ = MSG_ORIG(mm_flags[flags & EF_SPARCV9_MM]);
			conv_arg.rflags &= ~EF_SPARCV9_MM;
		}
		*lstr = NULL;

		(void) conv_expn_field(&conv_arg, fmt_flags);

		return (conv_arg.buf);
	}

	return (conv_invalid_val(&flags_buf->inv_buf, flags, CONV_FMT_DECIMAL));
}

/*
 * Make a string representation of the e_ident[EI_OSABI] field.
 */
const char *
conv_ehdr_osabi(uchar_t osabi, Conv_fmt_flags_t fmt_flags,
    Conv_inv_buf_t *inv_buf)
{
	static const Msg	osabi_arr[] = {
		MSG_OSABI_NONE,		MSG_OSABI_HPUX,
		MSG_OSABI_NETBSD,	MSG_OSABI_LINUX,
		MSG_OSABI_UNKNOWN4,	MSG_OSABI_UNKNOWN5,
		MSG_OSABI_SOLARIS,	MSG_OSABI_AIX,
		MSG_OSABI_IRIX,		MSG_OSABI_FREEBSD,
		MSG_OSABI_TRU64,	MSG_OSABI_MODESTO,
		MSG_OSABI_OPENBSD,	MSG_OSABI_OPENVMS,
		MSG_OSABI_NSK,		MSG_OSABI_AROS
	};
	static const Msg	osabi_arr_alt[] = {
		MSG_OSABI_NONE_ALT,	MSG_OSABI_HPUX_ALT,
		MSG_OSABI_NETBSD_ALT,	MSG_OSABI_LINUX_ALT,
		MSG_OSABI_UNKNOWN4,	MSG_OSABI_UNKNOWN5,
		MSG_OSABI_SOLARIS_ALT,	MSG_OSABI_AIX_ALT,
		MSG_OSABI_IRIX_ALT,	MSG_OSABI_FREEBSD_ALT,
		MSG_OSABI_TRU64_ALT,	MSG_OSABI_MODESTO_ALT,
		MSG_OSABI_OPENBSD_ALT,	MSG_OSABI_OPENVMS_ALT,
		MSG_OSABI_NSK_ALT,	MSG_OSABI_AROS_ALT
	};

	const char *str;

	switch (osabi) {
	case ELFOSABI_ARM:
		switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
		case CONV_FMT_ALT_DUMP:
		case CONV_FMT_ALT_FILE:
			str = MSG_ORIG(MSG_OSABI_ARM_ALT);
			break;
		default:
			str = MSG_ORIG(MSG_OSABI_ARM);
		}
		break;

	case ELFOSABI_STANDALONE:
		switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
		case CONV_FMT_ALT_DUMP:
		case CONV_FMT_ALT_FILE:
			str = MSG_ORIG(MSG_OSABI_STANDALONE_ALT);
			break;
		default:
			str = MSG_ORIG(MSG_OSABI_STANDALONE);
		}
		break;

	default:
		switch (CONV_TYPE_FMT_ALT(fmt_flags)) {
		case CONV_FMT_ALT_DUMP:
		case CONV_FMT_ALT_FILE:
			str = conv_map2str(inv_buf, osabi, fmt_flags,
			    ARRAY_NELTS(osabi_arr_alt), osabi_arr_alt);
			break;
		default:
			str = conv_map2str(inv_buf, osabi, fmt_flags,
			    ARRAY_NELTS(osabi_arr), osabi_arr);
		}
		break;
	}

	return (str);
}

/*
 * A generic means of returning additional information for a rejected file in
 * terms of a string.
 */
const char *
conv_reject_desc(Rej_desc * rej, Conv_reject_desc_buf_t *reject_desc_buf,
    Half mach)
{
	ushort_t	type = rej->rej_type;
	uint_t		info = rej->rej_info;

	switch (type) {
	case SGS_REJ_MACH:
		return (conv_ehdr_mach((Half)info, 0,
		    &reject_desc_buf->inv_buf));
	case SGS_REJ_CLASS:
		return (conv_ehdr_class((uchar_t)info, 0,
		    &reject_desc_buf->inv_buf));
	case SGS_REJ_DATA:
		return (conv_ehdr_data((uchar_t)info, 0,
		    &reject_desc_buf->inv_buf));
	case SGS_REJ_TYPE:
		return (conv_ehdr_type((Half)info, 0,
		    &reject_desc_buf->inv_buf));
	case SGS_REJ_BADFLAG:
	case SGS_REJ_MISFLAG:
	case SGS_REJ_HAL:
	case SGS_REJ_US3:
		return (conv_ehdr_flags(mach, (Word)info, 0,
		    &reject_desc_buf->flags_buf));
	case SGS_REJ_UNKFILE:
		return ((const char *)0);
	case SGS_REJ_STR:
	case SGS_REJ_HWCAP_1:
	case SGS_REJ_SFCAP_1:
		if (rej->rej_str)
			return ((const char *)rej->rej_str);
		else
			return (MSG_ORIG(MSG_STR_EMPTY));
	default:
		return (conv_invalid_val(&reject_desc_buf->inv_buf, info,
		    CONV_FMT_DECIMAL));
	}
}