/*
 * 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 (c) 1988 AT&T
 *	  All Rights Reserved
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include	<stdio.h>
#include	<string.h>
#include	"msg.h"
#include	"_libld.h"


/*
 * Print a virtual address map of input and output sections together with
 * multiple symbol definitions (if they exist).
 */
static Boolean	symbol_title = TRUE;

static void
sym_muldef_title()
{
	(void) printf(MSG_INTL(MSG_ENT_MUL_FMT_TIL_0),
	    MSG_INTL(MSG_ENT_MUL_TIL_0));
	(void) printf(MSG_INTL(MSG_ENT_MUL_FMT_TIL_1),
	    MSG_INTL(MSG_ENT_MUL_ITM_SYM),
	    MSG_INTL(MSG_ENT_MUL_ITM_DEF_0),
	    MSG_INTL(MSG_ENT_MUL_ITM_DEF_1));
	symbol_title = FALSE;
}

void
ld_map_out(Ofl_desc *ofl)
{
	Sg_desc		*sgp;
	Is_desc		*isp;
	Sym_avlnode	*sav;
	Aliste		idx1;

	(void) printf(MSG_INTL(MSG_ENT_MAP_FMT_TIL_1),
	    MSG_INTL(MSG_ENT_MAP_TITLE_1));
	if (ofl->ofl_flags & FLG_OF_RELOBJ)
		(void) printf(MSG_INTL(MSG_ENT_MAP_FMT_TIL_2),
		    MSG_INTL(MSG_ENT_ITM_OUTPUT),
		    MSG_INTL(MSG_ENT_ITM_INPUT),
		    MSG_INTL(MSG_ENT_ITM_NEW),
		    MSG_INTL(MSG_ENT_ITM_SECTION),
		    MSG_INTL(MSG_ENT_ITM_SECTION),
		    MSG_INTL(MSG_ENT_ITM_DISPMNT),
		    MSG_INTL(MSG_ENT_ITM_SIZE));
	else
		(void) printf(MSG_INTL(MSG_ENT_MAP_FMT_TIL_3),
		    MSG_INTL(MSG_ENT_ITM_OUTPUT),
		    MSG_INTL(MSG_ENT_ITM_INPUT),
		    MSG_INTL(MSG_ENT_ITM_VIRTUAL),
		    MSG_INTL(MSG_ENT_ITM_SECTION),
		    MSG_INTL(MSG_ENT_ITM_SECTION),
		    MSG_INTL(MSG_ENT_ITM_ADDRESS),
		    MSG_INTL(MSG_ENT_ITM_SIZE));

	for (APLIST_TRAVERSE(ofl->ofl_segs, idx1, sgp)) {
		Os_desc	*osp;
		Aliste	idx2;

		if (sgp->sg_phdr.p_type != PT_LOAD)
			continue;

		for (APLIST_TRAVERSE(sgp->sg_osdescs, idx2, osp)) {
			int	os_isdescs_idx;
			Aliste	idx3;

			(void) printf(MSG_INTL(MSG_ENT_MAP_ENTRY_1),
			    osp->os_name, EC_ADDR(osp->os_shdr->sh_addr),
			    EC_XWORD(osp->os_shdr->sh_size));

			OS_ISDESCS_TRAVERSE(os_isdescs_idx, osp, idx3, isp) {
				Addr	addr;

				/*
				 * Although there seems little point in printing
				 * discarded (empty) sections, especially as
				 * diagnostics under -Dsegments,details are more
				 * informative, continue printing them.  There
				 * are user scripts, fragile to say the least,
				 * that grep(1) through load-map output to
				 * discover object requirements.  These scripts
				 * don't grep for all input sections types (ie.
				 * .picdata), and have become dependent on null
				 * sections (ie. .text) existing in the
				 * load-map output.
				 */
				if (isp->is_flags & FLG_IS_DISCARD) {
					addr = 0;
				} else {
					addr = (Addr)
					    _elf_getxoff(isp->is_indata);
					if (!(ofl->ofl_flags & FLG_OF_RELOBJ))
						addr += isp->is_osdesc->
						    os_shdr->sh_addr;
				}

				(void) printf(MSG_INTL(MSG_ENT_MAP_ENTRY_2),
				    isp->is_name, EC_ADDR(addr),
				    EC_XWORD(isp->is_shdr->sh_size),
				    ((isp->is_file != NULL) ?
				    (char *)(isp->is_file->ifl_name) :
				    MSG_INTL(MSG_STR_NULL)));
			}
		}
	}

	if (ofl->ofl_flags & FLG_OF_RELOBJ)
		return;

	/*
	 * Check for any multiply referenced symbols (ie. symbols that have
	 * been overridden from a shared library).
	 */
	for (sav = avl_first(&ofl->ofl_symavl); sav;
	    sav = AVL_NEXT(&ofl->ofl_symavl, sav)) {
		Sym_desc	*sdp = sav->sav_symdesc;
		const char	*name = sdp->sd_name, *ducp, *adcp;
		APlist		*dfiles;
		Aliste		idx;

		if (((dfiles = sdp->sd_aux->sa_dfiles) == NULL) ||
		    (aplist_nitems(dfiles) == 1))
			continue;

		/*
		 * Files that define a symbol are saved on the `sa_dfiles' list.
		 * Ignore symbols that aren't needed, and any special symbols
		 * that the link editor may produce (symbols of type ABS and
		 * COMMON are not recorded in the first place, however functions
		 * like _init() and _fini() commonly have multiple occurrences).
		 */
		if ((sdp->sd_ref == REF_DYN_SEEN) ||
		    (sdp->sd_aux && sdp->sd_aux->sa_symspec) ||
		    (strcmp(MSG_ORIG(MSG_SYM_FINI_U), name) == 0) ||
		    (strcmp(MSG_ORIG(MSG_SYM_INIT_U), name) == 0) ||
		    (strcmp(MSG_ORIG(MSG_SYM_LIBVER_U), name) == 0))
			continue;

		if (symbol_title)
			sym_muldef_title();

		ducp = sdp->sd_file->ifl_name;
		(void) printf(MSG_INTL(MSG_ENT_MUL_ENTRY_1), demangle(name),
		    ducp);
		for (APLIST_TRAVERSE(dfiles, idx, adcp)) {
			/*
			 * Ignore the referenced symbol.
			 */
			if (strcmp(adcp, ducp) != 0)
				(void) printf(MSG_INTL(MSG_ENT_MUL_ENTRY_2),
				    adcp);
		}
	}
}

