/*
 * 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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Map file parsing (Original SysV syntax).
 */
#include	<string.h>
#include	<strings.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<errno.h>
#include	<limits.h>
#include	<ctype.h>
#include	<elfcap.h>
#include	<debug.h>
#include	"msg.h"
#include	"_libld.h"
#include	"_map.h"

/*
 * Process a hardware/software capabilities segment declaration definition.
 *	hwcap_1	= val,... [ OVERRIDE ]
 *	sfcap_1	= val,... [ OVERRIDE ]
 *	hwcap_2	= val,... [ OVERRIDE ]
 *	platcap	= name,... [ OVERRIDE ]
 *	machcap	= name,... [ OVERRIDE ]
 *
 * The values can be defined as a list of machine specify tokens, or numerics.
 * Tokens are representations of the sys/auxv_$MACH.h capabilities, for example:
 *
 *	#define AV_386_FPU 0x0001	is represented as	FPU
 *	#define AV_386_TSC 0x0002	 "    "    "   " 	TSC
 *
 * Or, the above two capabilities could be represented as V0x3.  Note, the
 * OVERRIDE flag is used to ensure that only those values provided via this
 * mapfile entry are recorded in the final image, ie. this overrides any
 * hardware capabilities that may be defined in the objects read as part of
 * this link-edit.  Specifying:
 *
 *	V0x0 OVERRIDE
 *
 * effectively removes any capabilities information from the final image.
 */
static Boolean
map_cap(Mapfile *mf, Word type, Capmask *capmask)
{
	Token		tok;		/* Current token. */
	Xword		number;
	int		used = 0;
	Ofl_desc	*ofl = mf->mf_ofl;
	ld_map_tkval_t	tkv;		/* Value of token */
	elfcap_mask_t	value = 0;

	if (DBG_ENABLED) {
		Dbg_cap_mapfile_title(ofl->ofl_lml, mf->mf_lineno);
		Dbg_cap_val_entry(ofl->ofl_lml, DBG_STATE_CURRENT, CA_SUNW_HW_1,
		    capmask->cm_val, ld_targ.t_m.m_mach);
	}

	while ((tok = ld_map_gettoken(mf, TK_F_STRLC, &tkv)) !=
	    TK_SEMICOLON) {
		if (tok != TK_STRING) {
			if (tok != TK_ERROR)
				mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSEGATT));
			return (FALSE);
		}

		/*
		 * First, determine if the token represents the reserved
		 * OVERRIDE keyword.
		 */
		if (strncmp(tkv.tkv_str, MSG_ORIG(MSG_MAP_OVERRIDE),
		    MSG_MAP_OVERRIDE_SIZE) == 0) {
			ld_map_cap_set_ovflag(mf, type);
			used++;
			continue;
		}

		/* Is the token a symbolic capability name? */
		if ((number = (Xword)elfcap_tag_from_str(ELFCAP_STYLE_LC,
		    type, tkv.tkv_str, ld_targ.t_m.m_mach)) != 0) {
			value |= number;
			used++;
			continue;
		}

		/*
		 * Is the token a numeric value?
		 */
		if (tkv.tkv_str[0] == 'v') {
			if (ld_map_strtoxword(&tkv.tkv_str[1], NULL,
			    &number) != STRTOXWORD_OK) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_BADCAPVAL),
				    tkv.tkv_str);
				return (FALSE);
			}
			value |= number;
			used++;
			continue;
		}

		/*
		 * We have an unknown token.
		 */
		used++;
		mf_fatal(mf, MSG_INTL(MSG_MAP_UNKCAPATTR), tkv.tkv_str);
		return (FALSE);
	}

	/* Catch empty declarations */
	if (used == 0) {
		mf_warn0(mf, MSG_INTL(MSG_MAP_EMPTYCAP));
		return (TRUE);
	}

	DBG_CALL(Dbg_cap_val_entry(ofl->ofl_lml, DBG_STATE_NEW, type, value,
	    ld_targ.t_m.m_mach));
	capmask->cm_val |= value;

	/* Sanity check the resulting bits */
	if (!ld_map_cap_sanitize(mf, type, capmask))
		return (FALSE);

	return (TRUE);
}

/*
 * Parse the flags for a segment definition. Called by map_equal().
 *
 * entry:
 *	mf - Mapfile descriptor
 *	sgp - Segment being defined
 *	b_flags - Address of b_flags variable from map_equal().
 *		*bflags is TRUE if flags have already been seen in, the
 *		current segment definition directive, and FALSE otherwise.
 *	flag_tok - Flags string, starting with the '?' character.
 *
 * exit:
 *	On success, the flags have been parsed and the segment updated,
 *	*b_flags is set to TRUE, and TRUE is returned. On error, FALSE
 *	is returned.
 */
static Boolean
map_equal_flags(Mapfile *mf, Sg_desc *sgp, Boolean *b_flags,
    const char *flag_tok)
{
	Word	tmp_flags = 0;

	if (*b_flags) {
		mf_fatal(mf, MSG_INTL(MSG_MAP_MOREONCE),
		    MSG_INTL(MSG_MAP_SEGFLAG));
		return (FALSE);
	}

	/* Skip over the leading '?' character */
	flag_tok++;

	/*
	 * If ? has nothing following leave the flags cleared,
	 * otherwise OR in any flags specified.
	 */
	while (*flag_tok) {
		switch (*flag_tok) {
		case 'r':
			tmp_flags |= PF_R;
			break;
		case 'w':
			tmp_flags |= PF_W;
			break;
		case 'x':
			tmp_flags |= PF_X;
			break;
		case 'e':
			sgp->sg_flags |= FLG_SG_EMPTY;
			break;
		case 'o':
			/*
			 * The version 1 ?O option is incompatible with
			 * the version 2 SEGMENT IS_ORDER attribute.
			 */
			if (aplist_nitems(sgp->sg_is_order) > 0) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_ISORDVER),
				    sgp->sg_name);
				return (FALSE);
			}

			/*
			 * Set FLG_SG_IS_ORDER to indicate that segment has
			 * had the ?O flag set by a version 1 mapfile.
			 */
			sgp->sg_flags |= FLG_SG_IS_ORDER;
			break;
		case 'n':
			/*
			 * If segment ends up as the first loadable segment,
			 * it will not include the the ELF and program headers.
			 */
			sgp->sg_flags |= FLG_SG_NOHDR;
			break;
		default:
			mf_fatal(mf, MSG_INTL(MSG_MAP_UNKSEGFLG), *flag_tok);
			return (FALSE);
		}
		flag_tok++;
	}

	/*
	 * Warn when changing flags except when we're adding or removing "X"
	 * from a RW PT_LOAD segment.
	 */
	if ((sgp->sg_flags & FLG_SG_P_FLAGS) &&
	    (sgp->sg_phdr.p_flags != tmp_flags) &&
	    !(sgp->sg_phdr.p_type == PT_LOAD &&
	    (tmp_flags & (PF_R|PF_W)) == (PF_R|PF_W) &&
	    (tmp_flags ^ sgp->sg_phdr.p_flags) == PF_X))
		mf_warn(mf, MSG_INTL(MSG_MAP_REDEFATT),
		    MSG_INTL(MSG_MAP_SEGFLAG), sgp->sg_name);

	sgp->sg_flags |= FLG_SG_P_FLAGS;
	sgp->sg_phdr.p_flags = tmp_flags;
	*b_flags = TRUE;

	return (TRUE);
}

