/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2004 Sun Microsystems, Inc.  All rights reserved.
 *	Use is subject to license terms.
 */
#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include	<stdio.h>
#include	<dlfcn.h>
#include	<strings.h>
#include	"msg.h"
#include	"_debug.h"
#include	"libld.h"

/*
 * Print out a single `symbol table node' entry.
 */
#if	!defined(_ELF64)
void
Gelf_sym_table_title(GElf_Ehdr *ehdr, const char *index, const char *name)
{
	if ((int)ehdr->e_ident[EI_CLASS] == ELFCLASS64) {
		if (DBG_NOTLONG())
			dbg_print(MSG_ORIG(MSG_SYM_TITLE_64), index, name);
		else
			dbg_print(MSG_ORIG(MSG_SYM_L_TITLE_64), index, name);
	} else {
		if (DBG_NOTLONG())
			dbg_print(MSG_ORIG(MSG_SYM_TITLE), index, name);
		else
			dbg_print(MSG_ORIG(MSG_SYM_L_TITLE), index, name);
	}
}
void
Elf_sym_table_entry(const char *prestr, Elf32_Ehdr *ehdr, Elf32_Sym *sym,
    Elf32_Word verndx, const char *sec, const char *poststr)
{
	const char *msg;

	if (DBG_NOTLONG())
		msg = MSG_ORIG(MSG_SYM_ENTRY);
	else
		msg = MSG_ORIG(MSG_SYM_L_ENTRY);

	dbg_print(msg, prestr,
	    conv_sym_value_str(ehdr->e_machine, ELF32_ST_TYPE(sym->st_info),
	    EC_XWORD(sym->st_value)), EC_XWORD(sym->st_size),
	    conv_info_type_str(ehdr->e_machine, ELF32_ST_TYPE(sym->st_info)),
	    conv_info_bind_str(ELF32_ST_BIND(sym->st_info)),
	    conv_sym_stother(sym->st_other), EC_WORD(verndx),
	    sec ? sec : conv_shndx_str(sym->st_shndx),
	    _Dbg_sym_dem(poststr));
}

void
Gelf_sym_table_entry(const char *prestr, GElf_Ehdr *ehdr, GElf_Sym *sym,
    GElf_Word verndx, const char *sec, const char *poststr)
{
	const char *msg;

	if ((int)ehdr->e_ident[EI_CLASS] == ELFCLASS64) {
		if (DBG_NOTLONG())
			msg = MSG_ORIG(MSG_SYM_ENTRY_64);
		else
			msg = MSG_ORIG(MSG_SYM_L_ENTRY_64);
	} else {
		if (DBG_NOTLONG())
			msg = MSG_ORIG(MSG_SYM_ENTRY);
		else
			msg = MSG_ORIG(MSG_SYM_L_ENTRY);
	}

	dbg_print(msg, prestr, conv_sym_value_str(ehdr->e_machine,
	    GELF_ST_TYPE(sym->st_info), EC_XWORD(sym->st_value)),
	    EC_XWORD(sym->st_size),
	    conv_info_type_str(ehdr->e_machine, GELF_ST_TYPE(sym->st_info)),
	    conv_info_bind_str(GELF_ST_BIND(sym->st_info)),
	    conv_sym_stother(sym->st_other), EC_WORD(verndx),
	    sec ? sec : conv_shndx_str(sym->st_shndx),
	    _Dbg_sym_dem(poststr));
}

void
Gelf_syminfo_title()
{
	dbg_print(MSG_INTL(MSG_SYMI_TITLE2));
}