/*
 * Traverse the entrance criteria list searching for those sections that haven't
 * been met and print error message.  (only in the case of reordering)
 */
void
ld_ent_check(Ofl_desc * ofl)
{
	Ent_desc	*enp;
	Aliste		ndx;

	/*
	 *  Try to give as much information to the user about the specific
	 *  line in the mapfile.  If the line contains a file name then
	 *  output the filename too.  Hence we have two warning lines -
	 *  one for criterias where a filename is used and the other
	 *  for those without a filename.
	 */
	for (ALIST_TRAVERSE(ofl->ofl_ents, ndx, enp)) {
		const char	*file;

		if (((enp->ec_segment->sg_flags & FLG_SG_ORDER) == 0) ||
		    (enp->ec_flags & FLG_EC_USED) || (enp->ec_ordndx == 0))
			continue;


		if (enp->ec_files &&
		    ((file = enp->ec_files->apl_data[0]) != NULL)) {
			eprintf(ofl->ofl_lml, ERR_WARNING,
			    MSG_INTL(MSG_ENT_NOSEC_1), enp->ec_segment->sg_name,
			    enp->ec_name, file);
		} else {
			eprintf(ofl->ofl_lml, ERR_WARNING,
			    MSG_INTL(MSG_ENT_NOSEC_2), enp->ec_segment->sg_name,
			    enp->ec_name);
		}
	}
}