/*
 * Read an address (value) or size Xword from a TK_STRING token value
 * where the first letter of the string is a letter ('v', 'l', 's', ...)
 * followed by the numeric value.
 *
 * entry:
 *	mf - Mapfile descriptor
 *	tkv - TK_STRING token to parse
 *	value - Address of variable to receive the resulting value.
 *
 * exit:
 *	Returns TRUE for success. On failure, issues an error message
 *	and returns FALSE.
 */
static Boolean
valuetoxword(Mapfile *mf, ld_map_tkval_t *tkv, Xword *value)
{
	switch (ld_map_strtoxword(&tkv->tkv_str[1], NULL, value)) {
	case STRTOXWORD_OK:
		return (TRUE);

	case STRTOXWORD_TOOBIG:
		mf_fatal(mf, MSG_INTL(MSG_MAP_SEGADDR), tkv->tkv_str,
		    MSG_INTL(MSG_MAP_EXCLIMIT));
		break;
	default:
		mf_fatal(mf, MSG_INTL(MSG_MAP_SEGADDR), tkv->tkv_str,
		    MSG_INTL(MSG_MAP_NOBADFRM));
		break;
	}

	return (FALSE);
}

/*
 * Process a mapfile segment declaration definition.
 *	segment_name	= segment_attribute;
 * 	segment_attribute : segment_type  segment_flags  virtual_addr
 *			    physical_addr  length alignment
 */