void
Gelf_syminfo_entry(int ndx, GElf_Syminfo *sip, const char *sname,
    const char *needed)
{
	const char	*bind_str;
	char		flags[16], index[32], bind_index[32] = " ";
	int		flgndx = 0;
	Half		symflags;

	symflags = sip->si_flags;

	if (symflags & SYMINFO_FLG_DIRECT) {
		if (sip->si_boundto == SYMINFO_BT_SELF)
			bind_str = MSG_INTL(MSG_SYMI_SELF);
		else if (sip->si_boundto == SYMINFO_BT_PARENT)
			bind_str = MSG_INTL(MSG_SYMI_PARENT);
		else {
			bind_str = needed;
			(void) sprintf(bind_index, MSG_ORIG(MSG_FMT_INDEX),
				sip->si_boundto);
		}
		flags[flgndx++] = 'D';
		symflags &= ~SYMINFO_FLG_DIRECT;

	} else if (symflags & (SYMINFO_FLG_FILTER | SYMINFO_FLG_AUXILIARY)) {
		bind_str = needed;
		(void) sprintf(bind_index, MSG_ORIG(MSG_FMT_INDEX),
		    sip->si_boundto);

		if (symflags & SYMINFO_FLG_FILTER) {
			flags[flgndx++] = 'F';
			symflags &= ~SYMINFO_FLG_FILTER;
		}
		if (symflags & SYMINFO_FLG_AUXILIARY) {
			flags[flgndx++] = 'A';
			symflags &= ~SYMINFO_FLG_AUXILIARY;
		}
	} else if (sip->si_boundto == SYMINFO_BT_EXTERN) {
		bind_str = MSG_INTL(MSG_SYMI_EXTERN);
	} else
		bind_str = MSG_ORIG(MSG_STR_EMPTY);

	if (symflags & SYMINFO_FLG_DIRECTBIND) {
		flags[flgndx++] = 'B';
		symflags &= ~SYMINFO_FLG_DIRECTBIND;
	}
	if (symflags & SYMINFO_FLG_COPY) {
		flags[flgndx++] = 'C';
		symflags &= ~SYMINFO_FLG_COPY;
	}
	if (symflags & SYMINFO_FLG_LAZYLOAD) {
		flags[flgndx++] = 'L';
		symflags &= ~SYMINFO_FLG_LAZYLOAD;
	}
	if (symflags & SYMINFO_FLG_NOEXTDIRECT) {
		flags[flgndx++] = 'N';
		symflags &= ~SYMINFO_FLG_NOEXTDIRECT;
	}

	/*
	 * Did we account for all of the flags?
	 */
	if (symflags)
		(void) sprintf(&flags[flgndx], " 0x%x", symflags);
	else
		flags[flgndx] = '\0';

	(void) sprintf(index, MSG_ORIG(MSG_FMT_INDEX), ndx);
	dbg_print(MSG_ORIG(MSG_SYMI_FMT), index, flags, bind_index, bind_str,
	    _Dbg_sym_dem(sname));
}


const char *
Gelf_sym_dem(const char *name)
{
	return (conv_sym_dem(name));
}

const char *
_Dbg_sym_dem(const char *name)
{
	if (DBG_NOTCLASS(DBG_DEMANGLE))
		return (name);

	return (conv_sym_dem(name));
}

void
Dbg_syms_ar_title(const char *file, int again)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYM_AR_FILE), file,
	    again ? MSG_INTL(MSG_STR_AGAIN) : MSG_ORIG(MSG_STR_EMPTY));
}

void
Dbg_syms_lazy_rescan(const char *name)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS | DBG_FILES))
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYM_LAZY_RESCAN), _Dbg_sym_dem(name));
}

#endif /* !defined(_ELF64) */

void
Dbg_syms_ar_entry(Xword ndx, Elf_Arsym *arsym)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_AR_ENTRY), EC_XWORD(ndx),
	    _Dbg_sym_dem(arsym->as_name));
}

void
Dbg_syms_ar_checking(Xword ndx, Elf_Arsym *arsym, const char *name)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_AR_CHECK), EC_XWORD(ndx),
	    _Dbg_sym_dem(arsym->as_name), name);
}

void
Dbg_syms_ar_resolve(Xword ndx, Elf_Arsym *arsym, const char *fname, int flag)
{
	const char	*fmt;

	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	if (flag)
		fmt = MSG_INTL(MSG_SYM_AR_FORCEDEXRT);
	else
		fmt = MSG_INTL(MSG_SYM_AR_RESOLVE);

	dbg_print(fmt, EC_XWORD(ndx), _Dbg_sym_dem(arsym->as_name), fname);
}

void
Dbg_syms_spec_title()
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYM_SPECIAL));
}

void
Dbg_syms_discarded(Sym_desc *sdp, Is_desc *disp)
{
	const char	*sectname;
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	if ((sectname = disp->is_basename) == 0)
		sectname = disp->is_name;

	dbg_print(MSG_INTL(MSG_SYM_DISCARDED), _Dbg_sym_dem(sdp->sd_name),
		sectname, disp->is_file->ifl_name);
}


