/* * 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 #include #include #include #include #include #include #include #include #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; ms.ms_size_set = TRUE; 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, NULL)) 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; DBG_CALL(Dbg_map_seg(ofl, DBG_STATE_MOD_AFTER, ndx, sgp1, mf->mf_lineno)); free(sgp1); 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); }