static Boolean
map_equal(Mapfile *mf, Sg_desc *sgp)
{
	/*
	 * Segment type.  Users are permitted to define PT_LOAD,
	 * PT_NOTE, PT_SUNWSTACK and PT_NULL segments.  Other segment
	 * types are only defined in seg_desc[].
	 */
	typedef struct {
		const char	*name;	/* Name for segment type  */
		Word		p_type;	/* PT_ constant corresponding to name */
		sg_flags_t	sg_flags; /* Seg descriptor flags to apply */
	} seg_types_t;

	static seg_types_t seg_type_arr[] = {
		{ MSG_ORIG(MSG_MAP_LOAD),	PT_LOAD,	FLG_SG_P_TYPE },
		{ MSG_ORIG(MSG_MAP_STACK),	PT_SUNWSTACK,
		    FLG_SG_P_TYPE | FLG_SG_EMPTY },
		{ MSG_ORIG(MSG_MAP_NULL),	PT_NULL,	FLG_SG_P_TYPE },
		{ MSG_ORIG(MSG_MAP_NOTE),	PT_NOTE,	FLG_SG_P_TYPE },

		/* Array must be NULL terminated */
		{ NULL }
	};


	seg_types_t	*seg_type;
	Token	tok;			/* Current token. */
	ld_map_tkval_t	tkv;		/* Value of token */
	Boolean	b_type  = FALSE;	/* True if seg types found. */
	Boolean	b_flags = FALSE;	/* True if seg flags found. */
	Boolean	b_len   = FALSE;	/* True if seg length found. */
	Boolean	b_round = FALSE;	/* True if seg rounding found. */
	Boolean	b_vaddr = FALSE;	/* True if seg virtual addr found. */
	Boolean	b_paddr = FALSE;	/* True if seg physical addr found. */
	Boolean	b_align = FALSE;	/* True if seg alignment found. */

	while ((tok = ld_map_gettoken(mf, TK_F_STRLC, &tkv)) !=
	    TK_SEMICOLON) {
		if (tok != TK_STRING) {
			if (tok != TK_ERROR)
				mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSEGATT));
			return (FALSE);
		}

		/*
		 * If it is the name of a segment type, set the type
		 * and flags fields in the descriptor.
		 */
		for (seg_type = seg_type_arr; seg_type->name; seg_type++) {
			if (strcmp(tkv.tkv_str, seg_type->name) == 0) {
				if (b_type) {
					mf_fatal(mf, MSG_INTL(MSG_MAP_MOREONCE),
					    MSG_INTL(MSG_MAP_SEGTYP));
					return (FALSE);
				}
				if ((sgp->sg_flags & FLG_SG_P_TYPE) &&
				    (sgp->sg_phdr.p_type != seg_type->p_type)) {
					mf_warn(mf, MSG_INTL(MSG_MAP_REDEFATT),
					    MSG_INTL(MSG_MAP_SEGTYP),
					    sgp->sg_name);
				}

				sgp->sg_phdr.p_type = seg_type->p_type;
				sgp->sg_flags |= seg_type->sg_flags;
				break;
			}
		}
		if (seg_type->name != NULL)	/* Matched segment type */
			continue;		/* next token */

		/* Segment Flags */
		if (*tkv.tkv_str == '?') {
			if (!map_equal_flags(mf, sgp, &b_flags, tkv.tkv_str))
				return (FALSE);
			continue;		/* next token */
		}


		/* Segment address, length, alignment or rounding number */
		if ((tkv.tkv_str[0] == 'l') || (tkv.tkv_str[0] == 'v') ||
		    (tkv.tkv_str[0] == 'a') || (tkv.tkv_str[0] == 'p') ||
		    (tkv.tkv_str[0] == 'r')) {
			Xword	number;

			if (!valuetoxword(mf, &tkv, &number))
				return (FALSE);

			switch (*tkv.tkv_str) {
			case 'l':
				if (b_len) {
					mf_fatal(mf,
					    MSG_INTL(MSG_MAP_MOREONCE),
					    MSG_INTL(MSG_MAP_SEGLEN));
					return (FALSE);
				}
				if ((sgp->sg_flags & FLG_SG_LENGTH) &&
				    (sgp->sg_length != number))
					mf_warn(mf,
					    MSG_INTL(MSG_MAP_REDEFATT),
					    MSG_INTL(MSG_MAP_SEGLEN),
					    sgp->sg_name);
				sgp->sg_length = number;
				sgp->sg_flags |= FLG_SG_LENGTH;
				b_len = TRUE;
				break;
			case 'r':
				if (b_round) {
					mf_fatal(mf,
					    MSG_INTL(MSG_MAP_MOREONCE),
					    MSG_INTL(MSG_MAP_SEGROUND));
					return (FALSE);
				}
				if ((sgp->sg_flags & FLG_SG_ROUND) &&
				    (sgp->sg_round != number))
					mf_warn(mf,
					    MSG_INTL(MSG_MAP_REDEFATT),
					    MSG_INTL(MSG_MAP_SEGROUND),
					    sgp->sg_name);
				sgp->sg_round = number;
				sgp->sg_flags |= FLG_SG_ROUND;
				b_round = TRUE;
				break;
			case 'v':
				if (b_vaddr) {
					mf_fatal(mf,
					    MSG_INTL(MSG_MAP_MOREONCE),
					    MSG_INTL(MSG_MAP_SEGVADDR));
					return (FALSE);
				}
				if ((sgp->sg_flags & FLG_SG_P_VADDR) &&
				    (sgp->sg_phdr.p_vaddr != number))
					mf_warn(mf,
					    MSG_INTL(MSG_MAP_REDEFATT),
					    MSG_INTL(MSG_MAP_SEGVADDR),
					    sgp->sg_name);
				/* LINTED */
				sgp->sg_phdr.p_vaddr = (Addr)number;
				sgp->sg_flags |= FLG_SG_P_VADDR;
				b_vaddr = TRUE;
				break;
			case 'p':
				if (b_paddr) {
					mf_fatal(mf,
					    MSG_INTL(MSG_MAP_MOREONCE),
					    MSG_INTL(MSG_MAP_SEGPHYS));
					return (FALSE);
				}
				if ((sgp->sg_flags & FLG_SG_P_PADDR) &&
				    (sgp->sg_phdr.p_paddr != number))
					mf_warn(mf,
					    MSG_INTL(MSG_MAP_REDEFATT),
					    MSG_INTL(MSG_MAP_SEGPHYS),
					    sgp->sg_name);
				/* LINTED */
				sgp->sg_phdr.p_paddr = (Addr)number;
				sgp->sg_flags |= FLG_SG_P_PADDR;
				b_paddr = TRUE;
				break;
			case 'a':
				if (b_align) {
					mf_fatal(mf,
					    MSG_INTL(MSG_MAP_MOREONCE),
					    MSG_INTL(MSG_MAP_SEGALIGN));
					return (FALSE);
				}
				if ((sgp->sg_flags & FLG_SG_P_ALIGN) &&
				    (sgp->sg_phdr.p_align != number))
					mf_warn(mf,
					    MSG_INTL(MSG_MAP_REDEFATT),
					    MSG_INTL(MSG_MAP_SEGALIGN),
					    sgp->sg_name);
				/* LINTED */
				sgp->sg_phdr.p_align = (Xword)number;
				sgp->sg_flags |= FLG_SG_P_ALIGN;
				b_align = TRUE;
				break;
			}

			continue;		/* next token */
		}

		/*
		 * If we reach the bottom of this loop, we have an
		 * unrecognized token.
		 */
		mf_fatal(mf, MSG_INTL(MSG_MAP_UNKSEGATT), tkv.tkv_str);
		return (FALSE);
	}

	/*
	 * Empty segments can be used to define PT_LOAD segment reservations, or
	 * to reserve PT_NULL program headers.
	 *
	 * PT_LOAD reservations are only allowed within executables, as the
	 * reservation must be established through exec() as part of initial
	 * process loading.  In addition, PT_LOAD reservations must have an
	 * associated address and size. Note: This is an obsolete feature,
	 * not supported by the newer mapfile syntax.
	 *
	 * PT_NULL program headers are established for later use by applications
	 * such as the post-optimizer.  PT_NULL headers should have no other
	 * attributes assigned.
	 */
	if ((sgp->sg_flags & FLG_SG_EMPTY) &&
	    (sgp->sg_phdr.p_type != PT_SUNWSTACK)) {

		/*
		 * Any style of empty segment should have no permissions.
		 */
		if (sgp->sg_phdr.p_flags != 0) {
			mf_fatal(mf, MSG_INTL(MSG_MAP_SEGEMNOPERM),
			    EC_WORD(sgp->sg_phdr.p_flags));
			return (FALSE);
		}

		if (sgp->sg_phdr.p_type == PT_LOAD) {
			if ((mf->mf_ofl->ofl_flags & FLG_OF_EXEC) == 0) {
				mf_fatal0(mf, MSG_INTL(MSG_MAP_SEGEMPEXE));
				return (FALSE);
			}
			if ((sgp->sg_flags &
			    (FLG_SG_LENGTH | FLG_SG_P_VADDR)) !=
			    (FLG_SG_LENGTH | FLG_SG_P_VADDR)) {
				mf_fatal0(mf, MSG_INTL(MSG_MAP_SEGEMPATT));
				return (FALSE);
			}
		} else if (sgp->sg_phdr.p_type == PT_NULL) {
			if ((sgp->sg_flags &
			    (FLG_SG_LENGTH | FLG_SG_P_VADDR)) &&
			    ((sgp->sg_length != 0) ||
			    (sgp->sg_phdr.p_vaddr != 0))) {
				mf_fatal0(mf, MSG_INTL(MSG_MAP_SEGEMPNOATT));
				return (FALSE);
			}
		} else {
			mf_warn0(mf, MSG_INTL(MSG_MAP_SEGEMPLOAD));
			sgp->sg_phdr.p_type = PT_LOAD;
		}
	}

	/*
	 * All segment attributes have now been scanned.  Certain flags do not
	 * make sense if this is not a loadable segment, fix if necessary.
	 * Note, if the segment is of type PT_NULL it must be new, and any
	 * defaults will be applied by ld_map_seg_insert(). When clearing an
	 * attribute leave the flag set as an indicator for later entries
	 * re-specifying the same segment.
	 */
	if ((sgp->sg_phdr.p_type != PT_NULL) &&
	    (sgp->sg_phdr.p_type != PT_LOAD)) {
		const char	*fmt;

		if (sgp->sg_phdr.p_type == PT_SUNWSTACK)
			fmt = MSG_INTL(MSG_MAP_NOSTACK1);
		else
			fmt = MSG_INTL(MSG_MAP_NONLOAD);

		if ((sgp->sg_flags & FLG_SG_P_FLAGS) &&
		    (sgp->sg_phdr.p_type != PT_SUNWSTACK)) {
			if (sgp->sg_phdr.p_flags != 0) {
				mf_warn(mf, MSG_INTL(MSG_MAP_NONLOAD),
				    MSG_INTL(MSG_MAP_SEGFLAG));
				sgp->sg_phdr.p_flags = 0;
			}
		}
		if (sgp->sg_flags & FLG_SG_LENGTH)
			if (sgp->sg_length != 0) {
				mf_warn(mf, fmt, MSG_INTL(MSG_MAP_SEGLEN));
				sgp->sg_length = 0;
			}
		if (sgp->sg_flags & FLG_SG_ROUND)
			if (sgp->sg_round != 0) {
				mf_warn(mf, fmt, MSG_INTL(MSG_MAP_SEGROUND));
				sgp->sg_round = 0;
			}
		if (sgp->sg_flags & FLG_SG_P_VADDR) {
			if (sgp->sg_phdr.p_vaddr != 0) {
				mf_warn(mf, fmt, MSG_INTL(MSG_MAP_SEGVADDR));
				sgp->sg_phdr.p_vaddr = 0;
			}
		}
		if (sgp->sg_flags & FLG_SG_P_PADDR)
			if (sgp->sg_phdr.p_paddr != 0) {
				mf_warn(mf, fmt, MSG_INTL(MSG_MAP_SEGPHYS));
				sgp->sg_phdr.p_paddr = 0;
			}
		if (sgp->sg_flags & FLG_SG_P_ALIGN)
			if (sgp->sg_phdr.p_align != 0) {
				mf_warn(mf, fmt, MSG_INTL(MSG_MAP_SEGALIGN));
				sgp->sg_phdr.p_align = 0;
			}
	}
	return (TRUE);
}


