map_core.c revision 1007fd6fd24227460e77ce89f5ca85641a85a576
/*
* 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
*
*/
/*
* Map file parsing (Shared Core Code).
*/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <ctype.h>
#include <debug.h>
#include "msg.h"
#include "_libld.h"
#include "_map.h"
/*
* There are two styles of mapfile supported by the link-editor:
*
* 1) The original System V defined syntax, as augmented at Sun
* from Solaris 2.0 through Solaris 10. This style is also known
* as version 1.
*
* 2) A newer syntax, currently at version 2.
*
* The original syntax uses special characters (=, :, -, |, etc) as
* operators to indicate the operation being specified. Over the years,
* this syntax has been problematic:
*
* 1) Too cryptic: It's hard for people to remember which character
* means what.
*
* 2) Limited expansion potential: There only a few special characters
* available on the keyboard for new features, and it is difficult to
* add options to existing ones.
*
* Adding new features into this framework (2) have the effect of
* making the syntax even more cryptic (1). The newer syntax addresses
* these issues by moving to an extendible identifier based syntax that
* allows new features to be added without complicating old ones.
*
* The new syntax uses the following terminology:
*
* - Control directives are the directives that start with a '$'.
* They control how the mapfile is interpreted. We use the 'cdir_'
* prefix on functions and variables related to these directives.
*
* - Conditional Expressions are the expressions found in $if and $elif
* We use the 'cexp_' prefix for functions and variables related to
* these expressions.
*
* - Regular Directives are names (SYMBOL, VERSION, etc) that convey
* directions to the link-editor for building the output object.
*
* This file contains core code used by both mapfile styles: File management,
* lexical analysis, and other shared core functionality. It also contains
* the code for control directives, as they are intrinsically part of
* lexical analysis --- this is disabled when processing Sysv mapfiles.
*/
/*
* We use a stack of cdir_level_t structs to manage $if/$elif/$else/$endif
* processing. At each level, we keep track of the information needed to
* determine whether or not to process nested input lines or skip them,
* along with information needed to report errors.
*/
typedef struct {
int cdl_done; /* True if no longer accepts input */
int cdl_pass; /* True if currently accepting input */
} cdir_level_t;
/* Operators in the expressions accepted by $if/$elif */
typedef enum {
CEXP_OP_NONE, /* Not an operator */
CEXP_OP_AND, /* && */
CEXP_OP_OR, /* || */
CEXP_OP_NEG, /* ! */
CEXP_OP_OPAR, /* ( */
CEXP_OP_CPAR /* ) */
} cexp_op_t;
/*
* Type of conditional expression identifier AVL tree nodes
*/
typedef struct cexp_name_node {
const char *ceid_name; /* boolean identifier name */
/*
* Declare a "stack" type, containing a pointer to data, a count of
* allocated, and currently used items in the stack. The data type
* is specified as the _type argument.
*/
struct { \
}
/*
* The following type represents a "generic" stack, where the data
* type is (void). This type is never instantiated. However, it has
* the same struct layout as any other STACK(), and is therefore a good
* generic type that can be used for stack_resize().
*/
typedef STACK(void) generic_stack_t;
/*
* Ensure that the stack has enough room to push one more item
*/
/*
* Reset a stack to empty.
*/
/*
* True if stack is empty, False otherwise.
*/
/*
* Push a value onto a stack. Caller must ensure that stack has room.
* This macro is intended to be used as the LHS of an assignment, the
* RHS of which is the value:
*
* STACK_PUSH(stack) = value;
*/
/*
* Pop a value off a stack. Caller must ensure
* that stack is not empty.
*/
/*
* Access top element on stack without popping. Caller must ensure
* that stack is not empty.
*/
/*
* Initial sizes used for the stacks: The stacks are allocated on demand
* to these sizes, and then doubled as necessary until they are large enough.
*
* The ideal size would be large enough that only a single allocation
* occurs, and our defaults should generally have that effect. However,
* in doing so, we run the risk of a latent error in the resize code going
* undetected until triggered by a large task in the field. For this reason,
* we set the sizes to the smallest size possible when compiled for debug.
*/
#ifdef DEBUG
#define CDIR_STACK_INIT 1
#define CEXP_OP_STACK_INIT 1
#define CEXP_VAL_STACK_INIT 1
#else
#define CDIR_STACK_INIT 16
#define CEXP_OP_STACK_INIT 8
#endif
/*
* Persistent state maintained by map module in between calls.
*
* This is kept as static file scope data, because it is only used
* when libld is called by ld, and not by rtld. If that should change,
* the code is designed so that it can become reentrant easily:
*
* - Add a pointer to the output descriptor to a structure of this type,
* allocated dynamically on the first call to ld_map_parse().
* - Change all references to lms to instead reference the pointer in
* the output descriptor.
*
* Until then, it is simpler not to expose these details.
*/
typedef struct {
int lms_cdir_valid; /* Allow control dir. on entry to gettoken() */
static ld_map_state_t lms;
/*
* Version 1 (SysV) syntax dispatch table for ld_map_gettoken(). For each
* of the 7-bit ASCII characters, determine how the lexical analyzer
* should behave.
*
* This table must be kept in sync with tkid_attr[] below.
*
* Identifier Note:
* The Linker and Libraries Guide states that the original syntax uses
* C identifier rules, allowing '.' to be treated as a letter. However,
* the implementation is considerably looser than that: Any character
* with an ASCII code (0-127) which is printable and not used to start
* another token is allowed to start an identifier, and they are terminated
* by any of: space, double quote, tab, newline, ':', ';', '=', or '#'.
* The original code has been replaced, but this table encodes the same
* rules, to ensure backward compatibility.
*/
static const mf_tokdisp_t gettok_dispatch_v1 = {
TK_OP_EOF, /* 0 - NUL */
TK_OP_ILLCHR, /* 1 - SOH */
TK_OP_ILLCHR, /* 2 - STX */
TK_OP_ILLCHR, /* 3 - ETX */
TK_OP_ILLCHR, /* 4 - EOT */
TK_OP_ILLCHR, /* 5 - ENQ */
TK_OP_ILLCHR, /* 6 - ACK */
TK_OP_ILLCHR, /* 7 - BEL */
TK_OP_ILLCHR, /* 8 - BS */
TK_OP_WS, /* 9 - HT */
TK_OP_NL, /* 10 - NL */
TK_OP_WS, /* 11 - VT */
TK_OP_WS, /* 12 - FF */
TK_OP_WS, /* 13 - CR */
TK_OP_ILLCHR, /* 14 - SO */
TK_OP_ILLCHR, /* 15 - SI */
TK_OP_ILLCHR, /* 16 - DLE */
TK_OP_ILLCHR, /* 17 - DC1 */
TK_OP_ILLCHR, /* 18 - DC2 */
TK_OP_ILLCHR, /* 19 - DC3 */
TK_OP_ILLCHR, /* 20 - DC4 */
TK_OP_ILLCHR, /* 21 - NAK */
TK_OP_ILLCHR, /* 22 - SYN */
TK_OP_ILLCHR, /* 23 - ETB */
TK_OP_ILLCHR, /* 24 - CAN */
TK_OP_ILLCHR, /* 25 - EM */
TK_OP_ILLCHR, /* 26 - SUB */
TK_OP_ILLCHR, /* 27 - ESC */
TK_OP_ILLCHR, /* 28 - FS */
TK_OP_ILLCHR, /* 29 - GS */
TK_OP_ILLCHR, /* 30 - RS */
TK_OP_ILLCHR, /* 31 - US */
TK_OP_WS, /* 32 - SP */
TK_OP_ID, /* 33 - ! */
TK_OP_SIMQUOTE, /* 34 - " */
TK_OP_CMT, /* 35 - # */
TK_OP_ID, /* 36 - $ */
TK_OP_ID, /* 37 - % */
TK_OP_ID, /* 38 - & */
TK_OP_ID, /* 39 - ' */
TK_OP_ID, /* 40 - ( */
TK_OP_ID, /* 41 - ) */
TK_OP_ID, /* 42 - * */
TK_OP_ID, /* 43 - + */
TK_OP_ID, /* 44 - , */
TK_DASH, /* 45 - - */
TK_OP_ID, /* 46 - . */
TK_OP_ID, /* 47 - / */
TK_OP_ID, /* 48 - 0 */
TK_OP_ID, /* 49 - 1 */
TK_OP_ID, /* 50 - 2 */
TK_OP_ID, /* 51 - 3 */
TK_OP_ID, /* 52 - 4 */
TK_OP_ID, /* 53 - 5 */
TK_OP_ID, /* 54 - 6 */
TK_OP_ID, /* 55 - 7 */
TK_OP_ID, /* 56 - 8 */
TK_OP_ID, /* 57 - 9 */
TK_COLON, /* 58 - : */
TK_SEMICOLON, /* 59 - ; */
TK_OP_ID, /* 60 - < */
TK_EQUAL, /* 61 - = */
TK_OP_ID, /* 62 - > */
TK_OP_ID, /* 63 - ? */
TK_ATSIGN, /* 64 - @ */
TK_OP_ID, /* 65 - A */
TK_OP_ID, /* 66 - B */
TK_OP_ID, /* 67 - C */
TK_OP_ID, /* 68 - D */
TK_OP_ID, /* 69 - E */
TK_OP_ID, /* 70 - F */
TK_OP_ID, /* 71 - G */
TK_OP_ID, /* 72 - H */
TK_OP_ID, /* 73 - I */
TK_OP_ID, /* 74 - J */
TK_OP_ID, /* 75 - K */
TK_OP_ID, /* 76 - L */
TK_OP_ID, /* 77 - M */
TK_OP_ID, /* 78 - N */
TK_OP_ID, /* 79 - O */
TK_OP_ID, /* 80 - P */
TK_OP_ID, /* 81 - Q */
TK_OP_ID, /* 82 - R */
TK_OP_ID, /* 83 - S */
TK_OP_ID, /* 84 - T */
TK_OP_ID, /* 85 - U */
TK_OP_ID, /* 86 - V */
TK_OP_ID, /* 87 - W */
TK_OP_ID, /* 88 - X */
TK_OP_ID, /* 89 - Y */
TK_OP_ID, /* 90 - Z */
TK_OP_ID, /* 91 - [ */
TK_OP_ID, /* 92 - \ */
TK_OP_ID, /* 93 - ] */
TK_OP_ID, /* 94 - ^ */
TK_OP_ID, /* 95 - _ */
TK_OP_ID, /* 96 - ` */
TK_OP_ID, /* 97 - a */
TK_OP_ID, /* 98 - b */
TK_OP_ID, /* 99 - c */
TK_OP_ID, /* 100 - d */
TK_OP_ID, /* 101 - e */
TK_OP_ID, /* 102 - f */
TK_OP_ID, /* 103 - g */
TK_OP_ID, /* 104 - h */
TK_OP_ID, /* 105 - i */
TK_OP_ID, /* 106 - j */
TK_OP_ID, /* 107 - k */
TK_OP_ID, /* 108 - l */
TK_OP_ID, /* 109 - m */
TK_OP_ID, /* 110 - n */
TK_OP_ID, /* 111 - o */
TK_OP_ID, /* 112 - p */
TK_OP_ID, /* 113 - q */
TK_OP_ID, /* 114 - r */
TK_OP_ID, /* 115 - s */
TK_OP_ID, /* 116 - t */
TK_OP_ID, /* 117 - u */
TK_OP_ID, /* 118 - v */
TK_OP_ID, /* 119 - w */
TK_OP_ID, /* 120 - x */
TK_OP_ID, /* 121 - y */
TK_OP_ID, /* 122 - z */
TK_LEFTBKT, /* 123 - { */
TK_PIPE, /* 124 - | */
TK_RIGHTBKT, /* 125 - } */
TK_OP_ID, /* 126 - ~ */
TK_OP_ILLCHR, /* 127 - DEL */
};
/*
* Version 2 syntax dispatch table for ld_map_gettoken(). For each of the
* 7-bit ASCII characters, determine how the lexical analyzer should behave.
*
* This table must be kept in sync with tkid_attr[] below.
*
* Identifier Note:
* We define a letter as being one of the character [A-Z], [a-z], or [_%/.]
* A digit is the numbers [0-9], or [$-]. An unquoted identifier is defined
* as a letter, followed by any number of letters or digits. This is a loosened
* version of the C definition of an identifier. The extra characters not
*/
static const mf_tokdisp_t gettok_dispatch_v2 = {
TK_OP_EOF, /* 0 - NUL */
TK_OP_ILLCHR, /* 1 - SOH */
TK_OP_ILLCHR, /* 2 - STX */
TK_OP_ILLCHR, /* 3 - ETX */
TK_OP_ILLCHR, /* 4 - EOT */
TK_OP_ILLCHR, /* 5 - ENQ */
TK_OP_ILLCHR, /* 6 - ACK */
TK_OP_ILLCHR, /* 7 - BEL */
TK_OP_ILLCHR, /* 8 - BS */
TK_OP_WS, /* 9 - HT */
TK_OP_NL, /* 10 - NL */
TK_OP_WS, /* 11 - VT */
TK_OP_WS, /* 12 - FF */
TK_OP_WS, /* 13 - CR */
TK_OP_ILLCHR, /* 14 - SO */
TK_OP_ILLCHR, /* 15 - SI */
TK_OP_ILLCHR, /* 16 - DLE */
TK_OP_ILLCHR, /* 17 - DC1 */
TK_OP_ILLCHR, /* 18 - DC2 */
TK_OP_ILLCHR, /* 19 - DC3 */
TK_OP_ILLCHR, /* 20 - DC4 */
TK_OP_ILLCHR, /* 21 - NAK */
TK_OP_ILLCHR, /* 22 - SYN */
TK_OP_ILLCHR, /* 23 - ETB */
TK_OP_ILLCHR, /* 24 - CAN */
TK_OP_ILLCHR, /* 25 - EM */
TK_OP_ILLCHR, /* 26 - SUB */
TK_OP_ILLCHR, /* 27 - ESC */
TK_OP_ILLCHR, /* 28 - FS */
TK_OP_ILLCHR, /* 29 - GS */
TK_OP_ILLCHR, /* 30 - RS */
TK_OP_ILLCHR, /* 31 - US */
TK_OP_WS, /* 32 - SP */
TK_BANG, /* 33 - ! */
TK_OP_CQUOTE, /* 34 - " */
TK_OP_CMT, /* 35 - # */
TK_OP_CDIR, /* 36 - $ */
TK_OP_ID, /* 37 - % */
TK_OP_BADCHR, /* 38 - & */
TK_OP_SIMQUOTE, /* 39 - ' */
TK_OP_BADCHR, /* 40 - ( */
TK_OP_BADCHR, /* 41 - ) */
TK_STAR, /* 42 - * */
TK_OP_CEQUAL, /* 43 - + */
TK_OP_BADCHR, /* 44 - , */
TK_OP_CEQUAL, /* 45 - - */
TK_OP_ID, /* 46 - . */
TK_OP_ID, /* 47 - / */
TK_OP_NUM, /* 48 - 0 */
TK_OP_NUM, /* 49 - 1 */
TK_OP_NUM, /* 50 - 2 */
TK_OP_NUM, /* 51 - 3 */
TK_OP_NUM, /* 52 - 4 */
TK_OP_NUM, /* 53 - 5 */
TK_OP_NUM, /* 54 - 6 */
TK_OP_NUM, /* 55 - 7 */
TK_OP_NUM, /* 56 - 8 */
TK_OP_NUM, /* 57 - 9 */
TK_COLON, /* 58 - : */
TK_SEMICOLON, /* 59 - ; */
TK_OP_BADCHR, /* 60 - < */
TK_EQUAL, /* 61 - = */
TK_OP_BADCHR, /* 62 - > */
TK_OP_BADCHR, /* 63 - ? */
TK_OP_BADCHR, /* 64 - @ */
TK_OP_ID, /* 65 - A */
TK_OP_ID, /* 66 - B */
TK_OP_ID, /* 67 - C */
TK_OP_ID, /* 68 - D */
TK_OP_ID, /* 69 - E */
TK_OP_ID, /* 70 - F */
TK_OP_ID, /* 71 - G */
TK_OP_ID, /* 72 - H */
TK_OP_ID, /* 73 - I */
TK_OP_ID, /* 74 - J */
TK_OP_ID, /* 75 - K */
TK_OP_ID, /* 76 - L */
TK_OP_ID, /* 77 - M */
TK_OP_ID, /* 78 - N */
TK_OP_ID, /* 79 - O */
TK_OP_ID, /* 80 - P */
TK_OP_ID, /* 81 - Q */
TK_OP_ID, /* 82 - R */
TK_OP_ID, /* 83 - S */
TK_OP_ID, /* 84 - T */
TK_OP_ID, /* 85 - U */
TK_OP_ID, /* 86 - V */
TK_OP_ID, /* 87 - W */
TK_OP_ID, /* 88 - X */
TK_OP_ID, /* 89 - Y */
TK_OP_ID, /* 90 - Z */
TK_OP_BADCHR, /* 91 - [ */
TK_OP_BADCHR, /* 92 - \ */
TK_OP_BADCHR, /* 93 - ] */
TK_OP_BADCHR, /* 94 - ^ */
TK_OP_ID, /* 95 - _ */
TK_OP_BADCHR, /* 96 - ` */
TK_OP_ID, /* 97 - a */
TK_OP_ID, /* 98 - b */
TK_OP_ID, /* 99 - c */
TK_OP_ID, /* 100 - d */
TK_OP_ID, /* 101 - e */
TK_OP_ID, /* 102 - f */
TK_OP_ID, /* 103 - g */
TK_OP_ID, /* 104 - h */
TK_OP_ID, /* 105 - i */
TK_OP_ID, /* 106 - j */
TK_OP_ID, /* 107 - k */
TK_OP_ID, /* 108 - l */
TK_OP_ID, /* 109 - m */
TK_OP_ID, /* 110 - n */
TK_OP_ID, /* 111 - o */
TK_OP_ID, /* 112 - p */
TK_OP_ID, /* 113 - q */
TK_OP_ID, /* 114 - r */
TK_OP_ID, /* 115 - s */
TK_OP_ID, /* 116 - t */
TK_OP_ID, /* 117 - u */
TK_OP_ID, /* 118 - v */
TK_OP_ID, /* 119 - w */
TK_OP_ID, /* 120 - x */
TK_OP_ID, /* 121 - y */
TK_OP_ID, /* 122 - z */
TK_LEFTBKT, /* 123 - { */
TK_OP_BADCHR, /* 124 - | */
TK_RIGHTBKT, /* 125 - } */
TK_OP_BADCHR, /* 126 - ~ */
TK_OP_ILLCHR, /* 127 - DEL */
};
/*
* Table used to identify unquoted identifiers. Each element of this array
* contains a bitmask indicating whether the character it represents starts,
* or continues an identifier, for each supported mapfile syntax version.
*/
static const char tkid_attr[128] = {
0, /* 0 - NUL */
0, /* 9 - HT */
0, /* 10 - NL */
0, /* 32 - SP */
0, /* 34 - " */
0, /* 35 - # */
0, /* 58 - : */
0, /* 59 - ; */
0, /* 61 - = */
};
/*
* Advance the given string pointer to the next newline character,
* or the terminating NULL if there is none.
*/
inline static void
advance_to_eol(char **str)
{
char *s = *str;
while ((*s != '\n') && (*s != '\0'))
s++;
*str = s;
}
/*
* Insert a NULL patch at the given address
*/
inline static void
{
*str = '\0';
}
/*
* Undo a NULL patch
*/
inline static void
{
}
/*
* Insert a NULL patch at the end of the line containing str.
*/
static void
{
}
/*
* Locate the end of an unquoted identifier.
*
* entry:
* mf - Mapfile descriptor, positioned to first character
* of identifier.
*
* exit:
* If the item pointed at by mf is not an identifier, returns NULL.
* Otherwise, returns pointer to character after the last character
* of the identifier.
*/
inline static char *
{
int c = *str++;
/* If not a valid start character, report the error */
return (NULL);
}
/* Keep going until we hit a non-continuing character */
c = *++str)
;
return (str);
}
/*
* Allocate memory for a stack.
*
* entry:
* stack - Pointer to stack for which memory is required, cast
* to the generic stack type.
* n_default - Size to use for initial allocation.
* elt_size - sizeof(elt), where elt is the actual stack data type.
*
* exit:
* Returns (1) on success. On error (memory allocation), a message
* is printed and False (0) is returned.
*
* note:
* The caller casts the pointer to their actual datatype-specific stack
* to be a (generic_stack_t *). The C language will give all stack
* structs the same size and layout as long as the underlying platform
* uses a single integral type for pointers. Hence, this cast is safe,
* and lets a generic routine modify data-specific types without being
* aware of those types.
*/
static Boolean
{
void *newaddr;
/* Use initial size first, and double the allocation on each call */
return (FALSE);
return (TRUE);
}
/*
* AVL comparison function for cexp_id_node_t items.
*
* entry:
* n1, n2 - pointers to nodes to be compared
*
* exit:
* Returns -1 if (n1 < n2), 0 if they are equal, and 1 if (n1 > n2)
*/
static int
{
int rc;
if (rc > 0)
return (1);
if (rc < 0)
return (-1);
return (0);
}
/*
* Returns True (1) if name is in the conditional expression identifier
* AVL tree, and False (0) otherwise.
*/
static int
cexp_ident_test(const char *name)
{
}
/*
* Add a new boolean identifier to the conditional expression identifier
* AVL tree.
*
* entry:
* mf - If non-NULL, the mapfile descriptor for the mapfile
* containing the $add directive. NULL if this is an
* initialization call.
* name - Name of identifier. Must point at stable storage that will
* not be moved or modified by the caller following this call.
*
* exit:
* On success, True (1) is returned and name has been entered.
* On failure, False (0) is returned and an error has been printed.
*/
static int
{
/* If is already known, don't do it again */
if (cexp_ident_test(name))
return (1);
}
return (0);
return (1);
}
/*
* Remove a boolean identifier from the conditional expression identifier
* AVL tree.
*
* entry:
* mf - Mapfile descriptor
* name - Name of identifier.
*
* exit:
* If the name was in the tree, it has been removed. If not,
* then this routine quietly returns.
*/
static void
{
}
/*
* Initialize the AVL tree that holds the names of the currently defined
* boolean identifiers for conditional expressions ($if/$elif).
*
* entry:
* ofl - Output file descriptor
*
* exit:
* On success, TRUE (1) is returned and lms.lms_cexp_id is ready for use.
* On failure, FALSE (0) is returned.
*/
static Boolean
cexp_ident_init(void)
{
/* If already done, use it */
return (TRUE);
return (FALSE);
/* ELFCLASS */
return (FALSE);
/* Machine */
case EM_386:
case EM_AMD64:
return (FALSE);
break;
case EM_SPARC:
case EM_SPARCV9:
return (FALSE);
break;
}
/* true is always defined */
return (FALSE);
return (TRUE);
}
/*
* Validate the string starting at mf->mf_next as being a
* boolean conditional expression identifier.
*
* entry:
* mf - Mapfile descriptor
* len - NULL, or address of variable to receive strlen() of identifier
* directive - If (len == NULL), string giving name of directive being
* processed. Ignored if (len != NULL).
*
* exit:
* On success:
* - If len is NULL, a NULL is inserted following the final
* character of the identifier, and the remainder of the string
* is tested to ensure it is empty, or only contains whitespace.
* - If len is non-NULL, *len is set to the number of characters
* in the identifier, and the rest of the string is not modified.
* - TRUE (1) is returned
*
* On failure, returns FALSE (0).
*/
static Boolean
{
char *tail;
return (FALSE);
/*
* If len is non-NULL, we simple count the number of characters
* consumed by the identifier and are done. If len is NULL, then
* ensure there's nothing left but whitespace, and NULL terminate
* the identifier to remove it.
*/
} else if (*tail != '\0') {
*tail++ = '\0';
tail++;
if (*tail != '\0') {
return (FALSE);
}
}
return (TRUE);
}
/*
* Push a new operator onto the conditional expression operator stack.
*
* entry:
* mf - Mapfile descriptor
* op - Operator to push
*
* exit:
* On success, TRUE (1) is returned, otherwise FALSE (0).
*/
static Boolean
{
return (FALSE);
return (TRUE);
}
/*
* Evaluate the basic operator (non-paren) at the top of lms.lms_cexp_op_stack,
* and push the results on lms.lms_cexp_val_stack.
*
* exit:
* On success, returns TRUE (1). On error, FALSE (0) is returned,
* and the caller is responsible for issuing the error.
*/
static Boolean
cexp_eval_op(void)
{
switch (op) {
case CEXP_OP_AND:
return (FALSE);
break;
case CEXP_OP_OR:
return (FALSE);
break;
case CEXP_OP_NEG:
return (FALSE);
break;
default:
return (FALSE);
}
return (TRUE);
}
/*
* Evaluate an expression for a $if/$elif control directive.
*
* entry:
* mf - Mapfile descriptor for NULL terminated string
* containing the expression.
*
* exit:
* The contents of str are modified by this routine.
* One of the following values are returned:
* -1 Syntax error encountered (an error is printed)
* 0 The expression evaluates to False
* 1 The expression evaluates to True.
*
* note:
* A simplified version of Dijkstra's Shunting Yard algorithm is used
* to convert this syntax into postfix form and then evaluate it.
* Our version has no functions and a tiny set of operators.
*
* The expressions consist of boolean identifiers, which can be
* combined using the following operators, listed from highest
* precedence to least:
*
* Operator Meaning
* -------------------------------------------------
* (expr) sub-expression, non-associative
* ! logical negation, prefix, left associative
*
* The operands manipulated by these operators are names, consisting of
* a sequence of letters and digits. The first character must be a letter.
* Underscore (_) and period (.) are also considered to be characters.
* An operand is considered True if it is found in our set of known
* names (lms.lms_cexp_id), and False otherwise.
*
* The Shunting Yard algorithm works using two stacks, one for operators,
* and a second for operands. The infix input expression is tokenized from
* left to right and processed in order. Issues of associativity and
* precedence are managed by reducing (poping and evaluating) items with
* higer precedence before pushing additional tokens with lower precedence.
*/
static int
{
char *ident;
/* Skip whitespace */
str++;
if (!*str)
break;
switch (*str) {
case '&':
case '|':
goto token_error;
if ((new_op != CEXP_OP_NONE) &&
(new_op != CEXP_OP_CPAR)) {
return (-1);
}
str++;
/*
* As this is a left associative binary operator, we
* need to process all operators of equal or higher
* precedence before pushing the new operator.
*/
(op != CEXP_OP_NEG))
break;
if (!cexp_eval_op())
goto semantic_error;
}
if (!cexp_push_op(new_op))
return (-1);
break;
case '!':
if (!cexp_push_op(new_op))
return (-1);
break;
case '(':
if (!cexp_push_op(new_op))
return (-1);
break;
case ')':
/* Evaluate the operator stack until reach '(' */
if (!cexp_eval_op())
goto semantic_error;
/*
* If the top of operator stack is not an open paren,
* when we have an error. In this case, the operator
* stack will be empty due to the loop above.
*/
goto unbalpar_error;
break;
default:
/* Ensure there's room to push another operand */
CEXP_VAL_STACK_INIT) == 0)
return (0);
/*
* Operands cannot be numbers. However, we accept two
* special cases: '0' means false, and '1' is true.
* This is done to support the common C idiom of
* '#if 1' and '#if 0' to conditionalize code under
* development.
*/
(*str == '1');
break;
}
/* Look up the identifier */
return (-1);
cexp_ident_test(ident);
break;
}
}
/* Evaluate the operator stack until empty */
goto unbalpar_error;
if (!cexp_eval_op())
goto semantic_error;
}
/* There should be exactly one value left */
goto semantic_error;
/* Final value is the result */
/* Errors issued more than once are handled below, accessed via goto */
token_error: /* unexpected characters in input stream */
return (-1);
semantic_error: /* valid tokens, but in invalid arrangement */
return (-1);
unbalpar_error: /* Extra or missing parenthesis */
return (-1);
}
/*
* Process a mapfile control directive. These directives start with
* the dollar character, and are used to manage details of the mapfile
* itself, such as version and conditional input.
*
* entry:
* mf - Mapfile descriptor
*
* exit:
* Returns TRUE (1) for success, and FALSE (0) on error. In the
* error case, a descriptive error is issued.
*/
static Boolean
{
typedef enum { /* Directive types */
CDIR_T_UNKNOWN = 0, /* Unrecognized control directive */
CDIR_T_ADD, /* $add */
CDIR_T_CLEAR, /* $clear */
CDIR_T_ERROR, /* $error */
CDIR_T_VERSION, /* $mapfile_version */
CDIR_T_IF, /* $if */
CDIR_T_ELIF, /* $elif */
CDIR_T_ELSE, /* $else */
CDIR_T_ENDIF, /* $endif */
} cdir_t;
typedef enum { /* Types of arguments accepted by directives */
ARG_T_NONE, /* Directive takes no arguments */
ARG_T_EXPR, /* Directive takes a conditional expression */
ARG_T_ID, /* Conditional expression identifier */
ARG_T_STR, /* Non-empty string */
ARG_T_IGN /* Ignore the argument */
} cdir_arg_t;
typedef struct {
const char *md_name; /* Directive name */
} cdir_match_t;
/* Control Directives: The most likely items are listed first */
static cdir_match_t match_data[] = {
ARG_T_EXPR, CDIR_T_IF },
ARG_T_ID, CDIR_T_ADD },
ARG_T_ID, CDIR_T_CLEAR },
{ NULL, 0,
};
char *tail;
int expr_eval; /* Result of evaluating ARG_T_EXPR */
/* Is the immediate context passing input? */
/* Is the surrounding (parent) context passing input? */
/* Prefix must match, or we move on */
continue;
/*
* If there isn't whitespace, or a NULL terminator following
* the prefix, then even though our prefix matched, the actual
* token is longer, and we don't have a match.
*/
continue;
/* We have matched a valid control directive */
break;
}
/* Advance input to end of the current line */
/*
* Set up a temporary mapfile descriptor to reference the
* argument string. The benefit of this second block, is that
* we can advance the real one to the next line now, which allows
* us to return at any time knowing that the input has been moved
* to the proper spot. This simplifies the error cases.
*
* If we had a match, tail points at the start of the string.
* Otherwise, we want to point at the end of the line.
*/
else
/*
* Null terminate the arguments, and advance the main mapfile
* state block to the next line.
*/
}
/* Skip leading whitespace to arguments */
/* Strip off any comment present on the line */
if (*tail == '#') {
*tail = '\0';
break;
}
/*
* Process the arguments as necessary depending on their type.
* If this control directive is nested inside a surrounding context
* that is not currently passing text, then we skip the argument
* evaluation. This follows the behavior of the C preprocessor,
* which only examines enough to detect the operation within
* a disabled section, without issuing errors about the arguments.
*/
case ARG_T_NONE:
break;
/* Args are present, but not wanted */
return (FALSE);
case ARG_T_EXPR:
/* Ensure that arguments are present */
goto error_reqarg;
if (expr_eval == -1)
return (FALSE);
break;
case ARG_T_ID:
/* Ensure that arguments are present */
goto error_reqarg;
return (FALSE);
break;
case ARG_T_STR:
/* Ensure that arguments are present */
goto error_reqarg;
/* Remove trailing whitespace */
tail--;
*tail = '\0';
break;
}
}
/*
* Carry out the specified control directive:
*/
case CDIR_T_UNKNOWN: /* Unrecognized control directive */
if (!pass)
break;
return (FALSE);
case CDIR_T_ADD:
return (FALSE);
break;
case CDIR_T_CLEAR:
if (pass)
break;
case CDIR_T_ERROR:
if (!pass)
break;
return (FALSE);
case CDIR_T_VERSION:
/*
* A $mapfile_version control directive can only appear
* as the first directive in a mapfile, and is used to
* determine the syntax for the rest of the file. It's
* too late to be using it here.
*/
if (!pass)
break;
return (FALSE);
case CDIR_T_IF:
/* Push a new level on the conditional input stack */
return (FALSE);
level->cdl_else_lineno = 0;
/*
* If previous level is not passing, this level is disabled.
* Otherwise, the expression value determines what happens.
*/
if (pass) {
} else {
}
break;
case CDIR_T_ELIF:
/* $elif requires an open $if construct */
return (FALSE);
}
/* $elif cannot follow $else */
if (level->cdl_else_lineno > 0) {
return (FALSE);
}
/*
* Accept text from $elif if the level isn't already
* done and the expression evaluates to true.
*/
break;
case CDIR_T_ELSE:
/* $else requires an open $if construct */
return (FALSE);
}
/* There can only be one $else in the chain */
if (level->cdl_else_lineno > 0) {
return (FALSE);
}
/* Accept text from $else if the level isn't already done */
break;
case CDIR_T_ENDIF:
/* $endif requires an open $if construct */
return (FALSE);
}
break;
default:
return (FALSE);
}
/* Evaluating the control directive above can change pass status */
}
/*
* At this point, we have processed a control directive,
* updated our conditional state stack, and the input is
* positioned at the start of the line following the directive.
* If the current level is accepting input, then give control
* back to ld_map_gettoken() to resume its normal operation.
*/
if (pass)
return (TRUE);
/*
* The current level is not accepting input. Only another
* control directive can change this, so read and discard input
* until we encounter one of the following:
*
* EOF: Return and let ld_map_gettoken() report it
* Control Directive: Restart this function / evaluate new directive
*/
/* Skip leading whitespace */
/*
* Control directives start with a '$'. If we hit
* one, restart the function at this point
*/
goto restart;
/* Not a control directive, so advance input to next line */
}
}
return (TRUE);
/*
* Control directives that require an argument that is not present
* jump here to report the error and exit.
*/
return (FALSE);
}
#ifndef _ELF64
/*
* Convert a string to lowercase.
*/
void
ld_map_lowercase(char *str)
{
str++;
}
#endif
/*
* Wrappper on strtoul()/strtoull(), adapted to return an Xword.
*
* entry:
* str - Pointer to string to be converted.
* endptr - As documented for strtoul(3C). Either NULL, or
* address of pointer to receive the address of the first
* unused character in str (called "final" in strtoul(3C)).
* ret_value - Address of Xword variable to receive result.
*
* exit:
* On success, *ret_value receives the result, *endptr is updated if
* endptr is non-NULL, and STRTOXWORD_OK is returned.
* On failure, STRTOXWORD_TOBIG is returned if an otherwise valid
* value was too large, and STRTOXWORD_BAD is returned if the string
* is malformed.
*/
{
#if defined(_ELF64) /* _ELF64 */
#else /* _ELF32 */
#endif
char *endptr_local; /* Used if endptr is NULL */
endptr = &endptr_local;
errno = 0;
return (STRTOXWORD_TOOBIG);
else
return (STRTOXWORD_BAD);
}
/*
* If this is a 64-bit linker building an ELFCLASS32 object,
* the FUNC return type is a 64-bit value, while an Xword is
* 32-bit. It is possible for FUNC to be able to convert a value
* too large for our return type.
*/
return (STRTOXWORD_TOOBIG);
#endif
return (STRTOXWORD_OK);
}
/*
* Convert the unsigned integer value at the current mapfile input
* into binary form. All numeric values in mapfiles are treated as
* unsigned integers of the appropriate width for an address on the
* given target. Values can be decimal, hex, or octal.
*
* entry:
* str - String to process.
* value - Address of variable to receive resulting value.
* notail - If TRUE, an error is issued if non-whitespace
* characters other than '#' (comment) are found following
* the numeric value before the end of line.
*
* exit:
* On success:
* - *str is advanced to the next character following the value
* - *value receives the value
* - Returns TRUE (1).
* On failure, returns FALSE (0).
*/
static Boolean
{
char *endptr;
if (s2xw_ret != STRTOXWORD_OK) {
if (s2xw_ret == STRTOXWORD_TOOBIG)
else
return (FALSE);
}
/* Advance position to item following value, skipping whitespace */
/* If requested, ensure there's nothing left */
return (FALSE);
}
return (TRUE);
}
/*
* Convert a an unquoted identifier into a TK_STRING token, using the
* rules for syntax version in use. Used exclusively by ld_map_gettoken().
*
* entry:
* mf - Mapfile descriptor, positioned to the first character of
* the string.
* flags - Bitmask of options to control ld_map_gettoken()s behavior
* tkv- Address of pointer to variable to receive token value.
*
* exit:
* On success, mf is advanced past the token, tkv is updated with
* the string, and TK_STRING is returned. On error, TK_ERROR is returned.
*/
inline static Token
{
char *end;
return (TK_ERROR);
/*
* One advantage of reading the entire mapfile into memory is that
* we can access the strings within it without having to allocate
* more memory or make copies. In order to do that, we need to NULL
* terminate this identifier. That is going to overwrite the
* following character. The problem this presents is that the next
* character may well be the first character of a subsequent token.
* The solution to this is:
*
* 1) Disallow the case where the next character is able to
* start a string. This is not legal mapfile syntax anyway,
* so catching it here simplifies matters.
* 2) Copy the character into the special mf->mf_next_ch
* 3) The next call to ld_map_gettoken() checks mf->mf_next_ch,
* and if it is non-0, uses it instead of dereferencing the
* mf_next pointer.
*/
switch (tok) {
case TK_OP_BADCHR:
return (TK_ERROR);
case TK_OP_SIMQUOTE:
case TK_OP_CQUOTE:
case TK_OP_CDIR:
case TK_OP_NUM:
case TK_OP_ID:
return (TK_ERROR);
}
/* Null terminate, saving the replaced character */
if (flags & TK_F_STRLC)
return (TK_STRING);
}
/*
* Convert a quoted string into a TK_STRING token, using simple
* quoting rules:
* - Start and end quotes must be present and match
* - There are no special characters or escape sequences.
* This function is used exclusively by ld_map_gettoken().
*
* entry:
* mf - Mapfile descriptor, positioned to the opening quote character.
* flags - Bitmask of options to control ld_map_gettoken()s behavior
* tkv- Address of pointer to variable to receive token value.
*
* exit:
* On success, mf is advanced past the token, tkv is updated with
* the string, and TK_STRING is returned. On error, TK_ERROR is returned.
*/
inline static Token
{
char quote;
end++;
return (TK_ERROR);
}
/*
* end is pointing at the closing quote. We can turn that into NULL
* termination for the string without needing to restore it later.
*/
*end = '\0';
if (flags & TK_F_STRLC)
return (TK_STRING);
}
/*
* Convert a quoted string into a TK_STRING token, using C string literal
* quoting rules:
* - Start and end quotes must be present and match
* - Backslash is an escape, used to introduce special characters
* This function is used exclusively by ld_map_gettoken().
*
* entry:
* mf - Mapfile descriptor, positioned to the opening quote character.
* flags - Bitmask of options to control ld_map_gettoken()s behavior
* tkv- Address of pointer to variable to receive token value.
*
* exit:
* On success, mf is advanced past the token, tkv is updated with
* the string, and TK_STRING is returned. On error, TK_ERROR is returned.
*/
inline static Token
{
char quote;
int c;
/*
* This function goes through the quoted string and copies
* it on top of itself, replacing escape sequences with the
* characters they denote. There is always enough room for this,
* because escapes are multi-character sequences that are converted
* to single character results.
*/
c = *end++) {
if (c == '\\') {
c = conv_translate_c_esc(&end);
if (c == -1) {
return (TK_ERROR);
}
}
*cur++ = c;
}
if (c != quote) {
return (TK_ERROR);
}
/* end is pointing one character past the closing quote */
if (flags & TK_F_STRLC)
return (TK_STRING);
}
/*
* Get a token from the mapfile.
*
* entry:
* mf - Mapfile descriptor
* flags - Bitmask of options to control ld_map_gettoken()s behavior
* tkv- Address of pointer to variable to receive token value.
*
* exit:
* Returns one of the TK_* values, to report the result. If the resulting
* token has a value (TK_STRING / TK_INT), and tkv is non-NULL, tkv
* is filled in with the resulting value.
*/
{
int cdir_allow, ch;
/*
* Mapfile control directives all start with a '$' character. However,
* they are only valid when they are the first thing on a line. That
* happens on the first call to ld_map_gettoken() for a new a new
* mapfile, as tracked with lms.lms_cdir_valid, and immediately
* following each newline seen in the file.
*/
lms.lms_cdir_valid = 0;
/* Cycle through the characters looking for tokens. */
for (;;) {
/*
* Process the next character. This is normally *mf->mf_next,
* but if mf->mf_next_ch is non-0, then it contains the
* character, and *mf->mf_next contains a NULL termination
* from the TK_STRING token returned on the previous call.
*
* gettoken_ident() ensures that this is never done to
* a character that starts a string.
*/
if (mf->mf_next_ch == 0) {
} else {
}
/* Map the character to a dispatch action */
/*
* Items that require processing are identified as OP tokens.
* We process them, and return a result non-OP token.
*
* Non-OP tokens are single character tokens, and we return
* them immediately.
*/
switch (tok) {
case TK_OP_EOF:
/* If EOFOK is set, quietly report it as TK_EOF */
if ((flags & TK_F_EOFOK) != 0)
return (TK_EOF);
/* Treat it as a standard error */
return (TK_ERROR);
case TK_OP_ILLCHR:
return (TK_ERROR);
case TK_OP_BADCHR:
return (TK_ERROR);
case TK_OP_WS: /* White space */
break;
case TK_OP_NL: /* White space too, but bump line number. */
cdir_allow = 1;
break;
case TK_OP_SIMQUOTE:
if (flags & TK_F_KEYWORD)
goto tk_op_badkwquote;
case TK_OP_CQUOTE:
if (flags & TK_F_KEYWORD) {
return (TK_ERROR);
}
case TK_OP_CMT:
break;
case TK_OP_CDIR:
/*
* Control directives are only valid at the start
* of a line.
*/
if (!cdir_allow) {
return (TK_ERROR);
}
if (!cdir_process(mf))
return (TK_ERROR);
break;
case TK_OP_NUM: /* Decimal, hex(0x...), or octal (0...) value */
return (TK_ERROR);
return (TK_INT);
case TK_OP_ID: /* Unquoted identifier */
case TK_OP_CEQUAL: /* += or -= */
goto tk_op_badchr;
return (tok);
default: /* Non-OP token */
return (tok);
}
}
/*NOTREACHED*/
assert(0);
return (TK_ERROR);
}
/*
* Given a token and value returned by ld_map_gettoken(), return a string
* representation of it suitable for use in an error message.
*
* entry:
* tok - Token code. Must not be an OP-token
* tkv - Token value
*/
const char *
{
switch (tok) {
case TK_ERROR:
return (MSG_ORIG(MSG_STR_ERROR));
case TK_EOF:
return (MSG_ORIG(MSG_STR_EOF));
case TK_STRING:
case TK_COLON:
return (MSG_ORIG(MSG_QSTR_COLON));
case TK_SEMICOLON:
return (MSG_ORIG(MSG_QSTR_SEMICOLON));
case TK_EQUAL:
return (MSG_ORIG(MSG_QSTR_EQUAL));
case TK_PLUSEQ:
return (MSG_ORIG(MSG_QSTR_PLUSEQ));
case TK_MINUSEQ:
return (MSG_ORIG(MSG_QSTR_MINUSEQ));
case TK_ATSIGN:
return (MSG_ORIG(MSG_QSTR_ATSIGN));
case TK_DASH:
return (MSG_ORIG(MSG_QSTR_DASH));
case TK_LEFTBKT:
return (MSG_ORIG(MSG_QSTR_LEFTBKT));
case TK_RIGHTBKT:
return (MSG_ORIG(MSG_QSTR_RIGHTBKT));
case TK_PIPE:
return (MSG_ORIG(MSG_QSTR_PIPE));
case TK_INT:
case TK_STAR:
return (MSG_ORIG(MSG_QSTR_STAR));
case TK_BANG:
return (MSG_ORIG(MSG_QSTR_BANG));
default:
assert(0);
break;
}
/*NOTREACHED*/
return (MSG_INTL(MSG_MAP_INTERR));
}
/*
* Advance the input to the first non-empty line, and determine
* the mapfile version. The version is specified by the mapfile
* using a $mapfile_version directive. The original System V
* syntax lacks this directive, and we use that fact to identify
* such files. SysV mapfile are implicitly defined to have version 1.
*
* entry:
* ofl - Output file descriptor
* mf - Mapfile block
*
* exit:
* On success, updates mf->mf_version, and returns TRUE (1).
* On failure, returns FALSE (0).
*/
static Boolean
{
/*
* Cycle through the characters looking for tokens. Although the
* true version is not known yet, we use the v2 dispatch table.
* It contains control directives, which we need for this search,
* and the other TK_OP_ tokens we will recognize and act on are the
* same for both tables.
*
* It is important not to process any tokens that would lead to
* a non-OP token:
*
* - The version is required to interpret them
* - Our mapfile descriptor is not fully initialized,
* attempts to run that code will crash the program.
*/
while (cont) {
/* Map the character to a dispatch action */
switch (tok) {
case TK_OP_WS: /* White space */
break;
case TK_OP_NL: /* White space too, but bump line number. */
break;
case TK_OP_CMT:
break;
case TK_OP_CDIR:
/*
* Control directives are only valid at the start
* of a line. However, as we have not yet seen
* a token, we do not need to test for this, and
* can safely assume that we are at the start.
*/
break;
}
/*
* Is it a valid version? Note that we
* intentionally do not allow you to
* specify version 1 using the $mapfile_version
* syntax, because that's reserved to version
* 2 and up.
*/
const char *fmt;
break;
}
break;
}
/*
* Not a version directive. Reset the current position
* to the start of the current line and stop here.
* SysV syntax applies.
*/
break;
default:
/*
* If we see anything else, then stop at this point.
* The file has System V syntax (version 1), and the
* next token should be interpreted as such.
*/
break;
}
}
return (status);
}
/*
* Parse the mapfile.
*/
{
int mapfile_fd; /* descriptor for mapfile */
int err;
/*
* Determine if we're dealing with a file or a directory.
*/
return (FALSE);
}
/*
* Open the directory and interpret each visible file as a
* mapfile.
*/
return (TRUE);
/*
* Ignore any hidden filenames. Construct the full
* pathname to the new mapfile.
*/
continue;
return (FALSE);
}
return (TRUE);
return (FALSE);
}
/* Open file */
return (FALSE);
}
/*
* Allocate enough memory to hold the state block, mapfile name,
* and mapfile text. Text has alignment 1, so it can follow the
* state block without padding.
*/
return (FALSE);
(void) close(mapfile_fd);
return (FALSE);
}
(void) close(mapfile_fd);
/*
* Read just enough from the mapfile to determine the version,
* and then dispatch to the appropriate code for further processing
*/
if (!mapfile_version(mf))
return (FALSE);
/*
* Start and continuation masks for unquoted identifier at this
* mapfile version level.
*/
switch (mf->mf_version) {
case MFV_SYSV:
/* Guidance: Use newer mapfile syntax */
if (!ld_map_parse_v1(mf))
return (FALSE);
break;
case MFV_SOLARIS:
/*
* If the conditional expression identifier tree has not been
* initialized, set it up. This is only done on the first
* mapfile, because the identifier control directives accumulate
* across all the mapfiles.
*/
return (FALSE);
/*
* Tell ld_map_gettoken() we will accept a '$' as starting a
* control directive on the first call. Normally, they are
* only allowed after a newline.
*/
if (!ld_map_parse_v2(mf))
return (FALSE);
/* Did we leave any open $if control directives? */
}
return (FALSE);
}
break;
}
return (TRUE);
}
/*
* Sort the segment list. This is necessary if a mapfile has set explicit
* virtual addresses for segments, or defined a SEGMENT_ORDER directive.
*
* Only PT_LOAD segments can be assigned a virtual address. These segments can
* be one of two types:
*
* - Standard segments for text, data or bss. These segments will have been
* inserted before the default text (first PT_LOAD) segment.
*
* - Empty (reservation) segments. These segment will have been inserted at
* the end of any default PT_LOAD segments.
*
* Any standard segments that are assigned a virtual address will be sorted,
* and as their definitions precede any default PT_LOAD segments, these segments
* will be assigned sections before any defaults.
*
* Any reservation segments are also sorted amoung themselves, as these segments
* must still follow the standard default segments.
*/
static Boolean
{
/*
* We know the number of elements in the sorted list will be
* the same as the original, so use this as the initial allocation
* size for the replacement aplist.
*/
/* Add the items below SGID_TEXT to the list */
break;
return (FALSE);
}
/*
* If there are any SEGMENT_ORDER items, add them, and set their
* FLG_SG_ORDERED flag to identify them in debug output, and to
* prevent them from being added again below.
*/
return (FALSE);
}
/*
* Add the loadable segments to another list in sorted order.
*/
/* Only interested in PT_LOAD items not in SEGMENT_ORDER list */
continue;
/*
* If the loadable segment does not contain a vaddr, simply
* append it to the new list.
*/
NULL)
return (FALSE);
} else {
int inserted = 0;
/*
* Traverse the segment list we are creating, looking
* for a segment that defines a vaddr.
*/
/*
* Any real segments that contain vaddr's need
* to be sorted. Any reservation segments also
* need to be sorted. However, any reservation
* segments should be placed after any real
* segments.
*/
(FLG_SG_P_VADDR | FLG_SG_EMPTY)) == 0) &&
continue;
return (FALSE);
}
continue;
}
/*
* Insert this segment before the segment on
* the load_segs list.
*/
return (FALSE);
inserted = 1;
break;
}
/*
* If the segment being inspected has not been inserted
* in the segment list, simply append it to the list.
*/
return (FALSE);
}
}
/*
* Add the sorted loadable segments to our initial segment list.
*/
return (FALSE);
}
/*
* Add all other segments to our list.
*/
continue;
return (FALSE);
}
/*
* Free the original list, and the pt_load list, and use
* the new list as the segment list.
*/
if (DBG_ENABLED) {
}
}
return (TRUE);
}
/*
* After all mapfiles have been processed, this routine is used to
* finish any remaining mapfile related work.
*
* exit:
* Returns TRUE on success, and FALSE on failure.
*/
{
/*
* Per-segment processing:
* - Identify segments with explicit virtual address
* - Details of input and output section order
*/
/*
* We are looking for segments. Program headers that represent
* segments are required to have a non-NULL name pointer,
* while that those that do not are required to have a
* NULL name pointer.
*/
continue;
/* Remember the first non-disabled segment */
/*
* If a segment has an explicit virtual address, we will
* need to sort the segments.
*/
/*
* The FLG_OF_OS_ORDER flag enables the code that does
* output section ordering. Set if the segment has
* a non-empty output section order list.
*/
/*
* The version 1 and version 2 syntaxes for input section
* ordering are different and incompatible enough that we
* only allow the use of one or the other for a given segment:
*
* v1) The version 1 syntax has the user set the ?O flag on
* the segment. If this is done, all input sections placed
* via an entrance criteria that has a section name are to
* be sorted, using the order of the entrance criteria
* as the sort key.
*
* v2) The version 2 syntax has the user specify a name for
* the entry criteria, and then provide a list of entry
* criteria names via the IS_ORDER segment attribute.
* Sections placed via the criteria listed in IS_ORDER
* are sorted, and the others are not.
*
* Regardless of the syntax version used, the section sorting
* code expects the following:
*
* - Segments requiring input section sorting have the
* FLG_SG_IS_ORDER flag set
*
* - Entrance criteria referencing the segment that
* participate in input section sorting have a non-zero
* sort key in their ec_ordndx field.
*
* At this point, the following are true:
*
* - All entrance criteria have ec_ordndx set to 0.
* - Segments that require the version 1 behavior have
* the FLG_SG_IS_ORDER flag set, and the segments
* sg_is_order list is empty.
* - Segments that require the version 2 behavior do not
* have FLG_SG_IS_ORDER set, and the sg_is_order list is
* non-empty. This list contains the names of the entrance
* criteria that will participate in input section sorting,
* and their relative order in the list provides the
* sort key to use.
*
* We must detect these two cases, set the FLG_SG_IS_ORDER
* flag as necessary, and fill in all entrance criteria
* sort keys. If any input section sorting is to be done,
* we also set the FLG_OF_IS_ORDER flag on the output descriptor
* to enable the code that does that work.
*/
/* Version 1: ?O flag? */
/*
* Give each user defined entrance criteria for this
* segment that specifies a section name a
* monotonically increasing sort key.
*/
continue;
}
/* Version 2: SEGMENT IS_ORDER list? */
/*
* Give each entrance criteria in the sg_is_order
* list a monotonically increasing sort key.
*/
}
}
}
/* Sort the segment descriptors if necessary */
!sort_seg_list(ofl))
return (FALSE);
/*
* If the output file is a static file without an interpreter, and
* if any virtual address is specified, then set the NOHDR flag for
* backward compatibility.
*/
/*
* NOHDR has no effect on a relocatable file.
* Make sure this flag isn't set.
*/
/*
* DF_1_NOHDR might have been set globally by the HDR_NOALLOC
* directive. If not, then we want to check the per-segment
* flag for the first loadable segment and propagate it
* if set.
*/
/*
* If we sorted the segments, the first segment
* may have changed.
*/
continue;
0) {
break;
}
}
}
/*
* If the per-segment NOHDR flag is set on our first
* segment, then make it take effect.
*/
}
/*
* For executable and shared objects, the first segment must
* be loadable unless NOHDR was specified, because the ELF
* header must simultaneously lie at offset 0 of the file and
* be included in the first loadable segment. This isn't
* possible if some other segment type starts the file
*/
return (FALSE);
}
}
/*
* Mapfiles may have been used to create symbol definitions
* with backing storage. Although the backing storage is
* associated with an input section, the association of the
* section to an output section (and segment) is initially
* deferred. Now that all mapfile processing is complete, any
* entrance criteria requirements have been processed, and
* these backing storage sections can be associated with the
* appropriate output section (and segment).
*/
return (FALSE);
}
return (FALSE);
}
return (TRUE);
}