/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Map file parsing. */ #include #include #include #include #include #include #include #include #include #include #include #include "msg.h" #include "_libld.h" #if defined(_ELF64) #define STRTOADDR strtoull #define XWORD_MAX ULLONG_MAX #else /* Elf32 */ #define STRTOADDR strtoul #define XWORD_MAX UINT_MAX #endif /* _ELF64 */ /* Possible return values from gettoken */ typedef enum { TK_ERROR = -1, /* Error in lexical analysis */ TK_STRING = 0, TK_COLON = 1, TK_SEMICOLON = 2, TK_EQUAL = 3, TK_ATSIGN = 4, TK_DASH = 5, TK_LEFTBKT = 6, TK_RIGHTBKT = 7, TK_PIPE = 8, TK_EOF = 9 } Token; static char *Mapspace; /* Malloc space holding map file. */ static ulong_t Line_num; /* Current map file line number. */ static char *Start_tok; /* First character of current token. */ static char *nextchr; /* Next char in mapfile to examine. */ /* * Convert a string to lowercase. */ static void lowercase(char *str) { while (*str = tolower(*str)) str++; } /* * Get a token from the mapfile. * * entry: * ofl - Output file descriptor * mapfile - Name of mapfile * eof_ok - If False, end of file causes a premature EOF error to be * issued. If True, TK_EOF is returned quietly. */ static Token gettoken(Ofl_desc *ofl, const char *mapfile, int eof_ok) { static char oldchr = '\0'; /* Char at end of current token. */ char *end; /* End of the current token. */ /* Cycle through the characters looking for tokens. */ for (;;) { if (oldchr != '\0') { *nextchr = oldchr; oldchr = '\0'; } if (!isascii(*nextchr) || (!isprint(*nextchr) && !isspace(*nextchr) && (*nextchr != '\0'))) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_ILLCHAR), mapfile, EC_XWORD(Line_num), *((uchar_t *)nextchr)); return (TK_ERROR); } switch (*nextchr) { case '\0': /* End of file. */ if (!eof_ok) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_PREMEOF), mapfile, EC_XWORD(Line_num)); return (TK_EOF); case ' ': /* White space. */ case '\t': nextchr++; break; case '\n': /* White space too, but bump line number. */ nextchr++; Line_num++; break; case '#': /* Comment. */ while (*nextchr != '\n' && *nextchr != '\0') nextchr++; break; case ':': nextchr++; return (TK_COLON); case ';': nextchr++; return (TK_SEMICOLON); case '=': nextchr++; return (TK_EQUAL); case '@': nextchr++; return (TK_ATSIGN); case '-': nextchr++; return (TK_DASH); case '|': nextchr++; return (TK_PIPE); case '{': nextchr++; return (TK_LEFTBKT); case '}': nextchr++; return (TK_RIGHTBKT); case '"': Start_tok = ++nextchr; if (((end = strpbrk(nextchr, MSG_ORIG(MSG_MAP_TOK_1))) == NULL) || (*end != '"')) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_NOTERM), mapfile, EC_XWORD(Line_num)); return (TK_ERROR); } *end = '\0'; nextchr = end + 1; return (TK_STRING); default: /* string. */ Start_tok = nextchr; /* CSTYLED */ end = strpbrk(nextchr, MSG_ORIG(MSG_MAP_TOK_2)); if (end == NULL) nextchr = Start_tok + strlen(Start_tok); else { nextchr = end; oldchr = *nextchr; *nextchr = '\0'; } return (TK_STRING); } } } /* * Process a hardware/software capabilities segment declaration definition. * hwcap_1 = val,... [ OVERRIDE ] * sfcap_1 = val,... [ 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 insure 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 uintptr_t map_cap(const char *mapfile, Word type, Ofl_desc *ofl) { Token tok; /* Current token. */ Xword number; int used = 0; while ((tok = gettoken(ofl, mapfile, 0)) != TK_SEMICOLON) { if (tok != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSEGATT), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } lowercase(Start_tok); /* * First, determine if the token represents the reserved * OVERRIDE keyword. */ if (strncmp(Start_tok, MSG_ORIG(MSG_MAP_OVERRIDE), MSG_MAP_OVERRIDE_SIZE) == 0) { if (type == CA_SUNW_HW_1) ofl->ofl_flags1 |= FLG_OF1_OVHWCAP; else ofl->ofl_flags1 |= FLG_OF1_OVSFCAP; used++; continue; } /* * Next, determine if the token represents a machine specific * hardware capability, or a generic software capability. */ if (type == CA_SUNW_HW_1) { if ((number = (Xword)elfcap_hw1_from_str( ELFCAP_STYLE_LC, Start_tok, ld_targ.t_m.m_mach)) != 0) { ofl->ofl_hwcap_1 |= number; used++; continue; } } else { if ((number = (Xword)elfcap_sf1_from_str( ELFCAP_STYLE_LC, Start_tok, ld_targ.t_m.m_mach)) != 0) { ofl->ofl_sfcap_1 |= number; used++; continue; } } /* * Next, determine if the token represents a numeric value. */ if (Start_tok[0] == 'v') { char *end_tok; errno = 0; number = (Xword)strtoul(&Start_tok[1], &end_tok, 0); if (errno) { int err = errno; eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_BADCAPVAL), mapfile, EC_XWORD(Line_num), Start_tok, strerror(err)); return (S_ERROR); } if (end_tok != strchr(Start_tok, '\0')) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_BADCAPVAL), mapfile, EC_XWORD(Line_num), Start_tok, MSG_INTL(MSG_MAP_NOBADFRM)); return (S_ERROR); } if (type == CA_SUNW_HW_1) ofl->ofl_hwcap_1 |= number; else ofl->ofl_sfcap_1 |= number; used++; continue; } /* * We have an unknown token. */ used++; eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKCAPATTR), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } /* * Catch any empty declarations, and indicate any software capabilities * have been initialized if necessary. */ if (used == 0) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_EMPTYCAP), mapfile, EC_XWORD(Line_num)); } else if (type == CA_SUNW_SF_1) { Lword badsf1; /* * Note, hardware capabilities, beyond the tokens that are * presently known, can be accepted using the V0xXXX notation, * and as these simply get or'd into the output image, we allow * any values to be supplied. Software capability tokens * however, have an algorithm of acceptance and update (see * sf1_cap() in files.c). Therefore only allow software * capabilities that are known. */ if ((badsf1 = (ofl->ofl_sfcap_1 & ~SF1_SUNW_MASK)) != 0) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_BADSF1), mapfile, EC_XWORD(Line_num), EC_LWORD(badsf1)); ofl->ofl_sfcap_1 &= SF1_SUNW_MASK; } if ((ofl->ofl_sfcap_1 & (SF1_SUNW_FPKNWN | SF1_SUNW_FPUSED)) == SF1_SUNW_FPUSED) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_FIL_BADSF1), mapfile, EC_XWORD(Line_num), EC_LWORD(SF1_SUNW_FPUSED)); ofl->ofl_sfcap_1 &= ~SF1_SUNW_FPUSED; } #if !defined(_ELF64) /* * The SF1_SUNW_ADDR32 software capability is only meaningful * when building a 64-bit object. Warn the user, and remove the * setting, if we're building a 32-bit object. */ if (ofl->ofl_sfcap_1 & SF1_SUNW_ADDR32) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_INADDR32SF1), mapfile, EC_XWORD(Line_num)); ofl->ofl_sfcap_1 &= ~SF1_SUNW_ADDR32; } #endif } return (1); } /* * Common segment error checking. */ static Boolean seg_check(const char *mapfile, Sg_desc *sgp, Ofl_desc *ofl, Boolean b_type, Word p_type) { if (b_type) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGTYP)); return (FALSE); } if ((sgp->sg_flags & FLG_SG_TYPE) && (sgp->sg_phdr.p_type != p_type)) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_REDEFATT), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGTYP), sgp->sg_name); } return (TRUE); } /* * Process a mapfile segment declaration definition. * segment_name = segment_attribute; * segment_attribute : segment_type segment_flags virtual_addr * physical_addr length alignment */ static uintptr_t map_equal(const char *mapfile, Sg_desc *sgp, Ofl_desc *ofl) { Token tok; /* Current 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 = gettoken(ofl, mapfile, 0)) != TK_SEMICOLON) { if (tok != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSEGATT), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } lowercase(Start_tok); /* * Segment type. Users are permitted to define PT_LOAD, * PT_NOTE, PT_STACK and PT_NULL segments. Other segment types * are only defined in seg_desc[]. */ if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_LOAD)) == 0) { if ((b_type = seg_check(mapfile, sgp, ofl, b_type, PT_LOAD)) == FALSE) return (S_ERROR); sgp->sg_phdr.p_type = PT_LOAD; sgp->sg_flags |= FLG_SG_TYPE; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_STACK)) == 0) { if ((b_type = seg_check(mapfile, sgp, ofl, b_type, PT_SUNWSTACK)) == FALSE) return (S_ERROR); sgp->sg_phdr.p_type = PT_SUNWSTACK; sgp->sg_flags |= (FLG_SG_TYPE | FLG_SG_EMPTY); } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_NULL)) == 0) { if ((b_type = seg_check(mapfile, sgp, ofl, b_type, PT_NULL)) == FALSE) return (S_ERROR); sgp->sg_phdr.p_type = PT_NULL; sgp->sg_flags |= FLG_SG_TYPE; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_NOTE)) == 0) { if ((b_type = seg_check(mapfile, sgp, ofl, b_type, PT_NOTE)) == FALSE) return (S_ERROR); sgp->sg_phdr.p_type = PT_NOTE; sgp->sg_flags |= FLG_SG_TYPE; } /* Segment Flags. */ else if (*Start_tok == '?') { Word tmp_flags = 0; char *flag_tok = Start_tok + 1; if (b_flags) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGFLAG)); return (S_ERROR); } /* * If ? has nothing following leave the flags cleared, * otherwise or in any flags specified. */ if (*flag_tok) { 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': sgp->sg_flags |= FLG_SG_ORDER; ofl->ofl_flags |= FLG_OF_SEGORDER; break; case 'n': sgp->sg_flags |= FLG_SG_NOHDR; break; default: eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSEGFLG), mapfile, EC_XWORD(Line_num), *flag_tok); return (S_ERROR); } 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_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)) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_REDEFATT), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGFLAG), sgp->sg_name); } sgp->sg_flags |= FLG_SG_FLAGS; sgp->sg_phdr.p_flags = tmp_flags; b_flags = TRUE; } /* Segment address, length, alignment or rounding number. */ else if ((Start_tok[0] == 'l') || (Start_tok[0] == 'v') || (Start_tok[0] == 'a') || (Start_tok[0] == 'p') || (Start_tok[0] == 'r')) { char *end_tok; Xword number; if ((number = (Xword)STRTOADDR(&Start_tok[1], &end_tok, 0)) >= XWORD_MAX) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGADDR), mapfile, EC_XWORD(Line_num), Start_tok, MSG_INTL(MSG_MAP_EXCLIMIT)); return (S_ERROR); } if (end_tok != strchr(Start_tok, '\0')) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGADDR), mapfile, EC_XWORD(Line_num), Start_tok, MSG_INTL(MSG_MAP_NOBADFRM)); return (S_ERROR); } switch (*Start_tok) { case 'l': if (b_len) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGLEN)); return (S_ERROR); } if ((sgp->sg_flags & FLG_SG_LENGTH) && (sgp->sg_length != number)) eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_REDEFATT), mapfile, EC_XWORD(Line_num), 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) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGROUND)); return (S_ERROR); } if ((sgp->sg_flags & FLG_SG_ROUND) && (sgp->sg_round != number)) eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_REDEFATT), mapfile, EC_XWORD(Line_num), 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) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGVADDR)); return (S_ERROR); } if ((sgp->sg_flags & FLG_SG_VADDR) && (sgp->sg_phdr.p_vaddr != number)) eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_REDEFATT), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGVADDR), sgp->sg_name); /* LINTED */ sgp->sg_phdr.p_vaddr = (Addr)number; sgp->sg_flags |= FLG_SG_VADDR; ofl->ofl_flags1 |= FLG_OF1_VADDR; b_vaddr = TRUE; break; case 'p': if (b_paddr) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGPHYS)); return (S_ERROR); } if ((sgp->sg_flags & FLG_SG_PADDR) && (sgp->sg_phdr.p_paddr != number)) eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_REDEFATT), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGPHYS), sgp->sg_name); /* LINTED */ sgp->sg_phdr.p_paddr = (Addr)number; sgp->sg_flags |= FLG_SG_PADDR; b_paddr = TRUE; break; case 'a': if (b_align) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGALIGN)); return (S_ERROR); } if ((sgp->sg_flags & FLG_SG_ALIGN) && (sgp->sg_phdr.p_align != number)) eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_REDEFATT), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGALIGN), sgp->sg_name); /* LINTED */ sgp->sg_phdr.p_align = (Xword)number; sgp->sg_flags |= FLG_SG_ALIGN; b_align = TRUE; break; } } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSEGATT), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } } /* * 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. * * 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) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGEMNOPERM), mapfile, EC_XWORD(Line_num), EC_WORD(sgp->sg_phdr.p_flags)); return (S_ERROR); } if (sgp->sg_phdr.p_type == PT_LOAD) { if ((ofl->ofl_flags & FLG_OF_EXEC) == 0) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGEMPEXE), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } if ((sgp->sg_flags & (FLG_SG_LENGTH | FLG_SG_VADDR)) != (FLG_SG_LENGTH | FLG_SG_VADDR)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGEMPATT), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } } else if (sgp->sg_phdr.p_type == PT_NULL) { if ((sgp->sg_flags & (FLG_SG_LENGTH | FLG_SG_VADDR)) && ((sgp->sg_length != 0) || (sgp->sg_phdr.p_vaddr != 0))) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGEMPNOATT), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } } else { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_SEGEMPLOAD), mapfile, EC_XWORD(Line_num)); 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 back in ld_map_parse(). * 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_FLAGS) && (sgp->sg_phdr.p_type != PT_SUNWSTACK)) { if (sgp->sg_phdr.p_flags != 0) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_NONLOAD), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGFLAG)); sgp->sg_phdr.p_flags = 0; } } if (sgp->sg_flags & FLG_SG_LENGTH) if (sgp->sg_length != 0) { eprintf(ofl->ofl_lml, ERR_WARNING, fmt, mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGLEN)); sgp->sg_length = 0; } if (sgp->sg_flags & FLG_SG_ROUND) if (sgp->sg_round != 0) { eprintf(ofl->ofl_lml, ERR_WARNING, fmt, mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGROUND)); sgp->sg_round = 0; } if (sgp->sg_flags & FLG_SG_VADDR) { if (sgp->sg_phdr.p_vaddr != 0) { eprintf(ofl->ofl_lml, ERR_WARNING, fmt, mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGVADDR)); sgp->sg_phdr.p_vaddr = 0; } } if (sgp->sg_flags & FLG_SG_PADDR) if (sgp->sg_phdr.p_paddr != 0) { eprintf(ofl->ofl_lml, ERR_WARNING, fmt, mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGPHYS)); sgp->sg_phdr.p_paddr = 0; } if (sgp->sg_flags & FLG_SG_ALIGN) if (sgp->sg_phdr.p_align != 0) { eprintf(ofl->ofl_lml, ERR_WARNING, fmt, mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SEGALIGN)); sgp->sg_phdr.p_align = 0; } } return (1); } /* * Process a mapfile mapping directives definition. * segment_name : section_attribute [ : file_name ] * segment_attribute : section_name section_type section_flags; */ static uintptr_t map_colon(Ofl_desc *ofl, const char *mapfile, Ent_desc *enp) { Token tok; /* Current token. */ Boolean b_name = FALSE; Boolean b_type = FALSE; Boolean b_attr = FALSE; Boolean b_bang = FALSE; static Xword index = 0; while (((tok = gettoken(ofl, mapfile, 0)) != TK_COLON) && (tok != TK_SEMICOLON)) { if ((tok == TK_ERROR) || (tok == TK_EOF)) return (S_ERROR); /* Segment type. */ if (*Start_tok == '$') { if (b_type) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SECTYP)); return (S_ERROR); } b_type = TRUE; Start_tok++; lowercase(Start_tok); if (strcmp(Start_tok, MSG_ORIG(MSG_STR_PROGBITS)) == 0) enp->ec_type = SHT_PROGBITS; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_SYMTAB)) == 0) enp->ec_type = SHT_SYMTAB; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_DYNSYM)) == 0) enp->ec_type = SHT_DYNSYM; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_STRTAB)) == 0) enp->ec_type = SHT_STRTAB; else if ((strcmp(Start_tok, MSG_ORIG(MSG_STR_REL)) == 0) || (strcmp(Start_tok, MSG_ORIG(MSG_STR_RELA)) == 0)) enp->ec_type = ld_targ.t_m.m_rel_sht_type; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_HASH)) == 0) enp->ec_type = SHT_HASH; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_LIB)) == 0) enp->ec_type = SHT_SHLIB; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_LD_DYNAMIC)) == 0) enp->ec_type = SHT_DYNAMIC; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_NOTE)) == 0) enp->ec_type = SHT_NOTE; else if (strcmp(Start_tok, MSG_ORIG(MSG_STR_NOBITS)) == 0) enp->ec_type = SHT_NOBITS; else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSECTYP), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } /* * 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 (*Start_tok == '?') { if (b_attr) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SECFLAG)); return (S_ERROR); } b_attr = TRUE; b_bang = FALSE; Start_tok++; lowercase(Start_tok); for (; *Start_tok != '\0'; Start_tok++) switch (*Start_tok) { case '!': if (b_bang) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_BADFLAG), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } b_bang = TRUE; break; case 'a': if (enp->ec_attrmask & SHF_ALLOC) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_BADFLAG), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } 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) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_BADFLAG), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } 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) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_BADFLAG), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } enp->ec_attrmask |= SHF_EXECINSTR; if (!b_bang) enp->ec_attrbits |= SHF_EXECINSTR; b_bang = FALSE; break; default: eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_BADFLAG), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } /* * Section name. */ } else { if (b_name) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SECNAME)); return (S_ERROR); } b_name = TRUE; if ((enp->ec_name = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy((char *)enp->ec_name, Start_tok); /* * get the index for text reordering */ /* LINTED */ enp->ec_ndx = (Word)++index; } } if (tok == TK_COLON) { /* * File names. */ while ((tok = gettoken(ofl, mapfile, 0)) != TK_SEMICOLON) { char *file; if (tok != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MALFORM), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } if ((file = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(file, Start_tok); if (list_appendc(&(enp->ec_files), file) == 0) return (S_ERROR); } } return (1); } /* * Obtain a pseudo input file descriptor to assign to a mapfile. This is * required any time a symbol is generated. First traverse the input file * descriptors looking for a match. As all mapfile processing occurs before * any real input file processing this list is going to be small and we don't * need to do any filename clash checking. */ static Ifl_desc * map_ifl(const char *mapfile, Ofl_desc *ofl) { Ifl_desc *ifl; Listnode *lnp; for (LIST_TRAVERSE(&ofl->ofl_objs, lnp, ifl)) if (strcmp(ifl->ifl_name, mapfile) == 0) return (ifl); if ((ifl = libld_calloc(sizeof (Ifl_desc), 1)) == 0) return ((Ifl_desc *)S_ERROR); ifl->ifl_name = mapfile; ifl->ifl_flags = (FLG_IF_MAPFILE | FLG_IF_NEEDED | FLG_IF_FILEREF); if ((ifl->ifl_ehdr = libld_calloc(sizeof (Ehdr), 1)) == 0) return ((Ifl_desc *)S_ERROR); ifl->ifl_ehdr->e_type = ET_REL; if (list_appendc(&ofl->ofl_objs, ifl) == 0) return ((Ifl_desc *)S_ERROR); else return (ifl); } /* * Process a mapfile size symbol definition. * segment_name @ symbol_name; */ static uintptr_t map_atsign(const char *mapfile, Sg_desc *sgp, Ofl_desc *ofl) { Sym *sym; /* New symbol pointer */ Sym_desc *sdp; /* New symbol node pointer */ Ifl_desc *ifl; /* Dummy input file structure */ Token tok; /* Current token. */ avl_index_t where; if ((tok = gettoken(ofl, mapfile, 0)) != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSYM_1), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } if (sgp->sg_sizesym != NULL) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGSIZE), mapfile, EC_XWORD(Line_num), sgp->sg_name); return (S_ERROR); } /* * Make sure we have a pseudo file descriptor to associate to the * symbol. */ if ((ifl = map_ifl(mapfile, ofl)) == (Ifl_desc *)S_ERROR) return (S_ERROR); /* * Make sure the symbol doesn't already exist. It is possible that the * symbol has been scoped or versioned, in which case it does exist * but we can freely update it here. */ if ((sdp = ld_sym_find(Start_tok, SYM_NOHASH, &where, ofl)) == NULL) { char *name; Word hval; if ((name = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(name, Start_tok); if ((sym = libld_calloc(sizeof (Sym), 1)) == 0) return (S_ERROR); sym->st_shndx = SHN_ABS; sym->st_size = 0; sym->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT); DBG_CALL(Dbg_map_size_new(ofl->ofl_lml, name)); /* LINTED */ hval = (Word)elf_hash(name); if ((sdp = ld_sym_enter(name, sym, hval, ifl, ofl, 0, SHN_ABS, (FLG_SY_SPECSEC | FLG_SY_GLOBREF), 0, &where)) == (Sym_desc *)S_ERROR) return (S_ERROR); sdp->sd_flags &= ~FLG_SY_CLEAN; DBG_CALL(Dbg_map_symbol(ofl, sdp)); } else { sym = sdp->sd_sym; if (sym->st_shndx == SHN_UNDEF) { sdp->sd_shndx = sym->st_shndx = SHN_ABS; sdp->sd_flags |= FLG_SY_SPECSEC; sym->st_size = 0; sym->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT); sdp->sd_flags &= ~FLG_SY_MAPREF; DBG_CALL(Dbg_map_size_old(ofl, sdp)); } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SYMDEF1), mapfile, EC_XWORD(Line_num), demangle(sdp->sd_name), sdp->sd_file->ifl_name, MSG_INTL(MSG_MAP_DIFF_SYMMUL)); return (S_ERROR); } } /* * Assign the symbol to the segment. */ sgp->sg_sizesym = sdp; if (gettoken(ofl, mapfile, 0) != TK_SEMICOLON) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSCOL), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } return (1); } static uintptr_t map_pipe(Ofl_desc *ofl, const char *mapfile, Sg_desc *sgp) { char *sec_name; /* section name */ Token tok; /* current token. */ Sec_order *sc_order; static Word index = 0; /* used to maintain a increasing */ /* index for section ordering. */ if ((tok = gettoken(ofl, mapfile, 0)) != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSEC), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } if ((sec_name = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(sec_name, Start_tok); if ((sc_order = libld_malloc(sizeof (Sec_order))) == 0) return (S_ERROR); sc_order->sco_secname = sec_name; sc_order->sco_index = ++index; if (aplist_append(&sgp->sg_secorder, sc_order, AL_CNT_SG_SECORDER) == NULL) return (S_ERROR); ofl->ofl_flags |= FLG_OF_SECORDER; DBG_CALL(Dbg_map_pipe(ofl->ofl_lml, sgp, sec_name, index)); if ((tok = gettoken(ofl, mapfile, 0)) != TK_SEMICOLON) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSCOL), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } return (1); } /* * Process a mapfile library specification definition. * shared_object_name - shared object definition * shared object definition : [ shared object type [ = SONAME ]] * [ versions ]; */ static uintptr_t map_dash(const char *mapfile, char *name, Ofl_desc *ofl) { char *version; Token tok; Sdf_desc *sdf; Sdv_desc *sdv; enum { MD_NONE = 0, MD_SPECVERS, MD_ADDVERS, MD_NEEDED } dolkey = MD_NONE; /* * If a shared object definition for this file already exists use it, * otherwise allocate a new descriptor. */ if ((sdf = sdf_find(name, &ofl->ofl_socntl)) == 0) { if ((sdf = sdf_add(name, &ofl->ofl_socntl)) == (Sdf_desc *)S_ERROR) return (S_ERROR); sdf->sdf_rfile = mapfile; } /* * Get the shared object descriptor string. */ while ((tok = gettoken(ofl, mapfile, 0)) != TK_SEMICOLON) { if ((tok != TK_STRING) && (tok != TK_EQUAL)) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSO), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } /* * Determine if the library type is accompanied with a SONAME * definition. */ if (tok == TK_EQUAL) { if ((tok = gettoken(ofl, mapfile, 0)) != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSO), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } switch (dolkey) { case MD_NEEDED: if (sdf->sdf_flags & FLG_SDF_SONAME) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_MULSONAME), mapfile, EC_XWORD(Line_num), name, sdf->sdf_soname, Start_tok); dolkey = MD_NONE; continue; } if ((sdf->sdf_soname = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy((char *)sdf->sdf_soname, Start_tok); sdf->sdf_flags |= FLG_SDF_SONAME; break; case MD_SPECVERS: case MD_ADDVERS: if ((sdv = libld_calloc( sizeof (Sdv_desc), 1)) == 0) return (S_ERROR); if (dolkey == MD_SPECVERS) sdf->sdf_flags |= FLG_SDF_SPECVER; else sdf->sdf_flags |= FLG_SDF_ADDVER; if ((sdf->sdf_flags & (FLG_SDF_SPECVER | FLG_SDF_ADDVER)) == (FLG_SDF_SPECVER | FLG_SDF_ADDVER)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_INCOMPFLG), mapfile, EC_XWORD(Line_num), sdf->sdf_name); return (S_ERROR); } if ((version = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(version, Start_tok); sdv->sdv_name = version; sdv->sdv_ref = mapfile; if (list_appendc(&sdf->sdf_verneed, sdv) == 0) return (S_ERROR); break; case MD_NONE: eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNEXTOK), mapfile, EC_XWORD(Line_num), '='); return (S_ERROR); } dolkey = MD_NONE; continue; } /* * A shared object type has been specified. This may also be * accompanied by an SONAME redefinition (see above). */ if (*Start_tok == '$') { if (dolkey != MD_NONE) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNEXTOK), mapfile, EC_XWORD(Line_num), '$'); return (S_ERROR); } Start_tok++; lowercase(Start_tok); if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_NEED)) == 0) dolkey = MD_NEEDED; else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_SPECVERS)) == 0) dolkey = MD_SPECVERS; else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_ADDVERS)) == 0) dolkey = MD_ADDVERS; else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSOTYP), mapfile, EC_XWORD(Line_num), Start_tok); return (S_ERROR); } continue; } /* * shared object version requirement. */ if ((version = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(version, Start_tok); if ((sdv = libld_calloc(sizeof (Sdv_desc), 1)) == 0) return (S_ERROR); sdv->sdv_name = version; sdv->sdv_ref = mapfile; sdf->sdf_flags |= FLG_SDF_SELECT; if (list_appendc(&sdf->sdf_vers, sdv) == 0) return (S_ERROR); } DBG_CALL(Dbg_map_dash(ofl->ofl_lml, name, sdf)); return (1); } /* * 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 ]; * */ #define FLG_SCOPE_HIDD 0 /* symbol defined hidden/local */ #define FLG_SCOPE_DFLT 1 /* symbol defined default/global */ #define FLG_SCOPE_PROT 2 /* symbol defined protected/symbolic */ #define FLG_SCOPE_EXPT 3 /* symbol defined exported */ #define FLG_SCOPE_SNGL 4 /* symbol defined singleton */ #define FLG_SCOPE_ELIM 5 /* symbol defined eliminate */ static uintptr_t map_version(const char *mapfile, char *name, Ofl_desc *ofl) { Token tok; Sym *sym; int scope = FLG_SCOPE_DFLT, errcnt = 0; Ver_desc *vdp; Word hash; Ifl_desc *ifl; avl_index_t where; /* * If we're generating segments within the image then any symbol * reductions will be processed (ie. applied to relocations and symbol * table entries). Otherwise (when creating a relocatable object) any * versioning information is simply recorded for use in a later * (segment generating) link-edit. */ if (ofl->ofl_flags & FLG_OF_RELOBJ) ofl->ofl_flags |= FLG_OF_VERDEF; /* * If this is a new mapfile reference generate an input file descriptor * to represent it. Otherwise this must simply be a new version within * the mapfile we've previously been processing, in this case continue * to use the original input file descriptor. */ if ((ifl = map_ifl(mapfile, ofl)) == (Ifl_desc *)S_ERROR) return (S_ERROR); /* * If no version descriptors have yet been set up, initialize a base * version to represent the output file itself. This `base' version * catches any internally generated symbols (_end, _etext, etc.) and * serves to initialize the output version descriptor count. */ if (ofl->ofl_vercnt == 0) { if (ld_vers_base(ofl) == (Ver_desc *)S_ERROR) return (S_ERROR); } /* * If this definition has an associated version name then generate a * new version descriptor and an associated version symbol index table. */ if (name) { ofl->ofl_flags |= FLG_OF_VERDEF; /* * Traverse the present version descriptor list to see if there * is already one of the same name, otherwise create a new one. */ /* LINTED */ hash = (Word)elf_hash(name); if ((vdp = ld_vers_find(name, hash, &ofl->ofl_verdesc)) == 0) { if ((vdp = ld_vers_desc(name, hash, &ofl->ofl_verdesc)) == (Ver_desc *)S_ERROR) return (S_ERROR); } /* * Initialize any new version with an index, the file from which * it was first referenced, and a WEAK flag (indicates that * there are no symbols assigned to it yet). */ if (vdp->vd_ndx == 0) { /* LINTED */ vdp->vd_ndx = (Half)++ofl->ofl_vercnt; vdp->vd_file = ifl; vdp->vd_flags = VER_FLG_WEAK; } } else { /* * If a version definition hasn't been specified assign any * symbols to the base version. */ vdp = (Ver_desc *)ofl->ofl_verdesc.head->data; } /* * Scan the mapfile entry picking out scoping and symbol definitions. */ while ((tok = gettoken(ofl, mapfile, 0)) != TK_RIGHTBKT) { Sym_desc * sdp; Word shndx = SHN_UNDEF; uchar_t type = STT_NOTYPE; Addr value = 0, size = 0; char *_name, *filtee = 0; Word sym_flags = 0; Half sym_flags1 = 0; uint_t filter = 0, novalue = 1, dftflag; const char *conflict; if ((tok != TK_STRING) && (tok != TK_COLON)) { if (tok == TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSYM_2), mapfile, EC_XWORD(Line_num)); if ((tok == TK_ERROR) || (tok == TK_EOF)) return (S_ERROR); errcnt++; continue; } if ((_name = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(_name, Start_tok); if (tok != TK_COLON) { tok = gettoken(ofl, mapfile, 0); if ((tok == TK_ERROR) || (tok == TK_EOF)) { 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). */ vdp->vd_flags &= ~VER_FLG_WEAK; switch (tok) { case TK_COLON: /* * Establish a new scope. All symbols added by this * mapfile are actually global entries, and are assigned * the scope that is presently in effect. * * If a protected/symbolic scope is detected, remember * this. If a protected/symbolic scope is the only * scope defined in this (or any other mapfiles), then * the mode -Bsymbolic is established. */ if ((strcmp(MSG_ORIG(MSG_MAP_DEFAULT), _name) == 0) || (strcmp(MSG_ORIG(MSG_MAP_GLOBAL), _name) == 0)) { scope = FLG_SCOPE_DFLT; ofl->ofl_flags |= FLG_OF_MAPGLOB; } else if ((strcmp(MSG_ORIG(MSG_MAP_HIDDEN), _name) == 0) || (strcmp(MSG_ORIG(MSG_STR_LOCAL), _name) == 0)) { scope = FLG_SCOPE_HIDD; } else if ((strcmp(MSG_ORIG(MSG_MAP_PROTECTED), _name) == 0) || (strcmp(MSG_ORIG(MSG_STR_SYMBOLIC), _name) == 0)) { scope = FLG_SCOPE_PROT; ofl->ofl_flags |= FLG_OF_MAPSYMB; } else if (strcmp(MSG_ORIG(MSG_STR_EXPORTED), _name) == 0) { scope = FLG_SCOPE_EXPT; } else if (strcmp(MSG_ORIG(MSG_STR_SINGLETON), _name) == 0) { scope = FLG_SCOPE_SNGL; ofl->ofl_flags |= FLG_OF_MAPGLOB; } else if (strcmp(MSG_ORIG(MSG_STR_ELIMINATE), _name) == 0) { scope = FLG_SCOPE_ELIM; } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSYMSCO), mapfile, EC_XWORD(Line_num), _name); errcnt++; } 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 = gettoken(ofl, mapfile, 0)) != TK_SEMICOLON) { if ((tok == TK_ERROR) || (tok == TK_EOF)) return (S_ERROR); /* * If we had previously seen a filter or * auxiliary filter requirement, the next string * is the filtee itself. */ if (filter) { if (filtee) { /* BEGIN CSTYLED */ eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MULTFILTEE), mapfile, EC_XWORD(Line_num), _name); errcnt++; continue; /* END CSTYLED */ } if ((filtee = libld_malloc( strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(filtee, Start_tok); filter = 0; continue; } /* * Determine any Value or Size attributes. */ lowercase(Start_tok); if (Start_tok[0] == 'v' || Start_tok[0] == 's') { char *end_tok; Lword number; if ((number = (Lword)STRTOADDR( &Start_tok[1], &end_tok, 0)) == XWORD_MAX) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGADDR), mapfile, EC_XWORD(Line_num), Start_tok, MSG_INTL(MSG_MAP_EXCLIMIT)); errcnt++; continue; } if (end_tok != strchr(Start_tok, '\0')) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGADDR), mapfile, EC_XWORD(Line_num), Start_tok, MSG_INTL(MSG_MAP_NOBADFRM)); errcnt++; continue; } switch (*Start_tok) { case 'v': /* BEGIN CSTYLED */ if (value) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SYMVAL)); errcnt++; continue; } /* LINTED */ value = (Addr)number; novalue = 0; break; /* END CSTYLED */ case 's': /* BEGIN CSTYLED */ if (size) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_MOREONCE), mapfile, EC_XWORD(Line_num), MSG_INTL(MSG_MAP_SYMSIZE)); errcnt++; continue; } /* LINTED */ size = (Addr)number; break; /* END CSTYLED */ } } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_FUNCTION)) == 0) { shndx = SHN_ABS; sym_flags |= FLG_SY_SPECSEC; type = STT_FUNC; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_DATA)) == 0) { shndx = SHN_ABS; sym_flags |= FLG_SY_SPECSEC; type = STT_OBJECT; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_COMMON)) == 0) { shndx = SHN_COMMON; sym_flags |= FLG_SY_SPECSEC; type = STT_OBJECT; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_PARENT)) == 0) { sym_flags |= FLG_SY_PARENT; ofl->ofl_flags |= FLG_OF_SYMINFO; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_EXTERN)) == 0) { sym_flags |= FLG_SY_EXTERN; ofl->ofl_flags |= FLG_OF_SYMINFO; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_DIRECT)) == 0) { sym_flags1 |= FLG_SY1_DIR; ofl->ofl_flags |= FLG_OF_SYMINFO; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_NODIRECT)) == 0) { sym_flags1 |= FLG_SY1_NDIR; ofl->ofl_flags |= FLG_OF_SYMINFO; ofl->ofl_flags1 |= FLG_OF1_NDIRECT; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_FILTER)) == 0) { dftflag = filter = FLG_SY_STDFLTR; sym_flags |= FLG_SY_STDFLTR; ofl->ofl_flags |= FLG_OF_SYMINFO; continue; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_AUXILIARY)) == 0) { dftflag = filter = FLG_SY_AUXFLTR; sym_flags |= FLG_SY_AUXFLTR; ofl->ofl_flags |= FLG_OF_SYMINFO; continue; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_INTERPOSE)) == 0) { if (!(ofl->ofl_flags & FLG_OF_EXEC)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_NOINTPOSE), mapfile, EC_XWORD(Line_num)); errcnt++; continue; } sym_flags |= FLG_SY_INTPOSE; ofl->ofl_flags |= FLG_OF_SYMINFO; ofl->ofl_dtflags_1 |= DF_1_SYMINTPOSE; continue; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_DYNSORT)) == 0) { sym_flags |= FLG_SY_DYNSORT; sym_flags &= ~FLG_SY_NODYNSORT; continue; } else if (strcmp(Start_tok, MSG_ORIG(MSG_MAP_NODYNSORT)) == 0) { sym_flags &= ~FLG_SY_DYNSORT; sym_flags |= FLG_SY_NODYNSORT; continue; } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSYMDEF), mapfile, EC_XWORD(Line_num), Start_tok); errcnt++; continue; } } /* FALLTHROUGH */ case TK_SEMICOLON: /* * The special auto-reduction directive `*' can be * specified in hidden/local, and eliminate scope. This * directive indicates that all symbols processed that * are not explicitly defined to be global are to be * reduced to hidden/local scope in, or eliminated from, * the output image. * * An auto-reduction directive also implies that a * version definition be created, as the user has * effectively defined an interface. */ if (*_name == '*') { if (scope == FLG_SCOPE_HIDD) ofl->ofl_flags |= (FLG_OF_VERDEF | FLG_OF_AUTOLCL); else if (scope == FLG_SCOPE_ELIM) { ofl->ofl_flags |= (FLG_OF_VERDEF | FLG_OF_AUTOELM); } 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. */ /* LINTED */ hash = (Word)elf_hash(_name); DBG_CALL(Dbg_map_version(ofl->ofl_lml, name, _name, scope)); if ((sdp = ld_sym_find(_name, hash, &where, ofl)) == NULL) { if ((sym = libld_calloc(sizeof (Sym), 1)) == 0) return (S_ERROR); /* * Make sure any parent or external declarations * fall back to references. */ if (sym_flags & (FLG_SY_PARENT | FLG_SY_EXTERN)) { /* * Turn it into a reference by setting * the section index to UNDEF. */ sym->st_shndx = shndx = SHN_UNDEF; /* * It is wrong to to specify size * or value for an external symbol. */ if ((novalue == 0) || (size != 0)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_NOEXVLSZ), mapfile, EC_XWORD(Line_num)); errcnt++; continue; } } else { sym->st_shndx = (Half)shndx; } sym->st_value = value; sym->st_size = size; sym->st_info = ELF_ST_INFO(STB_GLOBAL, type); if ((sdp = ld_sym_enter(_name, sym, hash, ifl, ofl, 0, shndx, sym_flags, sym_flags1, &where)) == (Sym_desc *)S_ERROR) return (S_ERROR); sdp->sd_flags &= ~FLG_SY_CLEAN; /* * Identify any references. FLG_SY_MAPREF is * turned off once a relocatable object with * the same symbol is found, thus the existence * of FLG_SY_MAPREF at symbol validation is * used to flag undefined/misspelled entries. */ if (sym->st_shndx == SHN_UNDEF) sdp->sd_flags |= (FLG_SY_MAPREF | FLG_SY_GLOBREF); } else { conflict = NULL; sym = sdp->sd_sym; /* * If this symbol already exists, make sure this * definition doesn't conflict with the former. * Provided it doesn't, multiple definitions * from different mapfiles can augment each * other. */ /* BEGIN CSTYLED */ if (sym->st_value) { if (value && (sym->st_value != value)) conflict = MSG_INTL(MSG_MAP_DIFF_SYMVAL); } else { sym->st_value = value; } if (sym->st_size) { if (size && (sym->st_size != size)) conflict = MSG_INTL(MSG_MAP_DIFF_SYMSZ); } else { sym->st_size = size; } if (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE) { if ((type != STT_NOTYPE) && (ELF_ST_TYPE(sym->st_info) != type)) conflict = MSG_INTL(MSG_MAP_DIFF_SYMTYP); } else { sym->st_info = ELF_ST_INFO(STB_GLOBAL, type); } if (sym->st_shndx != SHN_UNDEF) { if ((shndx != SHN_UNDEF) && (sym->st_shndx != shndx)) conflict = MSG_INTL(MSG_MAP_DIFF_SYMNDX); } else { sdp->sd_shndx = sym->st_shndx = shndx; } /* END CSTYLED */ if ((sdp->sd_flags1 & MSK_SY1_GLOBAL) && (sdp->sd_aux->sa_overndx != VER_NDX_GLOBAL) && (vdp->vd_ndx != VER_NDX_GLOBAL) && (sdp->sd_aux->sa_overndx != vdp->vd_ndx)) { conflict = MSG_INTL(MSG_MAP_DIFF_SYMVER); } if (conflict) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SYMDEF1), mapfile, EC_XWORD(Line_num), demangle(_name), sdp->sd_file->ifl_name, conflict); errcnt++; continue; } /* * If this mapfile entry supplies a definition, * indicate that the symbol is now used. */ if (shndx != SHN_UNDEF) sdp->sd_flags |= FLG_SY_MAPUSED; } /* * A symbol declaration that defines a size but no * value is processed as a request to create an * associated backing section. The intent behind this * functionality is to provide OBJT definitions within * filters that are not ABS. ABS symbols don't allow * copy-relocations to be established to filter OBJT * definitions. */ if ((shndx == SHN_ABS) && size && novalue) { /* Create backing section if not there */ if (sdp->sd_isc == NULL) { Is_desc *isp; if (type == STT_OBJECT) { if ((isp = ld_make_data(ofl, size)) == (Is_desc *)S_ERROR) return (S_ERROR); } else { if ((isp = ld_make_text(ofl, size)) == (Is_desc *)S_ERROR) return (S_ERROR); } sdp->sd_isc = isp; isp->is_file = ifl; } /* * Now that backing storage has been created, * associate the symbol descriptor. Remove the * symbols special section tag so that it will * be assigned the correct section index as part * of update symbol processing. */ sdp->sd_flags &= ~FLG_SY_SPECSEC; sym_flags &= ~FLG_SY_SPECSEC; } /* * Indicate the new symbols scope. Although the * symbols st_other field will eventually be updated as * part of writing out the final symbol, update the * st_other field here to trigger better diagnostics * during symbol validation (for example, undefined * references that are defined symbolic in a mapfile). */ if (scope == FLG_SCOPE_HIDD) { /* * This symbol needs to be reduced to local. */ if (ofl->ofl_flags & FLG_OF_REDLSYM) { sdp->sd_flags1 |= (FLG_SY1_HIDDEN | FLG_SY1_ELIM); sdp->sd_sym->st_other = STV_ELIMINATE; } else { sdp->sd_flags1 |= FLG_SY1_HIDDEN; sdp->sd_sym->st_other = STV_HIDDEN; } } else if (scope == FLG_SCOPE_ELIM) { /* * This symbol needs to be eliminated. Note, * the symbol is also tagged as local to trigger * any necessary relocation processing prior * to the symbol being eliminated. */ sdp->sd_flags1 |= (FLG_SY1_HIDDEN | FLG_SY1_ELIM); sdp->sd_sym->st_other = STV_ELIMINATE; } else { /* * This symbol is explicitly defined to remain * global. */ sdp->sd_flags |= sym_flags; sdp->sd_flags1 |= sym_flags1; /* * Qualify any global scope. */ if (scope == FLG_SCOPE_SNGL) { sdp->sd_flags1 |= (FLG_SY1_SINGLE | FLG_SY1_NDIR); sdp->sd_sym->st_other = STV_SINGLETON; } else if (scope == FLG_SCOPE_PROT) { sdp->sd_flags1 |= FLG_SY1_PROTECT; sdp->sd_sym->st_other = STV_PROTECTED; } else if (scope == FLG_SCOPE_EXPT) { sdp->sd_flags1 |= FLG_SY1_EXPORT; sdp->sd_sym->st_other = STV_EXPORTED; } else sdp->sd_flags1 |= FLG_SY1_DEFAULT; /* * Record the present version index for later * potential versioning. */ if ((sdp->sd_aux->sa_overndx == 0) || (sdp->sd_aux->sa_overndx == VER_NDX_GLOBAL)) sdp->sd_aux->sa_overndx = vdp->vd_ndx; vdp->vd_flags |= FLG_VER_REFER; } conflict = NULL; /* * Carry out some validity checks to ensure incompatible * symbol characteristics have not been defined. * These checks are carried out after symbols are added * or resolved, to catch single instance, and * multi-instance definition inconsistencies. */ if ((sdp->sd_flags1 & (FLG_SY1_HIDDEN | FLG_SY1_ELIM)) && ((scope != FLG_SCOPE_HIDD) && (scope != FLG_SCOPE_ELIM))) { conflict = MSG_INTL(MSG_MAP_DIFF_SYMLCL); } else if (((sdp->sd_flags1 & FLG_SY1_SINGLE) || (sdp->sd_flags1 & FLG_SY1_EXPORT)) && ((scope != FLG_SCOPE_DFLT) && (scope != FLG_SCOPE_EXPT) && (scope != FLG_SCOPE_SNGL))) { conflict = MSG_INTL(MSG_MAP_DIFF_SYMGLOB); } else if ((sdp->sd_flags1 & FLG_SY1_PROTECT) && ((scope != FLG_SCOPE_DFLT) && (scope != FLG_SCOPE_PROT))) { conflict = MSG_INTL(MSG_MAP_DIFF_SYMPROT); } else if ((sdp->sd_flags1 & FLG_SY1_NDIR) && (scope == FLG_SCOPE_PROT)) { conflict = MSG_INTL(MSG_MAP_DIFF_PROTNDIR); } else if ((sdp->sd_flags1 & FLG_SY1_DIR) && (scope == FLG_SCOPE_SNGL)) { conflict = MSG_INTL(MSG_MAP_DIFF_SNGLDIR); } if (conflict) { /* * Select the conflict message from either a * single instance or multi-instance definition. */ if (sdp->sd_file->ifl_name == mapfile) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SYMDEF2), mapfile, EC_XWORD(Line_num), demangle(_name), conflict); } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SYMDEF1), mapfile, EC_XWORD(Line_num), demangle(_name), sdp->sd_file->ifl_name, conflict); } errcnt++; continue; } /* * Indicate that this symbol has been explicitly * contributed from a mapfile. */ sdp->sd_flags1 |= (FLG_SY1_MAPFILE | FLG_SY1_EXPDEF); /* * If we've encountered a symbol definition simulate * that an input file has been processed - this allows * things like filters to be created purely from a * mapfile. */ if (type != STT_NOTYPE) ofl->ofl_objscnt++; DBG_CALL(Dbg_map_symbol(ofl, sdp)); /* * If this symbol has an associated filtee, record the * filtee string and associate the string index with the * symbol. This is used later to associate the syminfo * information with the necessary .dynamic entry. */ if (filter && (filtee == 0)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_NOFILTER), mapfile, EC_XWORD(Line_num), _name); errcnt++; continue; } if (filtee) { Dfltr_desc * dftp; Sfltr_desc sft; Aliste idx, _idx, nitems; /* * Make sure we don't duplicate any filtee * strings, and create a new descriptor if * necessary. */ idx = nitems = alist_nitems(ofl->ofl_dtsfltrs); for (ALIST_TRAVERSE(ofl->ofl_dtsfltrs, _idx, dftp)) { if ((dftflag != dftp->dft_flag) || (strcmp(dftp->dft_str, filtee))) continue; idx = _idx; break; } if (idx == nitems) { Dfltr_desc dft; dft.dft_str = filtee; dft.dft_flag = dftflag; dft.dft_ndx = 0; /* * The following append puts the new * item at the offset contained in * idx, because we know idx contains * the index of the next available slot. */ if (alist_append(&ofl->ofl_dtsfltrs, &dft, sizeof (Dfltr_desc), AL_CNT_OFL_DTSFLTRS) == NULL) return (S_ERROR); } /* * Create a new filter descriptor for this * symbol. */ sft.sft_sdp = sdp; sft.sft_idx = idx; if (alist_append(&ofl->ofl_symfltrs, &sft, sizeof (Sfltr_desc), AL_CNT_OFL_SYMFLTRS) == NULL) return (S_ERROR); } break; default: eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSCOL), mapfile, EC_XWORD(Line_num)); errcnt++; continue; } } if (errcnt) return (S_ERROR); /* * Determine if any version references are provided after the close * bracket. */ while ((tok = gettoken(ofl, mapfile, 0)) != TK_SEMICOLON) { Ver_desc *_vdp; char *_name; if (tok != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPVERS), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } name = Start_tok; if (vdp->vd_ndx == VER_NDX_GLOBAL) { eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_UNEXDEP), mapfile, EC_XWORD(Line_num), name); continue; } /* * Generate a new version descriptor if it doesn't already * exist. */ /* LINTED */ hash = (Word)elf_hash(name); if ((_vdp = ld_vers_find(name, hash, &ofl->ofl_verdesc)) == 0) { if ((_name = libld_malloc(strlen(name) + 1)) == 0) return (S_ERROR); (void) strcpy(_name, name); if ((_vdp = ld_vers_desc(_name, hash, &ofl->ofl_verdesc)) == (Ver_desc *)S_ERROR) return (S_ERROR); } /* * Add the new version descriptor to the parent version * descriptors reference list. Indicate the version descriptors * first reference (used for error disgnostics if undefined * version dependencies remain). */ if (ld_vers_find(name, hash, &vdp->vd_deps) == 0) if (list_appendc(&vdp->vd_deps, _vdp) == 0) return (S_ERROR); if (_vdp->vd_ref == 0) _vdp->vd_ref = vdp; } return (1); } /* * Sort the segment list by increasing virtual address. */ uintptr_t ld_sort_seg_list(Ofl_desc *ofl) { List seg1, seg2; Listnode *lnp1, *lnp2, *lnp3; Sg_desc *sgp1, *sgp2; seg1.head = seg1.tail = seg2.head = seg2.tail = NULL; /* * Add the .phdr and .interp segments to our list. These segments must * occur before any PT_LOAD segments (refer exec/elf/elf.c). Also add * the capabilities segment. This isn't essential, but the capabilities * section is one of the first in an object. */ for (LIST_TRAVERSE(&ofl->ofl_segs, lnp1, sgp1)) { Word type = sgp1->sg_phdr.p_type; if ((type == PT_PHDR) || (type == PT_INTERP) || (type == PT_SUNWCAP)) { if (list_appendc(&seg1, sgp1) == 0) return (S_ERROR); } } /* * Add the loadable segments to another list in sorted order. */ for (LIST_TRAVERSE(&ofl->ofl_segs, lnp1, sgp1)) { DBG_CALL(Dbg_map_sort_orig(ofl->ofl_lml, sgp1)); if (sgp1->sg_phdr.p_type != PT_LOAD) continue; if (!(sgp1->sg_flags & FLG_SG_VADDR) || (sgp1->sg_flags & FLG_SG_EMPTY)) { if (list_appendc(&seg2, sgp1) == 0) return (S_ERROR); } else { if (seg2.head == NULL) { if (list_appendc(&seg2, sgp1) == 0) return (S_ERROR); continue; } lnp3 = NULL; for (LIST_TRAVERSE(&seg2, lnp2, sgp2)) { if (!(sgp2->sg_flags & FLG_SG_VADDR) || (sgp2->sg_flags & FLG_SG_EMPTY)) { if (lnp3 == NULL) { if (list_prependc(&seg2, sgp1) == 0) return (S_ERROR); } else { if (list_insertc(&seg2, sgp1, lnp3) == 0) return (S_ERROR); } lnp3 = NULL; break; } if (sgp1->sg_phdr.p_vaddr < sgp2->sg_phdr.p_vaddr) { if (lnp3 == NULL) { if (list_prependc(&seg2, sgp1) == 0) return (S_ERROR); } else { if (list_insertc(&seg2, sgp1, lnp3) == 0) return (S_ERROR); } lnp3 = NULL; break; } else if (sgp1->sg_phdr.p_vaddr > sgp2->sg_phdr.p_vaddr) { lnp3 = lnp2; } else { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGSAME), sgp1->sg_name, sgp2->sg_name); return (S_ERROR); } } if (lnp3 != NULL) if (list_appendc(&seg2, sgp1) == 0) return (S_ERROR); } } /* * Add the sorted loadable segments to our list. */ for (LIST_TRAVERSE(&seg2, lnp1, sgp1)) { if (list_appendc(&seg1, sgp1) == 0) return (S_ERROR); } /* * Add all other segments to our list. */ for (LIST_TRAVERSE(&ofl->ofl_segs, lnp1, sgp1)) { Word type = sgp1->sg_phdr.p_type; if ((type != PT_PHDR) && (type != PT_INTERP) && (type != PT_SUNWCAP) && (type != PT_LOAD)) { if (list_appendc(&seg1, sgp1) == 0) return (S_ERROR); } } ofl->ofl_segs.head = ofl->ofl_segs.tail = NULL; /* * Now rebuild the original list and process all of the * segment/section ordering information if present. */ for (LIST_TRAVERSE(&seg1, lnp1, sgp1)) { DBG_CALL(Dbg_map_sort_fini(ofl->ofl_lml, sgp1)); if (list_appendc(&ofl->ofl_segs, sgp1) == 0) return (S_ERROR); } return (1); } /* * Parse the mapfile. */ uintptr_t ld_map_parse(const char *mapfile, Ofl_desc *ofl) { struct stat stat_buf; /* stat of mapfile */ int mapfile_fd; /* descriptor for mapfile */ Listnode *lnp1; /* node pointer */ Listnode *lnp2; /* node pointer */ Sg_desc *sgp1; /* seg descriptor being manipulated */ Sg_desc *sgp2; /* temp segment descriptor pointer */ Ent_desc *enp; /* Segment entrance criteria. */ Token tok; /* current token. */ Listnode *e_next = NULL; /* next place for entrance criterion */ Boolean new_segment; /* If true, defines new segment. */ char *name; static int num_stack = 0; /* number of stack segment */ int err; DBG_CALL(Dbg_map_parse(ofl->ofl_lml, mapfile)); /* * Determine if we're dealing with a file or a directory. */ if (stat(mapfile, &stat_buf) == -1) { err = errno; eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_SYS_STAT), mapfile, strerror(err)); return (S_ERROR); } if (S_ISDIR(stat_buf.st_mode)) { DIR *dirp; struct dirent *denp; /* * Open the directory and interpret each visible file as a * mapfile. */ if ((dirp = opendir(mapfile)) == 0) return (1); while ((denp = readdir(dirp)) != NULL) { char path[PATH_MAX]; /* * Ignore any hidden filenames. Construct the full * pathname to the new mapfile. */ if (*denp->d_name == '.') continue; (void) snprintf(path, PATH_MAX, MSG_ORIG(MSG_STR_PATH), mapfile, denp->d_name); if (ld_map_parse(path, ofl) == S_ERROR) return (S_ERROR); } (void) closedir(dirp); return (1); } else if (!S_ISREG(stat_buf.st_mode)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_SYS_NOTREG), mapfile); return (S_ERROR); } /* * We read the entire mapfile into memory. */ if ((Mapspace = libld_malloc(stat_buf.st_size + 1)) == 0) return (S_ERROR); if ((mapfile_fd = open(mapfile, O_RDONLY)) == -1) { err = errno; eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), mapfile, strerror(err)); return (S_ERROR); } if (read(mapfile_fd, Mapspace, stat_buf.st_size) != stat_buf.st_size) { err = errno; eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_SYS_READ), mapfile, strerror(err)); return (S_ERROR); } Mapspace[stat_buf.st_size] = '\0'; nextchr = Mapspace; /* * Set up any global variables, the line number counter and file name. */ Line_num = 1; /* * We now parse the mapfile until the gettoken routine returns EOF. */ while ((tok = gettoken(ofl, mapfile, 1)) != TK_EOF) { int ndx = -1; /* * Don't know which segment yet. */ sgp1 = NULL; /* * At this point we are at the beginning of a line, and the * variable `Start_tok' 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(mapfile, (char *)0, ofl) == S_ERROR) return (S_ERROR); continue; } if (tok != TK_STRING) { if (tok != TK_ERROR) eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPSEGNAM), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } /* * Save the initial token. */ if ((name = libld_malloc(strlen(Start_tok) + 1)) == 0) return (S_ERROR); (void) strcpy(name, Start_tok); /* * Now check the second character on the line. The special `-' * and `{' characters do not involve any segment manipulation so * we handle them first. */ tok = gettoken(ofl, mapfile, 0); if ((tok == TK_ERROR) || (tok == TK_EOF)) return (S_ERROR); if (tok == TK_DASH) { if (map_dash(mapfile, name, ofl) == S_ERROR) return (S_ERROR); continue; } if (tok == TK_LEFTBKT) { if (map_version(mapfile, name, ofl) == S_ERROR) return (S_ERROR); continue; } /* * If we're here we need to interpret the first string as a * segment name. Find the segment named in the token. */ for (LIST_TRAVERSE(&ofl->ofl_segs, lnp1, sgp2)) { ndx++; if (strcmp(sgp2->sg_name, name) == 0) { sgp1 = sgp2; sgp2->sg_flags &= ~FLG_SG_DISABLED; new_segment = FALSE; break; } } /* * If the second token is a '|' then we had better * of 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) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SECINSEG), mapfile, EC_XWORD(Line_num), name); return (S_ERROR); } else { if (map_pipe(ofl, mapfile, sgp1) == S_ERROR) return (S_ERROR); continue; } } /* * If segment is still NULL then it does not exist. Create a * new segment, and leave its values as 0 so that map_equal() * can detect changing attributes. */ if (sgp1 == NULL) { if ((sgp1 = libld_calloc(sizeof (Sg_desc), 1)) == 0) return (S_ERROR); sgp1->sg_phdr.p_type = PT_NULL; sgp1->sg_name = name; new_segment = TRUE; ndx = -1; } if ((strcmp(sgp1->sg_name, MSG_ORIG(MSG_STR_INTERP)) == 0) || (strcmp(sgp1->sg_name, MSG_ORIG(MSG_STR_LD_DYNAMIC)) == 0)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGRESV), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } /* * Now check the second token from the input line. */ if (tok == TK_EQUAL) { if (strcmp(sgp1->sg_name, MSG_ORIG(MSG_STR_HWCAP_1)) == 0) { if (map_cap(mapfile, CA_SUNW_HW_1, ofl) == S_ERROR) return (S_ERROR); DBG_CALL(Dbg_cap_mapfile(ofl->ofl_lml, CA_SUNW_HW_1, ofl->ofl_hwcap_1, ld_targ.t_m.m_mach)); continue; } else if (strcmp(sgp1->sg_name, MSG_ORIG(MSG_STR_SFCAP_1)) == 0) { if (map_cap(mapfile, CA_SUNW_SF_1, ofl) == S_ERROR) return (S_ERROR); DBG_CALL(Dbg_cap_mapfile(ofl->ofl_lml, CA_SUNW_SF_1, ofl->ofl_sfcap_1, ld_targ.t_m.m_mach)); continue; } else { if (map_equal(mapfile, sgp1, ofl) == S_ERROR) return (S_ERROR); ofl->ofl_flags |= FLG_OF_SEGSORT; DBG_CALL(Dbg_map_set_equal(new_segment)); } } else if (tok == TK_COLON) { /* * If this is an existing segment reservation, sections * can't be assigned to it. */ if ((new_segment == FALSE) && (sgp1->sg_flags & FLG_SG_EMPTY)) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_SEGEMPSEC), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } /* * We are looking at a new entrance criteria line. * Note that entrance criteria are added in the order * they are found in the map file, but are placed * before any default criteria. */ if ((enp = libld_calloc(sizeof (Ent_desc), 1)) == 0) return (S_ERROR); enp->ec_segment = sgp1; if (e_next == NULL) { if ((e_next = list_prependc(&ofl->ofl_ents, enp)) == 0) return (S_ERROR); } else { if ((e_next = list_insertc(&ofl->ofl_ents, enp, e_next)) == 0) return (S_ERROR); } if (map_colon(ofl, mapfile, enp) == S_ERROR) return (S_ERROR); ofl->ofl_flags |= FLG_OF_SEGSORT; DBG_CALL(Dbg_map_ent(ofl->ofl_lml, new_segment, enp, ofl)); } else if (tok == TK_ATSIGN) { if (map_atsign(mapfile, sgp1, ofl) == S_ERROR) return (S_ERROR); DBG_CALL(Dbg_map_set_atsign(new_segment)); } else if (tok != TK_ERROR) { eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_EXPEQU), mapfile, EC_XWORD(Line_num)); return (S_ERROR); } /* * Having completed parsing an entry in the map file determine * if the segment to which it applies is new. */ if (new_segment) { int src_type, dst_type; /* * If specific fields have not been supplied via * map_equal(), make sure defaults are supplied. */ if (((sgp1->sg_flags & FLG_SG_TYPE) == 0) && (sgp1->sg_phdr.p_type == PT_NULL)) { /* * Default to a loadable segment. */ sgp1->sg_phdr.p_type = PT_LOAD; sgp1->sg_flags |= FLG_SG_TYPE; } if (sgp1->sg_phdr.p_type == PT_LOAD) { if ((sgp1->sg_flags & FLG_SG_FLAGS) == 0) { /* * Default to read/write and execute. */ sgp1->sg_phdr.p_flags = PF_R + PF_W + PF_X; sgp1->sg_flags |= FLG_SG_FLAGS; } if ((sgp1->sg_flags & FLG_SG_ALIGN) == 0) { /* * Default to segment alignment */ sgp1->sg_phdr.p_align = ld_targ.t_m.m_segm_align; sgp1->sg_flags |= FLG_SG_ALIGN; } } /* * Determine where the new segment should be inserted * in the seg_desc[] list. Presently the user can * only add a LOAD or NOTE segment. Note that these * segments must be added after any PT_PHDR and * PT_INTERP (refer Generic ABI, Page 5-4). */ switch (sgp1->sg_phdr.p_type) { case PT_LOAD: case PT_NULL: if (sgp1->sg_flags & FLG_SG_EMPTY) src_type = 4; else src_type = 3; break; case PT_SUNWSTACK: src_type = 8; if (++num_stack >= 2) { /* * Currently the number of sunw_stack * segment is limited to 1. */ eprintf(ofl->ofl_lml, ERR_WARNING, MSG_INTL(MSG_MAP_NOSTACK2), mapfile, EC_XWORD(Line_num)); continue; } break; case PT_NOTE: src_type = 9; break; default: eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSEGTYP), mapfile, EC_XWORD(Line_num), EC_WORD(sgp1->sg_phdr.p_type)); return (S_ERROR); } lnp2 = NULL; for (LIST_TRAVERSE(&ofl->ofl_segs, lnp1, sgp2)) { ndx++; switch (sgp2->sg_phdr.p_type) { case PT_PHDR: dst_type = 0; break; case PT_INTERP: dst_type = 1; break; case PT_SUNWCAP: dst_type = 2; break; case PT_LOAD: dst_type = 3; break; case PT_DYNAMIC: dst_type = 5; break; case PT_SUNWDTRACE: dst_type = 6; break; case PT_SHLIB: dst_type = 7; break; case PT_SUNWSTACK: dst_type = 8; break; case PT_NOTE: dst_type = 9; break; case PT_SUNWBSS: dst_type = 10; break; case PT_TLS: dst_type = 11; break; case PT_NULL: dst_type = 12; break; default: eprintf(ofl->ofl_lml, ERR_FATAL, MSG_INTL(MSG_MAP_UNKSEGTYP), mapfile, EC_XWORD(Line_num), EC_WORD(sgp2->sg_phdr.p_type)); return (S_ERROR); } if (src_type <= dst_type) { if (lnp2 == NULL) { if (list_prependc( &ofl->ofl_segs, sgp1) == 0) return (S_ERROR); } else { if (list_insertc(&ofl->ofl_segs, sgp1, lnp2) == 0) return (S_ERROR); } break; } lnp2 = lnp1; } } DBG_CALL(Dbg_map_seg(ofl, ndx, sgp1)); } /* * If the output file is a static file without an interpreter, and * if any virtual address is specified, then set the ?N flag for * backward compatibility. */ if (!(ofl->ofl_flags & FLG_OF_DYNAMIC) && !(ofl->ofl_flags & FLG_OF_RELOBJ) && !(ofl->ofl_osinterp) && (ofl->ofl_flags1 & FLG_OF1_VADDR)) ofl->ofl_dtflags_1 |= DF_1_NOHDR; /* * If the output file is a relocatable file, then ?N has no effect. * Make sure this flag isn't set. */ if (ofl->ofl_flags & FLG_OF_RELOBJ) ofl->ofl_dtflags_1 &= ~DF_1_NOHDR; return (1); }