relocate.c revision c1d50c4dc3f2dff6b6055bd9bcc9780f66a7bad1
/*
* 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
*
*/
/*
* set-up for relocations
*/
#define ELF_TARGET_AMD64
#define ELF_TARGET_SPARC
#include <string.h>
#include <stdio.h>
#include <alloca.h>
#include <debug.h>
#include "msg.h"
#include "_libld.h"
/*
* Set up the relocation table flag test macros so that they use the
* relocation table for the current target machine.
*/
#define IS_GOT_RELATIVE(X) \
#define IS_GOT_REQUIRED(X) \
#define IS_ADD_RELATIVE(X) \
#define IS_SEG_RELATIVE(X) \
#define IS_SEC_RELATIVE(X) \
/*
* Structure to hold copy relocation items.
*/
typedef struct copy_rel {
} Copy_rel;
/*
* For each copy relocation symbol, determine if the symbol is:
* 1) to be *disp* relocated at runtime
* 2) a reference symbol for *disp* relocation
* 3) possibly *disp* relocated at ld time.
*
* The first and the second are serious errors.
*/
static void
{
/*
* This symbol may not be *disp* relocated at run time, but could
* already have been *disp* relocated when the shared object was
* created. Warn the user.
*/
return;
/*
* Traverse the input relocation sections.
*/
/*
* Decide entry size
*/
else
}
/*
* Traverse the relocation entries.
*/
const char *str;
continue;
/*
* Determine if symbol is referenced from a relocation.
*/
}
}
/*
* Determine whether the relocation entry is relocating
* this symbol.
*/
continue;
/*
* This symbol is truely *disp* relocated, so should
* really be fixed by user.
*/
}
}
}
}
/*
* The number of symbols provided by some objects can be very large. Use a
* binary search to match the associated value to a symbol table entry.
*/
static int
{
return (-1);
return (1);
return (0);
}
/*
* Given a sorted list of symbols, look for a symbol in which the relocation
* the symbol list is maintained in sorted order, we can bail once the
* relocation offset becomes less than the symbol values. The symbol is
* returned for use in error diagnostics.
*/
static Sym_desc *
{
/*
* Sorted symbol values have been uniquified by adding their associated
* section offset. Uniquify the relocation offset by adding its
* associated section offset, and search for the symbol.
*/
else
tsdp = 0;
if (inspect)
return (tsdp);
/*
* Determine the relocation reference symbol and its type.
*/
/*
* If there is no target symbol to match the relocation offset, then the
* offset is effectively local data. If the relocation symbol is global
* data we have a potential for this displacement relocation to be
* invalidated should the global symbol be copied.
*/
if (tsdp == 0) {
return (tsdp);
} else {
/*
* If both symbols are local, no copy relocations can occur to
* either symbol. Note, this test is very similar to the test
* used in ld_sym_adjust_vis().
*/
return (tsdp);
/*
* Determine the relocation target symbols type.
*/
/*
* If the reference symbol is local, and the target isn't a
* data element, then no copy relocations can occur to either
* symbol. Note, this catches pc-relative relocations against
* the _GLOBAL_OFFSET_TABLE_, which is effectively treated as
* a local symbol.
*/
(ttype != STT_SECTION))
return (tsdp);
/*
* Finally, one of the symbols must reference a data element.
*/
return (tsdp);
}
/*
* We have two global symbols, at least one of which is a data item.
* The last case where a displacement relocation can be ignored, is
* if the reference symbol is included in the target symbol.
*/
return (tsdp);
/*
* We have a displacement relocation that could be compromised by a
* copy relocation of one of the associated data items.
*/
return (tsdp);
}
void
{
const char *str;
else
}
/*
* qsort(3C) comparison routine used for the disp_sortsyms().
*/
static int
{
return (1);
return (-1);
return (0);
}
/*
* Determine whether a displacement relocation is between a local and global
* symbol pair. One symbol is used to perform the relocation, and the other
* is the destination offset of the relocation.
*/
static uintptr_t
{
/*
* If the input files symbols haven't been sorted yet, do so.
*/
if (ifl->ifl_sortsyms == 0) {
sizeof (Ssv_desc))) == 0)
return (S_ERROR);
/*
* As symbol resolution has already occurred, various
* symbols from this object may have been satisfied
* from other objects. Only select symbols from this
* object. For the displacement test, we only really
* need to observe data definitions, however, later as
* part of providing warning disgnostics, relating the
* relocation offset to a symbol is desirable. Thus,
* collect all symbols that define a memory area.
*/
continue;
/*
* As a further optimization for later checking, mark
* this section if this a global data definition.
*/
/*
* Capture the symbol. Within relocatable objects, a
* symbols value is its offset within its associated
* section. Add the section offset to this value to
* uniquify the symbol.
*/
nndx++;
}
/*
* Sort the list based on the symbols value (address).
*/
&disp_qsort);
}
/*
* If the reference symbol is local, and the section being relocated
* contains no global definitions, neither can be the target of a copy
* relocation.
*/
return (1);
/*
* Otherwise determine whether this relocation symbol and its offset
* could be candidates for a copy relocation.
*/
if (ifl->ifl_sortcnt)
return (1);
}
/*
* Return a Rel_cachebuf with an available Rel_desc entry from the
* specified cache, allocating a cache buffer if necessary.
*
* entry:
* ofl - Output file descriptor
* rcp - Relocation cache to allocate the descriptor from.
* One of &ofl->ofl_actrels or &ofl->ofl_outrels.
*
* exit:
* Returns the allocated descriptor, or NULL if the allocation fails.
*/
static Rel_cachebuf *
{
/*
* If there is space available in the present cache bucket, return the
* next free entry.
*/
return (rcbp);
/*
* Allocate a new bucket. As we cannot know the number of relocations
* we'll have in the active and output cache until after the link is
* complete, the size of the bucket is a heuristic.
*
* In general, if the output object is an executable, or a sharable
* object, then the size of the active relocation list will be nearly
* the same as the number of input relocations, and the output
* relocation list will be very short. If the output object is a
* relocatable object, then the reverse is true. Therefore, the initial
* allocation for the appropriate list is sized to fit all the input
* allocations in a single shot.
*
* All other allocations are done in units of REL_CACHEBUF_ALLOC,
* which is chosen to be large enough to cover most common cases,
* but small enough that not using it fully is inconsequential.
*
* In an ideal scenario, this results in one allocation on each list.
*/
}
/*
* Compute the total number of bytes to allocate. The first element
* of the array is built into the Rel_cachebuf header, so we subtract
* one from nelts.
*/
return (NULL);
return (rcbp);
}
/*
* Allocate a Rel_aux descriptor and attach it to the given Rel_desc,
* allocating an auxiliary cache buffer if necessary.
*
* entry:
* ofl - Output file descriptor
* rdp - Rel_desc descriptor that requires an auxiliary block
*
* exit:
* Returns TRUE on success, and FALSE if the allocation fails.
* On success, the caller is responsible for initializing the
* auxiliary block properly.
*/
static Boolean
{
/*
* If there is space available in the present cache bucket, use it.
* Otherwise, allocate a new bucket.
*/
if (ofl->ofl_relaux) {
}
/*
* Compute the total number of bytes to allocate. The first
* element of the array is built into the Rel_aux_cachebuf
* header, so we subtract one from the number of elements.
*/
size = sizeof (Rel_aux_cachebuf) +
NULL))
return (FALSE);
}
/* Take an auxiliary descriptor from the cache and add it to rdesc */
return (TRUE);
}
/*
* Enter a copy of the given Rel_desc relocation descriptor, and
* any associated auxiliary Rel_aux it may reference, into the
* specified relocation cache.
*
* entry:
* ofl - Output file descriptor
* rcp - Relocation descriptor cache to recieve relocation
* rdesc - Rel_desc image to be inserted
* flags - Flags to add to rdest->rel_flags in the inserted descriptor
*
* exit:
* Returns the pointer to the inserted descriptor on success.
* Returns NULL if an allocation error occurs.
*/
Rel_desc *
{
/*
* If no relocation cache structures are available, allocate a new
* one and link it to the buffer list.
*/
return (NULL);
/*
* If there is an auxiliary block on the original, allocate
* one for the clone. Save the pointer, because the struct copy
* below will crush it.
*/
return (NULL);
}
/* Copy contents of the original into the clone */
/*
* If there is an auxiliary block, restore the clone's pointer to
* it, and copy the auxiliary contents.
*/
}
return (arsp);
}
/*
* Initialize a relocation descriptor auxiliary block to default
* values.
*
* entry:
* rdesc - Relocation descriptor, with a non-NULL rel_aux field
* pointing at the auxiliary block to be initialized.
*
* exit:
* Each field in rdesc->rel_aux has been set to its default value
*/
static void
{
/*
* The default output section is the one the input section
* is assigned to, assuming that there is an input section.
* Failing that, NULL is the only possibility, and we expect
* that the caller will assign an explicit value.
*/
/* The ra_usym defaults to the value in rel_sym */
/* Remaining fields are zeroed */
rap->ra_typedata = 0;
}
/*
* The ld_reloc_set_aux_XXX() functions are used to set the value of an
* auxiliary relocation item on a relocation descriptor that exists in
* the active or output relocation cache. These descriptors are created
* via a call to ld_reloc_enter().
*
* These functions preserve the illusion that every relocation descriptor
* has a non-NULL auxiliary block into which values can be set, while
* only creating an auxiliary block if one is actually necessary, preventing
* the large memory allocations that would otherwise occur. They operate
* as follows:
*
* - If an auxiliary block already exists, set the desired value and
* and return TRUE.
*
* - If no auxiliary block exists, but the desired value is the default
* value for the specified item, then no auxiliary block is needed,
* and TRUE is returned.
*
* - If no auxiliary block exists, and the desired value is not the
* default for the specified item, allocate an auxiliary block for
* the descriptor, initialize its contents to default values for all
* items, set the specified value, and return TRUE.
*
* - If an auxiliary block needs to be added, but the allocation fails,
* an error is issued, and FALSE is returned.
*
* Note that we only provide an ld_reloc_set_aux_XXX() function for those
* auxiliary items that libld actually modifies in Rel_desc descriptors
* in the active or output caches. If another one is needed, add it here.
*
* The PROCESS_NULL_REL_AUX macro is used to provide a single implementation
* for the logic that determines if an auxiliary block is needed or not,
* and handles the details of allocating and initializing it. It accepts
* one argument, _isdefault_predicate, which should be a call to the
* RELAUX_ISDEFAULT_xxx() macro appropriate for the auxiliary item
*/
#define PROCESS_NULL_REL_AUX(_isdefault_predicate) \
/* If requested value is the default, no need for aux block */ \
if (_isdefault_predicate) \
return (TRUE); \
/* Allocate and attach an auxiliary block */ \
return (FALSE); \
/* Initialize the auxiliary block with default values */ \
ld_init_rel_aux(rdesc); \
}
{
return (TRUE);
}
{
return (TRUE);
}
/*
* Return a descriptive name for the symbol associated with the
* given relocation descriptor. This will be the actual symbol
* name if one exists, or a suitable alternative otherwise.
*
* entry:
* rsp - Relocation descriptor
*/
const char *
{
/* If the symbol has a valid name use it */
/*
* If the symbol is STT_SECTION, and the corresponding
* section symbol has the specially prepared string intended
* for this use, use that string. The string is of the form
* secname (section)
*/
} else {
/*
* Use an empty name for a register relocation with
* no symbol.
*/
return (MSG_ORIG(MSG_STR_EMPTY));
}
/* If all else fails, report it as <unknown> */
return (MSG_INTL(MSG_STR_UNKNOWN));
}
/*
* Add an active relocation record.
*/
{
return (S_ERROR);
/*
* Any GOT relocation reference requires the creation of a .got table.
* Most references to a .got require a .got entry, which is accounted
* for with the ofl_gotcnt counter. However, some references are
* relative to the .got table, but require no .got entry. This test
* insures a .got is created regardless of the type of reference.
*/
/*
* If this is a displacement relocation generate a warning.
*/
}
return (1);
}
/*
* In the platform specific machrel.XXX.c files, we sometimes write
* the running linker has the opposite byte order of the object being
* produced.
*/
{
return (BSWAP_WORD(v));
}
{
return (BSWAP_XWORD(v));
}
{
/*
* If this is the first time we've seen this symbol in a GOT
* relocation we need to assign it a GOT token. Once we've got
* all of the GOT's assigned we can assign the actual indexes.
*/
return (S_ERROR);
/*
* Initialize the GOT table entry.
*
* For global symbols, we clear the GOT table entry and create
* a GLOB_DAT relocation against the symbol.
*
* For local symbols, we enter the symbol value into a GOT
* table entry and create a relative relocation if all of
* the following hold:
*
* - Output is a shared object
* - Symbol is not ABS
* - Relocation is not against one of the special sections
* (COMMON, ...)
* - This is not one of the generated symbols we have
* to update after the output object has been fully
* laid out (_START_, _END_, ...)
*
* Local symbols that don't meet the above requirements
* are processed as is.
*/
if ((flags & FLG_OF_SHAROBJ) &&
return (S_ERROR);
((FLG_REL_GOT | FLG_REL_ADVAL),
return (S_ERROR);
} else {
return (S_ERROR);
}
} else {
return (S_ERROR);
}
} else {
return (S_ERROR);
}
/*
* Perform relocation to GOT table entry.
*/
}
/*
* Perform relocations for PLT's
*/
{
case EM_AMD64:
/*
* AMD64 TLS code sequences do not use a unique TLS
* relocation to reference the __tls_get_addr() function call.
*/
0))
break;
case EM_386:
/*
* GNUC IA32 TLS code sequences do not use a unique TLS
* relocation to reference the ___tls_get_addr() function call.
*/
0))
break;
}
/*
* if (not PLT yet assigned)
* then
* assign PLT index to symbol
* build output JMP_SLOT relocation
* fi
*/
/*
* If this symbol is binding to a lazy loadable, or deferred
* dependency, then identify the symbol.
*/
}
return (S_ERROR);
}
/*
* Perform relocation to PLT table entry.
*/
return (S_ERROR);
return (1);
} else
}
/*
* Round up to the next power of 2. Used to ensure section alignments that can
* be used for copy relocation symbol alignments are sane values.
*/
static Word
{
val--;
return (++val);
}
/*
* process GLOBAL undefined and ref_dyn_need symbols.
*/
static uintptr_t
{
/*
* Reference is to a function so simply create a plt entry for it.
*/
/*
* Catch absolutes - these may cause a text relocation.
*/
/*
* If -zabsexec is set then promote the ABSOLUTE symbol to
* current the current object and perform the relocation now.
*/
}
/*
* If the relocation is against a writable section simply compute the
* necessary output relocation. As an optimization, if the symbol has
* already been transformed into a copy relocation then we can perform
* the relocation directly (copy relocations should only be generated
* for references from the text segment and these relocations are
* normally carried out before we get to the data segment relocations).
*/
else
}
/*
* If the reference isn't to an object (normally because a .type
* directive wasn't defined in some assembler source), then apply
* a generic relocation (this has a tendency to result in text
* relocations).
*/
}
/*
* Prepare for generating a copy relocation.
*
* If this symbol is one of an alias pair, we need to ensure both
* symbols become part of the output (the strong symbol will be used to
* maintain the symbols state). And, if we did raise the precedence of
* a symbol we need to check and see if this is a weak symbol. If it is
* we want to use it's strong counter part.
*
* The results of this logic should be:
* ra_usym: assigned to strong
* rel_sym: assigned to symbol to perform
* copy_reloc against (weak or strong).
*/
if (sap->sa_linkndx) {
/*
* As we're going to replicate a symbol from a shared
* object, retain its correct binding status.
*/
/*
* As we're going to replicate a symbol from a shared
* object, retain its correct binding status.
*/
}
/*
* If this is a weak symbol then we want to move the strong
* symbol into local .bss. If there is a copy_reloc to be
* performed, that should still occur against the WEAK symbol.
*/
return (S_ERROR);
} else
_sdp = 0;
/*
* If the reference is to an object then allocate space for the object
* within the executables .bss. Relocations will now be performed from
* this new location. If the original shared objects data is
* initialized, then generate a copy relocation that will copy the data
* to the executables .bss at runtime.
*/
/*
* Diagnose the original copy reference, as this symbol
* information will be overridden with the new destination.
*/
/*
* Indicate that the symbol(s) against which we're relocating
* have been moved to the executables common. Also, insure that
* the symbol(s) remain marked as global, as the shared object
* from which they are copied must be able to relocate to the
* new common location within the executable.
*
* Note that even though a new symbol has been generated in the
* output files' .bss, the symbol must remain REF_DYN_NEED and
* not be promoted to REF_REL_NEED. sym_validate() still needs
* to carry out a number of checks against the symbols binding
* that are triggered by the REF_DYN_NEED state.
*/
if (_sdp) {
/*
* Make sure the symbol has a reference in case of any
* error diagnostics against it (perhaps this belongs
* to a version that isn't allowable for this build).
* The resulting diagnostic (see sym_undef_entry())
* might seem a little bogus, as the symbol hasn't
* really been referenced by this file, but has been
* promoted as a consequence of its alias reference.
*/
}
/*
* Assign the symbol to the bss.
*/
return (S_ERROR);
/*
* Ensure the symbol has sufficient alignment. The symbol
* definition has no alignment information that can be used,
* hence we use a heuristic. Historically, twice the native
* word alignment was sufficient for any data type, however,
* the developer may have requested larger alignments (pragma
* align). The most conservative approach is to use a power
* of two alignment, determined from the alignment of the
* section containing the symbol definition. Note that this
* can result in some bloat to the .bss as the not every item
* of copied data might need the section alignment.
*
* COMMON symbols carry their alignment requirements in the
* symbols st_value field. This alignment is applied to the
* symbol when it is eventually transformed into .bss.
*/
else {
else
}
/*
* Whether or not the symbol references initialized data we
* generate a copy relocation - this differs from the past
* where we would not create the COPY_RELOC if we were binding
* against .bss. This is done for *two* reasons.
*
* - If the symbol in the shared object changes to a
* initialized data - we need the COPY to pick it up.
* - Without the COPY RELOC we can't tell that the symbol
* from the COPY'd object has been moved and all bindings
* to it should bind here.
*
* Keep this symbol in the copy relocation list to check the
* validity later.
*/
AL_CNT_OFL_COPYRELS) == NULL)
return (S_ERROR);
return (S_ERROR);
/*
* If this symbol is a protected symbol, warn the user. A
* potential issue exists as the copy relocated symbol within
* the executable can be visible to others, whereas the shared
* object that defined the original copy data symbol is pre-
* bound to reference it's own definition. Any modification
* of the symbols data could lead to inconsistencies for the
* various users.
*/
}
}
}
/*
* All relocations should have been handled by the other routines. This
* routine is here as a catch all, if we do enter it we've goofed - but
* we'll try and do the best we can.
*/
static uintptr_t
{
/*
* If building a shared object then put the relocation off
* until runtime.
*/
/*
* Otherwise process relocation now.
*/
}
/*
* Process relocations when building a relocatable object. Typically, there
* aren't many relocations that can be caught at this point, most are simply
* passed through to the output relocatable object.
*/
static uintptr_t
{
/*
* Determine if we can do any relocations at this point. We can if:
*
* this is local_symbol and a non-GOT relocation, and
* the relocation is pc-relative, and
* the relocation is against a symbol in same section
*/
IS_PC_RELATIVE(rtype) &&
/*
* If -zredlocsym is in effect, translate all local symbol relocations
* to be against section symbols, since section symbols are the only
* local symbols which will be added to the .symtab.
*/
/*
* But if this is PIC code, don't allow it for now.
*/
return (S_ERROR);
}
/*
* Indicate that this relocation should be processed the same
* as a section symbol. For RELA, indicate that the addend
* also needs to be applied to this relocation.
*/
else
}
/*
* Intel (Rel) relocations do not contain an addend. Any
* addend is contained within the file at the location
* identified by the relocation offset. Therefore, if we're
* processing a section symbol, or a -zredlocsym relocation
* (that basically transforms a local symbol reference into
* a section reference), perform an active relocation to
* propagate any addend.
*/
(oflags == FLG_REL_SCNNDX))
return (S_ERROR);
}
}
/*
* Perform any generic TLS validations before passing control to machine
* specific routines. At this point we know we are dealing with an executable
* or shared object - relocatable objects have already been processed.
*/
static uintptr_t
{
unsigned char type;
/*
* All TLS relocations are illegal in a static executable.
*/
if (OFL_IS_STATIC_EXEC(ofl)) {
return (S_ERROR);
}
/*
* Any TLS relocation must be against a STT_TLS symbol, all others
* are illegal.
*/
return (S_ERROR);
}
/*
* A dynamic executable can not use the LD or LE reference models to
* reference an external symbol. A shared object can not use the LD
* reference model to reference an external symbol.
*/
return (S_ERROR);
}
/*
* The TLS LE model is only allowed for dynamic executables. The TLS IE
* model is allowed for shared objects, but this model has restrictions.
* This model can only be used freely in dependencies that are loaded
* immediately as part of process initialization. However, during the
* initial runtime handshake with libc that establishes the thread
* pointer, a small backup TLS reservation is created. This area can
* be used by objects that are loaded after threads are initialized.
* However, this area is limited in size and may have already been
* used. This area is intended for specialized applications, and does
* not provide the degree of flexibility dynamic TLS can offer. Under
* -z verbose indicate this restriction to the user.
*/
if ((flags & FLG_OF_EXEC) == 0) {
return (S_ERROR);
(flags & FLG_OF_VERBOSE)) {
}
}
}
{
/*
* Indicate this symbol is being used for relocation and therefore must
* have its output address updated accordingly (refer to update_osym()).
*/
/*
* Indicate the section this symbol is defined in has been referenced,
* therefor it *is not* a candidate for elimination.
*/
}
return (S_ERROR);
/*
* Determine if this symbol is actually an alias to another symbol. If
* so, and the alias is not REF_DYN_SEEN, set ra_usym to point to the
* weak symbols strong counter-part. The one exception is if the
* FLG_SY_MVTOCOMM flag is set on the weak symbol. If this is the case,
* the strong is only here because of its promotion, and the weak symbol
* should still be used for the relocation reference (see reloc_exec()).
*/
return (S_ERROR);
}
/*
* Determine whether this symbol should be bound locally or not.
* Symbols are bound locally if one of the following is true:
*
* - the symbol is of type STB_LOCAL.
*
* - the output image is not a relocatable object and the relocation
* is relative to the .got.
*
* - the section being relocated is of type SHT_SUNW_dof. These
* sections must be bound to the functions in the containing
* object and can not be interposed upon.
*
* - the symbol has been reduced (scoped to a local or symbolic) and
* reductions are being processed.
*
* - the -Bsymbolic flag is in use when building a shared object,
* and the symbol hasn't explicitly been defined as nodirect.
*
* - an executable (fixed address) is being created, and the symbol
* is defined in the executable.
*
* - the relocation is against a segment which will not be loaded
* into memory. In this case, the relocation must be resolved
* now, as ld.so.1 can not process relocations against unmapped
* segments.
*/
if (reld->rel_isdesc &&
} else if (!(flags & FLG_OF_RELOBJ) &&
/*
* Global symbols may have been individually reduced in
* scope. If the whole object is to be self contained,
* such as when generating an executable or a symbolic
* shared object, make sure all relocation symbol
* references (sections too) are treated locally. Note,
* explicit no-direct symbols should not be bound to
* locally.
*/
(FLG_SY_HIDDEN | FLG_SY_PROTECT)))
else if ((flags & FLG_OF_EXEC) ||
((flags & FLG_OF_SYMBOLIC) &&
}
}
}
/*
* If this is a PC_RELATIVE relocation, the relocation could be
* compromised if the relocated address is later used as a copy
* relocated symbol (PSARC 1999/636, bugid 4187211). Scan the input
* files symbol table to cross reference this relocation offset.
*/
IS_PC_RELATIVE(rtype) &&
return (S_ERROR);
}
/*
* GOT based relocations must bind to the object being built - since
* they are relevant to the current GOT. If not building a relocatable
* object - give a appropriate error message.
*/
IS_GOT_BASED(rtype)) {
return (S_ERROR);
}
/*
* TLS symbols can only have TLS relocations.
*/
(IS_TLS_INS(rtype) == 0)) {
/*
* The above test is relaxed if the target section is
* non-allocable.
*/
return (S_ERROR);
}
}
/*
* Select the relocation to perform.
*/
if (IS_REGISTER(rtype)) {
return (S_ERROR);
}
}
if (flags & FLG_OF_RELOBJ)
if (IS_TLS_INS(rtype))
if (IS_GOT_OPINS(rtype)) {
assert(0);
return (S_ERROR);
}
}
if (IS_GOT_RELATIVE(rtype))
if (local)
((flags & FLG_OF_BFLAG) == 0))
/*
* IS_NOT_REL(rtype)
*/
}
/*
* Given a relocation that references a local symbol from a discarded COMDAT
* section, replace the symbol with the corresponding symbol from the section
* that was kept.
*
* entry:
* reld - Relocation
* sdp - Symbol to be replaced. Must be a local symbol (STB_LOCAL).
* reject - Address of variable to receive rejection code
* if no replacement symbol is found.
*
* exit:
* Returns address of replacement symbol descriptor if one was
* found, and NULL otherwise. The result is also cached in
* ofl->ofl_sr_cache as an optimization to speed following calls
* for the same value of sdp.
*
* On success (non-NULL result), *reject is set to RLXREL_REJ_NONE.
* On failure (NULL result), *reject is filled in with a code
* describing the underlying reason.
*
* note:
* The word "COMDAT" is used to refer to actual COMDAT sections, COMDAT
* groups tied together with an SHF_GROUP section, and .gnu.linkonce
* sections which provide a simplified COMDAT requirement. COMDAT
* sections are identified with the FLG_IS_COMDAT section flag.
*
* In principle, this sort of sloppy relocation remapping is
* a questionable practice. All self-referential sections should
* be in a common SHF_GROUP so that they are all kept or removed
* together. The problem is that there is no way to ensure that the
* two sections are similar enough that the replacement section will
* really supply the correct information. However, we see a couple of
* situations where it is useful to do this: (1) Older Sun C compilers
* generated DWARF sections that would refer to one of the COMDAT
* sections, and (2) gcc, when its GNU linkonce COMDAT feature is enabled.
* It turns out that the GNU ld does these sloppy remappings.
*
* The GNU ld takes an approach that hard wires special section
* names and treats them specially. We avoid that practice and
* try to get the necessary work done relying only on the ELF
* attributes of the sections and symbols involved. This means
* that our heuristic is somewhat different than theirs, but the
* end result is close enough to solve the same problem.
*
* gcc is in the process of converting to SHF_GROUP. This will
* eventually phase out the need for sloppy relocations, and
* then this logic won't be needed. In the meantime, relaxed relocation
* processing allows us to interoperate.
*/
static Sym_desc *
{
const char *is_name;
/*
* Sloppy relocations are never applied to .eh_frame or
* .gcc_except_table sections. The entries in these sections
* for discarded sections are better left uninitialized.
*
* We match these sections by name, because on most platforms they
* are SHT_PROGBITS, and cannot be identified otherwise. On amd64
* architectures, .eh_frame is SHT_AMD64_UNWIND, but that is ambiguous
* (.eh_frame_hdr is also SHT_AMD64_UNWIND), so we still match it by
* name.
*/
return (NULL);
}
/*
* If we looked up the same symbol on the previous call, we can
* return the cached value.
*/
}
/*
* When a COMDAT section is discarded in favor of another COMDAT
* section, the replacement is recorded in its section descriptor
* (is_comdatkeep). We must validate the replacement before using
* it. The replacement section must:
* - Not have been discarded
* - Have the same size (*)
* - Have the same section type
* - Have the same SHF_GROUP flag setting (either on or off)
* - Must be a COMDAT section of one form or the other.
*
* (*) One might imagine that the replacement section could be
* larger than the original, rather than the exact size. However,
* we have verified that this is the same policy used by the GNU
* ld. If the sections are not the same size, the chance of them
* being interchangeable drops significantly.
*/
}
/*
* We found the kept COMDAT section. Now, look at all of the
* symbols from the input file that contains it to find the
* symbol that corresponds to the one we started with:
* - Hasn't been discarded
* - Has section index of kept section
* - If one symbol has a name, the other must have
* the same name. The st_name field of a symbol
* is 0 if there is no name, and is a string
* table offset otherwise. The string table
* offsets may well not agree --- it is the
* actual string that matters.
* - Type and binding attributes match (st_info)
* - Values match (st_value)
* - Sizes match (st_size)
* - Visibility matches (st_other)
*/
while (symscnt--) {
continue;
} else {
}
}
}
/* If didn't return above, we didn't find it */
}
/*
* Generate relocation descriptor and dispatch
*/
static uintptr_t
{
/*
* Make sure the relocation is in the valid range.
*/
rtype);
return (S_ERROR);
}
ofl->ofl_entrelscnt++;
/*
* Special case: a register symbol associated with symbol index 0 is
* initialized (i.e., relocated) to a constant from the r_addend field
* rather than from a symbol value.
*/
return (S_ERROR);
}
}
/*
* If this is a STT_SECTION symbol, make sure the associated
* section has a descriptive non-NULL is_sym_name field that can
* be accessed by ld_reloc_sym_name() to satisfy debugging output
* and errors.
*
* In principle, we could add this string to every input section
* as it is created, but we defer it until we see a relocation
* symbol that might need it. Not every section will have such
* a relocation, so we create fewer of them this way.
*/
return (S_ERROR);
/*
* If for some reason we have a null relocation record issue a
* warning and continue (the compiler folks can get into this
* state some time). Normal users should never see this error.
*/
return (1);
}
return (S_ERROR);
}
/*
* If we are here, we know that the relocation requires reference
* symbol. If no symbol is assigned, this is a fatal error.
*/
return (S_ERROR);
}
return (1);
/*
* If this symbol is part of a DISCARDED section attempt to find another
* definition.
*/
/*
* If "-z relaxreloc", and the input section is COMDAT
* that has been assigned to an output section, then
* determine if this is a reference to a discarded
* COMDAT section that can be replaced with a COMDAT
* that has been kept.
*/
/*
* A matching symbol was not found. We will
* ignore this relocation. Determine whether
* or not to issue a warning.
* Warnings are always issued under -z verbose,
* but otherwise, we will follow the lead of
* the GNU ld and suppress them for certain
* cases:
*
* - It is a non-allocable debug section.
* The GNU ld tests for these by name,
* but we are willing to extend it to
* any non-allocable section.
* - The target section is excluded from
* sloppy relocations by policy.
*/
(reject != RLXREL_REJ_TARGET)))
return (1);
}
return (S_ERROR);
}
return (S_ERROR);
}
/*
* If this is a global symbol, determine whether its visibility needs
* adjusting.
*/
/*
* Ignore any relocation against a section that will not be in the
* output file (has been stripped).
*/
return (1);
/*
* If the input section exists, but the section has not been associated
* to an output section, then this is a little suspicious.
*/
return (1);
}
/*
* If the symbol for this relocation is invalid (which should have
* generated a message during symbol processing), or the relocation
* record's symbol reference is in any other way invalid, then it's
* about time we gave up.
*/
return (S_ERROR);
}
/*
* Size relocations against section symbols are presently unsupported.
* There is a question as to whether the input section size, or output
* section size would be used. Until an explicit requirement is
* established for either case, we'll punt.
*/
return (S_ERROR);
}
}
static uintptr_t
{
/*
* Decide entry size.
*/
else
}
/*
* Build up the basic information in for the Rel_desc structure.
*/
flags |= FLG_REL_LOAD;
flags |= FLG_REL_NOINFO;
/*
* Initialize the relocation record information and process
* the individual relocation. Reinitialize the flags to
* insure we don't carry any state over from the previous
* relocation records processing.
*/
/*
* Determine whether or not to pass an auxiliary block
* in with this Rel_desc. It is not needed if both the
* osdesc and typedata fields have default values.
*/
}
return (ret);
}
static uintptr_t
{
continue;
osp->os_szoutrels = 0;
/*
* Determine the input section that this
* relocation information refers to.
*/
/*
* Do not process relocations against sections
* which are being discarded (COMDAT)
*/
continue;
return (S_ERROR);
}
/*
* Check for relocations against non-writable
* allocatable sections.
*/
if (osp->os_szoutrels &&
}
}
}
return (1);
}
/*
* Move Section related function
* Get move entry
*/
static Move *
{
/*
* Set info for the target move section
*/
if (mshdr->sh_entsize == 0)
return (NULL);
/*
* If this is an invalid entry, return NULL.
*/
return (NULL);
return (mvp);
}
/*
* Relocation against Move Table.
*/
static uintptr_t
{
/*
* Decide entry size.
*/
if ((entsize == 0) ||
else
}
/*
* The requirement for move data ensures that we have to supply a
* Rel_aux auxiliary block.
*/
/*
* Go through the relocation entries.
*/
/*
* Initialize the relocation record information.
*/
return (S_ERROR);
reld.rel_roffset +=
/* LINTED */
/*
* Generate Reld
*/
if (process_reld(ofl,
return (S_ERROR);
}
} else {
/*
* Generate Reld
*/
if (process_reld(ofl,
return (S_ERROR);
}
}
return (1);
}
/*
* This function is similar to reloc_init().
*
* This function is called when the SHT_SUNW_move table is expanded and there
* are relocations against the SHT_SUNW_move section.
*/
static uintptr_t
{
/*
*/
}
return (ret);
}
/*
* Count the number of output relocation entries, global offset table entries,
* and procedure linkage table entries. This function searches the segment and
* outsect lists and passes each input reloc section to process_reloc().
* It allocates space for any output relocations needed. And builds up
* the relocation structures for later processing.
*/
{
/*
* At this point we have finished processing all input symbols. Make
* sure we add any absolute (internal) symbols before continuing with
* any relocation processing.
*/
return (S_ERROR);
/*
* Process all of the relocations against NON-writable segments
* followed by relocations against the writable segments.
*
* This separation is so that when the writable segments are processed
* we know whether or not a COPYRELOC will be produced for any symbols.
* If relocations aren't processed in this order, a COPYRELOC and a
* regular relocation can be produced against the same symbol. The
* regular relocation would be redundant.
*/
return (S_ERROR);
return (S_ERROR);
/*
* Process any extra relocations. These are relocation sections that
* have a NULL sh_info.
*/
return (S_ERROR);
}
/*
* If there were relocation against move table,
* process the relocation sections.
*/
return (S_ERROR);
/*
* Now all the relocations are pre-processed,
* check the validity of copy relocations.
*/
if (ofl->ofl_copyrels) {
/*
* If there were no displacement relocation
* in this file, don't worry about it.
*/
}
}
/*
* GOT sections are created for dynamic executables and shared objects
* if the FLG_OF_BLDGOT is set, or explicit reference has been made to
* a GOT symbol.
*/
return (S_ERROR);
/* Allocate the GOT if required by target */
return (S_ERROR);
}
return (1);
}
/*
* Simple comparison routine to be used by qsort() for
* the sorting of the output relocation list.
*
* The reloc_compare() routine results in a relocation
* table which is located on:
*
* file referenced (NEEDED NDX)
* referenced symbol
* relocation offset
*
* This provides the most efficient traversal of the relocation
* table at run-time.
*/
static int
{
/*
* first - sort on neededndx
*/
return (1);
return (-1);
/*
* Then sort on symbol
*/
return (1);
return (-1);
/*
* i->key2 == j->key2
*
* At this point we fall back to key2 (offsets) to
* sort the output relocations. Ideally this will
* make for the most efficient processing of these
* relocations at run-time.
*/
return (1);
return (-1);
return (0);
}
static uintptr_t
{
int debug = 0;
return (S_ERROR);
/*
* All but the PLT output relocations are sorted in the output file
* based upon their sym_desc. By doing this multiple relocations
* against the same symbol are grouped together, thus when the object
* is later relocated by ld.so.1 it will take advantage of the symbol
* cache that ld.so.1 has. This can significantly reduce the runtime
* relocation cost of a dynamic object.
*
* PLT relocations are not sorted because the order of the PLT
* relocations is used by ld.so.1 to determine what symbol a PLT
* relocation is against.
*/
if (debug == 0) {
debug = 1;
}
/*
* If it's a PLT relocation we output it now in the
* order that it was originally processed.
*/
continue;
}
/* LINTED */
} else {
}
} else {
} else {
rel_isdesc->is_indata) +
}
}
}
(int (*)(const void *, const void *))reloc_compare);
/*
* All output relocations have now been sorted, go through
* and process each relocation.
*/
}
return (error);
}
/*
* Process relocations. Finds every input relocation section for each output
* section and invokes reloc_section() to relocate that section.
*/
{
/*
* Determine the index of the symbol table that will be referenced by
* the relocation entries.
*/
if (OFL_ALLOW_DYNSYM(ofl))
/* LINTED */
/* LINTED */
/*
* Re-initialize counters. These are used to provide relocation
* offsets within the output buffers.
*/
ofl->ofl_relocpltsz = 0;
ofl->ofl_relocgotsz = 0;
ofl->ofl_relocbsssz = 0;
/*
* Now that the output file is created and symbol update has occurred,
* process the relocations collected in process_reloc().
*/
return (S_ERROR);
return (S_ERROR);
if ((flags & FLG_OF_COMREL) == 0) {
/*
* Process the relocation sections. For each relocation
* section generated for the output image update its shdr
* information to reflect the symbol table it needs (sh_link)
* and the section to which the relocation must be applied
* (sh_info).
*/
if (osp->os_relosdesc == 0)
continue;
/* LINTED */
}
}
/*
* Since the .rel[a] section is not tied to any specific
* section, we'd not have found it above.
*/
}
} else {
/*
* We only have two relocation sections here, (PLT's,
* coalesced) so just hit them directly instead of stepping
* over the output sections.
*/
}
/* LINTED */
}
}
/*
* If the -z text option was given, and we have output relocations
* against a non-writable, allocatable section, issue a diagnostic and
* return (the actual entries that caused this error would have been
* output during the relocating section phase).
*/
(FLG_OF_PURETXT | FLG_OF_TEXTREL)) {
return (S_ERROR);
}
/*
* Finally, initialize the first got entry with the address of the
* .dynamic section (_DYNAMIC).
*/
if (flags & FLG_OF_DYNAMIC) {
return (S_ERROR);
}
/*
* Now that any GOT information has been written, display the debugging
* information if required.
*/
return (1);
}
/*
* If the -z text option was given, and we have output relocations against a
* non-writable, allocatable section, issue a diagnostic. Print offending
* symbols in tabular form similar to the way undefined symbols are presented.
* Called from reloc_count(). The actual fatal error condition is triggered on
* in reloc_process() above.
*
* Note. For historic reasons -ztext is not a default option (however all OS
* shared object builds use this option). It can be argued that this option
* should also be default when generating an a.out (see 1163979). However, if
* an a.out contains text relocations it is either because the user is creating
* something pretty weird (they've used the -b or -znodefs options), or because
* the library against which they're building wasn't constructed correctly (ie.
* a function has a NOTYPE type, in which case the a.out won't generate an
* associated plt). In the latter case the builder of the a.out can't do
* anything to fix the error - thus we've chosen not to give the user an error,
* or warning, for this case.
*/
static void
{
const char *str1;
if (warning)
else
}
void
{
/*
* -ztextoff
*/
return;
/*
* Only give relocation errors against loadable read-only segments.
*/
return;
/*
* If we are in -ztextwarn mode, it's a silent error if a relocation is
* due to a 'WEAK REFERENCE'. This is because if the symbol is not
* provided at run-time we will not perform a text-relocation.
*/
return;
if (reloc_title) {
/*
* If building with '-ztext' then emit a fatal error. If
* building a executable then only emit a 'warning'.
*/
reloc_remain_title(ofl, 0);
else
reloc_title = FALSE;
}
}
/*
* Generic encapsulation for generating a TLS got index.
*/
{
return (S_ERROR);
if (local)
rflags |= FLG_REL_SCNNDX;
return (S_ERROR);
/*
* If this is a local LE TLS symbol, then the symbol won't be
* available at runtime. The value of the local symbol will
* be placed in the associated got entry, and the got
* relocation is reassigned to a section symbol.
*/
return (S_ERROR);
}
if (rtype2) {
if (local) {
return (S_ERROR);
} else {
return (S_ERROR);
}
}
return (1);
}
/*
* Move Section related function
*/
static void
{
/*
* Search for matching move entry.
*/
/*
* Update r_offset
*/
return;
}
}
}
void
{
/*
* We are relocating the move table itself.
*/
&newoffset);
} else {
/*
* We are expanding the partial symbol. So we are generating
* the relocation entry relocating the expanded partial symbol.
*/
}
}
/*
* Partially Initialized Symbol Handling routines
* For RELA architecture, the second argument is reld->rel_raddend. For REL
* architecure, the second argument is the value stored at the relocation
* target address.
*/
Sym_desc *
{
for (i = 1; i < nlocs; i++) {
continue;
continue;
return (symd);
}
return (NULL);
}
/*
* Return True (1) if the code processing the given relocation
* needs to perform byte swapping when accessing the section data.
*/
int
{
/*
* In a cross-link situation where the linker host and target
* have opposite byte orders, it can be necessary to swap bytes
* when doing relocation processing. This is indicated by the
* presence of the FLG_OF1_ENCDIFF flag bit. However, swapping
* is only needed for the section types that libelf doesn't
* automatically xlate.
*/
case SHT_PROGBITS:
return (1);
case SHT_SPARC_GOTDATA:
return (1);
break;
case SHT_AMD64_UNWIND:
return (1);
break;
}
}
/*
* If FLG_OF1_ENCDIFF isn't set, or the section isn't
* progbits (or similar), then no swapping is needed.
*/
return (0);
}
/*
* Obtain the current value at the given relocation target.
*
* entry:
* ofl - Output file descriptor
* rsp - Relocation record
* data - Pointer to relocation target
* value - Address of variable to recieve value
*
* exit:
* The value of the data at the relocation target has
* been stored in value.
*/
int
{
case 1:
/* LINTED */
break;
case 2:
{
Half v;
} else {
}
}
break;
case 4:
{
Word v;
} else {
}
}
break;
default:
{
}
return (0);
}
return (1);
}
/*
* Set the value at the given relocation target.
*
* entry:
* ofl - Output file descriptor
* rsp - Relocation record
* data - Pointer to relocation target
* value - Address of variable to recieve value
*
* exit:
* The value of the data at the relocation target has
* been stored in value.
*/
int
{
case 1:
/* LINTED */
break;
case 2:
{
} else {
}
}
break;
case 4:
{
} else {
}
}
break;
default:
{
}
return (0);
}
return (1);
}
/*
* Because of the combinations of 32-bit lib providing 64-bit support, and
* visa-versa, the use of krtld's dorelocs can result in differing message
* Thus the actual message files contain a couple of entries to satisfy
* each architectures build. Here we add dummy calls to quieten chkmsg.
*
* chkmsg: MSG_INTL(MSG_REL_NOFIT)
* chkmsg: MSG_INTL(MSG_REL_NONALIGN)
*/