/*
* 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
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1988 AT&T
* All Rights Reserved
*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Map file parsing (Original SysV syntax).
*/
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <elfcap.h>
#include <debug.h>
#include "msg.h"
#include "_libld.h"
#include "_map.h"
/*
* hwcap_1 = val,... [ OVERRIDE ]
* sfcap_1 = val,... [ OVERRIDE ]
* hwcap_2 = val,... [ OVERRIDE ]
* platcap = name,... [ OVERRIDE ]
* machcap = name,... [ OVERRIDE ]
*
* The values can be defined as a list of machine specify tokens, or numerics.
*
* #define AV_386_FPU 0x0001 is represented as FPU
* #define AV_386_TSC 0x0002 " " " " TSC
*
* Or, the above two capabilities could be represented as V0x3. Note, the
* OVERRIDE flag is used to ensure that only those values provided via this
* mapfile entry are recorded in the final image, ie. this overrides any
* hardware capabilities that may be defined in the objects read as part of
* this link-edit. Specifying:
*
* V0x0 OVERRIDE
*
* effectively removes any capabilities information from the final image.
*/
static Boolean
{
int used = 0;
if (DBG_ENABLED) {
}
TK_SEMICOLON) {
return (FALSE);
}
/*
* First, determine if the token represents the reserved
* OVERRIDE keyword.
*/
MSG_MAP_OVERRIDE_SIZE) == 0) {
used++;
continue;
}
/* Is the token a symbolic capability name? */
used++;
continue;
}
/*
* Is the token a numeric value?
*/
&number) != STRTOXWORD_OK) {
return (FALSE);
}
used++;
continue;
}
/*
* We have an unknown token.
*/
used++;
return (FALSE);
}
/* Catch empty declarations */
if (used == 0) {
return (TRUE);
}
/* Sanity check the resulting bits */
return (FALSE);
return (TRUE);
}
/*
* Parse the flags for a segment definition. Called by map_equal().
*
* entry:
* mf - Mapfile descriptor
* sgp - Segment being defined
* b_flags - Address of b_flags variable from map_equal().
* *bflags is TRUE if flags have already been seen in, the
* current segment definition directive, and FALSE otherwise.
* flag_tok - Flags string, starting with the '?' character.
*
* exit:
* On success, the flags have been parsed and the segment updated,
* *b_flags is set to TRUE, and TRUE is returned. On error, FALSE
* is returned.
*/
static Boolean
const char *flag_tok)
{
if (*b_flags) {
return (FALSE);
}
/* Skip over the leading '?' character */
flag_tok++;
/*
* If ? has nothing following leave the flags cleared,
* otherwise OR in any flags specified.
*/
while (*flag_tok) {
switch (*flag_tok) {
case 'r':
break;
case 'w':
break;
case 'x':
break;
case 'e':
break;
case 'o':
/*
* The version 1 ?O option is incompatible with
* the version 2 SEGMENT IS_ORDER attribute.
*/
return (FALSE);
}
/*
* Set FLG_SG_IS_ORDER to indicate that segment has
* had the ?O flag set by a version 1 mapfile.
*/
break;
case 'n':
/*
* If segment ends up as the first loadable segment,
* it will not include the the ELF and program headers.
*/
break;
default:
return (FALSE);
}
flag_tok++;
}
/*
* Warn when changing flags except when we're adding or removing "X"
* from a RW PT_LOAD segment.
*/
return (TRUE);
}
/*
* Read an address (value) or size Xword from a TK_STRING token value
* where the first letter of the string is a letter ('v', 'l', 's', ...)
* followed by the numeric value.
*
* entry:
* mf - Mapfile descriptor
* tkv - TK_STRING token to parse
* value - Address of variable to receive the resulting value.
*
* exit:
* Returns TRUE for success. On failure, issues an error message
* and returns FALSE.
*/
static Boolean
{
case STRTOXWORD_OK:
return (TRUE);
case STRTOXWORD_TOOBIG:
break;
default:
break;
}
return (FALSE);
}
/*
* Process a mapfile segment declaration definition.
* segment_name = segment_attribute;
* segment_attribute : segment_type segment_flags virtual_addr
* physical_addr length alignment
*/
static Boolean
{
/*
* Segment type. Users are permitted to define PT_LOAD,
* PT_NOTE, PT_SUNWSTACK and PT_NULL segments. Other segment
* types are only defined in seg_desc[].
*/
typedef struct {
} seg_types_t;
/* Array must be NULL terminated */
{ NULL }
};
TK_SEMICOLON) {
return (FALSE);
}
/*
* If it is the name of a segment type, set the type
* and flags fields in the descriptor.
*/
if (b_type) {
return (FALSE);
}
}
break;
}
}
continue; /* next token */
/* Segment Flags */
return (FALSE);
continue; /* next token */
}
/* Segment address, length, alignment or rounding number */
return (FALSE);
case 'l':
if (b_len) {
return (FALSE);
}
break;
case 'r':
if (b_round) {
return (FALSE);
}
break;
case 'v':
if (b_vaddr) {
return (FALSE);
}
/* LINTED */
break;
case 'p':
if (b_paddr) {
return (FALSE);
}
/* LINTED */
break;
case 'a':
if (b_align) {
return (FALSE);
}
/* LINTED */
break;
}
continue; /* next token */
}
/*
* If we reach the bottom of this loop, we have an
* unrecognized token.
*/
return (FALSE);
}
/*
* Empty segments can be used to define PT_LOAD segment reservations, or
* to reserve PT_NULL program headers.
*
* PT_LOAD reservations are only allowed within executables, as the
* reservation must be established through exec() as part of initial
* process loading. In addition, PT_LOAD reservations must have an
* associated address and size. Note: This is an obsolete feature,
* not supported by the newer mapfile syntax.
*
* PT_NULL program headers are established for later use by applications
* such as the post-optimizer. PT_NULL headers should have no other
* attributes assigned.
*/
/*
* Any style of empty segment should have no permissions.
*/
return (FALSE);
}
return (FALSE);
}
(FLG_SG_LENGTH | FLG_SG_P_VADDR)) !=
(FLG_SG_LENGTH | FLG_SG_P_VADDR)) {
return (FALSE);
}
(FLG_SG_LENGTH | FLG_SG_P_VADDR)) &&
return (FALSE);
}
} else {
}
}
/*
* All segment attributes have now been scanned. Certain flags do not
* make sense if this is not a loadable segment, fix if necessary.
* Note, if the segment is of type PT_NULL it must be new, and any
* defaults will be applied by ld_map_seg_insert(). When clearing an
* attribute leave the flag set as an indicator for later entries
* re-specifying the same segment.
*/
const char *fmt;
else
}
}
}
}
}
}
}
}
}
return (TRUE);
}
/*
* Process a mapfile mapping directives definition.
*
* segment_name : section_attribute [ : file_name ]
*
* Where segment_attribute is one of: section_name section_type section_flags;
*/
static Boolean
{
/*
* Start out assuming that this entrance criteria will be empty,
* and therefore match anything. We clear the CATCHALL flag below
* if this turns out not to be the case.
*/
(tok != TK_SEMICOLON)) {
return (FALSE);
return (FALSE);
}
/* Segment type. */
if (b_type) {
return (FALSE);
}
0)
MSG_ORIG(MSG_STR_SYMTAB)) == 0)
MSG_ORIG(MSG_STR_DYNSYM)) == 0)
MSG_ORIG(MSG_STR_STRTAB)) == 0)
MSG_ORIG(MSG_STR_REL)) == 0) ||
0)
0)
MSG_ORIG(MSG_STR_LD_DYNAMIC)) == 0)
0)
MSG_ORIG(MSG_STR_NOBITS)) == 0)
else {
return (FALSE);
}
/*
* 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.
*/
if (b_attr) {
return (FALSE);
}
case '!':
if (b_bang) {
return (FALSE);
}
break;
case 'a':
return (FALSE);
}
if (!b_bang)
break;
case 'w':
return (FALSE);
}
if (!b_bang)
break;
case 'x':
return (FALSE);
}
if (!b_bang)
enp->ec_attrbits |=
break;
default:
return (FALSE);
}
if (enp->ec_attrmask != 0)
/*
* Section name.
*/
} else {
if (b_name) {
return (FALSE);
}
}
}
/*
* File names.
*/
return (FALSE);
}
/*
* A leading '*' means that this should be a basename
* comparison rather than a full path. It's not a glob
* wildcard, although it looks like one.
*/
} else {
}
return (FALSE);
}
}
return (TRUE);
}
/*
* Process a mapfile size symbol definition.
* segment_name @ symbol_name;
*/
static Boolean
{
return (FALSE);
}
/* Add the symbol to the segment */
return (FALSE);
return (FALSE);
}
return (TRUE);
}
static Boolean
{
return (FALSE);
}
return (FALSE);
return (FALSE);
}
return (TRUE);
}
/*
* Process a mapfile library specification definition.
* shared_object_name - shared object definition
* shared object definition : [ shared object type [ = SONAME ]]
* [ versions ];
*/
static Boolean
{
enum {
MD_NONE = 0,
/* Get descriptor for dependency */
return (FALSE);
/*
* Get the shared object descriptor string.
*/
return (FALSE);
}
/*
* Determine if the library type is accompanied with a SONAME
* definition.
*/
TK_STRING) {
return (FALSE);
}
switch (dolkey) {
case MD_ADDVERS:
return (FALSE);
break;
case MD_NONE:
return (FALSE);
}
continue;
}
/*
* A shared object type has been specified. This may also be
* accompanied by an SONAME redefinition (see above).
*/
return (FALSE);
}
0) {
dolkey = MD_ADDVERS;
} else {
return (FALSE);
}
continue;
}
/*
* shared object version requirement.
*/
return (FALSE);
}
return (TRUE);
}
/*
* Process a symbol definition. Historically, this originated from processing
* a version definition. However, this has evolved into a generic means of
* defining symbol references and definitions (see Defining Additional Symbols
* in the Linker and Libraries guide for the complete syntax).
*
* [ name ] {
* scope:
* symbol [ = [ type ] [ value ] [ size ] [ attribute ] ];
* } [ dependency ];
*
*/
static Boolean
{
/* Establish the version descriptor and related data */
return (FALSE);
/*
* Scan the mapfile entry picking out scoping and symbol definitions.
*/
return (FALSE);
}
continue;
}
/* The default value for all the symbol attributes is 0 */
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).
*/
switch (tok) {
case TK_COLON:
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.
*/
TK_SEMICOLON) {
return (FALSE);
return (FALSE);
}
/*
* If we had previously seen AUX or FILTER,
* the next string is the filtee itself.
* Add it, and clear the filter flag.
*/
if (filter) {
filter = 0;
continue;
}
/*
* Determine any Value or Size attributes.
*/
return (FALSE);
}
case 'v':
/* BEGIN CSTYLED */
continue;
}
/* LINTED */
break;
/* END CSTYLED */
case 's':
/* BEGIN CSTYLED */
continue;
}
/* LINTED */
break;
/* END CSTYLED */
}
MSG_ORIG(MSG_MAP_FUNCTION)) == 0) {
MSG_ORIG(MSG_MAP_DATA)) == 0) {
MSG_ORIG(MSG_MAP_COMMON)) == 0) {
MSG_ORIG(MSG_MAP_PARENT)) == 0) {
MSG_ORIG(MSG_MAP_EXTERN)) == 0) {
MSG_ORIG(MSG_MAP_DIRECT)) == 0) {
MSG_ORIG(MSG_MAP_NODIRECT)) == 0) {
ofl->ofl_flags1 |=
MSG_ORIG(MSG_MAP_FILTER)) == 0) {
/* Next token is the filtee */
continue;
MSG_ORIG(MSG_MAP_AUXILIARY)) == 0) {
/* Next token is the filtee */
continue;
MSG_ORIG(MSG_MAP_INTERPOSE)) == 0) {
/* BEGIN CSTYLED */
break;
}
/* END CSTYLED */
continue;
MSG_ORIG(MSG_MAP_DYNSORT)) == 0) {
continue;
MSG_ORIG(MSG_MAP_NODYNSORT)) == 0) {
continue;
} else {
continue;
}
}
/* FALLTHROUGH */
case TK_SEMICOLON:
/* Auto-reduction directive ('*')? */
continue;
}
/*
* Catch the error where the AUX or FILTER keyword
* was used, but the filtee wasn't supplied.
*/
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.
*/
return (FALSE);
break;
default:
continue;
}
}
return (FALSE);
/*
* Determine if any version references are provided after the close
* bracket, parsing up to the terminating ';'.
*/
return (FALSE);
return (TRUE);
}
/*
* Parse the mapfile --- Sysv syntax
*/
{
char *name;
/*
* We now parse the mapfile until the gettoken routine returns EOF.
*/
/*
* At this point we are at the beginning of a line, and the
* variable tkv.tkv_str points to the first string on the line.
* All mapfile entries start with some string token except it
* is possible for a scoping definition to start with `{'.
*/
if (tok == TK_LEFTBKT) {
return (FALSE);
continue;
}
return (FALSE);
}
/*
* Save the initial token.
*/
/*
* Now check the second character on the line. The special `-'
* and `{' characters do not involve any segment manipulation so
* we handle them first.
*/
return (FALSE);
return (FALSE);
continue;
}
if (tok == TK_LEFTBKT) {
return (FALSE);
continue;
}
/*
* If we're here we need to interpret the first string as a
* segment name. Is this an already known segment?
*/
if (!new_segment)
/*
* If the second token is a '|' then we had better have found a
* segment. It is illegal to perform section within segment
* ordering before the segment has been declared.
*/
name);
return (FALSE);
}
return (FALSE);
continue;
}
/*
* If segment does not exist, allocate a descriptor with
* its values set to 0 so that map_equal() can detect
* changing attributes.
*/
if (new_segment &&
return (FALSE);
/*
* Now check the second token from the input line.
*/
switch (tok) {
/*
* capabilities as we do for segments. If the
* "segment name" matches one of these, then
* process the capabilities instead of treating it
* as a segment. Note that no dynamic memory has
* been allocated for the segment descriptor yet,
* so we can bail without leaking memory.
*/
MSG_ORIG(MSG_STR_HWCAP_1)) == 0) {
return (FALSE);
continue;
}
MSG_ORIG(MSG_STR_SFCAP_1)) == 0) {
return (FALSE);
continue;
}
/*
* If not a new segment, show the initial value
* before modifying it.
*/
if (!new_segment && DBG_ENABLED) {
}
/* Process the segment */
return (FALSE);
/*
* Special case for STACK "segments":
*
* The ability to modify the stack flags was added
* long after this sysv syntax was designed. It was
* fit into the existing syntax by treating it as a
* segment. However, there can only be one stack program
* header, while segment syntax requires user to supply
* a name. This is confusing, and it allows the user to
* attempt to create more than one stack segment. The
* original implementation had a test to catch this.
*
* If this is a stack segment, locate the real stack
* descriptor and transfer the flags to it. We then
* free the allocated descriptor without inserting it.
* The end result is that all stack segments simply
* alter the one stack descriptor, and the segment
* name is ignored.
*/
break;
}
/*
* If this is a new segment, finish its initialization
* and insert it into the segment list.
*/
if (new_segment) {
case SEG_INS_SKIP:
continue;
case SEG_INS_FAIL:
return (FALSE);
}
} else {
/* Not new. Show what's changed */
}
break;
case TK_COLON: /* Section to segment mapping */
/*
* If this is a new segment, finish its initialization
* and insert it into the segment list.
*
* If it is not a new segment, ensure that it is
* not an empty segment reservation, as sections
* cannot be assigned to those.
*/
if (new_segment) {
switch (ld_map_seg_insert(mf,
case SEG_INS_SKIP:
continue;
case SEG_INS_FAIL:
return (FALSE);
}
return (FALSE);
}
/*
* Create new entrance criteria descriptor, and
* process the mapping directive.
*/
return (FALSE);
break;
case TK_ATSIGN: /* Section size symbol */
/*
* If this is a new segment, finish its initialization
* and insert it into the segment list.
*/
if (new_segment) {
switch (ld_map_seg_insert(mf,
case SEG_INS_SKIP:
continue;
case SEG_INS_FAIL:
return (FALSE);
}
}
return (FALSE);
break;
case TK_ERROR:
return (FALSE); /* Error was already issued */
default:
return (FALSE);
}
}
return (TRUE);
}