void
Dbg_syms_entered(Ehdr *ehdr, Sym *sym, Sym_desc *sdp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_INTL(MSG_STR_ENTERED), ehdr, sym, sdp->sd_aux ?
	    sdp->sd_aux->sa_overndx : 0, NULL, conv_deftag_str(sdp->sd_ref));
}

void
Dbg_syms_process(Ifl_desc *ifl)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYM_PROCESS), ifl->ifl_name,
	    conv_etype_str(ifl->ifl_ehdr->e_type));
}

void
Dbg_syms_entry(Xword ndx, Sym_desc *sdp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_BASIC), EC_XWORD(ndx),
	    _Dbg_sym_dem(sdp->sd_name));
}

void
Dbg_syms_global(Xword ndx, const char *name)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_ADDING), EC_XWORD(ndx), _Dbg_sym_dem(name));
}

void
Dbg_syms_sec_title()
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYM_INDEX));
}

void
Dbg_syms_sec_entry(int ndx, Sg_desc *sgp, Os_desc *osp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	dbg_print(MSG_INTL(MSG_SYM_SECTION), ndx, osp->os_name,
		(*sgp->sg_name ? sgp->sg_name : MSG_INTL(MSG_STR_NULL)));
}

void
Dbg_syms_up_title(Ehdr *ehdr)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYM_FINAL));
	/* LINTED */
	Gelf_sym_table_title((GElf_Ehdr *)ehdr,
	    MSG_ORIG(MSG_STR_EMPTY), MSG_ORIG(MSG_STR_EMPTY));
}

void
Dbg_syms_ignore(Ehdr *ehdr, Sym_desc *sdp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_INTL(MSG_STR_IGNORE), ehdr, sdp->sd_sym,
	    0, NULL, MSG_INTL(MSG_STR_UNUSED));
}

void
Dbg_syms_old(Ehdr *ehdr, Sym_desc *sdp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_INTL(MSG_STR_OLD), ehdr, sdp->sd_sym,
	    sdp->sd_aux ? sdp->sd_aux->sa_overndx : 0, NULL, sdp->sd_name);
}

void
Dbg_syms_new(Ehdr *ehdr, Sym *sym, Sym_desc *sdp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_INTL(MSG_STR_NEW), ehdr, sym,
	    sdp->sd_aux ? sdp->sd_aux->sa_overndx : 0, NULL,
	    conv_deftag_str(sdp->sd_ref));
}

void
Dbg_syms_updated(Ehdr *ehdr, Sym_desc *sdp, const char *name)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_UPDATE), name);

	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_ORIG(MSG_STR_EMPTY), ehdr, sdp->sd_sym,
	    sdp->sd_aux ? sdp->sd_aux->sa_overndx : 0, NULL,
	    conv_deftag_str(sdp->sd_ref));
}

void
Dbg_syms_created(const char *name)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_CREATE), _Dbg_sym_dem(name));
}

void
Dbg_syms_resolving1(Xword ndx, const char *name, int row, int col)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_RESOLVING), EC_XWORD(ndx),
	    _Dbg_sym_dem(name), row, col);
}

void
Dbg_syms_resolving2(Ehdr *ehdr, Sym *osym, Sym *nsym, Sym_desc *sdp,
	Ifl_desc *ifl)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_INTL(MSG_STR_OLD), ehdr, osym, sdp->sd_aux ?
	    sdp->sd_aux->sa_overndx : 0, NULL, sdp->sd_file->ifl_name);
	Elf_sym_table_entry(MSG_INTL(MSG_STR_NEW), ehdr, nsym, 0, NULL,
	    ifl->ifl_name);
}

void
Dbg_syms_resolved(Ehdr *ehdr, Sym_desc *sdp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_INTL(MSG_STR_RESOLVED), ehdr, sdp->sd_sym,
	    sdp->sd_aux ? sdp->sd_aux->sa_overndx : 0, NULL,
	    conv_deftag_str(sdp->sd_ref));
}

void
Dbg_syms_nl()
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
}

static Boolean	symbol_title = TRUE;

static void
_Dbg_syms_reloc_title()
{
	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYM_BSS));

	symbol_title = FALSE;
}
void
Dbg_syms_reloc(Ehdr *ehdr, Sym_desc *sdp)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	if (symbol_title)
		_Dbg_syms_reloc_title();
	dbg_print(MSG_INTL(MSG_SYM_UPDATE), _Dbg_sym_dem(sdp->sd_name));

	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_ORIG(MSG_SYM_COPY), ehdr, sdp->sd_sym,
	    sdp->sd_aux ? sdp->sd_aux->sa_overndx : 0, NULL,
	    conv_deftag_str(sdp->sd_ref));
}

