/*
* 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.
*/
/*
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
/*
* Map file parsing (Shared Support Code).
*/
#include <stdio.h>
#include <errno.h>
#include "msg.h"
#include "_libld.h"
#include "_map.h"
/*
* Given a NULL terminated array of structures of arbitrary type, where
* each struct contains (among other fields) a character pointer field
* giving that struct a unique name, return the address of the struct
* that matches the given name.
*
* entry:
* name - "Keyword" name to be found.
* array - Base address of array
* name_offset - Offset of the name field within the struct
* type used by this array, as generated via
* SGSOFFSETOF().
* elt_size - sizeof the basic array element type
*
* exit:
* Using a case insensitive comparison, name is compared to the
* name of each element of the array. The address of the first
* match found is returned. If the desired name is not found,
* NULL is returned.
*
* note:
* This routine is completely type-unsafe. The upside is that this
* single routine is able to search arrays of arbitrary type, leaving
* the caller free to structure their array in any way that is convenient
* to solve the problem at hand.
*/
#ifndef _ELF64
void *
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
const char *arr_name = *((const char **)
(name_offset + (const char *) array));
return (NULL);
return (array);
}
/*NOTREACHED*/
assert(0);
return (NULL);
}
#endif
/*
* Given the same NULL terminated array accepted by ld_map_kwfind(), format
* the strings into a comma separated list of names.
*
* entry:
* array - Base address of array
* name_offset - Offset of the name field within the struct
* type used by this array, as generated via
* SGSOFFSETOF().
* elt_size - sizeof the basic array element type
* buf - Buffer to receive output
* bufsize - sizeof(buf)
*
* exit:
* As many of the names as will fit are formatted into buf. If all the
* names do not fit, the remainder are quietly clipped. The caller must
* ensure that there is sufficient room. buf is returned, for convenience
* in using this function as an argument for printing.
*/
#ifndef _ELF64
char *
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
const char *arr_name = *((const char **)
(name_offset + (const char *) array));
break;
if (cnt > 0) {
if (bufsize < 3)
break;
*str++ = ',';
*str++ = ' ';
bufsize -= 2;
}
break;
}
return (buf);
}
#endif
/*
* Create a pseudo input file descriptor to represent the specified Mapfile.
* An input descriptor is required any time a symbol is generated.
*
* entry:
* mf - Mapfile descriptor.
*
* exit:
* If an input descriptor was already created for this mapfile
* by a previous call, it is returned. Otherwise, a new descriptor
* is created, entered into the mapfile descriptor, and returned.
*
* Success is indicated by a non-NULL return value, failure by NULL.
*/
Ifl_desc *
{
/*
* If we've already created a pseudo input descriptor for this
* mapfile, reuse it.
*/
return (NULL);
return (NULL);
return (NULL);
}
/*
* Given a capability tag type, set the override bit in the output descriptor.
* This prevents the use of capability values of that type from the input
* objects.
*/
void
{
/*
* Map capability tag to the corresponding output descriptor
* override flag.
*/
0, /* CA_SUNW_NULL */
FLG_OF1_OVHWCAP1, /* CA_SUNW_HW_1 */
FLG_OF1_OVSFCAP1, /* CA_SUNW_SF_1 */
FLG_OF1_OVHWCAP2, /* CA_SUNW_HW_2 */
FLG_OF1_OVPLATCAP, /* CA_SUNW_PLAT */
FLG_OF1_OVMACHCAP, /* CA_SUNW_MACH */
FLG_OF1_OVIDCAP /* CA_SUNW_ID */
};
#error "CA_SUNW_NUM has grown"
#endif
}
/*
* Sanity check the given capability bitmask.
*/
{
switch (type) {
case CA_SUNW_SF_1:
/*
* Unlike hardware capabilities, we do not allow setting
* software capability bits that do not have known definitions.
* Software capability tokens have to be validated as a unit
* as the bits can affect each others meaning (see sf1_cap()
* in files.c).
*/
}
}
#if !defined(_ELF64)
/*
* The SF1_SUNW_ADDR32 software capability is only meaningful
* when building a 64-bit object. Warn the user, and remove the
* setting, if we're building a 32-bit object.
*/
}
#endif
}
return (TRUE);
}
/*
* Return the shared object control definition structure (ofl_socntl)
* for the specified object, creating one if necessary.
*
* entry:
* mf - Mapfile descriptor
* obj_name - Name of object
*
* exit:
* Returns the pointer to the definition structure, or NULL on error.
*/
Sdf_desc *
{
/*
* If a shared object definition for this file already exists use it,
* otherwise allocate a new descriptor.
*/
return (NULL);
}
return (sdf);
}
const char *version)
{
if (require) {
/*
* Add a VERNEED entry for the specified version
* from this object:
*
* MapfileVersion Syntax
* ----------------------------------------
* 1 obj - $ADDVERS=version;
* 2 DEPENDENCY obj { REQUIRE=version };
*/
AL_CNT_SDF_VERSIONS) == NULL)
return (FALSE);
} else { /* Allow */
/*
* Allow linking to symbols found in this version, or
* from the versions it inherits from.
*
* MapfileVersion Syntax
* ----------------------------------------
* 1 obj - version;
* 2 DEPENDENCY obj { ALLOW=version };
*/
AL_CNT_SDF_VERSIONS) == NULL)
return (FALSE);
}
return (TRUE);
}
/*
* Given a segment descriptor, return its index.
*
* entry:
* mf - Mapfile descriptor
* sgp - Segment for which index is desired
*
* exit:
* Index of segment is returned.
*/
{
break;
return (idx);
}
/*
* Add a section name to the output section sort list for the given
* segment.
*
* entry:
* mf - Mapfile descriptor
* sgp - Segment in question
* sec_name - Name of section to be added.
*
* exit:
* Returns TRUE for success, FALSE for failure.
*/
{
/*
* Make sure it's not already on the list
*/
return (FALSE);
}
return (FALSE);
/*
* Output section ordering is a relatively expensive operation,
* and one that is generally not used. In order to avoid needless
* work, the FLG_OF_OS_ORDER must be set when it will be needed.
* The section we just added needs this flag to be set. However,
* it is possible that a subsequent mapfile directive may come
* along and clear the order list, making it unnecessary.
*
* Instead of setting it here, we do a final pass over the segments
* in ld_map_finalize() and set it there if a segment with sorting
* requirements is seen.
*/
return (TRUE);
}
/*
* Add a size symbol to a segment
*
* entry:
* mf - Mapfile descriptor
* sgp - Segment descriptor
* eq_tol - Type of assignment: TK_EQUAL, or TK_PLUSEQ
* symname - Name of symbol. Must be in stable static storage
* that can be retained.
*
* exit:
* On success, the symbol has been added and TRUE is returned.
* Otherwise an error is reported and FALSE is returned.
*/
const char *symname)
{
/*
* We don't allow resetting the list of size symbols, so if the
* operator is TK_EQUAL and the list is not empty, issue an error.
*
* If we want to lift this restriction, we would have to save the
* size symbols and enter them from ld_map_post_process(). Doing that
* well would require a significant overhead in saved error reporting
* state, and interactions with the same symbols created by symbol
* directives. As size symbols are of little practical use, and are
* maintained primarily for backward compatibility with SysV, we have
* decided not to do that, but to create the symbols as the mapfiles
* are processed, and to disallow later attempts to remove them.
*/
return (FALSE);
}
/*
* Make sure we have a pseudo file descriptor to associate to the
* symbol.
*/
return (FALSE);
/*
* Make sure the symbol doesn't already exist. It is possible that the
* symbol has been scoped or versioned, in which case it does exist
* but we can freely update it here.
*/
return (FALSE);
/* LINTED */
return (FALSE);
} else {
} else {
return (FALSE);
}
}
/*
* Assign the symbol to the segment.
*/
return (FALSE);
return (TRUE);
}
/*
* Allocate a zeroed segment descriptor.
*
* exit:
* Returns pointer to the descriptor on success, NULL on failure.
* The contents of the returned descriptor have been zeroed.
* The returned descriptor is not added to the segment list
* (ofl_segs). That is done using ld_map_seg_insert().
*/
Sg_desc *
{
return (NULL);
return (sgp);
}
/*
* Return the PT_SUNWSTACK segment descriptor from the ofl_segs list.
* This segment is part of the default set and cannot be removed, so
* this routine will always succeed.
*
* exit:
* The descriptor is located, a DBG_STATE_MOD_BEFORE debug
* message issued, the FLG_SG_DISABLED flag is cleared, and the
* descriptor pointer returned.
*/
Sg_desc *
{
/*
* The stack is established by exec(), using the executable's program
* headers, before any sharable objects are loaded. If there is a
* PT_SUNWSTACK program header, exec() will act on it. As such, stack
* program headers are normally only applicable to executables.
*
* However, ELF allows a sharable object with an interpreter to
* be executed directly, and in this extremely rare case, the
* PT_SUNWSTACK program header would have meaning. Rather than
* second guess user intent, we simply create it on demand for any
* dynamic object, trusting that the user has a good reason for it.
*/
return (sgp);
}
/*NOTREACHED*/
return (NULL);
}
/*
* Finish the initialization of a new segment descriptor allocated by
* ld_map_seg_alloc(), and enter it into the segment list.
*
* entry:
* mf - Mapfile descriptor
* seg_type - One of DBG_SEG_NEW or DBG_SEG_NEW_IMPLICIT
* ins_head - If TRUE, the new segment goes at the front of
* others of its type. If FALSE, it goes at the end.
* sgp - Segment descriptor to enter.
* where - Insertion point, initialized by a previous (failed) call to
* ld_seg_lookup(). Ignored if the segment has a NULL sg_name.
*
* exit:
* On success, returns SEG_INS_OK. A non-fatal error is indicated with
* a return value of SEG_INS_SKIP, in which case the descriptor is
* not entered, but the user is expected to discard it and continue
* running. On failure, returns SEG_INS_FAIL.
*
* note:
* This routine will modify the contents of the descriptor referenced
* by sgp_tmpl before allocating the new descriptor. The caller must
* not expect it to be unmodified.
*/
{
int ins_head;
/*
* If specific fields have not been supplied via
* map_equal(), make sure defaults are supplied.
*/
/*
* Default to a loadable segment.
*/
}
/*
*/
}
/*
* Default to segment alignment
*/
}
}
/*
* Determine where the new item should be inserted in
* the segment descriptor list.
*/
case PT_LOAD:
else
break;
case PT_NULL:
else
break;
case PT_NOTE:
break;
default:
return (SEG_INS_FAIL);
}
/*
* Add the descriptor to the segment list. In the v1 syntax,
* new sections are added at the head of their type, while in
* the newer syntax, they go at the end of their type.
*/
sg_ndx = 0;
if (ins_head) { /* Insert before the others of its type */
sg_ndx++;
continue;
}
} else { /* Insert after the others of its type */
sg_ndx++;
continue;
}
}
break;
}
return (SEG_INS_FAIL);
return (SEG_INS_OK);
}
/*
* Add an entrance criteria record for the specified segment
*
* entry:
* mf - Mapfile descriptor
* sgp - Segment for which a new entrance criteria record is needed
* name - NULL, or name by which the entrance criteria can be referenced.
*
* exit:
* On success, a pointer to the new entrace criteria record is
* returned, the contents of which have been zeroed. On failure,
* NULL is returned.
*/
Ent_desc *
{
return (NULL);
}
/* Allocate and initialize the entrace criteria descriptor */
return (NULL);
/*
* Insert into the APlist. The mf_ec_insndx field for each mapfile
* starts at 0, and is incremented with each insertion. This means
* that the entrance criteria for each mapfile go to the head of
* the list, but that within a single mapfile, they are inserted in
* the order they are seen.
*/
return (NULL);
mf->mf_ec_insndx++;
/*
* If the entrance criteria is named insert it into the AVL tree
* as well. This provides O(logN) lookups by name.
*/
return (enp);
}
{
/*
* The v1 sysv syntax can let an empty string get in, consisting of
* just a '*' where the '*' is interpreted as 'basename'.
*/
if (str[0] == '\0') {
return (FALSE);
}
/* Basename or objname string must not contain a path separator (/) */
return (FALSE);
}
/* Does it have an archive member suffix? */
AL_CNT_EC_FILES) == NULL)
return (FALSE);
/*
* Note that an entrance criteria requiring file name matching exists
* in the system. This is used by ld_place_path_info_init() to
* skip Place_pathinfo initialization in cases where there are
* no entrance criteria that will use the results.
*/
return (TRUE);
}
/*
* Prepare an ld_map_ver_t structure for a new mapfile defined version.
*
* exit:
* Returns TRUE for success, FALSE for failure.
*/
{
/*
* If we're generating segments within the image then any symbol
* reductions will be processed (ie. applied to relocations and symbol
* table entries). Otherwise (when creating a relocatable object) any
* versioning information is simply recorded for use in a later
* (segment generating) link-edit.
*/
/*
* If no version descriptors have yet been set up, initialize a base
* version to represent the output file itself. This `base' version
* catches any internally generated symbols (_end, _etext, etc.) and
* serves to initialize the output version descriptor count.
*/
if (ofl->ofl_vercnt == 0) {
return (FALSE);
}
/*
* If this definition has an associated version name then generate a
* new version descriptor and an associated version symbol index table.
*/
if (name) {
/*
* Traverse the present version descriptor list to see if there
* is already one of the same name, otherwise create a new one.
*/
/* LINTED */
return (FALSE);
/*
* Initialize any new version with an index, the file from
* which it was first referenced, and a WEAK flag (indicates
* that there are no symbols assigned to it yet).
*/
/* LINTED */
}
} else {
/*
* If a version definition hasn't been specified assign any
* symbols to the base version.
*/
}
return (TRUE);
}
/*
* Change the current scope for the given version.
*
* entry:
* mf - Mapfile descriptor
* scope_name - Name for new scope
* mv - Information related to version being defined
*
* exit:
* On success, mv is updated to change the current scope.
* On failure, mv->errcnt is incremented, and mv is otherwise unaltered.
*/
void
{
typedef struct {
} scope_t;
/*
* Valid symbol scope keywords
*
* All symbols added by a mapfile are actually global entries, and
* are assigned the scope that is presently in effect.
*
* (or any other mapfiles), then the mode -Bsymbolic is established.
*/
/* List must be null terminated */
{ 0 }
};
/*
* Size of buffer needed to format the names in scope_list[]. Must
* be kept in sync with scope_list.
*/
return;
}
}
/*
* Process the special auto-reduction directive ('*'). It can be specified
* symbols processed that are not explicitly defined to be global are to be
*
* An auto-reduction directive also implies that a version definition must
* be created, as the user has effectively defined an interface.
*/
void
{
case FLG_SCOPE_HIDD:
break;
case FLG_SCOPE_ELIM:
break;
default:
/*
* Auto reduction has been applied to a scope that doesn't
* support it. This should be a fatal error, but we limit
* it to a warning for version 1 mapfiles. For years, we
* quietly ignored this case, so there may be mapfiles in
* production use that we do not wish to break.
*/
} else {
}
}
}
/*
* Add a standard or auxiliary filter to the given symbol
*
* entry:
* mf - Mapfile descriptor
* mv - Information related to version being defined
* ms - Information related to symbol being defined
* dft_flag - One of FLG_SY_STDFLTR or FLG_SY_AUXFLTR,
* specifying the type of filter.
* filtee - String giving filtee to be added
*
* exit:
* On success, the filtee is added. On failure, mv->errcnt is
*/
void
{
/*
* A given symbol can only be tied to a single filter, be it
* a standard filter, or auxiliary.
*/
return;
}
/* Symbol filtering is only for sharable objects */
return;
}
}
/*
* Enter a mapfile defined symbol into the given version
*
* entry:
* mf - Mapfile descriptor
* ms - Information related to symbol being added to version
*
* exit:
* On success, returns TRUE. On failure that requires an immediate
* halt, returns FALSE.
*
* On failure that requires eventual halt, but for which it would
* be OK to continue parsing in hopes of flushing out additional
* problems, increments mv->mv_errcnt, and returns TRUE.
*/
{
const char *conflict;
/*
* Add the new symbol. It should be noted that all
* symbols added by the mapfile start out with global
* scope, thus they will fall through the normal symbol
* resolution process. Symbols defined as locals will
* be reduced in scope after all input file processing.
*/
/* LINTED */
/*
* Make sure that any parent or external declarations fall back to
* references.
*/
/*
* Turn it into a reference by setting the section index
* to UNDEF.
*/
/*
* It is wrong to specify size or value for an external symbol.
*/
return (TRUE);
}
}
return (FALSE);
return (FALSE);
/*
* Identify any references. FLG_SY_MAPREF is
* turned off once a relocatable object with
* the same symbol is found, thus the existence
* of FLG_SY_MAPREF at symbol validation is
* used to flag undefined/misspelled entries.
*/
} else {
/*
* If this symbol already exists, make sure this
* definition doesn't conflict with the former.
* Provided it doesn't, multiple definitions
* from different mapfiles can augment each
* other.
*/
} else {
}
} else {
}
} else {
}
} else {
}
}
if (conflict) {
return (TRUE);
}
/*
* If this mapfile entry supplies a definition,
* indicate that the symbol is now used.
*/
}
/*
* A symbol declaration that defines a size but no
* value is processed as a request to create an
* associated backing section. The intent behind this
* functionality is to provide OBJT definitions within
* filters that are not ABS. ABS symbols don't allow
* copy-relocations to be established to filter OBJT
* definitions.
*/
/* Create backing section if not there */
return (FALSE);
} else {
return (FALSE);
}
}
/*
* Now that backing storage has been created,
* associate the symbol descriptor. Remove the
* symbols special section tag so that it will
* be assigned the correct section index as part
* of update symbol processing.
*/
}
/*
* Indicate the new symbols scope. Although the
* symbols st_other field will eventually be updated as
* part of writing out the final symbol, update the
* st_other field here to trigger better diagnostics
* during symbol validation (for example, undefined
* references that are defined symbolic in a mapfile).
*/
/*
* This symbol needs to be reduced to local.
*/
} else {
}
/*
* This symbol needs to be eliminated. Note,
* the symbol is also tagged as local to trigger
* any necessary relocation processing prior
* to the symbol being eliminated.
*/
} else {
/*
* This symbol is explicitly defined to remain
* global.
*/
/*
* Qualify any global scope.
*/
} else
/*
* Record the present version index for later
* potential versioning.
*/
}
/*
* Carry out some validity checks to ensure incompatible
* symbol characteristics have not been defined.
* These checks are carried out after symbols are added
* or resolved, to catch single instance, and
* multi-instance definition inconsistencies.
*/
(FLG_SY_SINGLE | FLG_SY_EXPORT)) &&
}
if (conflict) {
/*
* Select the conflict message from either a
* single instance or multi-instance definition.
*/
} else {
}
return (TRUE);
}
/*
* Indicate that this symbol has been explicitly
* contributed from a mapfile.
*/
/*
* If we've encountered a symbol definition simulate
* that an input file has been processed - this allows
* things like filters to be created purely from a
* mapfile.
*/
ofl->ofl_objscnt++;
/*
* If this symbol has an associated filtee, record the
* filtee string and associate the string index with the
* symbol. This is used later to associate the syminfo
* information with the necessary .dynamic entry.
*/
/*
* Make sure we don't duplicate any filtee
* strings, and create a new descriptor if
* necessary.
*/
continue;
break;
}
/*
* The following append puts the new
* item at the offset contained in
* idx, because we know idx contains
* the index of the next available slot.
*/
return (FALSE);
}
/*
* Create a new filter descriptor for this
* symbol.
*/
AL_CNT_OFL_SYMFLTRS) == NULL)
return (FALSE);
}
return (TRUE);
}
/*
* In both the version 1 and version 2 syntaxes, a version definition
* can have 0 or more inherited versions following the closing '}',
* terminated by a ';'.
*
* Add the inherited names, and return when the terminator is seen.
*/
{
const char *name;
/*
* Read version names until we encounter the ';' terminator.
*/
while (!done) {
case TK_ERROR:
return (FALSE);
case TK_STRING:
/* The unnamed global scope can't inherit */
name);
return (FALSE);
}
/*
* Generate a new version descriptor if it doesn't
* already exist.
*/
/* LINTED */
return (FALSE);
/*
* Add the new version descriptor to the parent version
* descriptors reference list. Indicate the version
* descriptors first reference (used for error diags
* if undefined version dependencies remain).
*/
NULL)
AL_CNT_VERDESCS) == NULL)
return (FALSE);
break;
case TK_SEMICOLON:
break;
default:
return (FALSE);
}
}
return (TRUE);
}