/*
 * Process a mapfile mapping directives definition.
 *
 * 	segment_name : section_attribute [ : file_name ]
 *
 * Where segment_attribute is one of: section_name section_type section_flags;
 */
static Boolean
map_colon(Mapfile *mf, Ent_desc *enp)
{
	Token		tok;
	ld_map_tkval_t	tkv;
	Boolean		b_name = FALSE;
	Boolean		b_type = FALSE;
	Boolean		b_attr = FALSE;
	Boolean		b_bang = FALSE;


	/*
	 * Start out assuming that this entrance criteria will be empty,
	 * and therefore match anything. We clear the CATCHALL flag below
	 * if this turns out not to be the case.
	 */
	enp->ec_flags |= FLG_EC_CATCHALL;

	while (((tok = ld_map_gettoken(mf, 0, &tkv)) != TK_COLON) &&
	    (tok != TK_SEMICOLON)) {
		if (tok == TK_ERROR)
			return (FALSE);
		if (tok != TK_STRING) {
			mf_fatal0(mf, MSG_INTL(MSG_MAP_MALFORM));
			return (FALSE);
		}

		/* Segment type. */

		if (*tkv.tkv_str == '$') {
			if (b_type) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_MOREONCE),
				    MSG_INTL(MSG_MAP_SECTYP));
				return (FALSE);
			}
			b_type = TRUE;
			tkv.tkv_str++;
			ld_map_lowercase(tkv.tkv_str);
			if (strcmp(tkv.tkv_str, MSG_ORIG(MSG_STR_PROGBITS)) ==
			    0)
				enp->ec_type = SHT_PROGBITS;
			else if (strcmp(tkv.tkv_str,
			    MSG_ORIG(MSG_STR_SYMTAB)) == 0)
				enp->ec_type = SHT_SYMTAB;
			else if (strcmp(tkv.tkv_str,
			    MSG_ORIG(MSG_STR_DYNSYM)) == 0)
				enp->ec_type = SHT_DYNSYM;
			else if (strcmp(tkv.tkv_str,
			    MSG_ORIG(MSG_STR_STRTAB)) == 0)
				enp->ec_type = SHT_STRTAB;
			else if ((strcmp(tkv.tkv_str,
			    MSG_ORIG(MSG_STR_REL)) == 0) ||
			    (strcmp(tkv.tkv_str, MSG_ORIG(MSG_STR_RELA)) == 0))
				enp->ec_type = ld_targ.t_m.m_rel_sht_type;
			else if (strcmp(tkv.tkv_str, MSG_ORIG(MSG_STR_HASH)) ==
			    0)
				enp->ec_type = SHT_HASH;
			else if (strcmp(tkv.tkv_str, MSG_ORIG(MSG_STR_LIB)) ==
			    0)
				enp->ec_type = SHT_SHLIB;
			else if (strcmp(tkv.tkv_str,
			    MSG_ORIG(MSG_STR_LD_DYNAMIC)) == 0)
				enp->ec_type = SHT_DYNAMIC;
			else if (strcmp(tkv.tkv_str, MSG_ORIG(MSG_STR_NOTE)) ==
			    0)
				enp->ec_type = SHT_NOTE;
			else if (strcmp(tkv.tkv_str,
			    MSG_ORIG(MSG_STR_NOBITS)) == 0)
				enp->ec_type = SHT_NOBITS;
			else {
				mf_fatal(mf, MSG_INTL(MSG_MAP_UNKSECTYP),
				    tkv.tkv_str);
				return (FALSE);
			}

			enp->ec_flags &= ~FLG_EC_CATCHALL;

		/*
		 * Segment flags.
		 * If a segment flag is specified then the appropriate bit is
		 * set in the ec_attrmask, the ec_attrbits fields determine
		 * whether the attrmask fields must be tested true or false
		 * ie.	for  ?A the attrmask is set and the attrbit is set,
		 *	for ?!A the attrmask is set and the attrbit is clear.
		 */
		} else if (*tkv.tkv_str == '?') {
			if (b_attr) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_MOREONCE),
				    MSG_INTL(MSG_MAP_SECFLAG));
				return (FALSE);
			}
			b_attr = TRUE;
			b_bang = FALSE;
			tkv.tkv_str++;
			ld_map_lowercase(tkv.tkv_str);
			for (; *tkv.tkv_str != '\0'; tkv.tkv_str++)
				switch (*tkv.tkv_str) {
				case '!':
					if (b_bang) {
						mf_fatal(mf,
						    MSG_INTL(MSG_MAP_BADFLAG),
						    tkv.tkv_str);
						return (FALSE);
					}
					b_bang = TRUE;
					break;
				case 'a':
					if (enp->ec_attrmask & SHF_ALLOC) {
						mf_fatal(mf,
						    MSG_INTL(MSG_MAP_BADFLAG),
						    tkv.tkv_str);
						return (FALSE);
					}
					enp->ec_attrmask |= SHF_ALLOC;
					if (!b_bang)
						enp->ec_attrbits |= SHF_ALLOC;
					b_bang = FALSE;
					break;
				case 'w':
					if (enp->ec_attrmask & SHF_WRITE) {
						mf_fatal(mf,
						    MSG_INTL(MSG_MAP_BADFLAG),
						    tkv.tkv_str);
						return (FALSE);
					}
					enp->ec_attrmask |= SHF_WRITE;
					if (!b_bang)
						enp->ec_attrbits |= SHF_WRITE;
					b_bang = FALSE;
					break;
				case 'x':
					if (enp->ec_attrmask & SHF_EXECINSTR) {
						mf_fatal(mf,
						    MSG_INTL(MSG_MAP_BADFLAG),
						    tkv.tkv_str);
						return (FALSE);
					}
					enp->ec_attrmask |= SHF_EXECINSTR;
					if (!b_bang)
						enp->ec_attrbits |=
						    SHF_EXECINSTR;
					b_bang = FALSE;
					break;
				default:
					mf_fatal(mf,
					    MSG_INTL(MSG_MAP_BADFLAG),
					    tkv.tkv_str);
					return (FALSE);
				}
			if (enp->ec_attrmask != 0)
				enp->ec_flags &= ~FLG_EC_CATCHALL;

		/*
		 * Section name.
		 */
		} else {
			if (b_name) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_MOREONCE),
				    MSG_INTL(MSG_MAP_SECNAME));
				return (FALSE);
			}
			b_name = TRUE;
			enp->ec_is_name = tkv.tkv_str;
			enp->ec_flags &= ~FLG_EC_CATCHALL;
		}
	}
	if (tok == TK_COLON) {
		/*
		 * File names.
		 */
		while ((tok = ld_map_gettoken(mf, 0, &tkv)) != TK_SEMICOLON) {
			Word	ecf_type;

			if (tok != TK_STRING) {
				if (tok != TK_ERROR)
					mf_fatal0(mf,
					    MSG_INTL(MSG_MAP_MALFORM));
				return (FALSE);
			}

			/*
			 * A leading '*' means that this should be a basename
			 * comparison rather than a full path. It's not a glob
			 * wildcard, although it looks like one.
			 */
			if (tkv.tkv_str[0] == '*') {
				ecf_type = TYP_ECF_BASENAME;
				tkv.tkv_str++;
			} else {
				ecf_type = TYP_ECF_PATH;
			}
			if (!ld_map_seg_ent_files(mf, enp, ecf_type,
			    tkv.tkv_str))
				return (FALSE);
			enp->ec_flags &= ~FLG_EC_CATCHALL;
		}
	}
	return (TRUE);
}

