update.c revision a1926ac729b1b88f95c761a416f8a49b22b9e56f
/*
* 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.
*/
/*
* Update the new output file image, perform virtual address, offset and
* displacement calculations on the program headers and sections headers,
* and generate any new output section information.
*/
#define ELF_TARGET_AMD64
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <debug.h>
#include "msg.h"
#include "_libld.h"
/*
* Comparison routine used by qsort() for sorting of the global symbol list
* based off of the hashbuckets the symbol will eventually be deposited in.
*/
static int
{
}
/*
* Comparison routine used by qsort() for sorting of dyn[sym|tls]sort section
* indices based on the address of the symbols they reference. The
* use of the global dynsort_compare_syms variable is needed because
* we need to examine the symbols the indices reference. It is safe, because
* the linker is single threaded.
*/
static int
{
/*
* Note: the logical computation for this is
* (st_value1 - st_value2)
* However, that is only correct if the address type is smaller
* than a pointer. Writing it this way makes it immune to the
* class (32 or 64-bit) of the linker.
*/
}
/*
* Scan the sorted symbols, and issue warnings if there are any duplicate
* values in the list. We only do this if -zverbose is set, or we are
* running with LD_DEBUG defined
*
* entry:
* ofl - Output file descriptor
* ldynsym - Pointer to start of .SUNW_ldynsym section that the
* sort section indexes reference.
* symsort - Pointer to start of .SUNW_dynsymsort or .SUNW_dyntlssort
* section.
* n - # of indices in symsort array
* secname - Name of the symsort section.
*
* exit:
* If the symsort section contains indexes to more than one
* symbol with the same address value, a warning is issued.
*/
static void
{
/* Nothing to do if -zverbose or LD_DEBUG are not active */
if (!(zverbose || DBG_ENABLED))
return;
cmp_ndx = 0;
if (zverbose)
} else { /* Not a dup. Move reference up */
}
}
}
/*
* Build and update any output symbol tables. Here we work on all the symbol
* tables at once to reduce the duplication of symbol and string manipulation.
* Symbols and their associated strings are copied from the read-only input
* file images to the output image and their values and index's updated in the
* output image.
*/
static Addr
{
/*
* There are several places in this function where we wish
* to insert a symbol index to the combined .SUNW_ldynsym/.dynsym
* symbol table into one of the two sort sections (.SUNW_dynsymsort
* or .SUNW_dyntlssort), if that symbol has the right attributes.
* This macro is used to generate the necessary code from a single
* specification.
*
* entry:
* _sdp, _sym, _type - As per DYNSORT_COUNT. See _libld.h
* _sym_ndx - Index that _sym will have in the combined
* .SUNW_ldynsym/.dynsym symbol table.
*/
{ \
\
if (dynsymsort_symtype[_type]) { \
_dynsort_arr = dynsymsort; \
_dynsort_ndx = &dynsymsort_ndx; \
_dynsort_arr = dyntlssort; \
_dynsort_ndx = &dyntlssort_ndx; \
} else { \
_dynsort_arr = NULL; \
} \
}
#if defined(_ELF64)
#endif
Addr tlsbssaddr = 0;
int start_set = 0;
/* symbols */
/* vector */
/* vector */
/* relocation use) */
/* .SUNW_ldynsym */
/* information */
/*
* Initialize pointers to the symbol table entries and the symbol
* table strings. Skip the first symbol entry and the first string
* table byte. Note that if we are not generating any output symbol
* tables we must still generate and update internal copies so
* that the relocation phase has the correct information.
*/
if (ofl->ofl_ossymshndx)
symshndx =
}
if (OFL_ALLOW_DYNSYM(ofl)) {
/*
* If we are also constructing a .SUNW_ldynsym section
* to contain local function symbols, then set it up too.
*/
if (ofl->ofl_osldynsym) {
/*
* If there is a SUNW_ldynsym, then there may also
* sections, used to collect indices of function
* and data symbols sorted by address order.
*/
dynsymsort = (Word *)
dynsymsort_ndx = 0;
}
dyntlssort = (Word *)
dyntlssort_ndx = 0;
}
}
/*
* Initialize the hash table.
*/
if (ofl->ofl_osdynshndx)
dynshndx =
if (ofl->ofl_osldynshndx)
}
/*
* symndx is the symbol index to be used for relocation processing. It
* points to the relevant symtab's (.dynsym or .symtab) symbol ndx.
*/
if (dynsym)
symndx = &dynsym_ndx;
else
symndx = &symtab_ndx;
/*
* If we have version definitions initialize the version symbol index
* table. There is one entry for each symbol which contains the symbols
* version index.
*/
if (!(flags & FLG_OF_NOVERSEC) &&
} else
/*
* If syminfo section exists be prepared to fill it in.
*/
if (ofl->ofl_ossyminfo) {
} else
/*
* Setup our string tables.
*/
/*
* Put output file name to the first .symtab and .SUNW_ldynsym symbol.
*/
if (symtab) {
/* LINTED */
versym[1] = 0;
}
if (ldynsym) {
/* LINTED */
/* Scoped symbols get filled in global loop below */
}
/*
* If we are to display GOT summary information, then allocate
* the buffer to 'cache' the GOT symbols into now.
*/
if (DBG_ENABLED) {
}
/*
* Traverse the program headers. Determine the last executable segment
* and the last data segment so that we can update etext and edata. If
* we have empty segments (reservations) record them for setting _end.
*/
}
/*
* Generate a section symbol for each output section.
*/
/* LINTED */
if (symtab) {
if (sectndx >= SHN_LORESERVE) {
} else {
/* LINTED */
}
}
if (versym)
}
/*
* Generate the .shstrtab for this section.
*/
/*
* Find the section index for our special symbols.
*/
/* LINTED */
/* LINTED */
}
}
if (start_set == 0) {
/* LINTED */
start_set++;
}
/*
* While we're here, determine whether a .init or .fini
* section exist.
*/
MSG_ORIG(MSG_SCN_INIT)) == 0))
MSG_ORIG(MSG_SCN_FINI)) == 0))
}
}
/*
* Add local register symbols to the .dynsym. These are required as
* DT_REGISTER .dynamic entries must have a symbol to reference.
*/
int ndx;
continue;
if (!SYM_IS_HIDDEN(rsdp) &&
continue;
&stoff);
}
dynsym_ndx++;
}
}
/*
* Having traversed all the output segments, warn the user if the
* traditional text or data segments don't exist. Otherwise from these
* segments establish the values for `etext', `edata', `end', `END',
* and `START'.
*/
if (!(flags & FLG_OF_RELOBJ)) {
if (tsgp)
else {
etext_abs = 1;
if (flags & FLG_OF_VERBOSE)
}
if (dsgp) {
} else {
edata_abs = 1;
if (flags & FLG_OF_VERBOSE)
}
if (tsgp)
else
sgp = 0;
else {
/*
* One of the segments must be of zero size.
*/
else
}
if (sgp) {
/*
* If the last loadable segment is a read-only segment,
* then the application which uses the symbol _end to
* find the beginning of writable heap area may cause
* segmentation violation. We adjust the value of the
* _end to skip to the next page boundary.
*
* 6401812 System interface which returs beginning
* heap would be nice.
* When the above RFE is implemented, the changes below
* could be changed in a better way.
*/
/*
* If we're dealing with a memory reservation there are
* no sections to establish an index for _end, so assign
* it as an absolute.
*/
/*
* Determine the last section for this segment.
*/
/* LINTED */
} else {
end_abs = 1;
}
} else {
end_abs = 1;
}
}
/*
* Initialize the scoped symbol table entry point. This is for all
* the global symbols that have been scoped to locals and will be
* filled in during global symbol processing so that we don't have
* to traverse the globals symbol hash array more than once.
*/
if (symtab) {
}
/*
* If expanding partially expanded symbols under '-z nopartial',
* prepare to do that.
*/
if (ofl->ofl_isparexpn) {
/* LINTED */
}
/*
* If we are generating a .symtab collect all the local symbols,
* assigning a new virtual address or displacement (value).
*/
int update_done;
/*
* Assign a got offset if necessary.
*/
if (DBG_ENABLED) {
gottable++;
}
}
continue;
/*
* Ignore any symbols that have been marked as invalid
* during input processing. Providing these aren't used
* for relocation they'll just be dropped from the
* output image.
*/
continue;
/*
* If the section that this symbol was associated
* with has been discarded - then we discard
* the local symbol along with it.
*/
continue;
/*
* If this symbol is from a different file
* than the input descriptor we are processing,
* treat it as if it has FLG_SY_ISDISC set.
* This happens when sloppy_comdat_reloc()
* replaces a symbol to a discarded comdat section
* with an equivalent symbol from a different
* file. We only want to enter such a symbol
* once --- as part of the file that actually
* supplies it.
*/
continue;
/*
* Generate an output symbol to represent this input
* symbol. Even if the symbol table is to be stripped
* we still need to update any local symbols that are
* used during relocation.
*/
enter_in_symtab = symtab &&
ldynsym_symtype[type] &&
if (enter_in_symtab) {
if (!dynsym)
/*
* Provided this isn't an unnamed register
* symbol, update its name.
*/
(void) st_setstring(strtab,
}
if (symshndx)
continue;
} else if (enter_in_ldynsym) {
/*
* Not using symtab, but we do have ldynsym
* available.
*/
&stoff);
if (ldynshndx)
/* Add it to sort section if it qualifies */
ldynsym_ndx++;
} else { /* Not using symtab or ldynsym */
/*
* If this symbol requires modifying to provide
* for a relocation or move table update, make
* a copy of it.
*/
continue;
continue;
}
/*
* Update the symbols contents if necessary.
*/
update_done = 0;
update_done = 1;
}
/*
* If we are expanding the locally bound partially
* initialized symbols, then update the address here.
*/
if (ofl->ofl_isparexpn &&
if ((flags & FLG_OF_RELOBJ) == 0)
}
/*
* If this isn't an UNDEF symbol (ie. an input section
* is associated), update the symbols value and index.
*/
/* LINTED */
if ((flags & FLG_OF_RELOBJ) == 0) {
/*
* TLS symbols are relative to
* the TLS segment.
*/
(ofl->ofl_tlsphdr)) {
}
}
/* LINTED */
if (_symshndx) {
}
} else {
/* LINTED */
}
}
/*
* If entering the symbol in both the symtab and the
* ldynsym, then the one in symtab needs to be
* copied to ldynsym. If it is only in the ldynsym,
* then the code above already set it up and we have
* nothing more to do here.
*/
if (enter_in_symtab && enter_in_ldynsym) {
&stoff);
/* Add it to sort section if it qualifies */
ldynsym_ndx++;
}
}
/*
* If this input file has undergone object to symbol
* capabilities conversion, supply any new capabilities symbols.
* These symbols are copies of the original global symbols, and
* follow the existing local symbols that are supplied from this
* input file (which are identified with a preceding STT_FILE).
*/
/*
* Update the symbols value.
*/
/* LINTED */
if ((flags & FLG_OF_RELOBJ) == 0)
/*
* Update the symbols section index.
*/
}
&stoff);
}
}
}
/*
* Two special symbols are `_init' and `_fini'. If these are supplied
* by crti.o then they are used to represent the total concatenation of
* the `.init' and `.fini' sections.
*
* Determine whether any .init or .fini sections exist. If these
* sections exist and a dynamic object is being built, but no `_init'
* or `_fini' symbols are found, then the user is probably building
* this object directly from ld(1) rather than using a compiler driver
* that provides the symbols via crt's.
*
* If the .init or .fini section exist, and their associated symbols,
* determine the size of the sections and updated the symbols value
* accordingly.
*/
}
}
/*
* Assign .bss information for use with updating COMMON symbols.
*/
/* LINTED */
}
#if defined(_ELF64)
/*
* For amd64 target, assign .lbss information for use
* with updating LCOMMON symbols.
*/
/* LINTED */
}
#endif
/*
* Assign .tlsbss information for use with updating COMMON symbols.
*/
if (ofl->ofl_istlsbss) {
/* LINTED */
}
sizeof (*sorted_syms))) == NULL)
scndx = 0;
/*
* Traverse the internal symbol table updating global symbol information
* and allocating common.
*/
int local;
int restore;
/*
* Ignore any symbols that have been marked as invalid during
* input processing. Providing these aren't used for
* relocation, they will be dropped from the output image.
*/
continue;
}
/*
* Only needed symbols are copied to the output symbol table.
*/
continue;
local = 1;
else
local = 0;
} else {
ssndx++;
}
/*
* Note - expand the COMMON symbols here because an address
* must be assigned to them in the same order that space was
* calculated in sym_validate(). If this ordering isn't
* followed differing alignment requirements can throw us all
* out of whack.
*
* The expanded .bss global symbol is handled here as well.
*
* The actual adding entries into the symbol table still occurs
* below in hashbucket order.
*/
restore = 0;
/*
* An expanded symbol goes to a special .data section
* prepared for that purpose (ofl->ofl_isparexpn).
* Assign COMMON allocations to .bss.
* Otherwise leave it as is.
*/
restore = 1;
restore = 1;
restore = 1;
/*
* TLS symbols are relative to the TLS segment.
*/
}
#if defined(_ELF64)
restore = 1;
#endif
}
if (restore != 0) {
/*
* Make sure this COMMON symbol is returned to the same
* binding as was defined in the original relocatable
* object reference.
*/
bind = STB_GLOBAL;
else
}
}
/*
* If this is a dynamic object then add any local capabilities symbols.
*/
sdp));
&stoff);
/*
* Indicate that this is a capabilities symbol.
* Note, that this identification only provides
* information regarding the symbol that is
* visible from elfdump(1) -y. The association
* of a symbol to its capabilities is derived
* from a .SUNW_capinfo entry.
*/
if (syminfo) {
}
dynsym_ndx++;
}
}
}
if (ofl->ofl_hashbkts) {
(int (*)(const void *, const void *))sym_hash_compare);
}
const char *name;
sectndx = 0;
if (symtab)
enter_in_symtab = 1;
else
enter_in_symtab = 0;
/*
* Assign a got offset if necessary.
*/
if (DBG_ENABLED) {
gottable++;
}
gottable++;
}
}
/*
* If this symbol has been marked as being reduced to local
* scope then it will have to be placed in the scoped portion
* of the .symtab. Retain the appropriate index for use in
* version symbol indexing and relocation.
*/
local = 1;
else
enter_in_symtab = 0;
dynlocal = 1;
}
} else {
}
/*
* Copy basic symbol and string information.
*/
/*
* If we require to record version symbol indexes, update the
* associated version symbol information for all defined
* symbols. If a version definition is required any zero value
* symbol indexes would have been flagged as undefined symbol
* errors, however if we're just scoping these need to fall into
* the base of global symbols.
*/
if ((vndx == 0) &&
if (SYM_IS_HIDDEN(sdp))
else
}
(sap->sa_dverndx > 0) &&
/* Use index of verneed record */
}
}
/*
* If we are creating the .syminfo section then set per symbol
* flags here.
*/
/*
* Identify a copy relocation symbol.
*/
/*
* A reference is bound to a needed dependency.
* Save the syminfo entry, so that when the
* .dynamic section has been updated, a
* DT_NEEDED entry can be associated
* (see update_osyminfo()).
*/
return (0);
/*
* Flag that the symbol has a direct association
* with the external reference (this is an old
* tagging, that has no real effect by itself).
* And flag whether this reference is lazy
* loadable.
*/
/*
* Enable direct symbol bindings if:
*
* - Symbol was identified with the DIRECT
* keyword in a mapfile.
*
* - Symbol reference has been bound to a
* dependency which was specified as
* requiring direct bindings with -zdirect.
*
* - All symbol references are required to
* use direct bindings via -Bdirect.
*/
/*
* If this symbol has been explicitly defined
* as external, and remains unresolved, mark
* it as external.
*/
/*
* If this symbol has been explicitly defined
* to be a reference to a parent object,
* indicate whether a direct binding should be
* established.
*/
/*
* A filter definition. Although this symbol
* can only be a stub, it might be necessary to
* prevent external direct bindings.
*/
/*
* An auxiliary filter definition. By nature,
* this definition is direct, in that should the
* filtee lookup fail, we'll fall back to this
* object. It may still be necessary to
* prevent external direct bindings.
*/
/*
* This definition exists within the object
* being created. Provide a default boundto
* definition, which may be overridden later.
*/
/*
* Indicate whether it is necessary to prevent
* external direct bindings.
*/
}
/*
* Indicate that this symbol is acting as an
* individual interposer.
*/
}
/*
* If external bindings are allowed, indicate
* the binding, and a direct binding if
* necessary.
*/
/*
* Provide a default boundto definition,
* which may be overridden later.
*/
}
/*
* Indicate that this is a capabilities symbol.
* Note, that this identification only provides
* information regarding the symbol that is
* visible from elfdump(1) -y. The association
* of a symbol to its capabilities is derived
* from a .SUNW_capinfo entry.
*/
ofl->ofl_oscapinfo) {
}
}
}
/*
* Note that the `sym' value is reset to be one of the new
* symbol table entries. This symbol will be updated further
* depending on the type of the symbol. Process the .symtab
* first, followed by the .dynsym, thus the `sym' value will
* remain as the .dynsym value when the .dynsym is present.
* This ensures that any versioning symbols st_name value will
* be appropriate for the string table used by version
* entries.
*/
if (enter_in_symtab) {
if (local)
else
}
if (dynlocal) {
/* Add it to sort section if it qualifies */
}
/*
* Provided this isn't an unnamed register symbol,
* update the symbols name and hash value.
*/
if (stoff) {
hashval =
/* LINTED */
_hashndx =
}
} else {
}
}
}
/*
* Add it to sort section if it qualifies.
* The indexes in that section are relative to the
* the adjacent SUNW_ldynsym/dymsym pair, so we
* add the number of items in SUNW_ldynsym to the
* dynsym index.
*/
}
continue;
} else
/*
* If we have a weak data symbol for which we need the real
* symbol also, save this processing until later.
*
* assigned to them. In that case we don't do the post-weak
* processing because the PLT's must be maintained so that we
* can do 'interpositioning' on both of the symbols.
*/
if ((sap->sa_linkndx) &&
if (enter_in_symtab) {
if (local) {
scopesym_ndx++;
} else {
&symtab[symtab_ndx];
symtab_ndx++;
}
} else {
}
if (dynsym) {
if (!local) {
&dynsym[dynsym_ndx];
dynsym_ndx++;
} else if (dynlocal) {
}
} else {
}
continue;
}
}
/*
* assign new symbol value.
*/
}
/*
* Undefined weak global, if we are generating a static
* executable, output as an absolute zero. Otherwise
* leave it as is, ld.so.1 will skip symbols of this
* type (this technique allows applications and
* libraries to test for the existence of a symbol as an
* indication of the presence or absence of certain
* functionality).
*/
if (OFL_IS_STATIC_EXEC(ofl) &&
}
(sectndx == SHN_COMMON)) {
/* COMMONs have already been processed */
/* EMPTY */
;
} else {
/* LINTED */
/*
* This is (or was) a COMMON symbol which was
* processed above - no processing
* required here.
*/
;
/*
* Make sure this undefined symbol is returned
* to the same binding as was defined in the
* original relocatable object reference.
*/
bind = STB_GLOBAL;
else
/* LINTED */
/*
* In an executable, the new symbol value is the
* old value (offset into defining section) plus
* virtual address of defining section. In a
* relocatable, the new value is the old value
* plus the displacement of the section within
* the file.
*/
/* LINTED */
if (!(flags & FLG_OF_RELOBJ)) {
/*
* TLS symbols are relative to
* the TLS segment.
*/
}
}
}
if (spec) {
switch (spec) {
case SDAUX_ID_ETEXT:
if (etext_abs)
else
break;
case SDAUX_ID_EDATA:
if (edata_abs)
else
break;
case SDAUX_ID_END:
if (end_abs)
else
break;
case SDAUX_ID_START:
break;
case SDAUX_ID_DYN:
if (flags & FLG_OF_DYNAMIC) {
/* LINTED */
}
break;
case SDAUX_ID_PLT:
/* LINTED */
}
break;
case SDAUX_ID_GOT:
/*
* Symbol bias for negative growing tables is
* stored in symbol's value during
* allocate_got().
*/
/* LINTED */
break;
default:
/* NOTHING */
;
}
}
/*
* If a plt index has been assigned to an undefined function,
* update the symbols value to the appropriate .plt address.
*/
!(flags & FLG_OF_BFLAG)) {
}
/*
* Finish updating the symbols.
*/
/*
* Sym Update: if scoped local - set local binding
*/
if (local)
/*
* Sym Updated: If both the .symtab and .dynsym
* are present then we've actually updated the information in
* the .dynsym, therefore copy this same information to the
* .symtab entry.
*/
}
if (enter_in_symtab) {
if (local)
_symndx = scopesym_ndx++;
else
_symndx = symtab_ndx++;
(sectndx >= SHN_LORESERVE)) {
} else {
/* LINTED */
}
}
/*
* dynsym and ldynsym are distinct tables, so
* we use indirection to access the right one
* and the related extended section index array.
*/
if (!local) {
_symndx = dynsym_ndx++;
} else {
_symndx = ldynscopesym_ndx++;
}
(sectndx >= SHN_LORESERVE)) {
} else {
/* LINTED */
}
}
}
/*
* Now that all the symbols have been processed update any weak symbols
* information (ie. copy all information except `st_name'). As both
* symbols will be represented in the output, return the weak symbol to
* its correct type.
*/
/*
* If the symbol definition has been scoped then assign it to
* be local, otherwise if it's from a shared object then we need
* to maintain the binding of the original reference.
*/
if (SYM_IS_HIDDEN(sdp)) {
if (flags & FLG_OF_PROCRED)
else
bind = STB_GLOBAL;
else
}
}
}
/*
* Now display GOT debugging information if required.
*/
/*
* Update the section headers information. sh_info is
* supposed to contain the offset at which the first
* global symbol resides in the symbol table, while
* sh_link contains the section index of the associated
* string table.
*/
if (symtab) {
/* LINTED */
if (symshndx)
/*
* Ensure that the expected number of symbols
* were entered into the right spots:
* - Scoped symbols in the right range
* - Globals start at the right spot
* (correct number of locals entered)
* - The table is exactly filled
* (correct number of globals entered)
*/
}
if (dynsym) {
/* LINTED */
/* LINTED */
if (dynshndx) {
}
}
if (ldynsym) {
/* ldynsym has no globals, so give index one past the end */
/*
* The ldynsym and dynsym must be adjacent. The
* idea is that rtld should be able to start with
* the ldynsym and march straight through the end
* of dynsym, seeing them as a single symbol table,
* despite the fact that they are in distinct sections.
* Ensure that this happened correctly.
*
* Note that I use ldynsym_ndx here instead of the
* computation I used to set the section size
* (found in ldynsym_cnt). The two will agree, unless
* we somehow miscounted symbols or failed to insert them
* all. Using ldynsym_ndx here catches that error in
* addition to checking for adjacency.
*/
/* LINTED */
if (ldynshndx) {
}
/*
* The presence of .SUNW_ldynsym means that there may be
* associated sort sections, one for regular symbols
* and the other for TLS. Each sort section needs the
* following done:
* - Section header link references .SUNW_ldynsym
* - Should have received the expected # of items
* - Sorted by increasing address
*/
if (dynsymsort_ndx > 1) {
sizeof (*dynsymsort), dynsort_compare);
}
}
if (dyntlssort_ndx > 1) {
sizeof (*dyntlssort), dynsort_compare);
}
}
}
/*
* Used by ld.so.1 only.
*/
return (etext);
}
/*
* Build the dynamic section.
*
* This routine must be maintained in parallel with make_dynamic()
* in sections.c
*/
static int
{
/*
* Relocatable objects can be built with -r and -dy to trigger the
* creation of a .dynamic section. This model is used to create kernel
* device drivers. The .dynamic section provides a subset of userland
* .dynamic entries, typically entries such as DT_NEEDED and DT_RUNPATH.
*
* Within a dynamic object, any .dynamic string references are to the
* .dynstr table. Within a relocatable object, these strings can reside
* within the .strtab.
*/
if (OFL_IS_STATIC_OBJ(ofl)) {
} else {
}
/* LINTED */
continue;
/*
* Create and set up the DT_POSFLAG_1 entry here if required.
*/
dyn++;
}
else
continue;
/* LINTED */
sizeof (Dyn));
dyn++;
}
if (not_relobj) {
else
&stoff);
dyn++;
}
}
dyn++;
}
dyn++;
}
if (ofl->ofl_soname) {
dyn++;
}
if (ofl->ofl_filtees) {
if (flags & FLG_OF_AUX) {
} else {
}
dyn++;
}
}
dyn++;
dyn++;
}
if (not_relobj) {
if (ofl->ofl_config) {
dyn++;
}
if (ofl->ofl_depaudit) {
dyn++;
}
dyn++;
}
dyn++;
dyn++;
dyn++;
/*
* Note, the shdr is set and used in the ofl->ofl_osldynsym case
* that follows.
*/
dyn++;
dyn++;
if (ofl->ofl_osldynsym) {
/*
* We have arranged for the .SUNW_ldynsym data to be
* immediately in front of the .dynsym data.
* This means that you could start at the top
* of .SUNW_ldynsym and see the data for both tables
* without a break. This is the view we want to
* provide for DT_SUNW_SYMTAB, which is why we
* add the lengths together.
*/
dyn++;
dyn++;
}
dyn++;
}
if (ofl->ofl_osdynsymsort) {
dyn++;
dyn++;
}
if (ofl->ofl_osdyntlssort) {
dyn++;
dyn++;
}
/*
* Reserve the DT_CHECKSUM entry. Its value will be filled in
* after the complete image is built.
*/
dyn++;
/*
* Versioning sections: DT_VERDEF and DT_VERNEED.
*
* The Solaris ld does not produce DT_VERSYM, but the GNU ld
* does, in order to support their style of versioning, which
* differs from ours:
*
* - The top bit of the 16-bit Versym index is
* not part of the version, but is interpreted
* as a "hidden bit".
*
* - External (SHN_UNDEF) symbols can have non-zero
* Versym values, which specify versions in
* referenced objects, via the Verneed section.
*
* - The vna_other field of the Vernaux structures
* found in the Verneed section are not zero as
* with Solaris, but instead contain the version
* index to be used by Versym indices to reference
* the given external version.
*
* The Solaris ld, rtld, and elfdump programs all interpret the
* presence of DT_VERSYM as meaning that GNU versioning rules
* apply to the given file. If DT_VERSYM is not present,
* then Solaris versioning rules apply. If we should ever need
* to change our ld so that it does issue DT_VERSYM, then
* this rule for detecting GNU versioning will no longer work.
* In that case, we will have to invent a way to explicitly
* specify the style of versioning in use, perhaps via a
* new dynamic entry named something like DT_SUNW_VERSIONSTYLE,
* where the d_un.d_val value specifies which style is to be
* used.
*/
dyn++;
dyn++;
}
dyn++;
dyn++;
}
dyn++;
}
if (flags & FLG_OF_TEXTREL) {
/*
* Only the presence of this entry is used in this
* implementation, not the value stored.
*/
dyn++;
}
if (ofl->ofl_osfiniarray) {
dyn++;
dyn++;
}
if (ofl->ofl_osinitarray) {
dyn++;
dyn++;
}
if (ofl->ofl_ospreinitarray) {
dyn++;
dyn++;
}
if (ofl->ofl_pltcnt) {
dyn++;
dyn++;
dyn++;
}
if (ofl->ofl_pltpad) {
if (ofl->ofl_pltcnt) {
} else
dyn++;
dyn++;
}
if (ofl->ofl_relocsz) {
dyn++;
dyn++;
else
dyn++;
}
if (ofl->ofl_ossyminfo) {
dyn++;
dyn++;
dyn++;
}
if (ofl->ofl_osmove) {
dyn++;
dyn++;
dyn++;
}
if (ofl->ofl_regsymcnt) {
int ndx;
continue;
dyn++;
}
}
dyn++;
}
if (ofl->ofl_osinterp) {
dyn++;
}
if (ofl->ofl_osmove)
else
dyn++;
}
dyn++;
}
if (ofl->ofl_oscapinfo) {
dyn++;
}
if (ofl->ofl_oscapchain) {
dyn++;
dyn++;
dyn++;
}
if (flags & FLG_OF_SYMBOLIC) {
dyn++;
}
}
dyn++;
/*
* If -Bdirect was specified, but some NODIRECT symbols were specified
* via a mapfile, or -znodirect was used on the command line, then
* clear the DF_1_DIRECT flag. The resultant object will use per-symbol
* direct bindings rather than be enabled for global direct bindings.
*
* If any no-direct bindings exist within this object, set the
* DF_1_NODIRECT flag. ld(1) recognizes this flag when processing
* dependencies, and performs extra work to ensure that no direct
* bindings are established to the no-direct symbols that exist
* within these dependencies.
*/
dyn++;
dyn++;
dyn++;
}
/*
* Ensure that we wrote the right number of entries. If not, we either
* miscounted in make_dynamic(), or we did something wrong in this
* function.
*/
return (1);
}
/*
* Build the version definition section
*/
static int
{
int num = 0;
/*
* Determine which string table to use.
*/
if (OFL_IS_STATIC_OBJ(ofl)) {
} else {
}
/*
* Traverse the version descriptors and update the version structures
* to point to the dynstr name in preparation for building the version
* section structure.
*/
/*
* Create a new string table entry to represent the base
* version name (there is no corresponding symbol for
* this).
*/
/* LINTED */
} else {
/* LINTED */
}
}
/*
* Traverse the version descriptors and update the version section to
* reflect each version and its associated dependencies.
*/
/* LINTED */
vdap++;
/* LINTED */
/*
* Traverse this versions dependency list generating the
* appropriate version dependency entries.
*/
/* LINTED */
/* LINTED */
}
/*
* Record the versions auxiliary array offset and the associated
* dependency count.
*/
/* LINTED */
/*
* Record the next versions offset and update the version
* pointer. Remember the previous version offset as the very
* last structures next pointer should be null.
*/
/* LINTED */
}
/*
* Record the string table association with the version definition
* section, and the symbol table associated with the version symbol
* table (the actual contents of the version symbol table are filled
* in during symbol update).
*/
/* LINTED */
/*
* The version definition sections `info' field is used to indicate the
* number of entries in this section.
*/
return (1);
}
/*
* Finish the version symbol index section
*/
static void
{
/*
* Record the symbol table associated with the version symbol table.
* The contents of the version symbol table are filled in during
* symbol update.
*/
if (OFL_IS_STATIC_OBJ(ofl))
else
/* LINTED */
}
/*
* Build the version needed section
*/
static int
{
/*
* Determine which string table is appropriate.
*/
if (OFL_IS_STATIC_OBJ(ofl)) {
} else {
}
/*
* Traverse the shared object list looking for dependencies that have
* versions defined within them.
*/
continue;
/*
* Traverse the version index list recording
* each version as a needed dependency.
*/
&stoff);
} else {
}
/*
* If version A inherits version B, then
* B is implicit in A. It suffices for ld.so.1
* to verify A at runtime and skip B. The
* version normalization process sets the INFO
* flag for the versions we want ld.so.1 to
* skip.
*/
/* LINTED */
}
}
/*
* Record the versions auxiliary array offset and
* the associated dependency count.
*/
/* LINTED */
/* LINTED */
/*
* Record the next versions offset and update the version
* pointer. Remember the previous version offset as the very
* last structures next pointer should be null.
*/
/* LINTED */
}
/*
* Use sh_link to record the associated string table section, and
* sh_info to indicate the number of entries contained in the section.
*/
/* LINTED */
return (1);
}
/*
* Update syminfo section.
*/
static uintptr_t
{
char *strtab;
} else {
}
/* LINTED */
if (ofl->ofl_osdynamic)
/* LINTED */
/*
* Update any references with the index into the dynamic table.
*/
/*
* Update any filtee references with the index into the dynamic table.
*/
}
/*
* Display debugging information about section.
*/
if (DBG_ENABLED) {
if (ofl->ofl_osdynamic)
else
/* LINTED */
}
}
return (1);
}
/*
* Build the output elf header.
*/
static uintptr_t
{
/*
* If an entry point symbol has already been established (refer
* sym_validate()) simply update the elf header entry point with the
* symbols value. If no entry point is defined it will have been filled
* with the start address of the first section within the text segment
* (refer update_outfile()).
*/
/*
* Note. it may be necessary to update the `e_flags' field in the
* machine dependent section.
*/
return (S_ERROR);
return (S_ERROR);
}
else
return (1);
}
/*
* Perform move table expansion.
*/
static void
{
/* LINTED */
/*
* Update the target address based upon the move entry size.
* This size was validated in ld_process_move().
*/
/* LINTED */
case 1:
/* LINTED */
break;
case 2:
/* LINTED */
break;
case 4:
/* LINTED */
break;
case 8:
/* LINTED */
break;
}
}
}
/*
* Update Move sections.
*/
static void
{
/*
* Determine the index of the symbol table that will be referenced by
* the Move section.
*/
if (OFL_ALLOW_DYNSYM(ofl))
/* LINTED */
/* LINTED */
/*
* Update sh_link of the Move section, and point to the new Move data.
*/
if (ofl->ofl_osmove) {
}
/*
* Update symbol entry index
*/
/*
* Expand move table
*/
const char *str;
if (flags & FLG_OF_STATIC)
else
}
continue;
}
/*
* Process move table
*/
int idx = 1;
if ((flags & FLG_OF_RELOBJ) == 0) {
/* LINTED */
STT_SECTION) {
}
} else {
/* LINTED */
}
} else {
/* LINTED */
} else {
if (isredloc)
/* LINTED */
}
}
omvp++;
idx++;
}
}
}
/*
* fields as well as the section contents.
*/
static uintptr_t
{
/*
* Since input GROUP sections always create unique
* output GROUP sections - we know there is only one
* item on the list.
*/
/*
* Scan through the group data section and update
* all of the links to new values.
*/
for (i = 1; i < grpcnt; i++) {
/*
* If the referenced section didn't make it to the
* output file - just zero out the entry.
*/
gdata[i] = 0;
else
}
}
return (error);
}
static void
{
return;
/* If leaving an extra hole at the end, zero it */
if (extra > 0)
0x0, extra);
}
/*
* Update capabilities information.
*
* If string table capabilities exist, then the associated string must be
* translated into an offset into the string table.
*/
static void
{
/*
* Determine which symbol table or string table is appropriate.
*/
if (OFL_IS_STATIC_OBJ(ofl)) {
} else {
}
/*
* If symbol capabilities exist, set the sh_link field of the .SUNW_cap
* section to the .SUNW_capinfo section.
*/
if (ofl->ofl_oscapinfo) {
}
/*
* If there are capability strings to process, set the sh_info
* field of the .SUNW_cap section to the associated string table, and
* proceed to process any CA_SUNW_PLAT entries.
*/
return;
/*
* Determine whether an object capability identifier, or object
*/
}
}
}
/*
* capabilities.
*/
if (ofl->ofl_capgroups) {
&stoff);
}
capstr)) {
&stoff);
}
capstr)) {
&stoff);
}
}
}
}
/*
* Update the .SUNW_capinfo, and possibly the .SUNW_capchain sections.
*/
static void
{
/*
* Determine which symbol table is appropriate.
*/
if (OFL_IS_STATIC_OBJ(ofl))
else
/*
* Update the .SUNW_capinfo sh_link to point to the appropriate symbol
* table section. If we're creating a dynamic object, the
* .SUNW_capinfo sh_info is updated to point to the .SUNW_capchain
* section.
*/
if (OFL_IS_STATIC_OBJ(ofl) == 0) {
}
/*
* Establish the data for each section. The first element of each
* section defines the section's version number.
*/
ocapinfo[0] = CAPINFO_CURRENT;
if (ccosp) {
}
/*
* Traverse all capabilities families. Each member has a .SUNW_capinfo
* assignment. The .SUNW_capinfo entry differs for relocatable objects
* and dynamic objects.
*
* Relocatable objects:
* ELF_C_GROUP ELF_C_SYM
*
* Family lead: CAPINFO_SUNW_GLOB lead symbol index
* Family lead alias: CAPINFO_SUNW_GLOB lead symbol index
* Family member: .SUNW_cap index lead symbol index
*
* Dynamic objects:
* ELF_C_GROUP ELF_C_SYM
*
* Family lead: CAPINFO_SUNW_GLOB .SUNW_capchain index
* Family lead alias: CAPINFO_SUNW_GLOB .SUNW_capchain index
* Family member: .SUNW_cap index lead symbol index
*
* The ELF_C_GROUP field identifies a capabilities symbol. Lead
* capability symbols, and lead capability aliases are identified by
* a CAPINFO_SUNW_GLOB group identifier. For family members, the
* ELF_C_GROUP provides an index to the associate capabilities group
* (i.e, an index into the SUNW_cap section that defines a group).
*
* For relocatable objects, the ELF_C_SYM field identifies the lead
* capability symbol. For the lead symbol itself, the .SUNW_capinfo
* index is the same as the ELF_C_SYM value. For lead alias symbols,
* the .SUNW_capinfo index differs from the ELF_C_SYM value. This
* differentiation of CAPINFO_SUNW_GLOB symbols allows ld(1) to
* identify, and propagate lead alias symbols. For example, the lead
* capability symbol memcpy() would have the ELF_C_SYM for memcpy(),
* and the lead alias _memcpy() would also have the ELF_C_SYM for
* memcpy().
*
* For dynamic objects, both a lead capability symbol, and alias symbol
* would have a ELF_C_SYM value that represents the same capability
* chain index. The capability chain allows ld.so.1 to traverse a
* family chain for a given lead symbol, and select the most appropriate
* family member. The .SUNW_capchain array contains a series of symbol
* indexes for each family member:
*
* chaincap[n] chaincap[n + 1] chaincap[n + 2] chaincap[n + x]
* foo() ndx foo%x() ndx foo%y() ndx 0
*
* For family members, the ELF_C_SYM value associates the capability
* members with their family lead symbol. This association, although
* unused within a dynamic object, allows ld(1) to identify, and
* propagate family members when processing relocatable objects.
*/
if (ccosp) {
/*
* For a dynamic object, identify this lead symbol, and
* point it to the head of a capability chain. Set the
* head of the capability chain to the same lead symbol.
*/
} else {
/*
* For a relocatable object, identify this lead symbol,
* and set the lead symbol index to itself.
*/
}
/*
* Gather any lead symbol aliases.
*/
if (ccosp) {
/*
* For a dynamic object, identify this lead
* alias symbol, and point it to the same
* capability chain index as the lead symbol.
*/
} else {
/*
* For a relocatable object, identify this lead
* alias symbol, and set the lead symbol index
* to the lead symbol.
*/
}
}
chainndx++;
/*
* Gather the family members.
*/
/*
* Identify the members capability group, and the lead
* symbol of the family this symbol is a member of.
*/
if (ccosp) {
/*
* For a dynamic object, set the next capability
* chain to point to this family member.
*/
}
}
/*
* Any chain of family members is terminated with a 0 element.
*/
if (ccosp)
}
}
/*
* Translate the shdr->sh_{link, info} from its input section value to that
* of the corresponding shdr->sh_{link, info} output section value.
*/
static Word
{
/*
* Don't translate the special section numbers.
*/
if (link >= SHN_LORESERVE)
return (link);
/*
* Does this output section translate back to an input file. If not
* then there is no translation to do. In this case we will assume that
* if sh_link has a value, it's the right value.
*/
return (link);
/*
* Sanity check to make sure that the sh_{link, info} value
* is within range for the input file.
*/
return (link);
}
/*
* Follow the link to the input section.
*/
return (0);
return (0);
/* LINTED */
}
/*
* Having created all of the necessary sections, segments, and associated
* headers, fill in the program headers and update any other data in the
* output image. Some general rules:
*
* - If an interpreter is required always generate a PT_PHDR entry as
* well. It is this entry that triggers the kernel into passing the
* interpreter an aux vector instead of just a file descriptor.
*
* - When generating an image that will be interpreted (ie. a dynamic
* executable, a shared object, or a static executable that has been
* provided with an interpreter - weird, but possible), make the initial
* loadable segment include both the ehdr and phdr[]. Both of these
* tables are used by the interpreter therefore it seems more intuitive
* to explicitly defined them as part of the mapped image rather than
* relying on page rounding by the interpreter to allow their access.
*
* - When generating a static image that does not require an interpreter
* have the first loadable segment indicate the address of the first
* expect this behavior).
*/
{
/*
* Initialize the starting address for the first segment. Executables
* have different starting addresses depending upon the target ABI,
* where as shared objects have a starting address of 0. If this is
* a 64-bit executable that is being constructed to run in a restricted
* address space, use an alternative origin that will provide more free
* address space for the the eventual process.
*/
#if defined(_ELF64)
else
#endif
} else
vaddr = 0;
/*
* Loop through the segment descriptors and pick out what we need.
*/
segndx++;
/*
* If an interpreter is required generate a PT_INTERP and
* PT_PHDR program header entry. The PT_PHDR entry describes
* the program header table itself. This information will be
* passed via the aux vector to the interpreter (ld.so.1).
* The program header array is actually part of the first
* loadable segment (and the PT_PHDR entry is the first entry),
* therefore its virtual address isn't known until the first
* loadable segment is processed.
*/
if (ofl->ofl_osinterp) {
}
continue;
}
if (ofl->ofl_osinterp) {
}
continue;
}
/*
* If we are creating a PT_SUNWDTRACE segment, remember where
* the program header is. The header values are assigned after
* update_osym() has completed and the symbol table addresses
* have been updated.
*/
if (ofl->ofl_dtracesym &&
((flags & FLG_OF_RELOBJ) == 0)) {
dtracesndx = segndx;
dtracepndx = phdrndx++;
}
continue;
}
/*
* generate the PT_SUNWCAP header. Note, as this comes before
* the first loadable segment, we don't yet know its real
* virtual address. This is updated later.
*/
((flags & FLG_OF_RELOBJ) == 0)) {
}
continue;
}
/*
* As the dynamic program header occurs after the loadable
* headers in the segment descriptor table, all the address
* information for the .dynamic output section will have been
* figured out by now.
*/
if (OFL_ALLOW_DYNSYM(ofl)) {
}
continue;
}
/*
* As the unwind (.eh_frame_hdr) program header occurs after
* the loadable headers in the segment descriptor table, all
* the address information for the .eh_frame output section
* will have been figured out by now.
*/
continue;
continue;
}
/*
* The sunwstack program is used to convey non-default
* flags for the process stack. Only emit it if it would
* change the default.
*/
if (((flags & FLG_OF_RELOBJ) == 0) &&
continue;
}
/*
* As the TLS program header occurs after the loadable
* headers in the segment descriptor table, all the address
* information for the .tls output section will have been
* figured out by now.
*/
continue;
/*
* Scan the output sections that have contributed TLS.
* Remember the first and last so as to determine the
* TLS memory size requirement. Remember the last
* progbits section to determine the TLS data
* contribution, which determines the TLS program
* header filesz.
*/
}
/*
* Determine the initialized TLS data size. This
* address range is from the start of the TLS segment
* to the end of the last piece of initialized data.
*/
if (lastfileshdr)
else
/*
* Determine the total TLS memory size. This includes
* all TLS data and TLS uninitialized data. This
* address range is from the start of the TLS segment
* to the memory address of the last piece of
* uninitialized data.
*/
continue;
}
/*
* If this is an empty segment declaration, it will occur after
* all other loadable segments. As empty segments can be
* defined with fixed addresses, make sure that no loadable
* segments overlap. This might occur as the object evolves
* and the loadable segments grow, thus encroaching upon an
* existing segment reservation.
*
* Segments are only created for dynamic objects, thus this
* checking can be skipped when building a relocatable object.
*/
if (!(flags & FLG_OF_RELOBJ) &&
int i;
continue;
/*
* Check overlaps
*/
for (i = 0; i < phdrndx - 1; i++) {
continue;
}
continue;
}
/*
* Having processed any of the special program headers any
* remaining headers will be built to express individual
* segments. Segments are only built if they have output
* section descriptors associated with them (ie. some form of
* input section has been matched to this segment).
*/
continue;
/*
* Determine the segments offset and size from the section
* information provided from elf_update().
* Allow for multiple NOBITS sections.
*/
p_align = 0;
if (nobits) {
return (S_ERROR);
}
}
/*
* If this is the first loadable segment of a dynamic object,
* or an interpreter has been specified (a static object built
* with an interpreter will still be given a PT_HDR entry), then
* compensate for the elf header and program header array. Both
* of these are actually part of the loadable segment as they
* may be inspected by the interpreter. Adjust the segments
* size and offset accordingly.
*/
}
/*
* If segment size symbols are required (specified via a
* mapfile) update their value.
*/
/*
* If no file content has been assigned to this segment (it
* only contains no-bits sections), then reset the offset for
* consistency.
*/
/*
* If a virtual address has been specified for this segment
* from a mapfile use it and make sure the previous segment
* does not run into this segment.
*/
} else {
}
}
/*
* Adjust the address offset and p_align if needed.
*/
else
}
/*
* If an interpreter is required set the virtual address of the
* PT_PHDR program header now that we know the virtual address
* of the loadable segment that contains it. Update the
* PT_SUNWCAP header similarly.
*/
if (ofl->ofl_osinterp)
/*
* Finally, if we're creating a dynamic object
* (or a static object in which an interpreter
* is specified) update the vaddr to reflect
* the address of the first section within this
* segment.
*/
if ((ofl->ofl_osinterp) ||
(flags & FLG_OF_DYNAMIC))
} else {
/*
* If the DF_1_NOHDR flag was set, and an
* interpreter is being generated, the PT_PHDR
* will not be part of any loadable segment.
*/
if (ofl->ofl_osinterp) {
}
}
}
/*
* Ensure the ELF entry point defaults to zero. Typically, this
* value is overridden in update_oehdr() to one of the standard
* entry points. Historically, this default was set to the
* address of first executable section, but this has since been
* found to be more confusing than it is helpful.
*/
/*
* Traverse the output section descriptors for this segment so
* that we can update the section headers addresses. We've
* calculated the virtual address of the initial section within
* this segment, so each successive section can be calculated
* based on their offsets from each other.
*/
secndx = 0;
hshdr = 0;
if (!(flags & FLG_OF_RELOBJ) &&
if (hshdr)
}
secndx++;
}
/*
* Establish the virtual address of the end of the last section
* in this segment so that the next segments offset can be
* calculated from this.
*/
if (hshdr)
/*
* Output sections for this segment complete. Adjust the
* virtual offset for the last sections size, and make sure we
* haven't exceeded any maximum segment length specification.
*/
return (S_ERROR);
}
}
}
/*
* Update any new output sections. When building the initial output
* image, a number of sections were created but left uninitialized (eg.
* .dynsym, .dynstr, .symtab, .symtab, etc.). Here we update these
* sections with the appropriate data. Other sections may still be
* modified via reloc_process().
*
* Copy the interpreter name into the .interp section.
*/
if (ofl->ofl_interp)
ofl->ofl_interp);
/*
* Update the .shstrtab, .strtab and .dynstr sections.
*/
/*
* Build any output symbol tables, the symbols information is copied
* and updated into the new output image.
*/
return (S_ERROR);
/*
* If we have an PT_INTERP phdr, update it now from the associated
* section information.
*/
if (intpsgp) {
}
/*
* If we have a PT_SUNWDTRACE phdr, update it now with the address of
* the symbol. It's only now been updated via update_sym().
*/
if (dtracesgp) {
/*
* Take permissions from the segment that the symbol is
* associated with.
*/
}
/*
* If we have a PT_SUNWCAP phdr, update it now from the associated
* section information.
*/
if (capsgp) {
}
/*
* Update the GROUP sections.
*/
return (S_ERROR);
/*
* Update Move Table.
*/
/*
* Build any output headers, version information, dynamic structure and
* syminfo structure.
*/
return (S_ERROR);
if (!(flags & FLG_OF_NOVERSEC)) {
if ((flags & FLG_OF_VERDEF) &&
return (S_ERROR);
if ((flags & FLG_OF_VERNEED) &&
return (S_ERROR);
}
if (flags & FLG_OF_DYNAMIC) {
return (S_ERROR);
}
if (ofl->ofl_ossyminfo) {
return (S_ERROR);
}
/*
* Update capabilities information if required.
*/
if (ofl->ofl_oscapinfo)
/*
* Sanity test: the first and last data byte of a string table
* must be NULL.
*/
'\0'));
/*
* Emit Strtab diagnostics.
*/
ofl->ofl_shdrsttab));
ofl->ofl_strtab));
ofl->ofl_dynstrtab));
/*
* Initialize the section headers string table index within the elf
* header.
*/
/* LINTED */
/* LINTED */
} else {
/*
* If the STRTAB section index doesn't fit into
* e_shstrndx, then we store it in 'shdr[0].st_link'.
*/
return (S_ERROR);
}
return (S_ERROR);
}
}
}