/* * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2019 Joyent, Inc. */ /* * Map file parsing, Version 2 syntax (solaris). */ #include #include #include #include /* SHF_AMD64_LARGE */ #include #include "msg.h" #include "_libld.h" #include "_map.h" /* * Use a case insensitive string match when looking up capability mask * values by name, and omit the AV_ prefix. */ #define ELFCAP_STYLE ELFCAP_STYLE_LC | ELFCAP_STYLE_F_ICMP /* * Signature for functions used to parse top level mapfile directives */ typedef Token (*dir_func_t)(Mapfile *mf); /* * Signature for functions used to parse attribute level assignments * mf - Mapfile descriptor * eq_tok - One of the equal tokens (TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ) * or TK_ERROR. See the comment for attr_fmt_t below. * uvalue - An arbitrary pointer "user value" passed by the * caller to parse_attributes() for use by the function. */ typedef Token (* attr_func_t)(Mapfile *mf, Token eq_tok, void *uvalue); /* * Signature for gettoken_str() err_func argument. This is a function * called to issue an appropriate error message. * * The gts prefix stands for "Get Token Str" */ typedef void (* gts_efunc_t)(Mapfile *mf, Token tok, ld_map_tkval_t *tkv); /* * The attr_fmt_t tells parse_attributes how far to go in parsing * an attribute before it calls the at_func function to take over: * * ATTR_FMT_NAME - Parse the name, and immediately call the function. * This is useful in cases where there is more than * one possible syntax for a given attribute. The value of * eq_tok passed to the at_func function will be TK_ERROR, * reflecting the fact that it has no meaning in this context. * * ATTR_FMT_EQ - Parse the name, and the following '=', and then call * the function. The value passed to the at_func function for * eq_tok will be TK_EQUAL. * * ATTR_FMT_EQ_PEQ - Parse the name, and a following equal token which * can be '=' or '+=', and then call the function. The value * passed to the at_func function for eq_tok will be one of * TK_EQUAL, or TK_PLUSEQ. * * ATTR_FMT_EQ_ALL - Parse the name, and a following equal token which * can be any of the three forms (=, +=, -=), and then call * the function. The value passed to the at_func function for * eq_tok will be one of TK_EQUAL, TK_PLUSEQ, or TK_MINUSEQ. */ typedef enum { ATTR_FMT_NAME, ATTR_FMT_EQ, ATTR_FMT_EQ_PEQ, ATTR_FMT_EQ_ALL, } attr_fmt_t; /* * Type used to describe a set of valid attributes to parse_attributes(): * at_name - Name of attribute * at_func - Function to call when attribute is recognized, * at_all_eq - True if attribute allows the '+=' and '-=' forms of * assignment token, and False to only allow '='. * * The array of these structs passed to parse_attributes() must be * NULL terminated (the at_name field must be set to NULL). */ typedef struct { const char *at_name; /* Name of attribute */ attr_func_t at_func; /* Function to call */ attr_fmt_t at_fmt; /* How much to parse before calling */ /* at_func */ } attr_t; /* * Mapfile version and symbol state are separate but related concepts * that are best represented using two different types. However, our * style of passing a single uvalue via parse_attributes() makes it * convenient to be able to reference them from a single address. */ typedef struct { ld_map_ver_t ss_mv; ld_map_sym_t ss_ms; } symbol_state_t; /* * Process an expected equal operator. Deals with the fact that we * have three variants. * * entry: * mf - Mapfile descriptor * eq_type - Types of equal operators accepted. One of ATTR_FMT_EQ, * ATTR_FMT_EQ_PEQ, or ATTR_FMT_EQ_ALL. * lhs - Name that appears on the left hand side of the expected * equal operator. * * exit: * Returns one of TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ, or TK_ERROR. */ static Token gettoken_eq(Mapfile *mf, attr_fmt_t eq_type, const char *lhs) { Token tok; ld_map_tkval_t tkv; const char *err; Conv_inv_buf_t inv_buf; switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: case TK_EQUAL: return (tok); case TK_PLUSEQ: switch (eq_type) { case ATTR_FMT_EQ_PEQ: case ATTR_FMT_EQ_ALL: return (tok); } break; case TK_MINUSEQ: if (eq_type == ATTR_FMT_EQ_ALL) return (tok); break; } switch (eq_type) { case ATTR_FMT_EQ: err = MSG_INTL(MSG_MAP_EXP_EQ); break; case ATTR_FMT_EQ_PEQ: err = MSG_INTL(MSG_MAP_EXP_EQ_PEQ); break; case ATTR_FMT_EQ_ALL: err = MSG_INTL(MSG_MAP_EXP_EQ_ALL); break; default: /*NOTREACHED*/ assert(0); } mf_fatal(mf, err, lhs, ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } /* * Apply one of the three equal tokens to a bitmask value * * entry: * dst - Address of bitmask variable to alter * eq_tok - One of TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ, representing * the operation to carry out. * value - Value for right hand side * * exit: * The operation has been carried out: * * TK_EQUAL - *dst is set to value * TK_PLUSEQ - Bits in value have been set in *dst * TK_MINUSEQ - Bits in value have been removed from *dst */ static void setflags_eq(Word *dst, Token eq_tok, Word value) { switch (eq_tok) { case TK_EQUAL: *dst = value; break; case TK_PLUSEQ: *dst |= value; break; case TK_MINUSEQ: *dst &= ~value; break; default: /*NOTREACHED*/ assert(0); } } /* * Apply one of the three equal tokens to a capabilities Capmask. * * entry: * mf - Mapfile descriptor * capmask - Address of Capmask variable to alter * eq_tok - One of TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ, representing * the operation to carry out. * type - Capability type (CA_SUNW_*) * value - Value for right hand side * title - True if a title is needed, False otherwise. * * exit: * On success, returns TRUE (1), otherwise FALSE (0) */ static Boolean set_capmask(Mapfile *mf, Capmask *capmask, Token eq_tok, Word type, elfcap_mask_t value, Boolean title) { if (title) DBG_CALL(Dbg_cap_mapfile_title(mf->mf_ofl->ofl_lml, mf->mf_lineno)); DBG_CALL(Dbg_cap_val_entry(mf->mf_ofl->ofl_lml, DBG_STATE_CURRENT, type, capmask->cm_val, ld_targ.t_m.m_mach)); switch (eq_tok) { case TK_EQUAL: capmask->cm_val = value; capmask->cm_exc = 0; ld_map_cap_set_ovflag(mf, type); DBG_CALL(Dbg_cap_val_entry(mf->mf_ofl->ofl_lml, DBG_STATE_RESET, type, capmask->cm_val, ld_targ.t_m.m_mach)); break; case TK_PLUSEQ: DBG_CALL(Dbg_cap_val_entry(mf->mf_ofl->ofl_lml, DBG_STATE_ADD, type, value, ld_targ.t_m.m_mach)); capmask->cm_val |= value; capmask->cm_exc &= ~value; break; case TK_MINUSEQ: DBG_CALL(Dbg_cap_val_entry(mf->mf_ofl->ofl_lml, DBG_STATE_EXCLUDE, type, value, ld_targ.t_m.m_mach)); capmask->cm_val &= ~value; capmask->cm_exc |= value; break; default: /*NOTREACHED*/ assert(0); } /* Sanity check the resulting bits */ if (!ld_map_cap_sanitize(mf, type, capmask)) return (FALSE); /* Report the final configuration */ DBG_CALL(Dbg_cap_val_entry(mf->mf_ofl->ofl_lml, DBG_STATE_RESOLVED, type, capmask->cm_val, ld_targ.t_m.m_mach)); return (TRUE); } /* * Apply one of the three equal tokens to a capabilities Caplist. * * entry: * mf - Mapfile descriptor * caplist - Address of Caplist variable to alter * eq_tok - One of TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ, representing * the operation to carry out. * type - Capability type (CA_SUNW_*) * str - String for right hand side * title - True if a title is needed, False otherwise. * * exit: * On success, returns TRUE (1), otherwise FALSE (0) */ static Boolean set_capstr(Mapfile *mf, Caplist *caplist, Token eq_tok, Word type, APlist *strs) { Capstr *capstr; Aliste idx1; char *str; DBG_CALL(Dbg_cap_mapfile_title(mf->mf_ofl->ofl_lml, mf->mf_lineno)); if ((caplist->cl_val == NULL) || (alist_nitems(caplist->cl_val) == 0)) { DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_CURRENT, type, NULL)); } else { for (ALIST_TRAVERSE(caplist->cl_val, idx1, capstr)) { DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_CURRENT, type, capstr->cs_str)); } } switch (eq_tok) { case TK_EQUAL: if (caplist->cl_val) { (void) free(caplist->cl_val); caplist->cl_val = NULL; } if (caplist->cl_exc) { (void) free(caplist->cl_exc); caplist->cl_exc = NULL; } if (strs) { for (APLIST_TRAVERSE(strs, idx1, str)) { if ((capstr = alist_append(&caplist->cl_val, NULL, sizeof (Capstr), AL_CNT_CAP_NAMES)) == NULL) return (FALSE); capstr->cs_str = str; DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_RESET, type, capstr->cs_str)); } } else { DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_RESET, type, NULL)); } ld_map_cap_set_ovflag(mf, type); break; case TK_PLUSEQ: for (APLIST_TRAVERSE(strs, idx1, str)) { Aliste idx2; const char *ostr; int found = 0; /* * Add this name to the list of names, provided the * name doesn't already exist. */ for (ALIST_TRAVERSE(caplist->cl_val, idx2, capstr)) { if (strcmp(str, capstr->cs_str) == 0) { found++; break; } } if ((found == 0) && ((capstr = (Capstr *)alist_append(&caplist->cl_val, NULL, sizeof (Capstr), AL_CNT_CAP_NAMES)) == NULL)) return (FALSE); capstr->cs_str = str; /* * Remove this name from the list of excluded names, * provided the name already exists. */ for (APLIST_TRAVERSE(caplist->cl_exc, idx2, ostr)) { if (strcmp(str, ostr) == 0) { aplist_delete(caplist->cl_exc, &idx2); break; } } DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_ADD, type, str)); } break; case TK_MINUSEQ: for (APLIST_TRAVERSE(strs, idx1, str)) { Aliste idx2; const char *ostr; int found = 0; /* * Delete this name from the list of names, provided * the name already exists. */ for (ALIST_TRAVERSE(caplist->cl_val, idx2, capstr)) { if (strcmp(str, capstr->cs_str) == 0) { alist_delete(caplist->cl_val, &idx2); break; } } /* * Add this name to the list of excluded names, * provided the name already exists. */ for (APLIST_TRAVERSE(caplist->cl_exc, idx2, ostr)) { if (strcmp(str, ostr) == 0) { found++; break; } } if ((found == 0) && (aplist_append(&caplist->cl_exc, str, AL_CNT_CAP_NAMES) == NULL)) return (FALSE); DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_EXCLUDE, type, str)); } break; default: /*NOTREACHED*/ assert(0); } /* Report the final configuration */ if ((caplist->cl_val == NULL) || (alist_nitems(caplist->cl_val) == 0)) { DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_RESOLVED, type, NULL)); } else { for (ALIST_TRAVERSE(caplist->cl_val, idx1, capstr)) { DBG_CALL(Dbg_cap_ptr_entry(mf->mf_ofl->ofl_lml, DBG_STATE_RESOLVED, type, capstr->cs_str)); } } return (TRUE); } /* * Process the next token, which is expected to start an optional * nesting of attributes (';' or '{'). * * entry: * mf - Mapfile descriptor * lhs - Name of the directive or attribute being processed. * * exit: * Returns TK_SEMICOLON or TK_LEFTBKT for success, and TK_ERROR otherwise. */ static Token gettoken_optattr(Mapfile *mf, const char *lhs) { Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: case TK_SEMICOLON: case TK_LEFTBKT: return (tok); } mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEMLBKT), lhs, ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } /* * Process the next token, which is expected to be a line terminator * (';' or '}'). * * entry: * mf - Mapfile descriptor * lhs - Name of the directive or attribute being processed. * * exit: * Returns TK_SEMICOLON or TK_RIGHTBKT for success, and TK_ERROR otherwise. */ static Token gettoken_term(Mapfile *mf, const char *lhs) { Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: case TK_SEMICOLON: case TK_RIGHTBKT: return (tok); } mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEMRBKT), lhs, ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } /* * Process the next token, which is expected to be a semicolon. * * entry: * mf - Mapfile descriptor * lhs - Name of the directive or attribute being processed. * * exit: * Returns TK_SEMICOLON for success, and TK_ERROR otherwise. */ static Token gettoken_semicolon(Mapfile *mf, const char *lhs) { Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: case TK_SEMICOLON: return (tok); } mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEM), lhs, ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } /* * Process the next token, which is expected to be a '{' * * entry: * mf - Mapfile descriptor * lhs - Name of the item directly to the left of the expected left * bracket. * * exit: * Returns TK_LEFTBKT for success, and TK_ERROR otherwise. */ static Token gettoken_leftbkt(Mapfile *mf, const char *lhs) { Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: case TK_LEFTBKT: return (tok); } mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_LBKT), lhs, ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } /* * Process the next token, which is expected to be an integer * * entry: * mf - Mapfile descriptor * lhs - Name of the directive or attribute being processed. * tkv - Address of token value struct to be filled in * * exit: * Updates *tkv and returns TK_INT for success, TK_ERROR otherwise. */ static Token gettoken_int(Mapfile *mf, const char *lhs, ld_map_tkval_t *tkv) { Token tok; Conv_inv_buf_t inv_buf; switch (tok = ld_map_gettoken(mf, 0, tkv)) { case TK_ERROR: case TK_INT: return (tok); } mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_INT), lhs, ld_map_tokenstr(tok, tkv, &inv_buf)); return (TK_ERROR); } /* * Process the next token, which is expected to be a string * * entry: * mf - Mapfile descriptor * lhs - Name of the directive or attribute being processed. * tkv - Address of token value struct to be filled in * err_func - Function to call if an error occurs * * exit: * Updates *tkv and returns TK_STRING for success. Calls the * supplied err_func function and returns TK_ERROR otherwise. */ static Token gettoken_str(Mapfile *mf, int flags, ld_map_tkval_t *tkv, gts_efunc_t efunc) { Token tok; switch (tok = ld_map_gettoken(mf, flags, tkv)) { case TK_ERROR: case TK_STRING: return (tok); } /* User supplied function reports the error */ (* efunc)(mf, tok, tkv); return (TK_ERROR); } /* * Given a construct of the following common form: * * item_name { * attribute = ...; * ... * } * * where the caller has detected the item_name and opening bracket, * parse the construct and call the attribute functions for each * attribute detected, stopping when the closing '}' is seen. * * entry: * mf - Mapfile descriptor * item_name - Already detected name of item for which attributes * are being parsed. * attr_list - NULL terminated array of attr_t structures describing the * valid attributes for the item. * expect_str - Comma separated string listing the names of expected * attributes. * uvalue - User value, passed to the attribute functions without * examination by parse_attributes(), usable for maintaining * shared state between the caller and the functions. * * exit: * parse_attributes() reads the attribute name and equality token, * and then calls the attribute function given by the attr_list array * to handle everything up to and including the terminating ';'. * This continues until the closing '}' is seen. * * If everything is successful, TK_RIGHTBKT is returned. Otherwise, * a suitable error is issued and TK_ERROR is returned. */ static Token parse_attributes(Mapfile *mf, const char *item_name, attr_t *attr_list, size_t attr_list_bufsize, void *uvalue) { attr_t *attr; Token tok, op_tok; ld_map_tkval_t tkv; int done; int attr_cnt = 0; Conv_inv_buf_t inv_buf; /* Read attributes until the closing '}' is seen */ for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, TK_F_KEYWORD, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: attr = ld_map_kwfind(tkv.tkv_str, attr_list, SGSOFFSETOF(attr_t, at_name), sizeof (attr[0])); if (attr == NULL) goto bad_attr; /* * Depending on the value of at_fmt, there are * fout different actions to take: * ATTR_FMT_NAME - Call at_func function * ATTR_FMT_EQ - Read and verify a TK_EQUAL * ATTR_FMT_EQ_PEQ - Read and verify a TK_EQUAL * or TK_PLUSEQ. * ATTR_FMT_EQ_ALL - Read/Verify one of the * three possible equal tokens * (TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ). */ if (attr->at_fmt == ATTR_FMT_NAME) { /* Arbitrary value to pass to at_func */ op_tok = TK_ERROR; } else { /* Read/Verify appropriate equal operator */ op_tok = gettoken_eq(mf, attr->at_fmt, attr->at_name); if (op_tok == TK_ERROR) return (TK_ERROR); } /* Call the associated function */ switch (tok = attr->at_func(mf, op_tok, uvalue)) { default: return (TK_ERROR); case TK_SEMICOLON: break; case TK_RIGHTBKT: done = 1; break; } attr_cnt++; break; case TK_RIGHTBKT: done = 1; break; case TK_SEMICOLON: break; /* Ignore empty statement */ default: bad_attr: { char buf[VLA_SIZE(attr_list_bufsize)]; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_ATTR), ld_map_kwnames(attr_list, SGSOFFSETOF(attr_t, at_name), sizeof (attr[0]), buf, attr_list_bufsize), ld_map_tokenstr(tok, &tkv, &inv_buf)); } return (TK_ERROR); } } /* Make sure there was at least one attribute between the {} brackets */ if (attr_cnt == 0) { mf_fatal(mf, MSG_INTL(MSG_MAP_NOATTR), item_name); return (TK_ERROR); } return (tok); } /* * Read whitespace delimited segment flags from the input and convert into * bitmask of PF_ values they represent. Flags are terminated by a semicolon * or right bracket. * * entry: * mf - Mapfile descriptor * flags - Address of variable to be set to resulting flags value * * exit: * Returns the terminator token (TK_SEMICOLON or TK_LEFTBKT) on success, * and TK_ERROR otherwise. */ static Token parse_segment_flags(Mapfile *mf, Xword *flags) { /* * Map flag names to their values. Since DATA and STACK have * platform dependent values, we have to determine them at runtime. * We indicate this by setting the top bit. */ #define PF_DATA 0x80000000 #define PF_STACK 0x80000001 typedef struct { const char *name; Word value; } segflag_t; static segflag_t flag_list[] = { { MSG_ORIG(MSG_MAPKW_DATA), PF_DATA }, { MSG_ORIG(MSG_MAPKW_EXECUTE), PF_X }, { MSG_ORIG(MSG_MAPKW_READ), PF_R }, { MSG_ORIG(MSG_MAPKW_STACK), PF_STACK }, { MSG_ORIG(MSG_MAPKW_WRITE), PF_W }, /* List must be null terminated */ { 0 }, }; /* * Size of buffer needed to format the names in flag_list[]. Must * be kept in sync with flag_list. */ static size_t flag_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_DATA) + KW_NAME_SIZE(MSG_MAPKW_EXECUTE) + KW_NAME_SIZE(MSG_MAPKW_READ) + KW_NAME_SIZE(MSG_MAPKW_STACK) + KW_NAME_SIZE(MSG_MAPKW_WRITE); Token tok; ld_map_tkval_t tkv; segflag_t *flag; size_t cnt = 0; int done; Conv_inv_buf_t inv_buf; *flags = 0; /* Read attributes until the ';' terminator is seen */ for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, TK_F_KEYWORD, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: flag = ld_map_kwfind(tkv.tkv_str, flag_list, SGSOFFSETOF(segflag_t, name), sizeof (flag_list[0])); if (flag == NULL) goto bad_flag; switch (flag->value) { case PF_DATA: *flags |= ld_targ.t_m.m_dataseg_perm; break; case PF_STACK: *flags |= ld_targ.t_m.m_stack_perm; break; default: *flags |= flag->value; } cnt++; break; case TK_INT: /* * Accept 0 for notational convenience, but refuse * any other value. Note that we don't actually have * to set the flags to 0 here, because there are * already initialized to that before the main loop. */ if (tkv.tkv_int.tkvi_value != 0) goto bad_flag; cnt++; break; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: bad_flag: { char buf[VLA_SIZE(flag_list_bufsize)]; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEGFLAG), ld_map_kwnames(flag_list, SGSOFFSETOF(segflag_t, name), sizeof (flag[0]), buf, flag_list_bufsize), ld_map_tokenstr(tok, &tkv, &inv_buf)); } return (TK_ERROR); } } /* Make sure there was at least one flag */ if (cnt == 0) { mf_fatal(mf, MSG_INTL(MSG_MAP_NOVALUES), MSG_ORIG(MSG_MAPKW_FLAGS)); return (TK_ERROR); } return (tok); #undef PF_DATA #undef PF_STACK } /* * Parse one of the capabilities attributes that corresponds directly to a * capabilities bitmask value (CA_SUNW_HW_x, CA_SUNW_SF_xx). Values can be * integers, or symbolic names that correspond to the capabilities mask * in question. * * entry: * mf - Mapfile descriptor * eq_tok - One of TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ, representing * the operation to carry out. * capmask - Capmask from output descriptor for capability being processed. * type - Capability type (CA_SUNW_*) * elfcap_from_str_func - pointer to elfcap-string-to-value function * for capability being processed. * * exit: * Returns TK_SEMICOLON or TK_RIGHTBKT for success, and TK_ERROR otherwise. */ static Token parse_cap_mask(Mapfile *mf, Token eq_tok, Capmask *capmask, Word type, elfcap_from_str_func_t *elfcap_from_str_func) { int done; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; elfcap_mask_t value = 0; uint64_t v; for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, TK_F_KEYWORD, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: if ((v = (* elfcap_from_str_func)(ELFCAP_STYLE, tkv.tkv_str, ld_targ.t_m.m_mach)) != 0) { value |= v; break; } goto bad_flag; case TK_INT: value |= tkv.tkv_int.tkvi_value; break; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: bad_flag: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_CAPMASK), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } if (!set_capmask(mf, capmask, eq_tok, type, value, TRUE)) return (TK_ERROR); return (tok); } /* * Parse one of the capabilities attributes that manages lists of names * (CA_SUNW_PLAT and CA_SUNW_MACH). Values are symbolic names that correspond * to the capabilities mask in question. * * entry: * mf - Mapfile descriptor * eq_tok - One of TK_EQUAL, TK_PLUSEQ, TK_MINUSEQ, representing * the operation to carry out. * caplist - Caplist from output descriptor for capability being processed. * type - Capability type (CA_SUNW_*) * * exit: * Returns TK_SEMICOLON or TK_RIGHTBKT for success, and TK_ERROR otherwise. */ static Token parse_cap_list(Mapfile *mf, Token eq_tok, Caplist *caplist, Word type) { int done, found; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; APlist *strs = NULL; Aliste idx; const char *str; for (done = 0, found = 0; done == 0; found = 0) { switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: /* * The name is in tkv.tkv_str. Save this string for * set_capstr() processing, but remove any duplicates. */ for (APLIST_TRAVERSE(strs, idx, str)) { if (strcmp(str, tkv.tkv_str) == 0) { found++; break; } } if ((found == 0) && (aplist_append(&strs, tkv.tkv_str, AL_CNT_CAP_NAMES) == NULL)) return (TK_ERROR); break; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_CAPNAME), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } if (!set_capstr(mf, caplist, eq_tok, type, strs)) return (TK_ERROR); return (tok); } /* * CAPABILITY [capid] { HW = hwcap_flags... * -------------------------^ */ /* ARGSUSED 2 */ static Token at_cap_hw(Mapfile *mf, Token eq_tok, void *uvalue) { int done; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; Word hw1 = 0, hw2 = 0; uint64_t v; for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, TK_F_KEYWORD, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: if ((v = elfcap_hw1_from_str(ELFCAP_STYLE, tkv.tkv_str, ld_targ.t_m.m_mach)) != 0) { hw1 |= v; break; } if ((v = elfcap_hw2_from_str(ELFCAP_STYLE, tkv.tkv_str, ld_targ.t_m.m_mach)) != 0) { hw2 |= v; break; } goto bad_flag; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: bad_flag: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_CAPHW), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } if (!set_capmask(mf, &mf->mf_ofl->ofl_ocapset.oc_hw_1, eq_tok, CA_SUNW_HW_1, hw1, TRUE)) return (TK_ERROR); if (!set_capmask(mf, &mf->mf_ofl->ofl_ocapset.oc_hw_2, eq_tok, CA_SUNW_HW_2, hw2, FALSE)) return (TK_ERROR); return (tok); } /* * CAPABILITY [capid] { HW_1 = value ; * ---------------------------^ */ /* ARGSUSED 2 */ static Token at_cap_hw_1(Mapfile *mf, Token eq_tok, void *uvalue) { return (parse_cap_mask(mf, eq_tok, &mf->mf_ofl->ofl_ocapset.oc_hw_1, CA_SUNW_HW_1, elfcap_hw1_from_str)); } /* * CAPABILITY [capid] { HW_2 = value ; * ---------------------------^ */ /* ARGSUSED 2 */ static Token at_cap_hw_2(Mapfile *mf, Token eq_tok, void *uvalue) { return (parse_cap_mask(mf, eq_tok, &mf->mf_ofl->ofl_ocapset.oc_hw_2, CA_SUNW_HW_2, elfcap_hw2_from_str)); } /* * CAPABILITY [capid] { SF = sfcap_flags... * -------------------------^ */ /* ARGSUSED 2 */ static Token at_cap_sf(Mapfile *mf, Token eq_tok, void *uvalue) { int done; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; Word sf1 = 0; uint64_t v; for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, TK_F_KEYWORD, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: if ((v = elfcap_sf1_from_str(ELFCAP_STYLE, tkv.tkv_str, ld_targ.t_m.m_mach)) != 0) { sf1 |= v; break; } goto bad_flag; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: bad_flag: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_CAPSF), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } if (!set_capmask(mf, &mf->mf_ofl->ofl_ocapset.oc_sf_1, eq_tok, CA_SUNW_SF_1, sf1, TRUE)) return (TK_ERROR); return (tok); } /* * CAPABILITY [capid] { SF_1 = value ; * ---------------------------^ */ /* ARGSUSED 2 */ static Token at_cap_sf_1(Mapfile *mf, Token eq_tok, void *uvalue) { return (parse_cap_mask(mf, eq_tok, &mf->mf_ofl->ofl_ocapset.oc_sf_1, CA_SUNW_SF_1, elfcap_sf1_from_str)); } /* * CAPABILITY [capid] { MACHINE = value ; * ------------------------------^ */ /* ARGSUSED 2 */ static Token at_cap_mach(Mapfile *mf, Token eq_tok, void *uvalue) { return (parse_cap_list(mf, eq_tok, &mf->mf_ofl->ofl_ocapset.oc_mach, CA_SUNW_MACH)); } /* * CAPABILITY [capid] { PLATFORM = value ; * -------------------------------^ */ /* ARGSUSED 2 */ static Token at_cap_plat(Mapfile *mf, Token eq_tok, void *uvalue) { return (parse_cap_list(mf, eq_tok, &mf->mf_ofl->ofl_ocapset.oc_plat, CA_SUNW_PLAT)); } /* * Top Level Directive: * * CAPABILITY [capid] { ... * ----------^ */ static Token dir_capability(Mapfile *mf) { /* CAPABILITY attributes */ static attr_t attr_list[] = { { MSG_ORIG(MSG_MAPKW_HW), at_cap_hw, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_HW_1), at_cap_hw_1, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_HW_2), at_cap_hw_2, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_MACHINE), at_cap_mach, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_PLATFORM), at_cap_plat, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_SF), at_cap_sf, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_SF_1), at_cap_sf_1, ATTR_FMT_EQ_ALL }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in attr_list[]. Must * be kept in sync with attr_list. */ static size_t attr_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_HW) + KW_NAME_SIZE(MSG_MAPKW_HW_1) + KW_NAME_SIZE(MSG_MAPKW_HW_2) + KW_NAME_SIZE(MSG_MAPKW_MACHINE) + KW_NAME_SIZE(MSG_MAPKW_PLATFORM) + KW_NAME_SIZE(MSG_MAPKW_SF) + KW_NAME_SIZE(MSG_MAPKW_SF_1); Capstr *capstr; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; /* * The first token can be one of: * - An opening '{' * - A name, followed by a '{', or a ';'. * Read this initial sequence. */ switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: capstr = &mf->mf_ofl->ofl_ocapset.oc_id; /* * The ID name is in tkv.tkv_str. Save this name in the output * capabilities structure. Note, should multiple ID entries * be encounterd, the last entry wins. */ DBG_CALL(Dbg_cap_id(mf->mf_ofl->ofl_lml, mf->mf_lineno, capstr->cs_str, tkv.tkv_str)); capstr->cs_str = tkv.tkv_str; mf->mf_ofl->ofl_ocapset.oc_flags |= FLG_OCS_USRDEFID; /* * The name can be followed by an opening '{', or a * terminating ';' */ switch (tok = gettoken_optattr(mf, capstr->cs_str)) { case TK_SEMICOLON: return (TK_SEMICOLON); case TK_LEFTBKT: break; default: return (TK_ERROR); } break; case TK_LEFTBKT: /* Directive has no capid, but does supply attributes */ break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_CAPID), MSG_ORIG(MSG_MAPKW_CAPABILITY), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } /* Parse the attributes */ if (parse_attributes(mf, MSG_ORIG(MSG_MAPKW_CAPABILITY), attr_list, attr_list_bufsize, NULL) == TK_ERROR) return (TK_ERROR); /* Terminating ';' */ return (gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_CAPABILITY))); } /* * at_dv_allow(): Value for ALLOW= is not a version string */ static void gts_efunc_at_dv_allow(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_VERSION), MSG_ORIG(MSG_MAPKW_ALLOW), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * DEPEND_VERSIONS object_name { ALLOW = version * -------------------------------------^ */ /* ARGSUSED 1 */ static Token at_dv_allow(Mapfile *mf, Token eq_tok, void *uvalue) { ld_map_tkval_t tkv; if (gettoken_str(mf, 0, &tkv, gts_efunc_at_dv_allow) == TK_ERROR) return (TK_ERROR); /* Enter the version. uvalue points at the Sdf_desc descriptor */ if (!ld_map_dv_entry(mf, uvalue, FALSE, tkv.tkv_str)) return (TK_ERROR); /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_ALLOW))); } /* * at_dv_allow(): Value for REQUIRE= is not a version string */ static void gts_efunc_at_dv_require(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_VERSION), MSG_ORIG(MSG_MAPKW_REQUIRE), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * DEPEND_VERSIONS object_name { REQURE = version * --------------------------------------^ */ /* ARGSUSED 1 */ static Token at_dv_require(Mapfile *mf, Token eq_tok, void *uvalue) { ld_map_tkval_t tkv; /* version_name */ if (gettoken_str(mf, 0, &tkv, gts_efunc_at_dv_require) == TK_ERROR) return (TK_ERROR); /* Enter the version. uvalue points at the Sdf_desc descriptor */ if (!ld_map_dv_entry(mf, uvalue, TRUE, tkv.tkv_str)) return (TK_ERROR); /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_REQUIRE))); } /* * dir_depend_versions(): Expected object name is not present */ static void gts_efunc_dir_depend_versions(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_OBJNAM), MSG_ORIG(MSG_MAPKW_DEPEND_VERSIONS), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * Top Level Directive: * * DEPEND_VERSIONS object_name { ATTR = ... * ---------------^ */ static Token dir_depend_versions(Mapfile *mf) { /* DEPEND_VERSIONS attributes */ static attr_t attr_list[] = { { MSG_ORIG(MSG_MAPKW_ALLOW), at_dv_allow, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_REQUIRE), at_dv_require, ATTR_FMT_EQ }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in attr_list[]. Must * be kept in sync with attr_list. */ static size_t attr_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_ALLOW) + KW_NAME_SIZE(MSG_MAPKW_REQUIRE); ld_map_tkval_t tkv; Sdf_desc *sdf; /* object_name */ if (gettoken_str(mf, 0, &tkv, gts_efunc_dir_depend_versions) == TK_ERROR) return (TK_ERROR); /* Get descriptor for dependency */ if ((sdf = ld_map_dv(mf, tkv.tkv_str)) == NULL) return (TK_ERROR); /* Opening '{' token */ if (gettoken_leftbkt(mf, tkv.tkv_str) == TK_ERROR) return (TK_ERROR); /* Parse the attributes */ if (parse_attributes(mf, MSG_ORIG(MSG_MAPKW_DEPEND_VERSIONS), attr_list, attr_list_bufsize, sdf) == TK_ERROR) return (TK_ERROR); /* Terminating ';' */ return (gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_DEPEND_VERSIONS))); } /* * Top Level Directive: * * HDR_NOALLOC ; * -----------^ */ static Token dir_hdr_noalloc(Mapfile *mf) { mf->mf_ofl->ofl_dtflags_1 |= DF_1_NOHDR; DBG_CALL(Dbg_map_hdr_noalloc(mf->mf_ofl->ofl_lml, mf->mf_lineno)); /* ';' terminator token */ return (gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_HDR_NOALLOC))); } /* * Top Level Directive: * * PHDR_ADD_NULL = cnt ; * -------------^ */ static Token dir_phdr_add_null(Mapfile *mf) { Sg_desc *sgp; ld_map_tkval_t tkv; /* Value of token */ /* '=' token */ if (gettoken_eq(mf, ATTR_FMT_EQ, MSG_ORIG(MSG_MAPKW_PHDR_ADD_NULL)) == TK_ERROR) return (TK_ERROR); /* integer token */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_PHDR_ADD_NULL), &tkv) == TK_ERROR) return (TK_ERROR); while (tkv.tkv_int.tkvi_value-- > 0) { if ((sgp = ld_map_seg_alloc(NULL, PT_NULL, FLG_SG_P_TYPE | FLG_SG_EMPTY)) == NULL) return (TK_ERROR); if (ld_map_seg_insert(mf, DBG_STATE_NEW, sgp, 0) == SEG_INS_FAIL) return (TK_ERROR); } /* ';' terminator token */ return (gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_PHDR_ADD_NULL))); } /* * segment_directive segment_name { ALIGN = value * ----------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_align(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; ld_map_tkval_t tkv; /* value */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_ALIGN), &tkv) == TK_ERROR) return (TK_ERROR); sgp->sg_phdr.p_align = tkv.tkv_int.tkvi_value; sgp->sg_flags |= FLG_SG_P_ALIGN; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_ALIGN))); } /* * at_seg_assign_file_basename(): Value for FILE_BASENAME= is not a file name */ static void gts_efunc_at_seg_assign_file_basename(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_FILNAM), MSG_ORIG(MSG_MAPKW_FILE_BASENAME), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * segment_directive segment_name { ASSIGN { FILE_BASENAME = file_name * ---------------------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_assign_file_basename(Mapfile *mf, Token eq_tok, void *uvalue) { Ent_desc *enp = uvalue; ld_map_tkval_t tkv; /* file_name */ if (gettoken_str(mf, 0, &tkv, gts_efunc_at_seg_assign_file_basename) == TK_ERROR) return (TK_ERROR); if (!ld_map_seg_ent_files(mf, enp, TYP_ECF_BASENAME, tkv.tkv_str)) return (TK_ERROR); /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_FILE_BASENAME))); } /* * at_seg_assign_file_objname(): Value for FILE_OBJNAME= is not an object name */ static void gts_efunc_at_seg_assign_file_objname(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_OBJNAM), MSG_ORIG(MSG_MAPKW_FILE_OBJNAME), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * segment_directive segment_name { ASSIGN { FILE_OBJNAME = name * --------------------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_assign_file_objname(Mapfile *mf, Token eq_tok, void *uvalue) { Ent_desc *enp = uvalue; ld_map_tkval_t tkv; /* file_objname */ if (gettoken_str(mf, 0, &tkv, gts_efunc_at_seg_assign_file_objname) == TK_ERROR) return (TK_ERROR); if (!ld_map_seg_ent_files(mf, enp, TYP_ECF_OBJNAME, tkv.tkv_str)) return (TK_ERROR); /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_FILE_OBJNAME))); } /* * at_seg_assign_file_path(): Value for FILE_PATH= is not a file path */ static void gts_efunc_at_seg_assign_file_path(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_FILPATH), MSG_ORIG(MSG_MAPKW_FILE_PATH), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * segment_directive segment_name { ASSIGN { FILE_PATH = file_path * -----------------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_assign_file_path(Mapfile *mf, Token eq_tok, void *uvalue) { Ent_desc *enp = uvalue; ld_map_tkval_t tkv; /* file_path */ if (gettoken_str(mf, 0, &tkv, gts_efunc_at_seg_assign_file_path) == TK_ERROR) return (TK_ERROR); if (!ld_map_seg_ent_files(mf, enp, TYP_ECF_PATH, tkv.tkv_str)) return (TK_ERROR); /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_FILE_PATH))); } /* * segment_directive segment_name { ASSIGN { FLAGS = ... ; * -------------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_assign_flags(Mapfile *mf, Token eq_tok, void *uvalue) { typedef struct { const char *name; Word value; } secflag_t; static secflag_t flag_list[] = { { MSG_ORIG(MSG_MAPKW_ALLOC), SHF_ALLOC }, { MSG_ORIG(MSG_MAPKW_EXECUTE), SHF_EXECINSTR }, { MSG_ORIG(MSG_MAPKW_WRITE), SHF_WRITE }, { MSG_ORIG(MSG_MAPKW_AMD64_LARGE), SHF_AMD64_LARGE }, /* List must be null terminated */ { 0 }, }; /* * Size of buffer needed to format the names in flag_list[]. Must * be kept in sync with flag_list. */ static size_t flag_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_ALLOC) + KW_NAME_SIZE(MSG_MAPKW_EXECUTE) + KW_NAME_SIZE(MSG_MAPKW_WRITE) + KW_NAME_SIZE(MSG_MAPKW_AMD64_LARGE); Ent_desc *enp = uvalue; int bcnt = 0, cnt = 0; secflag_t *flag; int done; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; /* Read and process tokens until the closing terminator is seen */ for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_BANG: /* Ensure ! only specified once per flag */ if (bcnt != 0) { mf_fatal0(mf, MSG_INTL(MSG_MAP_SFLG_ONEBANG)); return (TK_ERROR); } bcnt++; break; case TK_STRING: flag = ld_map_kwfind(tkv.tkv_str, flag_list, SGSOFFSETOF(secflag_t, name), sizeof (flag[0])); if (flag == NULL) goto bad_flag; cnt++; enp->ec_attrmask |= flag->value; if (bcnt == 0) enp->ec_attrbits |= flag->value; bcnt = 0; break; case TK_RIGHTBKT: case TK_SEMICOLON: done = 1; break; default: bad_flag: { char buf[VLA_SIZE(flag_list_bufsize)]; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SECFLAG), ld_map_kwnames(flag_list, SGSOFFSETOF(secflag_t, name), sizeof (flag[0]), buf, flag_list_bufsize), ld_map_tokenstr(tok, &tkv, &inv_buf)); } return (TK_ERROR); } } /* * Ensure that a trailing '!' was not left at the end of the line * without a corresponding flag to apply it to. */ if (bcnt != 0) { mf_fatal0(mf, MSG_INTL(MSG_MAP_SFLG_EXBANG)); return (TK_ERROR); } /* Make sure there was at least one flag */ if (cnt == 0) { mf_fatal(mf, MSG_INTL(MSG_MAP_NOVALUES), MSG_ORIG(MSG_MAPKW_FLAGS)); return (TK_ERROR); } return (tok); /* Either TK_SEMICOLON or TK_RIGHTBKT */ } /* * at_seg_assign_is_name(): Value for IS_NAME= is not a section name */ static void gts_efunc_at_seg_assign_is_name(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SECNAM), MSG_ORIG(MSG_MAPKW_IS_NAME), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * segment_directive segment_name { ASSIGN { IS_NAME = section_name ; * ---------------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_assign_is_name(Mapfile *mf, Token eq_tok, void *uvalue) { Ent_desc *enp = uvalue; ld_map_tkval_t tkv; /* section_name */ if (gettoken_str(mf, 0, &tkv, gts_efunc_at_seg_assign_is_name) == TK_ERROR) return (TK_ERROR); enp->ec_is_name = tkv.tkv_str; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_IS_NAME))); } /* * at_seg_assign_type(): Value for TYPE= is not a section type */ static void gts_efunc_at_seg_assign_type(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SHTYPE), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * segment_directive segment_name { ASSIGN { TYPE = section_type ; * ------------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_assign_type(Mapfile *mf, Token eq_tok, void *uvalue) { Ent_desc *enp = uvalue; ld_map_tkval_t tkv; conv_strtol_uvalue_t conv_uvalue; /* section type */ if (gettoken_str(mf, TK_F_KEYWORD, &tkv, gts_efunc_at_seg_assign_type) == TK_ERROR) return (TK_ERROR); /* * Use the libconv iteration facility to map the given name to * its value. This allows us to keep up with any new sections * without having to change this code. */ if (conv_iter_strtol_init(tkv.tkv_str, &conv_uvalue) != 0) { conv_iter_ret_t status; /* Look at the canonical form */ status = conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL, CONV_FMT_ALT_CF, conv_iter_strtol, &conv_uvalue); /* Failing that, look at the normal form */ if (status != CONV_ITER_DONE) (void) conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL, CONV_FMT_ALT_NF, conv_iter_strtol, &conv_uvalue); /* If we didn't match anything report error */ if (!conv_uvalue.csl_found) { gts_efunc_at_seg_assign_type(mf, TK_STRING, &tkv); return (TK_ERROR); } } enp->ec_type = conv_uvalue.csl_value; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_TYPE))); } /* * segment_directive segment_name { ASSIGN { ... * -----------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_assign(Mapfile *mf, Token eq_tok, void *uvalue) { /* segment_directive ASSIGN sub-attributes */ static attr_t attr_list[] = { { MSG_ORIG(MSG_MAPKW_FILE_BASENAME), at_seg_assign_file_basename, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_FILE_OBJNAME), at_seg_assign_file_objname, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_FILE_PATH), at_seg_assign_file_path, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_FLAGS), at_seg_assign_flags, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_IS_NAME), at_seg_assign_is_name, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_TYPE), at_seg_assign_type, ATTR_FMT_EQ }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in attr_list[]. Must * be kept in sync with attr_list. */ static size_t attr_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_FILE_BASENAME) + KW_NAME_SIZE(MSG_MAPKW_FILE_PATH) + KW_NAME_SIZE(MSG_MAPKW_FLAGS) + KW_NAME_SIZE(MSG_MAPKW_FILE_OBJNAME) + KW_NAME_SIZE(MSG_MAPKW_IS_NAME) + KW_NAME_SIZE(MSG_MAPKW_TYPE); Sg_desc *sgp = uvalue; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; const char *name = NULL; Ent_desc *enp; /* * ASSIGN takes an optional name, plus attributes are optional, * so expect a name, an opening '{', or a ';'. */ tok = ld_map_gettoken(mf, 0, &tkv); switch (tok) { case TK_ERROR: return (TK_ERROR); case TK_STRING: name = tkv.tkv_str; tok = ld_map_gettoken(mf, 0, &tkv); break; } /* Add a new entrance criteria descriptor to the segment */ if ((enp = ld_map_seg_ent_add(mf, sgp, name)) == NULL) return (TK_ERROR); /* Having handled the name, expect either '{' or ';' */ switch (tok) { default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEMLBKT), MSG_ORIG(MSG_MAPKW_ASSIGN_SECTION), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); case TK_ERROR: return (TK_ERROR); case TK_SEMICOLON: case TK_RIGHTBKT: /* No attributes: It will match anything */ enp->ec_flags |= FLG_EC_CATCHALL; break; case TK_LEFTBKT: /* Parse the attributes */ if (parse_attributes(mf, MSG_ORIG(MSG_MAPKW_ASSIGN_SECTION), attr_list, attr_list_bufsize, enp) == TK_ERROR) return (TK_ERROR); /* Terminating ';', or '}' which also terminates caller */ tok = gettoken_term(mf, MSG_ORIG(MSG_MAPKW_ASSIGN_SECTION)); if (tok == TK_ERROR) return (TK_ERROR); break; } DBG_CALL(Dbg_map_ent(mf->mf_ofl->ofl_lml, enp, mf->mf_ofl, mf->mf_lineno)); return (tok); } /* * segment_directive segment_name { DISABLE ; * ----------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_disable(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; /* If the segment cannot be disabled, issue error */ if (sgp->sg_flags & FLG_SG_NODISABLE) { mf_fatal(mf, MSG_INTL(MSG_MAP_CNTDISSEG), sgp->sg_name); return (TK_ERROR); } /* Disable the segment */ sgp->sg_flags |= FLG_SG_DISABLED; /* terminator */ return (gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_DISABLE))); } /* * segment_directive segment_name { FLAGS eq-op ... * --------------------------------------------^ * * Note that this routine is also used for the STACK directive, * as STACK also manipulates a segment descriptor. * * STACK { FLAGS eq-op ... ; * -------------------^ */ /* ARGSUSED 2 */ static Token at_seg_flags(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; Token tok; Xword flags; tok = parse_segment_flags(mf, &flags); if (tok == TK_ERROR) return (TK_ERROR); setflags_eq(&sgp->sg_phdr.p_flags, eq_tok, flags); sgp->sg_flags |= FLG_SG_P_FLAGS; return (tok); } /* * segment_directive segment_name { IS_ORDER eq_op value * -----------------------------------------------^ */ /* ARGSUSED 2 */ static Token at_seg_is_order(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; int done; Aliste idx; Ent_desc *enp, *enp2; /* * The '=' form of assignment resets the list. The list contains * pointers to our mapfile text, so we do not have to free anything. */ if (eq_tok == TK_EQUAL) aplist_reset(sgp->sg_is_order); /* * One or more ASSIGN names, terminated by a semicolon. */ for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: /* * The referenced entrance criteria must have * already been defined. */ enp = ld_ent_lookup(mf->mf_ofl, tkv.tkv_str, NULL); if (enp == NULL) { mf_fatal(mf, MSG_INTL(MSG_MAP_UNKENT), tkv.tkv_str); return (TK_ERROR); } /* * Make sure it's not already on the list */ for (APLIST_TRAVERSE(sgp->sg_is_order, idx, enp2)) if (enp == enp2) { mf_fatal(mf, MSG_INTL(MSG_MAP_DUP_IS_ORD), tkv.tkv_str); return (TK_ERROR); } /* Put it at the end of the order list */ if (aplist_append(&sgp->sg_is_order, enp, AL_CNT_SG_IS_ORDER) == NULL) return (TK_ERROR); break; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_ECNAM), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } return (tok); } /* * segment_directive segment_name { MAX_SIZE = value * -------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_max_size(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; ld_map_tkval_t tkv; /* value */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_MAX_SIZE), &tkv) == TK_ERROR) return (TK_ERROR); sgp->sg_length = tkv.tkv_int.tkvi_value; sgp->sg_flags |= FLG_SG_LENGTH; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_MAX_SIZE))); } /* * segment_directive segment_name { NOHDR ; * --------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_nohdr(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; /* * Set the nohdr flag on the segment. If this segment is the * first loadable segment, the ELF and program headers will * not be included. * * The HDR_NOALLOC top level directive is preferred. This feature * exists to give 1:1 feature parity with version 1 mapfiles that * use the ?N segment flag and expect it to only take effect * if that segment ends up being first. */ sgp->sg_flags |= FLG_SG_NOHDR; /* terminator */ return (gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_NOHDR))); } /* * segment_directive segment_name { OS_ORDER eq_op assign_name... * -----------------------------------------------^ */ /* ARGSUSED 2 */ static Token at_seg_os_order(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; int done; /* * The '=' form of assignment resets the list. The list contains * pointers to our mapfile text, so we do not have to free anything. */ if (eq_tok == TK_EQUAL) alist_reset(sgp->sg_os_order); /* * One or more section names, terminated by a semicolon. */ for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: if (!ld_map_seg_os_order_add(mf, sgp, tkv.tkv_str)) return (TK_ERROR); break; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SECNAM), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } return (tok); } /* * segment_directive segment_name { PADDR = paddr * ----------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_paddr(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue, *sgp2; Aliste idx; ld_map_tkval_t tkv; /* * Ensure that the segment isn't in the segment order list. */ for (APLIST_TRAVERSE(mf->mf_ofl->ofl_segs_order, idx, sgp2)) if (sgp == sgp2) { mf_fatal(mf, MSG_INTL(MSG_MAP_CNTADDRORDER), sgp->sg_name); return (TK_ERROR); } /* value */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_PADDR), &tkv) == TK_ERROR) return (TK_ERROR); sgp->sg_phdr.p_paddr = tkv.tkv_int.tkvi_value; sgp->sg_flags |= FLG_SG_P_PADDR; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_PADDR))); } /* * segment_directive segment_name { ROUND = value * ----------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_round(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; ld_map_tkval_t tkv; /* value */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_ROUND), &tkv) == TK_ERROR) return (TK_ERROR); sgp->sg_round = tkv.tkv_int.tkvi_value; sgp->sg_flags |= FLG_SG_ROUND; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_ROUND))); } /* * segment_directive segment_name { SIZE_SYMBOL = symbol_name * ----------------------------------------------^ */ /* ARGSUSED 2 */ static Token at_seg_size_symbol(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; int done, cnt = 0; /* * One or more symbol names, terminated by a semicolon. */ for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: if (!ld_map_seg_size_symbol(mf, sgp, eq_tok, tkv.tkv_str)) return (TK_ERROR); cnt++; /* * If the operator is TK_EQUAL, turn it into * TK_PLUSEQ for any symbol names after the first. * These additional symbols are added, and are not * replacements for the first one. */ eq_tok = TK_PLUSEQ; break; case TK_SEMICOLON: case TK_RIGHTBKT: done = 1; break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SYMNAM), MSG_ORIG(MSG_MAPKW_SIZE_SYMBOL), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } /* Make sure there was at least one name */ if (cnt == 0) { mf_fatal(mf, MSG_INTL(MSG_MAP_NOVALUES), MSG_ORIG(MSG_MAPKW_SIZE_SYMBOL)); return (TK_ERROR); } return (tok); } /* * segment_directive segment_name { VADDR = vaddr * ----------------------------------------^ */ /* ARGSUSED 1 */ static Token at_seg_vaddr(Mapfile *mf, Token eq_tok, void *uvalue) { Sg_desc *sgp = uvalue, *sgp2; Aliste idx; ld_map_tkval_t tkv; /* * Ensure that the segment isn't in the segment order list. */ for (APLIST_TRAVERSE(mf->mf_ofl->ofl_segs_order, idx, sgp2)) if (sgp == sgp2) { mf_fatal(mf, MSG_INTL(MSG_MAP_CNTADDRORDER), sgp->sg_name); return (TK_ERROR); } /* value */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_VADDR), &tkv) == TK_ERROR) return (TK_ERROR); sgp->sg_phdr.p_vaddr = tkv.tkv_int.tkvi_value; sgp->sg_flags |= FLG_SG_P_VADDR; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_VADDR))); } /* * Top Level Directive: * * {LOAD|NOTE|NULL}_SEGMENT segment_name { ... * ------------------------^ * * Common implementation body for the family of segment directives. These * take the same syntax, and share a common subset of attributes. They differ * in the type of segments they handle and the specific attributes accepted. * * entry: * mf - Mapfile descriptor ({LOAD|NOTE|NULL}_SEGMENT) * dir_name - Name of directive. * seg_type - Type of segment (PT_LOAD, PT_NOTE, PT_NULL). * attr_list - NULL terminated attribute array * attr_list_bufsize - Size of required buffer to format all the * names in attr_list. * gts_efunc - Error function to pass to gettoken_str() when trying * to obtain a segment name token. */ static Token dir_segment_inner(Mapfile *mf, const char *dir_name, Word seg_type, attr_t *attr_list, size_t attr_list_bufsize, gts_efunc_t gts_efunc) { Token tok; ld_map_tkval_t tkv; Sg_desc *sgp; Boolean new_segment; Xword ndx; avl_index_t where; /* segment_name */ if (gettoken_str(mf, 0, &tkv, gts_efunc) == TK_ERROR) return (TK_ERROR); sgp = ld_seg_lookup(mf->mf_ofl, tkv.tkv_str, &where); new_segment = (sgp == NULL); if (new_segment) { /* Allocate a descriptor for new segment */ if ((sgp = ld_map_seg_alloc(tkv.tkv_str, seg_type, FLG_SG_P_TYPE)) == NULL) return (TK_ERROR); } else { /* Make sure it's the right type of segment */ if (sgp->sg_phdr.p_type != seg_type) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXPSEGTYPE), conv_phdr_type(ELFOSABI_SOLARIS, ld_targ.t_m.m_mach, sgp->sg_phdr.p_type, CONV_FMT_ALT_CF, &inv_buf), dir_name, tkv.tkv_str); return (TK_ERROR); } /* If it was disabled, being referenced enables it */ sgp->sg_flags &= ~FLG_SG_DISABLED; if (DBG_ENABLED) { /* * Not a new segment, so show the initial value * before modifying it. */ ndx = ld_map_seg_index(mf, sgp); DBG_CALL(Dbg_map_seg(mf->mf_ofl, DBG_STATE_MOD_BEFORE, ndx, sgp, mf->mf_lineno)); } } /* * Attributes are optional, so expect an opening '{', or a ';'. */ switch (tok = gettoken_optattr(mf, dir_name)) { default: tok = TK_ERROR; break; case TK_SEMICOLON: break; case TK_LEFTBKT: /* Parse the attributes */ if (parse_attributes(mf, dir_name, attr_list, attr_list_bufsize, sgp) == TK_ERROR) return (TK_ERROR); /* Terminating ';' */ tok = gettoken_semicolon(mf, dir_name); if (tok == TK_ERROR) return (TK_ERROR); break; } /* * If this is a new segment, finish its initialization * and insert it into the segment list. */ if (new_segment) { if (ld_map_seg_insert(mf, DBG_STATE_NEW, sgp, where) == SEG_INS_FAIL) return (TK_ERROR); } else { /* Not new. Show what's changed */ DBG_CALL(Dbg_map_seg(mf->mf_ofl, DBG_STATE_MOD_AFTER, ndx, sgp, mf->mf_lineno)); } return (tok); } /* * dir_load_segment(): Expected loadable segment name is not present */ static void gts_efunc_dir_load_segment(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEGNAM), MSG_ORIG(MSG_MAPKW_LOAD_SEGMENT), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * Top Level Directive: * * LOAD_SEGMENT segment_name { ... * ------------^ */ static Token dir_load_segment(Mapfile *mf) { /* LOAD_SEGMENT attributes */ static attr_t attr_list[] = { { MSG_ORIG(MSG_MAPKW_ALIGN), at_seg_align, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_ASSIGN_SECTION), at_seg_assign, ATTR_FMT_NAME }, { MSG_ORIG(MSG_MAPKW_DISABLE), at_seg_disable, ATTR_FMT_NAME }, { MSG_ORIG(MSG_MAPKW_FLAGS), at_seg_flags, ATTR_FMT_EQ_ALL }, { MSG_ORIG(MSG_MAPKW_IS_ORDER), at_seg_is_order, ATTR_FMT_EQ_PEQ }, { MSG_ORIG(MSG_MAPKW_MAX_SIZE), at_seg_max_size, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_NOHDR), at_seg_nohdr, ATTR_FMT_NAME }, { MSG_ORIG(MSG_MAPKW_OS_ORDER), at_seg_os_order, ATTR_FMT_EQ_PEQ }, { MSG_ORIG(MSG_MAPKW_PADDR), at_seg_paddr, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_ROUND), at_seg_round, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_SIZE_SYMBOL), at_seg_size_symbol, ATTR_FMT_EQ_PEQ }, { MSG_ORIG(MSG_MAPKW_VADDR), at_seg_vaddr, ATTR_FMT_EQ }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in attr_list[]. Must * be kept in sync with attr_list. */ static size_t attr_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_ALIGN) + KW_NAME_SIZE(MSG_MAPKW_ASSIGN_SECTION) + KW_NAME_SIZE(MSG_MAPKW_DISABLE) + KW_NAME_SIZE(MSG_MAPKW_FLAGS) + KW_NAME_SIZE(MSG_MAPKW_IS_ORDER) + KW_NAME_SIZE(MSG_MAPKW_MAX_SIZE) + KW_NAME_SIZE(MSG_MAPKW_PADDR) + KW_NAME_SIZE(MSG_MAPKW_ROUND) + KW_NAME_SIZE(MSG_MAPKW_OS_ORDER) + KW_NAME_SIZE(MSG_MAPKW_SIZE_SYMBOL) + KW_NAME_SIZE(MSG_MAPKW_VADDR); return (dir_segment_inner(mf, MSG_ORIG(MSG_MAPKW_LOAD_SEGMENT), PT_LOAD, attr_list, attr_list_bufsize, gts_efunc_dir_load_segment)); } /* * Common shared segment directive attributes */ static attr_t segment_core_attr_list[] = { { MSG_ORIG(MSG_MAPKW_ASSIGN_SECTION), at_seg_assign, ATTR_FMT_NAME }, { MSG_ORIG(MSG_MAPKW_DISABLE), at_seg_disable, ATTR_FMT_NAME }, { MSG_ORIG(MSG_MAPKW_IS_ORDER), at_seg_is_order, ATTR_FMT_EQ_PEQ }, { MSG_ORIG(MSG_MAPKW_OS_ORDER), at_seg_os_order, ATTR_FMT_EQ_PEQ }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in segment_core_attr_list[]. * Must be kept in sync with segment_core_attr_list. */ static size_t segment_core_attr_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_ASSIGN_SECTION) + KW_NAME_SIZE(MSG_MAPKW_DISABLE) + KW_NAME_SIZE(MSG_MAPKW_IS_ORDER) + KW_NAME_SIZE(MSG_MAPKW_OS_ORDER); /* * dir_note_segment(): Expected note segment name is not present */ static void gts_efunc_dir_note_segment(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEGNAM), MSG_ORIG(MSG_MAPKW_NOTE_SEGMENT), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * Top Level Directive: * * NOTE_SEGMENT segment_name { ... * ------------^ */ static Token dir_note_segment(Mapfile *mf) { return (dir_segment_inner(mf, MSG_ORIG(MSG_MAPKW_NOTE_SEGMENT), PT_NOTE, segment_core_attr_list, segment_core_attr_list_bufsize, gts_efunc_dir_note_segment)); } /* * dir_null_segment(): Expected null segment name is not present */ static void gts_efunc_dir_null_segment(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEGNAM), MSG_ORIG(MSG_MAPKW_NULL_SEGMENT), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * Top Level Directive: * * NULL_SEGMENT segment_name { ... * ------------^ */ static Token dir_null_segment(Mapfile *mf) { return (dir_segment_inner(mf, MSG_ORIG(MSG_MAPKW_NULL_SEGMENT), PT_NULL, segment_core_attr_list, segment_core_attr_list_bufsize, gts_efunc_dir_null_segment)); } /* * Top Level Directive: * * SEGMENT_ORDER segment_name ... ; */ static Token dir_segment_order(Mapfile *mf) { Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; Aliste idx; Sg_desc *sgp, *sgp2; int done; /* Expect either a '=' or '+=' */ tok = gettoken_eq(mf, ATTR_FMT_EQ_PEQ, MSG_ORIG(MSG_MAPKW_SEGMENT_ORDER)); if (tok == TK_ERROR) return (TK_ERROR); DBG_CALL(Dbg_map_seg_order(mf->mf_ofl, ELFOSABI_SOLARIS, ld_targ.t_m.m_mach, DBG_STATE_MOD_BEFORE, mf->mf_lineno)); /* * The '=' form of assignment resets the list. The list contains * pointers to our mapfile text, so we do not have to free anything. */ if (tok == TK_EQUAL) aplist_reset(mf->mf_ofl->ofl_segs_order); /* Read segment names, and add to list until terminator (';') is seen */ for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: /* * The segment must have already been defined. */ sgp = ld_seg_lookup(mf->mf_ofl, tkv.tkv_str, NULL); if (sgp == NULL) { mf_fatal(mf, MSG_INTL(MSG_MAP_UNKSEG), tkv.tkv_str); return (TK_ERROR); } /* * Make sure it's not already on the list */ for (APLIST_TRAVERSE(mf->mf_ofl->ofl_segs_order, idx, sgp2)) if (sgp == sgp2) { mf_fatal(mf, MSG_INTL(MSG_MAP_DUPORDSEG), MSG_ORIG(MSG_MAPKW_SEGMENT_ORDER), tkv.tkv_str); return (TK_ERROR); } /* * It can't be ordered and also have an explicit * paddr or vaddr. */ if (sgp->sg_flags & (FLG_SG_P_PADDR | FLG_SG_P_VADDR)) { mf_fatal(mf, MSG_INTL(MSG_MAP_CNTADDRORDER), sgp->sg_name); return (TK_ERROR); } /* Put it at the end of the list */ if (aplist_append(&mf->mf_ofl->ofl_segs_order, sgp, AL_CNT_SG_IS_ORDER) == NULL) return (TK_ERROR); break; case TK_SEMICOLON: done = 1; break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SEGNAM), MSG_ORIG(MSG_MAPKW_SEGMENT_ORDER), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } } DBG_CALL(Dbg_map_seg_order(mf->mf_ofl, ELFOSABI_SOLARIS, ld_targ.t_m.m_mach, DBG_STATE_MOD_AFTER, mf->mf_lineno)); return (tok); } /* * Top Level Directive: * * STACK { ... * -----^ */ static Token dir_stack(Mapfile *mf) { /* STACK attributes */ static attr_t attr_list[] = { { MSG_ORIG(MSG_MAPKW_FLAGS), at_seg_flags, ATTR_FMT_EQ_ALL }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in attr_list[]. Must * be kept in sync with attr_list. */ static size_t attr_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_FLAGS); Sg_desc *sgp; Token tok; /* Opening '{' token */ if (gettoken_leftbkt(mf, MSG_ORIG(MSG_MAPKW_STACK)) == TK_ERROR) return (TK_ERROR); /* Fetch the PT_SUNWSTACK segment descriptor */ sgp = ld_map_seg_stack(mf); /* Parse the attributes */ if (parse_attributes(mf, MSG_ORIG(MSG_MAPKW_STACK), attr_list, attr_list_bufsize, sgp) == TK_ERROR) return (TK_ERROR); /* Terminating ';' */ tok = gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_STACK)); if (tok == TK_ERROR) return (TK_ERROR); if (DBG_ENABLED) { Xword ndx = ld_map_seg_index(mf, sgp); Dbg_map_seg(mf->mf_ofl, DBG_STATE_MOD_AFTER, ndx, sgp, mf->mf_lineno); } return (tok); } /* * at_sym_aux(): Value for AUXILIARY= is not an object name */ static void gts_efunc_at_sym_aux(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_OBJNAM), MSG_ORIG(MSG_MAPKW_AUX), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * SYMBOL [version_name] { symbol_name { AUXILIARY = soname * -------------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_sym_aux(Mapfile *mf, Token eq_tok, void *uvalue) { symbol_state_t *ss = uvalue; ld_map_tkval_t tkv; /* auxiliary filter soname */ if (gettoken_str(mf, 0, &tkv, gts_efunc_at_sym_aux) == TK_ERROR) return (TK_ERROR); ld_map_sym_filtee(mf, &ss->ss_mv, &ss->ss_ms, FLG_SY_AUXFLTR, tkv.tkv_str); /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_AUX))); } /* * at_sym_filter(): Value for FILTER= is not an object name */ static void gts_efunc_at_sym_filter(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_OBJNAM), MSG_ORIG(MSG_MAPKW_FILTER), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * SYMBOL [version_name] { symbol_name { FILTER = soname * ----------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_sym_filter(Mapfile *mf, Token eq_tok, void *uvalue) { symbol_state_t *ss = uvalue; ld_map_tkval_t tkv; /* filter soname */ if (gettoken_str(mf, 0, &tkv, gts_efunc_at_sym_filter) == TK_ERROR) return (TK_ERROR); ld_map_sym_filtee(mf, &ss->ss_mv, &ss->ss_ms, FLG_SY_STDFLTR, tkv.tkv_str); /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_FILTER))); } /* * SYMBOL [version_name] { symbol_name { FLAGS = ... * ---------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_sym_flags(Mapfile *mf, Token eq_tok, void *uvalue) { typedef struct { const char *name; sd_flag_t value; } symflag_t; static symflag_t symflag_list[] = { { MSG_ORIG(MSG_MAPKW_DIRECT), FLG_SY_DIR }, { MSG_ORIG(MSG_MAPKW_DYNSORT), FLG_SY_DYNSORT }, { MSG_ORIG(MSG_MAPKW_EXTERN), FLG_SY_EXTERN }, { MSG_ORIG(MSG_MAPKW_INTERPOSE), FLG_SY_INTPOSE }, { MSG_ORIG(MSG_MAPKW_NODIRECT), FLG_SY_NDIR }, { MSG_ORIG(MSG_MAPKW_NODYNSORT), FLG_SY_NODYNSORT }, { MSG_ORIG(MSG_MAPKW_PARENT), FLG_SY_PARENT }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in flag_list[]. Must * be kept in sync with flag_list. */ static size_t symflag_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_DIRECT) + KW_NAME_SIZE(MSG_MAPKW_DYNSORT) + KW_NAME_SIZE(MSG_MAPKW_EXTERN) + KW_NAME_SIZE(MSG_MAPKW_INTERPOSE) + KW_NAME_SIZE(MSG_MAPKW_NODIRECT) + KW_NAME_SIZE(MSG_MAPKW_NODYNSORT) + KW_NAME_SIZE(MSG_MAPKW_PARENT); symbol_state_t *ss = uvalue; int done; symflag_t *symflag; int cnt = 0; Token tok; ld_map_tkval_t tkv; Conv_inv_buf_t inv_buf; Ofl_desc *ofl = mf->mf_ofl; for (done = 0; done == 0; ) { switch (tok = ld_map_gettoken(mf, TK_F_KEYWORD, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: symflag = ld_map_kwfind(tkv.tkv_str, symflag_list, SGSOFFSETOF(symflag_t, name), sizeof (symflag[0])); if (symflag == NULL) goto bad_flag; cnt++; /* * Apply the flag: * * Although tempting to make all of this table-driven * via added fields in symflag_t, there's enough * variation in what each flag does to make that * not quite worthwhile. * * Similarly, it is tempting to use common code to * to do this work from map_support.c. However, the * v1 code mixes unrelated things (flags, symbol types, * value, size, etc) in single cascading series of * strcmps, whereas our parsing separates those things * from each other. Merging the code would require doing * two strcmps for each item, or other complexity, * which I judge not to be worthwhile. */ switch (symflag->value) { case FLG_SY_DIR: ss->ss_ms.ms_sdflags |= FLG_SY_DIR; ofl->ofl_flags |= FLG_OF_SYMINFO; break; case FLG_SY_DYNSORT: ss->ss_ms.ms_sdflags |= FLG_SY_DYNSORT; ss->ss_ms.ms_sdflags &= ~FLG_SY_NODYNSORT; break; case FLG_SY_EXTERN: ss->ss_ms.ms_sdflags |= FLG_SY_EXTERN; ofl->ofl_flags |= FLG_OF_SYMINFO; break; case FLG_SY_INTPOSE: if (!(ofl->ofl_flags & FLG_OF_EXEC)) { mf_fatal0(mf, MSG_INTL(MSG_MAP_NOINTPOSE)); ss->ss_mv.mv_errcnt++; break; } ss->ss_ms.ms_sdflags |= FLG_SY_INTPOSE; ofl->ofl_flags |= FLG_OF_SYMINFO; ofl->ofl_dtflags_1 |= DF_1_SYMINTPOSE; break; case FLG_SY_NDIR: ss->ss_ms.ms_sdflags |= FLG_SY_NDIR; ofl->ofl_flags |= FLG_OF_SYMINFO; ofl->ofl_flags1 |= (FLG_OF1_NDIRECT | FLG_OF1_NGLBDIR); break; case FLG_SY_NODYNSORT: ss->ss_ms.ms_sdflags &= ~FLG_SY_DYNSORT; ss->ss_ms.ms_sdflags |= FLG_SY_NODYNSORT; break; case FLG_SY_PARENT: ss->ss_ms.ms_sdflags |= FLG_SY_PARENT; ofl->ofl_flags |= FLG_OF_SYMINFO; break; } break; case TK_RIGHTBKT: case TK_SEMICOLON: done = 1; break; default: bad_flag: { char buf[VLA_SIZE(symflag_list_bufsize)]; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SYMFLAG), ld_map_kwnames(symflag_list, SGSOFFSETOF(symflag_t, name), sizeof (symflag[0]), buf, symflag_list_bufsize), ld_map_tokenstr(tok, &tkv, &inv_buf)); } return (TK_ERROR); } } /* Make sure there was at least one flag specified */ if (cnt == 0) { mf_fatal(mf, MSG_INTL(MSG_MAP_NOVALUES), MSG_ORIG(MSG_MAPKW_FLAGS)); return (TK_ERROR); } return (tok); /* Either TK_SEMICOLON or TK_RIGHTBKT */ } /* * SYMBOL [version_name] { symbol_name { SIZE = value * --------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_sym_size(Mapfile *mf, Token eq_tok, void *uvalue) { symbol_state_t *ss = uvalue; ld_map_tkval_t tkv; /* value */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_SIZE), &tkv) == TK_ERROR) return (TK_ERROR); ss->ss_ms.ms_size = tkv.tkv_int.tkvi_value; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_SIZE))); } typedef struct { const char *name; /* type name */ Word ms_shndx; /* symbol section index */ uchar_t ms_type; /* STT_ symbol type */ } at_sym_type_t; static at_sym_type_t at_sym_type_list[] = { { MSG_ORIG(MSG_MAPKW_COMMON), SHN_COMMON, STT_OBJECT }, { MSG_ORIG(MSG_MAPKW_DATA), SHN_ABS, STT_OBJECT }, { MSG_ORIG(MSG_MAPKW_FUNCTION), SHN_ABS, STT_FUNC }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in at_sym_type_list[]. Must * be kept in sync with at_sym_type_list. */ static size_t at_sym_type_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_COMMON) + KW_NAME_SIZE(MSG_MAPKW_DATA) + KW_NAME_SIZE(MSG_MAPKW_FUNCTION); /* * at_sym_type(): Value for TYPE= is not a symbol type */ static void gts_efunc_at_sym_type(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; char buf[VLA_SIZE(at_sym_type_list_bufsize)]; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SYMTYPE), ld_map_kwnames(at_sym_type_list, SGSOFFSETOF(at_sym_type_t, name), sizeof (at_sym_type_list[0]), buf, at_sym_type_list_bufsize), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * SYMBOL [version_name] { symbol_name { TYPE = symbol_type * --------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_sym_type(Mapfile *mf, Token eq_tok, void *uvalue) { symbol_state_t *ss = uvalue; at_sym_type_t *type; ld_map_tkval_t tkv; /* type keyword */ if (gettoken_str(mf, TK_F_KEYWORD, &tkv, gts_efunc_at_sym_type) == TK_ERROR) return (TK_ERROR); type = ld_map_kwfind(tkv.tkv_str, at_sym_type_list, SGSOFFSETOF(at_sym_type_t, name), sizeof (type[0])); if (type == NULL) { gts_efunc_at_sym_type(mf, TK_STRING, &tkv); return (TK_ERROR); } ss->ss_ms.ms_shndx = type->ms_shndx; ss->ss_ms.ms_sdflags |= FLG_SY_SPECSEC; ss->ss_ms.ms_type = type->ms_type; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_TYPE))); } /* * SYMBOL [version_name] { symbol_name { VALUE = value * ---------------------------------------------^ */ /* ARGSUSED 1 */ static Token at_sym_value(Mapfile *mf, Token eq_tok, void *uvalue) { symbol_state_t *ss = uvalue; ld_map_tkval_t tkv; /* value */ if (gettoken_int(mf, MSG_ORIG(MSG_MAPKW_VALUE), &tkv) == TK_ERROR) return (TK_ERROR); ss->ss_ms.ms_value = tkv.tkv_int.tkvi_value; ss->ss_ms.ms_value_set = TRUE; /* terminator */ return (gettoken_term(mf, MSG_ORIG(MSG_MAPKW_VALUE))); } /* * Parse the attributes for a SCOPE or VERSION symbol directive. * * entry: * mf - Mapfile descriptor * dir_name - Name of directive. * ss - Pointer to symbol state block that has had its ss_nv * member initialzed via a call to ld_map_sym_ver_init(). * * exit: * parse_symbol_attributes() returns TK_RIGHTBKT on success, and TK_ERROR * on failure. */ static Token parse_symbol_attributes(Mapfile *mf, const char *dir_name, symbol_state_t *ss) { /* Symbol attributes */ static attr_t attr_list[] = { { MSG_ORIG(MSG_MAPKW_AUX), at_sym_aux, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_FILTER), at_sym_filter, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_FLAGS), at_sym_flags, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_SIZE), at_sym_size, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_TYPE), at_sym_type, ATTR_FMT_EQ }, { MSG_ORIG(MSG_MAPKW_VALUE), at_sym_value, ATTR_FMT_EQ }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in attr_list[]. Must * be kept in sync with attr_list. */ static size_t attr_list_bufsize = KW_NAME_SIZE(MSG_MAPKW_AUX) + KW_NAME_SIZE(MSG_MAPKW_FILTER) + KW_NAME_SIZE(MSG_MAPKW_FLAGS) + KW_NAME_SIZE(MSG_MAPKW_SIZE) + KW_NAME_SIZE(MSG_MAPKW_TYPE) + KW_NAME_SIZE(MSG_MAPKW_VALUE); Token tok; ld_map_tkval_t tkv, tkv_sym; int done; Conv_inv_buf_t inv_buf; /* Read attributes until the closing '}' is seen */ for (done = 0; done == 0; ) { /* * We have to allow quotes around symbol names, but the * name we read may also be a symbol scope keyword. We won't * know which until we read the following token, and so have * to allow quotes for both. Hence, symbol scope names can * be quoted --- an unlikely occurrence and not worth * complicating the code. */ switch (tok = ld_map_gettoken(mf, 0, &tkv_sym)) { case TK_ERROR: return (TK_ERROR); case TK_STRING: /* Default value for all symbol attributes is 0 */ (void) memset(&ss->ss_ms, 0, sizeof (ss->ss_ms)); ss->ss_ms.ms_name = tkv_sym.tkv_str; /* * 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). */ ss->ss_mv.mv_vdp->vd_flags &= ~VER_FLG_WEAK; /* * The meaning of this name depends on the following * character: * * : Scope * ; Symbol without attributes * { Symbol with attributes */ switch (tok = ld_map_gettoken(mf, 0, &tkv)) { case TK_ERROR: return (TK_ERROR); case TK_COLON: ld_map_sym_scope(mf, tkv_sym.tkv_str, &ss->ss_mv); break; case TK_LEFTBKT: /* name is a symbol with attributes */ if (parse_attributes(mf, tkv_sym.tkv_str, attr_list, attr_list_bufsize, ss) == TK_ERROR) return (TK_ERROR); /* Terminating ';', or '}' */ tok = gettoken_term(mf, MSG_INTL(MSG_MAP_SYMATTR)); if (tok == TK_ERROR) return (TK_ERROR); if (tok == TK_RIGHTBKT) done = 1; /* FALLTHROUGH */ case TK_SEMICOLON: /* * 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, &ss->ss_mv, &ss->ss_ms)) return (TK_ERROR); break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SYMDELIM), ld_map_tokenstr(tok, &tkv, &inv_buf)); return (TK_ERROR); } break; case TK_RIGHTBKT: done = 1; break; case TK_SEMICOLON: break; /* Ignore empty statement */ case TK_STAR: /* * Turn off the WEAK flag, as explained above for * TK_STRING. */ ss->ss_mv.mv_vdp->vd_flags &= ~VER_FLG_WEAK; ld_map_sym_autoreduce(mf, &ss->ss_mv); /* * Following token must be ';' to terminate the stmt, * or '}' to terminate the whole directive. */ switch (tok = gettoken_term(mf, dir_name)) { case TK_ERROR: return (TK_ERROR); case TK_RIGHTBKT: done = 1; break; } break; default: mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_SYM), ld_map_tokenstr(tok, &tkv_sym, &inv_buf)); return (TK_ERROR); } } /* * In the SYMBOL directive, we keep parsing in the face of * errors that don't involve resources, to maximize what we * can report in a single invocation. If we encountered such * an error, act on the error(s) now. */ if (ss->ss_mv.mv_errcnt) return (TK_ERROR); return (tok); } /* * Top Level Directive: * * SYMBOL_SCOPE { ... * ------------^ */ static Token dir_symbol_scope(Mapfile *mf) { symbol_state_t ss; /* The first token must be a '{' */ if (gettoken_leftbkt(mf, MSG_ORIG(MSG_MAPKW_SYMBOL_SCOPE)) == TK_ERROR) return (TK_ERROR); /* Establish the version descriptor and related data */ if (!ld_map_sym_ver_init(mf, NULL, &ss.ss_mv)) return (TK_ERROR); /* Read attributes until the closing '}' is seen */ if (parse_symbol_attributes(mf, MSG_ORIG(MSG_MAPKW_SYMBOL_SCOPE), &ss) == TK_ERROR) return (TK_ERROR); /* Terminating ';' */ return (gettoken_semicolon(mf, MSG_ORIG(MSG_MAPKW_SYMBOL_SCOPE))); } /* * at_dv_allow(): Value for ALLOW= is not a version string */ static void gts_efunc_dir_symbol_version(Mapfile *mf, Token tok, ld_map_tkval_t *tkv) { Conv_inv_buf_t inv_buf; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_VERSION), MSG_ORIG(MSG_MAPKW_SYMBOL_VERSION), ld_map_tokenstr(tok, tkv, &inv_buf)); } /* * Top Level Directive: * * SYMBOL_VERSION version_name { ... * --------------^ */ static Token dir_symbol_version(Mapfile *mf) { ld_map_tkval_t tkv; symbol_state_t ss; /* The first token must be a version name */ if (gettoken_str(mf, 0, &tkv, gts_efunc_dir_symbol_version) == TK_ERROR) return (TK_ERROR); /* The next token is expected to be '{' */ if (gettoken_leftbkt(mf, MSG_ORIG(MSG_MAPKW_SYMBOL_VERSION)) == TK_ERROR) return (TK_ERROR); /* Establish the version descriptor and related data */ if (!ld_map_sym_ver_init(mf, tkv.tkv_str, &ss.ss_mv)) return (TK_ERROR); /* Read attributes until the closing '}' is seen */ if (parse_symbol_attributes(mf, MSG_ORIG(MSG_MAPKW_SYMBOL_VERSION), &ss) == TK_ERROR) return (TK_ERROR); /* * Determine if any version references are provided after the close * bracket, parsing up to the terminating ';'. */ if (!ld_map_sym_ver_fini(mf, &ss.ss_mv)) return (TK_ERROR); return (TK_SEMICOLON); } /* * Parse the mapfile --- Solaris syntax */ Boolean ld_map_parse_v2(Mapfile *mf) { /* Valid top level mapfile directives */ typedef struct { const char *name; /* Directive */ dir_func_t func; /* Function to parse directive */ } tldir_t; tldir_t dirlist[] = { { MSG_ORIG(MSG_MAPKW_CAPABILITY), dir_capability }, { MSG_ORIG(MSG_MAPKW_DEPEND_VERSIONS), dir_depend_versions }, { MSG_ORIG(MSG_MAPKW_HDR_NOALLOC), dir_hdr_noalloc }, { MSG_ORIG(MSG_MAPKW_LOAD_SEGMENT), dir_load_segment }, { MSG_ORIG(MSG_MAPKW_NOTE_SEGMENT), dir_note_segment }, { MSG_ORIG(MSG_MAPKW_NULL_SEGMENT), dir_null_segment }, { MSG_ORIG(MSG_MAPKW_PHDR_ADD_NULL), dir_phdr_add_null }, { MSG_ORIG(MSG_MAPKW_SEGMENT_ORDER), dir_segment_order }, { MSG_ORIG(MSG_MAPKW_STACK), dir_stack }, { MSG_ORIG(MSG_MAPKW_SYMBOL_SCOPE), dir_symbol_scope }, { MSG_ORIG(MSG_MAPKW_SYMBOL_VERSION), dir_symbol_version }, /* List must be null terminated */ { 0 } }; /* * Size of buffer needed to format the names in dirlist[]. Must * be kept in sync with dirlist. */ static size_t dirlist_bufsize = KW_NAME_SIZE(MSG_MAPKW_CAPABILITY) + KW_NAME_SIZE(MSG_MAPKW_DEPEND_VERSIONS) + KW_NAME_SIZE(MSG_MAPKW_HDR_NOALLOC) + KW_NAME_SIZE(MSG_MAPKW_LOAD_SEGMENT) + KW_NAME_SIZE(MSG_MAPKW_NOTE_SEGMENT) + KW_NAME_SIZE(MSG_MAPKW_NULL_SEGMENT) + KW_NAME_SIZE(MSG_MAPKW_PHDR_ADD_NULL) + KW_NAME_SIZE(MSG_MAPKW_SEGMENT_ORDER) + KW_NAME_SIZE(MSG_MAPKW_STACK) + KW_NAME_SIZE(MSG_MAPKW_SYMBOL_SCOPE) + KW_NAME_SIZE(MSG_MAPKW_SYMBOL_VERSION); Token tok; /* current token. */ ld_map_tkval_t tkv; /* Value of token */ tldir_t *tldir; Conv_inv_buf_t inv_buf; for (;;) { tok = ld_map_gettoken(mf, TK_F_EOFOK | TK_F_KEYWORD, &tkv); switch (tok) { case TK_ERROR: return (FALSE); case TK_EOF: return (TRUE); case TK_SEMICOLON: /* Terminator, or empty directive: Ignore */ break; case TK_STRING: /* Map name to entry in dirlist[] */ tldir = ld_map_kwfind(tkv.tkv_str, dirlist, SGSOFFSETOF(tldir_t, name), sizeof (dirlist[0])); /* Not a directive we know? */ if (tldir == NULL) goto bad_dirtok; /* Call the function associated with this directive */ if (tldir->func(mf) == TK_ERROR) return (FALSE); break; default: bad_dirtok: { char buf[VLA_SIZE(dirlist_bufsize)]; mf_fatal(mf, MSG_INTL(MSG_MAP_EXP_DIR), ld_map_kwnames(dirlist, SGSOFFSETOF(tldir_t, name), sizeof (dirlist[0]), buf, dirlist_bufsize), ld_map_tokenstr(tok, &tkv, &inv_buf)); } return (FALSE); } } }