/*
 * Process a mapfile size symbol definition.
 * 	segment_name @ symbol_name;
 */
static Boolean
map_atsign(Mapfile *mf, Sg_desc *sgp)
{
	Token		tok;		/* Current token. */
	ld_map_tkval_t	tkv;		/* Value of token */

	if ((tok = ld_map_gettoken(mf, 0, &tkv)) != TK_STRING) {
		if (tok != TK_ERROR)
			mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSYM_1));
		return (FALSE);
	}

	/* Add the symbol to the segment */
	if (!ld_map_seg_size_symbol(mf, sgp, TK_PLUSEQ, tkv.tkv_str))
		return (FALSE);


	if (ld_map_gettoken(mf, 0, &tkv) != TK_SEMICOLON) {
		if (tok != TK_ERROR)
			mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSCOL));
		return (FALSE);
	}

	return (TRUE);
}


static Boolean
map_pipe(Mapfile *mf, Sg_desc *sgp)
{
	Token		tok;		/* current token. */
	ld_map_tkval_t	tkv;		/* Value of token */

	if ((tok = ld_map_gettoken(mf, 0, &tkv)) != TK_STRING) {
		if (tok != TK_ERROR)
			mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSEC));
		return (FALSE);
	}

	if (!ld_map_seg_os_order_add(mf, sgp, tkv.tkv_str))
		return (FALSE);

	if ((tok = ld_map_gettoken(mf, 0, &tkv)) != TK_SEMICOLON) {
		if (tok != TK_ERROR)
			mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSCOL));
		return (FALSE);
	}

	return (TRUE);
}

/*
 * Process a mapfile library specification definition.
 * 	shared_object_name - shared object definition
 *	shared object definition : [ shared object type [ = SONAME ]]
 *					[ versions ];
 */