void
Dbg_syms_lookup_aout(const char *name)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_AOUT), _Dbg_sym_dem(name));
}

void
Dbg_syms_lookup(const char *name, const char *file, const char *type)
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	dbg_print(MSG_INTL(MSG_SYM_LOOKUP), _Dbg_sym_dem(name), file, type);
}

void
Dbg_syms_dlsym(const char *sym, const char *from, const char *next, int flag)
{
	const char	*str;

	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;

	if (flag == DBG_DLSYM_NEXT)
		str = MSG_ORIG(MSG_SYM_NEXT);
	else if (flag == DBG_DLSYM_DEFAULT)
		str = MSG_ORIG(MSG_SYM_DEFAULT);
	else if (flag == DBG_DLSYM_SELF)
		str = MSG_ORIG(MSG_SYM_SELF);
	else if (flag == DBG_DLSYM_PROBE)
		str = MSG_ORIG(MSG_SYM_PROBE);
	else
		str = MSG_ORIG(MSG_STR_EMPTY);

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	if (next == 0)
		dbg_print(MSG_INTL(MSG_SYM_DLSYM_1), _Dbg_sym_dem(sym),
		    from, str);
	else
		dbg_print(MSG_INTL(MSG_SYM_DLSYM_2), _Dbg_sym_dem(sym),
		    from, next, str);
}

void
Dbg_syms_reduce(int which, Ehdr *ehdr, Sym_desc *sdp,
    int idx, const char *sname)
{
	static Boolean	sym_reduce_title = TRUE;
	static Boolean	sym_retain_title = TRUE;
	Boolean		isfromglobal = (which == DBG_SYM_REDUCE_GLOBAL);
	Boolean		isfromretain = (which == DBG_SYM_REDUCE_RETAIN);

	if (DBG_NOTCLASS(DBG_SYMBOLS | DBG_VERSIONS))
		return;

	if (sym_reduce_title && isfromglobal) {
		sym_reduce_title = FALSE;
		dbg_print(MSG_ORIG(MSG_STR_EMPTY));
		dbg_print(MSG_INTL(MSG_SYM_REDUCED));
	} else if (sym_retain_title && isfromretain) {
		sym_retain_title = FALSE;
		dbg_print(MSG_ORIG(MSG_STR_EMPTY));
		dbg_print(MSG_INTL(MSG_SYM_RETAINING));
	}

	if ((sdp->sd_flags1 & FLG_SY1_ELIM) && isfromglobal)
		dbg_print(MSG_INTL(MSG_SYM_ELIMINATING),
		    _Dbg_sym_dem(sdp->sd_name));
	else if (isfromglobal)
		dbg_print(MSG_INTL(MSG_SYM_REDUCING),
		    _Dbg_sym_dem(sdp->sd_name));
	else
		dbg_print(MSG_INTL(MSG_SYM_NOTELIMINATE),
		    _Dbg_sym_dem(sdp->sd_name), sname, idx);

	if (DBG_NOTDETAIL())
		return;

	Elf_sym_table_entry(MSG_ORIG(MSG_SYM_LOCAL), ehdr, sdp->sd_sym,
	    sdp->sd_aux ? sdp->sd_aux->sa_overndx : 0, NULL,
	    sdp->sd_file->ifl_name);
}


void
Dbg_syminfo_title()
{
	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	dbg_print(MSG_ORIG(MSG_STR_EMPTY));
	dbg_print(MSG_INTL(MSG_SYMI_TITLE1));
	Gelf_syminfo_title();
}

void
Dbg_syminfo_entry(int ndx, Syminfo *sip, Sym *sym, const char *strtab,
	Dyn *dyn)
{
	const char	*needed;

	if (DBG_NOTCLASS(DBG_SYMBOLS))
		return;
	if (DBG_NOTDETAIL())
		return;

	if (sip->si_boundto < SYMINFO_BT_LOWRESERVE)
		needed = strtab + dyn[sip->si_boundto].d_un.d_val;
	else
		needed = 0;

	Gelf_syminfo_entry(ndx, (GElf_Syminfo *)sip,
	    _Dbg_sym_dem(strtab + sym->st_name), needed);
}