static Boolean
map_dash(Mapfile *mf, char *name)
{
	Token		tok;
	Sdf_desc	*sdf;
	ld_map_tkval_t	tkv;		/* Value of token */
	enum {
	    MD_NONE = 0,
	    MD_ADDVERS,
	}		dolkey = MD_NONE;

	/* Get descriptor for dependency */
	if ((sdf = ld_map_dv(mf, name)) == NULL)
		return (FALSE);

	/*
	 * Get the shared object descriptor string.
	 */
	while ((tok = ld_map_gettoken(mf, 0, &tkv)) != TK_SEMICOLON) {
		if ((tok != TK_STRING) && (tok != TK_EQUAL)) {
			if (tok != TK_ERROR)
				mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSO));
			return (FALSE);
		}

		/*
		 * Determine if the library type is accompanied with a SONAME
		 * definition.
		 */
		if (tok == TK_EQUAL) {
			if ((tok = ld_map_gettoken(mf, 0, &tkv)) !=
			    TK_STRING) {
				if (tok != TK_ERROR)
					mf_fatal0(mf,
					    MSG_INTL(MSG_MAP_EXPSO));
				return (FALSE);
			}
			switch (dolkey) {
			case MD_ADDVERS:
				if (!ld_map_dv_entry(mf, sdf, TRUE,
				    tkv.tkv_str))
					return (FALSE);
				break;
			case MD_NONE:
				mf_fatal(mf, MSG_INTL(MSG_MAP_UNEXTOK), '=');
				return (FALSE);
			}
			dolkey = MD_NONE;
			continue;
		}

		/*
		 * A shared object type has been specified.  This may also be
		 * accompanied by an SONAME redefinition (see above).
		 */
		if (*tkv.tkv_str == '$') {
			if (dolkey != MD_NONE) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_UNEXTOK), '$');
				return (FALSE);
			}
			tkv.tkv_str++;
			ld_map_lowercase(tkv.tkv_str);
			if (strcmp(tkv.tkv_str, MSG_ORIG(MSG_MAP_ADDVERS)) ==
			    0) {
				dolkey = MD_ADDVERS;
			} else {
				mf_fatal(mf, MSG_INTL(MSG_MAP_UNKSOTYP),
				    tkv.tkv_str);
				return (FALSE);
			}
			continue;
		}

		/*
		 * shared object version requirement.
		 */
		if (!ld_map_dv_entry(mf, sdf, FALSE, tkv.tkv_str))
			return (FALSE);
	}

	return (TRUE);
}


/*
 * Process a symbol definition.  Historically, this originated from processing
 * a version definition.  However, this has evolved into a generic means of
 * defining symbol references and definitions (see Defining Additional Symbols
 * in the Linker and Libraries guide for the complete syntax).
 *
 * [ name ] {
 *	scope:
 *		 symbol [ = [ type ] [ value ] [ size ] [ attribute ] ];
 * } [ dependency ];
 *
 */
static Boolean
map_version(Mapfile *mf, char *name)
{
	Token		tok;
	ld_map_tkval_t	tkv;		/* Value of token */
	ld_map_ver_t	mv;
	ld_map_sym_t	ms;
	Ofl_desc	*ofl = mf->mf_ofl;

	/* Establish the version descriptor and related data */
	if (!ld_map_sym_ver_init(mf, name, &mv))
		return (FALSE);

	/*
	 * Scan the mapfile entry picking out scoping and symbol definitions.
	 */
	while ((tok = ld_map_gettoken(mf, 0, &tkv)) != TK_RIGHTBKT) {
		uint_t		filter = 0;

		if (tok != TK_STRING) {
			if (tok == TK_ERROR) {
				mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSYM_2));
				return (FALSE);
			}
			mv.mv_errcnt++;
			continue;
		}

		/* The default value for all the symbol attributes is 0 */
		(void) memset(&ms, 0, sizeof (ms));
		ms.ms_name = tkv.tkv_str;

		tok = ld_map_gettoken(mf, 0, &tkv);
		if (tok == TK_ERROR) {
			mv.mv_errcnt++;
			continue;
		}

		/*
		 * Turn off the WEAK flag to indicate that definitions are
		 * associated with this version.  It would probably be more
		 * accurate to only remove this flag with the specification of
		 * global symbols, however setting it here allows enough slop
		 * to compensate for the various user inputs we've seen so far.
		 * Only if a closed version is specified (i.e., "SUNW_1.x {};")
		 * will a user get a weak version (which is how we document the
		 * creation of weak versions).
		 */
		mv.mv_vdp->vd_flags &= ~VER_FLG_WEAK;

		switch (tok) {
		case TK_COLON:
			ld_map_sym_scope(mf, ms.ms_name, &mv);
			continue;

		case TK_EQUAL:
			/*
			 * A full blown symbol definition follows.
			 * Determine the symbol type and any virtual address or
			 * alignment specified and then fall through to process
			 * the entire symbols information.
			 */
			while ((tok = ld_map_gettoken(mf, 0, &tkv)) !=
			    TK_SEMICOLON) {
				if (tok == TK_ERROR)
					return (FALSE);
				if (tok != TK_STRING) {
					mf_fatal0(mf,
					    MSG_INTL(MSG_MAP_MALFORM));
					return (FALSE);
				}

				/*
				 * If we had previously seen AUX or FILTER,
				 * the next string is the filtee itself.
				 * Add it, and clear the filter flag.
				 */
				if (filter) {
					ld_map_sym_filtee(mf, &mv, &ms,
					    filter, tkv.tkv_str);
					filter = 0;
					continue;
				}

				/*
				 * Determine any Value or Size attributes.
				 */
				ld_map_lowercase(tkv.tkv_str);

				if (tkv.tkv_str[0] == 'v' ||
				    tkv.tkv_str[0] == 's') {
					Xword	number;

					if (!valuetoxword(mf, &tkv, &number)) {
						mv.mv_errcnt++;
						return (FALSE);
					}

					switch (*tkv.tkv_str) {
					case 'v':
					    /* BEGIN CSTYLED */
					    if (ms.ms_value) {
						mf_fatal(mf,
						    MSG_INTL(MSG_MAP_MOREONCE),
						    MSG_INTL(MSG_MAP_SYMVAL));
						mv.mv_errcnt++;
						continue;
					    }
					    /* LINTED */
					    ms.ms_value = (Addr)number;
					    ms.ms_value_set = TRUE;
					    break;
					    /* END CSTYLED */
					case 's':
					    /* BEGIN CSTYLED */
					    if (ms.ms_size) {
						mf_fatal(mf,
						    MSG_INTL(MSG_MAP_MOREONCE),
						    MSG_INTL(MSG_MAP_SYMSIZE));
						mv.mv_errcnt++;
						continue;
					    }
					    /* LINTED */
					    ms.ms_size = (Addr)number;
					    break;
					    /* END CSTYLED */
					}

				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_FUNCTION)) == 0) {
					ms.ms_shndx = SHN_ABS;
					ms.ms_sdflags |= FLG_SY_SPECSEC;
					ms.ms_type = STT_FUNC;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_DATA)) == 0) {
					ms.ms_shndx = SHN_ABS;
					ms.ms_sdflags |= FLG_SY_SPECSEC;
					ms.ms_type = STT_OBJECT;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_COMMON)) == 0) {
					ms.ms_shndx = SHN_COMMON;
					ms.ms_sdflags |= FLG_SY_SPECSEC;
					ms.ms_type = STT_OBJECT;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_PARENT)) == 0) {
					ms.ms_sdflags |= FLG_SY_PARENT;
					ofl->ofl_flags |= FLG_OF_SYMINFO;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_EXTERN)) == 0) {
					ms.ms_sdflags |= FLG_SY_EXTERN;
					ofl->ofl_flags |= FLG_OF_SYMINFO;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_DIRECT)) == 0) {
					ms.ms_sdflags |= FLG_SY_DIR;
					ofl->ofl_flags |= FLG_OF_SYMINFO;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_NODIRECT)) == 0) {
					ms.ms_sdflags |= FLG_SY_NDIR;
					ofl->ofl_flags |= FLG_OF_SYMINFO;
					ofl->ofl_flags1 |=
					    (FLG_OF1_NDIRECT | FLG_OF1_NGLBDIR);
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_FILTER)) == 0) {
					/* Next token is the filtee */
					filter = FLG_SY_STDFLTR;
					continue;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_AUXILIARY)) == 0) {
					/* Next token is the filtee */
					filter = FLG_SY_AUXFLTR;
					continue;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_INTERPOSE)) == 0) {
					/* BEGIN CSTYLED */
					if (!(ofl->ofl_flags & FLG_OF_EXEC)) {
					    mf_fatal0(mf,
						MSG_INTL(MSG_MAP_NOINTPOSE));
					    mv.mv_errcnt++;
					    break;
					}
					/* END CSTYLED */
					ms.ms_sdflags |= FLG_SY_INTPOSE;
					ofl->ofl_flags |= FLG_OF_SYMINFO;
					ofl->ofl_dtflags_1 |= DF_1_SYMINTPOSE;
					continue;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_DYNSORT)) == 0) {
					ms.ms_sdflags |= FLG_SY_DYNSORT;
					ms.ms_sdflags &= ~FLG_SY_NODYNSORT;
					continue;
				} else if (strcmp(tkv.tkv_str,
				    MSG_ORIG(MSG_MAP_NODYNSORT)) == 0) {
					ms.ms_sdflags &= ~FLG_SY_DYNSORT;
					ms.ms_sdflags |= FLG_SY_NODYNSORT;
					continue;
				} else {
					mf_fatal(mf,
					    MSG_INTL(MSG_MAP_UNKSYMDEF),
					    tkv.tkv_str);
					mv.mv_errcnt++;
					continue;
				}
			}
			/* FALLTHROUGH */

		case TK_SEMICOLON:
			/* Auto-reduction directive ('*')? */
			if (*ms.ms_name == '*') {
				ld_map_sym_autoreduce(mf, &mv);
				continue;
			}

			/*
			 * Catch the error where the AUX or FILTER keyword
			 * was used, but the filtee wasn't supplied.
			 */
			if (filter && (ms.ms_filtee == NULL)) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_NOFILTER),
				    ms.ms_name);
				mv.mv_errcnt++;
				continue;
			}

			/*
			 * Add the new symbol.  It should be noted that all
			 * symbols added by the mapfile start out with global
			 * scope, thus they will fall through the normal symbol
			 * resolution process.  Symbols defined as locals will
			 * be reduced in scope after all input file processing.
			 */
			if (!ld_map_sym_enter(mf, &mv, &ms))
				return (FALSE);
			break;

		default:
			mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSCOL));
			mv.mv_errcnt++;
			continue;
		}
	}

	if (mv.mv_errcnt)
		return (FALSE);

	/*
	 * Determine if any version references are provided after the close
	 * bracket, parsing up to the terminating ';'.
	 */
	if (!ld_map_sym_ver_fini(mf, &mv))
		return (FALSE);

	return (TRUE);
}

/*
 * Parse the mapfile --- Sysv syntax
 */
Boolean
ld_map_parse_v1(Mapfile *mf)
{
	Sg_desc		*sgp1;		/* seg descriptor being manipulated */
	Ent_desc	*enp;		/* segment entrance criteria. */
	Token		tok;		/* current token. */
	Boolean		new_segment;	/* If true, defines new segment */
	char		*name;
	Ofl_desc	*ofl = mf->mf_ofl;
	ld_map_tkval_t	tkv;		/* Value of token */
	avl_index_t 	where;

	/*
	 * We now parse the mapfile until the gettoken routine returns EOF.
	 */
	while ((tok = ld_map_gettoken(mf, TK_F_EOFOK, &tkv)) != TK_EOF) {
		Xword	ndx;

		/*
		 * At this point we are at the beginning of a line, and the
		 * variable tkv.tkv_str points to the first string on the line.
		 * All mapfile entries start with some string token except it
		 * is possible for a scoping definition to start with `{'.
		 */
		if (tok == TK_LEFTBKT) {
			if (!map_version(mf, NULL))
				return (FALSE);
			continue;
		}
		if (tok != TK_STRING) {
			if (tok != TK_ERROR)
				mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPSEGNAM));
			return (FALSE);
		}

		/*
		 * Save the initial token.
		 */
		name = tkv.tkv_str;

		/*
		 * Now check the second character on the line.  The special `-'
		 * and `{' characters do not involve any segment manipulation so
		 * we handle them first.
		 */
		tok = ld_map_gettoken(mf, 0, &tkv);
		if (tok == TK_ERROR)
			return (FALSE);
		if (tok == TK_DASH) {
			if (!map_dash(mf, name))
				return (FALSE);
			continue;
		}
		if (tok == TK_LEFTBKT) {
			if (!map_version(mf, name))
				return (FALSE);
			continue;
		}

		/*
		 * If we're here we need to interpret the first string as a
		 * segment name.  Is this an already known segment?
		 */
		sgp1 = ld_seg_lookup(mf->mf_ofl, name, &where);
		new_segment = sgp1 == NULL;
		if (!new_segment)
			sgp1->sg_flags &= ~FLG_SG_DISABLED;

		/*
		 * If the second token is a '|' then we had better have found a
		 * segment.  It is illegal to perform section within segment
		 * ordering before the segment has been declared.
		 */
		if (tok == TK_PIPE) {
			if (sgp1 == NULL) {
				mf_fatal(mf, MSG_INTL(MSG_MAP_SECINSEG),
				    name);
				return (FALSE);
			}
			if (!map_pipe(mf, sgp1))
				return (FALSE);
			continue;
		}

		/*
		 * If segment does not exist, allocate a descriptor with
		 * its values set to 0 so that map_equal() can detect
		 * changing attributes.
		 */
		if (new_segment &&
		    ((sgp1 = ld_map_seg_alloc(name, PT_NULL, 0)) == NULL))
			return (FALSE);

		/*
		 * Now check the second token from the input line.
		 */
		switch (tok) {
		case TK_EQUAL:		/* Create/modify segment */
			/*
			 * We use the same syntax for hardware/software
			 * capabilities as we do for segments. If the
			 * "segment name" matches one of these, then
			 * process the capabilities instead of treating it
			 * as a segment. Note that no dynamic memory has
			 * been allocated for the segment descriptor yet,
			 * so we can bail without leaking memory.
			 */
			if (strcmp(sgp1->sg_name,
			    MSG_ORIG(MSG_STR_HWCAP_1)) == 0) {
				if (!map_cap(mf, CA_SUNW_HW_1,
				    &ofl->ofl_ocapset.oc_hw_1))
					return (FALSE);
				continue;
			}
			if (strcmp(sgp1->sg_name,
			    MSG_ORIG(MSG_STR_SFCAP_1)) == 0) {
				if (!map_cap(mf, CA_SUNW_SF_1,
				    &ofl->ofl_ocapset.oc_sf_1))
					return (FALSE);
				continue;
			}

			/*
			 * If not a new segment, show the initial value
			 * before modifying it.
			 */
			if (!new_segment && DBG_ENABLED) {
				ndx = ld_map_seg_index(mf, sgp1);
				Dbg_map_seg(ofl, DBG_STATE_MOD_BEFORE,
				    ndx, sgp1, mf->mf_lineno);
			}

			/* Process the segment */
			if (!map_equal(mf, sgp1))
				return (FALSE);

			/*
			 * Special case for STACK "segments":
			 *
			 * The ability to modify the stack flags was added
			 * long after this sysv syntax was designed. It was
			 * fit into the existing syntax by treating it as a
			 * segment. However, there can only be one stack program
			 * header, while segment syntax requires user to supply
			 * a name. This is confusing, and it allows the user to
			 * attempt to create more than one stack segment. The
			 * original implementation had a test to catch this.
			 *
			 * If this is a stack segment, locate the real stack
			 * descriptor and transfer the flags to it. We then
			 * free the allocated descriptor without inserting it.
			 * The end result is that all stack segments simply
			 * alter the one stack descriptor, and the segment
			 * name is ignored.
			 */
			if (sgp1->sg_phdr.p_type == PT_SUNWSTACK) {
				Sg_desc	*stack = ld_map_seg_stack(mf);

				if (sgp1->sg_flags & FLG_SG_P_FLAGS)
					stack->sg_phdr.p_flags =
					    sgp1->sg_phdr.p_flags;
				free(sgp1);

				DBG_CALL(Dbg_map_seg(ofl,
				    DBG_STATE_MOD_AFTER, ndx, sgp1,
				    mf->mf_lineno));
				break;
			}

			/*
			 * If this is a new segment, finish its initialization
			 * and insert it into the segment list.
			 */
			if (new_segment) {
				switch (ld_map_seg_insert(mf, DBG_STATE_NEW,
				    sgp1, where)) {
				case SEG_INS_SKIP:
					continue;
				case SEG_INS_FAIL:
					return (FALSE);
				}
			} else {
				/* Not new. Show what's changed */
				DBG_CALL(Dbg_map_seg(ofl,
				    DBG_STATE_MOD_AFTER, ndx, sgp1,
				    mf->mf_lineno));
			}
			break;

		case TK_COLON:		/* Section to segment mapping */
			/*
			 * If this is a new segment, finish its initialization
			 * and insert it into the segment list.
			 *
			 * If it is not a new segment, ensure that it is
			 * not an empty segment reservation, as sections
			 * cannot be assigned to those.
			 */
			if (new_segment) {
				switch (ld_map_seg_insert(mf,
				    DBG_STATE_NEW_IMPLICIT, sgp1, where)) {
				case SEG_INS_SKIP:
					continue;
				case SEG_INS_FAIL:
					return (FALSE);
				}
			} else if (sgp1->sg_flags & FLG_SG_EMPTY) {
				mf_fatal0(mf, MSG_INTL(MSG_MAP_SEGEMPSEC));
				return (FALSE);
			}

			/*
			 * Create new entrance criteria descriptor, and
			 * process the mapping directive.
			 */
			enp = ld_map_seg_ent_add(mf, sgp1, NULL);
			if ((enp == NULL) || !map_colon(mf, enp))
				return (FALSE);
			DBG_CALL(Dbg_map_ent(ofl->ofl_lml, enp, ofl,
			    mf->mf_lineno));
			break;

		case TK_ATSIGN:		/* Section size symbol */
			/*
			 * If this is a new segment, finish its initialization
			 * and insert it into the segment list.
			 */
			if (new_segment) {
				switch (ld_map_seg_insert(mf,
				    DBG_STATE_NEW_IMPLICIT, sgp1, where)) {
				case SEG_INS_SKIP:
					continue;
				case SEG_INS_FAIL:
					return (FALSE);
				}
			}
			if (!map_atsign(mf, sgp1))
				return (FALSE);
			break;

		case TK_ERROR:
			return (FALSE);		/* Error was already issued */

		default:
			mf_fatal0(mf, MSG_INTL(MSG_MAP_EXPEQU));
			return (FALSE);
		}
	}

	return (TRUE);
}