freebsd-skq/gnu/usr.bin/ld/rrs.c
Nate Williams c79eac4c4c Weak symbol support from NetBSD. This should bring us in sync with the
NetBSD ld code except for local changes for dlopen() and friends and
the hashing on the minor value of the shlibs.  We should be binary
compatible now with all their libraries.

Obtained from: NetBSD
1995-03-04 17:49:20 +00:00

1247 lines
32 KiB
C

/*
* Copyright (c) 1993 Paul Kranenburg
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Paul Kranenburg.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: rrs.c,v 1.13 1994/12/23 22:30:48 nate Exp $
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <fcntl.h>
#include <ar.h>
#include <ranlib.h>
#include <a.out.h>
#include <stab.h>
#include <string.h>
#include "ld.h"
static struct _dynamic rrs_dyn; /* defined in link.h */
static struct so_debug rrs_so_debug; /* defined in link.h */
static struct section_dispatch_table rrs_sdt; /* defined in link.h */
static got_t *rrs_got;
static jmpslot_t *rrs_plt; /* defined in md.h */
static struct relocation_info *rrs_reloc;
static struct nzlist *rrs_symbols; /* RRS symbol table */
static char *rrs_strtab; /* RRS strings */
static struct rrs_hash *rrs_hashtab; /* RT hash table */
static struct shobj *rrs_shobjs;
static int reserved_rrs_relocs;
static int claimed_rrs_relocs;
static int discarded_rrs_relocs;
static int number_of_gotslots = 1;
static int number_of_jmpslots = 1;
static int number_of_rrs_hash_entries;
static int number_of_rrs_symbols;
static int rrs_strtab_size;
static int rrs_symbol_size;
static int current_jmpslot_offset;
static int current_got_offset;
static int got_origin;
static int current_reloc_offset;
static int current_hash_index;
int number_of_shobjs;
/* Convert a GOT offset into a table entry */
#define GOTP(off) ((got_t *)((long)rrs_got + got_origin + (off)))
struct shobj {
struct shobj *next;
struct file_entry *entry;
};
/*
RRS text segment:
+-------------------+ <-- sdt_rel (rrs_text_start)
| |
| relocation |
| |
+-------------------+ <-- <sdt>.sdt_hash
| |
| hash buckets |
| |
+-------------------+ <-- <sdt>.sdt_nzlist
| |
| symbols |
| |
+-------------------+ <-- <sdt>.sdt_strings
| |
| strings |
| |
+-------------------+ <-- <sdt>.sdt_sods
| |
| shobjs |
| |
+-------------------+
| |
| shobjs strings | <-- <shobj>.sod_name
| |
+-------------------+
RRS data segment:
+-------------------+ <-- __DYNAMIC (rrs_data_start)
| |
| _dymamic |
| |
+-------------------+ <-- __DYNAMIC.d_debug
| |
| so_debug |
| |
+-------------------+ <-- __DYNAMIC.d_un.d_sdt
| |
| sdt |
| |
+-------------------+ <-- sdt_got
| |
| _GOT_ | <-- _GLOBAL_OFFSET_TABLE_
| | ( == sdt_got + got_origin)
| |
+-------------------+ <-- sdt_plt
| |
| PLT |
| |
+-------------------+
*/
/*
* Add NAME to the list of needed run-time objects.
* Return 1 if ENTRY was added to the list.
*/
int
rrs_add_shobj(entry)
struct file_entry *entry;
{
struct shobj **p;
for (p = &rrs_shobjs; *p != NULL; p = &(*p)->next)
if (strcmp((*p)->entry->filename, entry->filename) == 0)
return 0;
*p = (struct shobj *)xmalloc(sizeof(struct shobj));
(*p)->next = NULL;
(*p)->entry = entry;
number_of_shobjs++;
return 1;
}
void
alloc_rrs_reloc(entry, sp)
struct file_entry *entry;
symbol *sp;
{
#ifdef DEBUG
printf("alloc_rrs_reloc: %s in %s\n", sp->name, get_file_name(entry));
#endif
reserved_rrs_relocs++;
}
void
alloc_rrs_segment_reloc(entry, r)
struct file_entry *entry;
struct relocation_info *r;
{
#ifdef DEBUG
printf("alloc_rrs_segment_reloc at %#x in %s\n",
r->r_address, get_file_name(entry));
#endif
reserved_rrs_relocs++;
}
void
alloc_rrs_jmpslot(entry, sp)
struct file_entry *entry;
symbol *sp;
{
if (sp->flags & GS_HASJMPSLOT)
return;
sp->flags |= GS_HASJMPSLOT;
number_of_jmpslots++;
reserved_rrs_relocs++;
}
void
alloc_rrs_gotslot(entry, r, lsp)
struct file_entry *entry;
struct relocation_info *r;
struct localsymbol *lsp;
{
symbol *sp = lsp->symbol;
if (!RELOC_EXTERN_P(r)) {
if (sp != NULL) {
warnx("%s: relocation for internal symbol "
"expected at %#x",
get_file_name(entry), RELOC_ADDRESS(r));
return;
}
if (!RELOC_STATICS_THROUGH_GOT_P(r))
/* No need for a GOT slot */
return;
if (lsp->flags & LS_HASGOTSLOT)
return;
lsp->flags |= LS_HASGOTSLOT;
} else {
if (sp == NULL) {
warnx("%s: relocation must refer "
"to global symbol at %#x",
get_file_name(entry), RELOC_ADDRESS(r));
return;
}
if (sp->alias)
sp = sp->alias;
if (sp->flags & GS_HASGOTSLOT)
return;
sp->flags |= GS_HASGOTSLOT;
}
number_of_gotslots++;
reserved_rrs_relocs++;
}
void
alloc_rrs_cpy_reloc(entry, sp)
struct file_entry *entry;
symbol *sp;
{
if (sp->flags & GS_CPYRELOCRESERVED)
return;
#ifdef DEBUG
printf("alloc_rrs_copy: %s in %s\n", sp->name, get_file_name(entry));
#endif
sp->flags |= GS_CPYRELOCRESERVED;
reserved_rrs_relocs++;
}
static struct relocation_info *
rrs_next_reloc()
{
struct relocation_info *r;
r = rrs_reloc + claimed_rrs_relocs++;
if (claimed_rrs_relocs > reserved_rrs_relocs)
errx(1, "internal error: RRS relocs exceed allocation %d",
reserved_rrs_relocs);
return r;
}
/*
* Claim a RRS relocation as a result of a regular (ie. non-PIC)
* relocation record in a rel file.
*
* Return 1 if the output file needs no further updating.
* Return 0 if the relocation value pointed to by RELOCATION must
* written to a.out.
*/
int
claim_rrs_reloc(entry, rp, sp, relocation)
struct file_entry *entry;
struct relocation_info *rp;
symbol *sp;
long *relocation;
{
struct relocation_info *r = rrs_next_reloc();
if (rp->r_address < text_start + text_size)
warnx("%s: RRS text relocation at %#x for \"%s\"",
get_file_name(entry), rp->r_address, sp->name);
#ifdef DEBUG
printf("claim_rrs_reloc: %s in %s\n", sp->name, get_file_name(entry));
#endif
r->r_address = rp->r_address;
r->r_symbolnum = sp->rrs_symbolnum;
if (link_mode & SYMBOLIC) {
if (!sp->defined)
warnx("Cannot reduce symbol \"%s\" in %s",
sp->name, get_file_name(entry));
RELOC_EXTERN_P(r) = 0;
*relocation += sp->value;
(void) md_make_reloc(rp, r, RELTYPE_RELATIVE);
return 0;
} else {
RELOC_EXTERN_P(r) = 1;
return md_make_reloc(rp, r, RELTYPE_EXTERN);
}
}
/*
* Claim a jmpslot. Setup RRS relocation if claimed for the first time.
*/
long
claim_rrs_jmpslot(entry, rp, sp, addend)
struct file_entry *entry;
struct relocation_info *rp;
symbol *sp;
long addend;
{
struct relocation_info *r;
if (!(sp->flags & GS_HASJMPSLOT))
errx(1, "internal error: "
"%s: claim_rrs_jmpslot: %s: no reservation",
get_file_name(entry),
sp->name);
if (sp->jmpslot_offset != -1)
return rrs_sdt.sdt_plt + sp->jmpslot_offset;
sp->jmpslot_offset = current_jmpslot_offset;
current_jmpslot_offset += sizeof(jmpslot_t);
#ifdef DEBUG
printf("claim_rrs_jmpslot: %s: %s(%d) -> offset %x\n",
get_file_name(entry),
sp->name, sp->rrs_symbolnum, sp->jmpslot_offset);
#endif
if ((link_mode & SYMBOLIC) || rrs_section_type == RRS_PARTIAL) {
if (!sp->defined)
warnx("Cannot reduce symbol \"%s\" in %s",
sp->name, get_file_name(entry));
md_fix_jmpslot( rrs_plt + sp->jmpslot_offset/sizeof(jmpslot_t),
rrs_sdt.sdt_plt + sp->jmpslot_offset,
sp->value);
if (rrs_section_type == RRS_PARTIAL || !JMPSLOT_NEEDS_RELOC) {
/* PLT is self-contained */
discarded_rrs_relocs++;
return rrs_sdt.sdt_plt + sp->jmpslot_offset;
}
} else {
md_make_jmpslot(rrs_plt + sp->jmpslot_offset/sizeof(jmpslot_t),
sp->jmpslot_offset,
claimed_rrs_relocs);
}
/*
* Install a run-time relocation for this PLT entry.
*/
r = rrs_next_reloc();
RELOC_SYMBOL(r) = sp->rrs_symbolnum;
r->r_address = (long)rrs_sdt.sdt_plt + sp->jmpslot_offset;
if (link_mode & SYMBOLIC) {
RELOC_EXTERN_P(r) = 0;
md_make_jmpreloc(rp, r, RELTYPE_RELATIVE);
} else {
RELOC_EXTERN_P(r) = 1;
md_make_jmpreloc(rp, r, 0);
}
return rrs_sdt.sdt_plt + sp->jmpslot_offset;
}
/*
* Claim GOT entry for a global symbol. If this is the first relocation
* claiming the entry, setup a RRS relocation for it.
* Return offset into the GOT allocated to this symbol.
*/
long
claim_rrs_gotslot(entry, rp, lsp, addend)
struct file_entry *entry;
struct relocation_info *rp;
struct localsymbol *lsp;
long addend;
{
struct relocation_info *r;
symbol *sp = lsp->symbol;
int reloc_type = 0;
if (sp == NULL) {
return 0;
}
if (sp->alias)
sp = sp->alias;
if (!(sp->flags & GS_HASGOTSLOT))
errx(1, "internal error: "
"%s: claim_rrs_gotslot: %s: no reservation",
get_file_name(entry), sp->name);
if (sp->gotslot_offset != -1) {
#ifdef DIAGNOSTIC
if (*GOTP(sp->gotslot_offset) != addend +
((!(link_mode & SHAREABLE) || (link_mode & SYMBOLIC))
? sp->value : 0))
errx(1, "%s: %s: gotslot at %#x is multiple valued, "
"*got = %#x, addend = %#x, sp->value = %#x",
get_file_name(entry), sp->name,
sp->gotslot_offset,
*GOTP(sp->gotslot_offset), addend, sp->value);
#endif
/* This symbol already passed here before. */
return sp->gotslot_offset;
}
if (current_got_offset == 0)
/* GOT offset 0 is reserved */
current_got_offset += sizeof(got_t);
if (current_got_offset > MAX_GOTOFF)
errx(1, "%s: GOT overflow on symbol `%s' at %#x",
get_file_name(entry), sp->name, RELOC_ADDRESS(rp));
sp->gotslot_offset = current_got_offset;
current_got_offset += sizeof(got_t);
#ifdef DEBUG
printf("claim_rrs_gotslot: %s(%d,%#x) slot offset %#x, addend %#x\n",
sp->name, sp->rrs_symbolnum, sp->value, sp->gotslot_offset, addend);
#endif
if (sp->defined &&
(!(link_mode & SHAREABLE) || (link_mode & SYMBOLIC))) {
/*
* Reduce to just a base-relative translation.
*/
*GOTP(sp->gotslot_offset) = sp->value + addend;
reloc_type = RELTYPE_RELATIVE;
} else if ((link_mode & SYMBOLIC) || rrs_section_type == RRS_PARTIAL) {
/*
* SYMBOLIC: all symbols must be known.
* RRS_PARTIAL: we don't link against shared objects,
* so again all symbols must be known.
*/
warnx("Cannot reduce symbol \"%s\" in %s",
sp->name, get_file_name(entry));
} else {
/*
* This gotslot will be updated with symbol value at run-time.
*/
*GOTP(sp->gotslot_offset) = addend;
}
if (rrs_section_type == RRS_PARTIAL) {
/*
* Base address is known, gotslot should be fully
* relocated by now.
* NOTE: RRS_PARTIAL implies !SHAREABLE.
*/
if (!sp->defined)
warnx("Cannot reduce symbol \"%s\" in %s",
sp->name, get_file_name(entry));
discarded_rrs_relocs++;
return sp->gotslot_offset;
}
/*
* Claim a relocation entry.
* If symbol is defined and in "main" (!SHAREABLE)
* we still put out a relocation as we cannot easily
* undo the allocation.
* `RELTYPE_RELATIVE' relocations have the external bit off
* as no symbol need be looked up at run-time.
*/
r = rrs_next_reloc();
r->r_address = got_symbol->value + sp->gotslot_offset;
RELOC_SYMBOL(r) = sp->rrs_symbolnum;
RELOC_EXTERN_P(r) = !(reloc_type == RELTYPE_RELATIVE);
md_make_gotreloc(rp, r, reloc_type);
return sp->gotslot_offset;
}
/*
* Claim a GOT entry for a static symbol. Return offset of the
* allocated GOT entry. If RELOC_STATICS_THROUGH_GOT_P is in effect,
* return the offset of the symbol with respect to the *location* of
* the GOT.
*/
long
claim_rrs_internal_gotslot(entry, rp, lsp, addend)
struct file_entry *entry;
struct relocation_info *rp;
struct localsymbol *lsp;
long addend;
{
struct relocation_info *r;
addend += lsp->nzlist.nz_value;
if (!RELOC_STATICS_THROUGH_GOT_P(r))
return addend - got_symbol->value;
if (!(lsp->flags & LS_HASGOTSLOT))
errx(1, "internal error: "
"%s: claim_rrs_internal_gotslot at %#x: no reservation",
get_file_name(entry), RELOC_ADDRESS(rp));
if (lsp->gotslot_offset != -1) {
/* Already claimed */
if (*GOTP(lsp->gotslot_offset) != addend)
errx(1, "%s: gotslot at %#x is multiple valued",
get_file_name(entry), lsp->gotslot_offset);
return lsp->gotslot_offset;
}
if (current_got_offset == 0)
/* GOT offset 0 is reserved */
current_got_offset += sizeof(got_t);
if (current_got_offset > MAX_GOTOFF)
errx(1, "%s: GOT overflow for relocation at %#x",
get_file_name(entry), RELOC_ADDRESS(rp));
lsp->gotslot_offset = current_got_offset;
current_got_offset += sizeof(got_t);
*GOTP(lsp->gotslot_offset) = addend;
#ifdef DEBUG
printf("claim_rrs_internal_gotslot: %s: slot offset %#x, addend = %#x\n",
get_file_name(entry), lsp->gotslot_offset, addend);
#endif
if (rrs_section_type == RRS_PARTIAL) {
discarded_rrs_relocs++;
return lsp->gotslot_offset;
}
/*
* Relocation entry needed for this static GOT entry.
*/
r = rrs_next_reloc();
r->r_address = got_symbol->value + lsp->gotslot_offset;
RELOC_EXTERN_P(r) = 0;
md_make_gotreloc(rp, r, RELTYPE_RELATIVE);
return lsp->gotslot_offset;
}
void
claim_rrs_cpy_reloc(entry, rp, sp)
struct file_entry *entry;
struct relocation_info *rp;
symbol *sp;
{
struct relocation_info *r;
if (sp->flags & GS_CPYRELOCCLAIMED)
return;
if (!(sp->flags & GS_CPYRELOCRESERVED))
errx(1, "internal error: "
"%s: claim_cpy_reloc: %s: no reservation",
get_file_name(entry), sp->name);
#ifdef DEBUG
printf("claim_rrs_copy: %s: %s -> %x\n",
get_file_name(entry), sp->name, sp->so_defined);
#endif
r = rrs_next_reloc();
sp->flags |= GS_CPYRELOCCLAIMED;
r->r_address = rp->r_address;
RELOC_SYMBOL(r) = sp->rrs_symbolnum;
RELOC_EXTERN_P(r) = RELOC_EXTERN_P(rp);
md_make_cpyreloc(rp, r);
}
void
claim_rrs_segment_reloc(entry, rp)
struct file_entry *entry;
struct relocation_info *rp;
{
struct relocation_info *r = rrs_next_reloc();
#ifdef DEBUG
printf("claim_rrs_segment_reloc: %s at %#x\n",
get_file_name(entry), rp->r_address);
#endif
r->r_address = rp->r_address;
RELOC_TYPE(r) = RELOC_TYPE(rp);
RELOC_EXTERN_P(r) = 0;
md_make_reloc(rp, r, RELTYPE_RELATIVE);
}
/*
* Fill the RRS hash table for the given symbol name.
* NOTE: the hash value computation must match the one in rtld.
*/
void
rrs_insert_hash(cp, index)
char *cp;
int index;
{
int hashval = 0;
struct rrs_hash *hp;
for (; *cp; cp++)
hashval = (hashval << 1) + *cp;
hashval = (hashval & 0x7fffffff) % rrs_sdt.sdt_buckets;
/* Get to the bucket */
hp = rrs_hashtab + hashval;
if (hp->rh_symbolnum == -1) {
/* Empty bucket, use it */
hp->rh_symbolnum = index;
hp->rh_next = 0;
return;
}
while (hp->rh_next != 0)
hp = rrs_hashtab + hp->rh_next;
hp->rh_next = current_hash_index++;
hp = rrs_hashtab + hp->rh_next;
hp->rh_symbolnum = index;
hp->rh_next = 0;
}
/*
* There are two interesting cases to consider here.
*
* 1) No shared objects were loaded, but there were PIC input rel files.
* In this case we must output a _GLOBAL_OFFSET_TABLE_ but no other
* RRS data. Also, the entries in the GOT must be fully resolved.
*
* 2) It's a genuine dynamically linked program, so the whole RRS scoop
* goes into a.out.
*/
void
consider_rrs_section_lengths()
{
int n;
struct shobj *shp, **shpp;
#ifdef notyet
/* We run into trouble with this as long as shared object symbols
are not checked for definitions */
/*
* First, determine the real number of shared objects we need.
*/
for (shpp = &rrs_shobjs; *shpp; shpp = &(*shpp)->next) {
while (*shpp && !((*shpp)->entry->flags & E_SYMBOLS_USED)) {
if (--number_of_shobjs < 0)
errx(1, "internal error: number_of_shobjs < 0");
*shpp = (*shpp)->next;
}
if (*shpp == NULL)
break;
}
#endif
/* First, determine what of the RRS we want */
if (relocatable_output)
rrs_section_type = RRS_NONE;
else if (link_mode & SHAREABLE)
rrs_section_type = RRS_FULL;
else if (number_of_shobjs == 0 /*&& !(link_mode & DYNAMIC)*/) {
/*
* First slots in both tables are reserved
* hence the "> 1" condition
*/
if (number_of_gotslots > 1 || number_of_jmpslots > 1)
rrs_section_type = RRS_PARTIAL;
else
rrs_section_type = RRS_NONE;
} else
rrs_section_type = RRS_FULL;
if (rrs_section_type == RRS_NONE) {
got_symbol->defined = 0;
if (reserved_rrs_relocs > 0)
errx(1, "internal error: empty RRS has reservations");
return;
}
rrs_symbol_size = LD_VERSION_NZLIST_P(soversion) ?
sizeof(struct nzlist) : sizeof(struct nlist);
/*
* If there is an entry point, __DYNAMIC must be referenced (usually
* from crt0), as this is the method used to determine whether the
* run-time linker must be called.
*/
if (!(link_mode & SHAREABLE) && !(dynamic_symbol->flags & GS_REFERENCED))
errx(1, "No reference to __DYNAMIC");
dynamic_symbol->flags |= GS_REFERENCED;
if (number_of_gotslots > 1)
got_symbol->flags |= GS_REFERENCED;
/* Next, allocate relocs, got and plt */
n = reserved_rrs_relocs * sizeof(struct relocation_info);
rrs_reloc = (struct relocation_info *)xmalloc(n);
bzero(rrs_reloc, n);
n = number_of_gotslots * sizeof(got_t);
rrs_got = (got_t *)xmalloc(n);
bzero(rrs_got, n);
n = number_of_jmpslots * sizeof(jmpslot_t);
rrs_plt = (jmpslot_t *)xmalloc(n);
bzero(rrs_plt, n);
/* Initialize first jmpslot */
md_fix_jmpslot(rrs_plt, 0, 0);
if (rrs_section_type == RRS_PARTIAL) {
rrs_data_size = number_of_gotslots * sizeof(got_t);
rrs_data_size += number_of_jmpslots * sizeof(jmpslot_t);
return;
}
/*
* Walk the symbol table, assign RRS symbol numbers
* and calculate string space.
* Assign number 0 to __DYNAMIC (!! Sun compatibility)
*/
dynamic_symbol->rrs_symbolnum = number_of_rrs_symbols++;
FOR_EACH_SYMBOL(i ,sp) {
if ((link_mode & SHAREABLE) && sp->warning) {
/* Allocate N_WARNING & co */
rrs_strtab_size +=
2 + strlen(sp->name) + strlen(sp->warning);
number_of_rrs_symbols += 2;
}
if (!(sp->flags & GS_REFERENCED))
continue;
rrs_strtab_size += 1 + strlen(sp->name);
if (sp != dynamic_symbol)
sp->rrs_symbolnum = number_of_rrs_symbols++;
if (sp->alias) {
/*
* (sigh) Always allocate space to hold the
* indirection. At this point there's not
* enough information to decide whether it's
* actually needed or not.
*/
number_of_rrs_symbols++;
rrs_strtab_size += 1 + strlen(sp->alias->name);
}
} END_EACH_SYMBOL;
/*
* Now that we know how many RRS symbols there are going to be,
* allocate and initialize the RRS symbol hash table.
*/
rrs_sdt.sdt_buckets = number_of_rrs_symbols/4;
if (rrs_sdt.sdt_buckets < 4)
rrs_sdt.sdt_buckets = 4;
number_of_rrs_hash_entries = rrs_sdt.sdt_buckets +
number_of_rrs_symbols;
rrs_hashtab = (struct rrs_hash *)xmalloc(
number_of_rrs_hash_entries * sizeof(struct rrs_hash));
for (n = 0; n < rrs_sdt.sdt_buckets; n++)
rrs_hashtab[n].rh_symbolnum = -1;
current_hash_index = rrs_sdt.sdt_buckets;
/*
* Get symbols into hash table now, so we can fine tune the size
* of the latter. We adjust the value of `number_of_rrs_hash_entries'
* to the number of hash link slots actually used.
*/
FOR_EACH_SYMBOL(i ,sp) {
if (sp->flags & GS_REFERENCED)
rrs_insert_hash(sp->name, sp->rrs_symbolnum);
} END_EACH_SYMBOL;
number_of_rrs_hash_entries = current_hash_index;
/*
* Calculate RRS section sizes.
*/
rrs_data_size = sizeof(struct _dynamic);
rrs_data_size += sizeof(struct so_debug);
rrs_data_size += sizeof(struct section_dispatch_table);
rrs_data_size += number_of_gotslots * sizeof(got_t);
rrs_data_size += number_of_jmpslots * sizeof(jmpslot_t);
rrs_data_size = MALIGN(rrs_data_size);
rrs_text_size = reserved_rrs_relocs * sizeof(struct relocation_info);
rrs_text_size += number_of_rrs_hash_entries * sizeof(struct rrs_hash);
rrs_text_size += number_of_rrs_symbols * rrs_symbol_size;
/* Align strings size */
rrs_strtab_size = MALIGN(rrs_strtab_size);
rrs_text_size += rrs_strtab_size;
/* Process needed shared objects */
for (shp = rrs_shobjs; shp; shp = shp->next) {
char *name = shp->entry->local_sym_name;
if (*name == '-' && *(name+1) == 'l')
name += 2;
rrs_text_size += sizeof(struct sod);
rrs_text_size += 1 + strlen(name);
}
/* Finally, align size */
rrs_text_size = MALIGN(rrs_text_size);
}
void
relocate_rrs_addresses()
{
dynamic_symbol->value = 0;
/*
* Get ready to allocate linkage table offsets.
* First jmpslot is reserved for the run-time binder
* GOT entry at offset 0 is reserved for `__DYNAMIC'.
*/
current_jmpslot_offset = sizeof(jmpslot_t);
current_got_offset = 0;
if (1 /* Not "-fPIC" seen */) {
int gotsize = number_of_gotslots * sizeof(got_t);
if (gotsize + MIN_GOTOFF - (int)sizeof(got_t) > MAX_GOTOFF)
warnx("Global Offset Table overflow");
if (gotsize > MAX_GOTOFF)
/* Position at "two-complements" origin */
current_got_offset += MIN_GOTOFF;
}
got_origin = -current_got_offset;
if (rrs_section_type == RRS_NONE)
return;
if (rrs_section_type == RRS_PARTIAL) {
rrs_sdt.sdt_got = rrs_data_start;
got_symbol->value = rrs_sdt.sdt_got + got_origin;
rrs_sdt.sdt_plt = rrs_sdt.sdt_got +
number_of_gotslots * sizeof(got_t);
return;
}
/*
* RRS data relocations.
*/
rrs_dyn.d_version = soversion;
rrs_dyn.d_debug = (struct so_debug *)
(rrs_data_start + sizeof(struct _dynamic));
rrs_dyn.d_un.d_sdt = (struct section_dispatch_table *)
((long)rrs_dyn.d_debug + sizeof(struct so_debug));
rrs_sdt.sdt_got = (long)rrs_dyn.d_un.d_sdt +
sizeof(struct section_dispatch_table);
rrs_sdt.sdt_plt = rrs_sdt.sdt_got + number_of_gotslots*sizeof(got_t);
/*
* RRS text relocations.
*/
rrs_sdt.sdt_rel = rrs_text_start;
/*
* Sun BUG compatibility alert.
* Main program's RRS text values are relative to TXTADDR? WHY??
*/
#ifdef SUN_COMPAT
if (soversion == LD_VERSION_SUN && !(link_mode & SHAREABLE))
rrs_sdt.sdt_rel -= N_TXTADDR(outheader);
#endif
rrs_sdt.sdt_hash = rrs_sdt.sdt_rel +
reserved_rrs_relocs * sizeof(struct relocation_info);
rrs_sdt.sdt_nzlist = rrs_sdt.sdt_hash +
number_of_rrs_hash_entries * sizeof(struct rrs_hash);
rrs_sdt.sdt_strings = rrs_sdt.sdt_nzlist +
number_of_rrs_symbols * rrs_symbol_size;
rrs_sdt.sdt_str_sz = rrs_strtab_size;
rrs_sdt.sdt_text_sz = text_size;
rrs_sdt.sdt_plt_sz = number_of_jmpslots * sizeof(jmpslot_t);
rrs_sdt.sdt_sods = rrs_shobjs ? rrs_sdt.sdt_strings+rrs_strtab_size : 0;
rrs_sdt.sdt_filler1 = 0;
rrs_sdt.sdt_filler2 = 0;
/*
* Assign addresses to _GLOBAL_OFFSET_TABLE_ and __DYNAMIC.
* The value `&__DYNAMIC' is in the GOT table at offset 0.
*/
got_symbol->value = rrs_sdt.sdt_got + got_origin;
*GOTP(0) = dynamic_symbol->value = rrs_data_start;
}
void
write_rrs_data()
{
long pos;
if (rrs_section_type == RRS_NONE)
return;
pos = rrs_data_start + (N_DATOFF(outheader) - DATA_START(outheader));
if (fseek(outstream, pos, SEEK_SET) != 0)
err(1, "write_rrs_data: fseek");
if (rrs_section_type == RRS_PARTIAL) {
/*
* Only a GOT and PLT are needed.
*/
md_swapout_got(rrs_got, number_of_gotslots);
mywrite(rrs_got, number_of_gotslots, sizeof(got_t), outstream);
md_swapout_jmpslot(rrs_plt, number_of_jmpslots);
mywrite(rrs_plt, number_of_jmpslots,
sizeof(jmpslot_t), outstream);
return;
}
md_swapout__dynamic(&rrs_dyn);
mywrite(&rrs_dyn, 1, sizeof(struct _dynamic), outstream);
md_swapout_so_debug(&rrs_so_debug);
mywrite(&rrs_so_debug, 1, sizeof(struct so_debug), outstream);
md_swapout_section_dispatch_table(&rrs_sdt);
mywrite(&rrs_sdt, 1, sizeof(struct section_dispatch_table), outstream);
md_swapout_got(rrs_got, number_of_gotslots);
mywrite(rrs_got, number_of_gotslots, sizeof(got_t), outstream);
md_swapout_jmpslot(rrs_plt, number_of_jmpslots);
mywrite(rrs_plt, number_of_jmpslots, sizeof(jmpslot_t), outstream);
}
void
write_rrs_text()
{
long pos;
int i;
int symsize;
struct nzlist *nlp;
int offset = 0;
struct shobj *shp;
struct sod *sodp;
int bind;
if (rrs_section_type == RRS_PARTIAL)
return;
pos = rrs_text_start + (N_TXTOFF(outheader) - TEXT_START(outheader));
if (fseek(outstream, pos, SEEK_SET) != 0)
err(1, "write_rrs_text: fseek");
/* Write relocation records */
md_swapout_reloc(rrs_reloc, reserved_rrs_relocs);
mywrite(rrs_reloc, reserved_rrs_relocs,
sizeof(struct relocation_info), outstream);
/* Write the RRS symbol hash tables */
md_swapout_rrs_hash(rrs_hashtab, number_of_rrs_hash_entries);
mywrite(rrs_hashtab, number_of_rrs_hash_entries,
sizeof(struct rrs_hash), outstream);
/*
* Determine size of an RRS symbol entry, allocate space
* to collect them in.
*/
symsize = number_of_rrs_symbols * rrs_symbol_size;
nlp = rrs_symbols = (struct nzlist *)alloca(symsize);
rrs_strtab = (char *)alloca(rrs_strtab_size);
#define INCR_NLP(p) ((p) = (struct nzlist *)((long)(p) + rrs_symbol_size))
/* __DYNAMIC symbol *must* be first for Sun compatibility */
nlp->nz_desc = nlp->nz_other = 0;
if (LD_VERSION_NZLIST_P(soversion))
nlp->nz_size = 0;
nlp->nz_type = dynamic_symbol->defined;
nlp->nz_value = dynamic_symbol->value;
nlp->nz_value = dynamic_symbol->value;
nlp->nz_strx = offset;
strcpy(rrs_strtab + offset, dynamic_symbol->name);
offset += 1 + strlen(dynamic_symbol->name);
INCR_NLP(nlp);
/*
* Now, for each global symbol, construct a nzlist element
* for inclusion in the RRS symbol table.
*/
FOR_EACH_SYMBOL(i, sp) {
if (sp == dynamic_symbol)
continue;
if ((link_mode & SHAREABLE) && sp->warning) {
/*
* Write a N_WARNING duo.
*/
nlp->nz_type = N_WARNING;
nlp->nz_un.n_strx = offset;
nlp->nz_value = 0;
nlp->nz_other = 0;
nlp->nz_desc = 0;
nlp->nz_size = 0;
strcpy(rrs_strtab + offset, sp->warning);
offset += 1 + strlen(sp->warning);
INCR_NLP(nlp);
nlp->nz_type = N_UNDF + N_EXT;
nlp->nz_un.n_strx = offset;
nlp->nz_value = 0;
nlp->nz_other = 0;
nlp->nz_desc = 0;
nlp->nz_size = 0;
strcpy(rrs_strtab + offset, sp->name);
offset += 1 + strlen(sp->name);
INCR_NLP(nlp);
}
if (!(sp->flags & GS_REFERENCED))
continue;
if ((long)nlp - (long)rrs_symbols >=
number_of_rrs_symbols * rrs_symbol_size)
errx(1, "internal error: "
"rrs symbols exceed allocation %d",
number_of_rrs_symbols);
nlp->nz_desc = 0;
nlp->nz_other = 0;
if (LD_VERSION_NZLIST_P(soversion))
nlp->nz_size = 0;
bind = (sp->flags & GS_WEAK) ? BIND_WEAK : 0;
if (sp->defined > 1) {
/* defined with known type */
if (!(link_mode & SHAREABLE) &&
sp->alias && sp->alias->defined > 1) {
/*
* If the target of an indirect symbol has
* been defined and we are outputting an
* executable, resolve the indirection; it's
* no longer needed.
*/
nlp->nz_type = sp->alias->defined;
nlp->nz_value = sp->alias->value;
nlp->nz_other = N_OTHER(bind, sp->alias->aux);
} else if (sp->defined == N_SIZE) {
/*
* Make sure this symbol isn't going
* to define anything.
*/
nlp->nz_type = N_UNDF;
nlp->nz_value = 0;
} else {
nlp->nz_type = sp->defined;
nlp->nz_value = sp->value;
nlp->nz_other = N_OTHER(bind, sp->aux);
}
if (LD_VERSION_NZLIST_P(soversion))
nlp->nz_size = sp->size;
} else if (sp->common_size) {
/*
* A common definition.
*/
nlp->nz_type = N_UNDF | N_EXT;
nlp->nz_value = sp->common_size;
nlp->nz_other = N_OTHER(bind, 0);
} else if (!sp->defined) {
/* undefined */
nlp->nz_type = N_UNDF | N_EXT;
nlp->nz_value = 0;
if (sp->so_defined && sp->jmpslot_offset != -1) {
/*
* A PLT entry. The auxiliary type -- which
* must be AUX_FUNC -- is used by the run-time
* linker to unambiguously resolve function
* address references.
*/
if (sp->aux != AUX_FUNC)
errx(1, "%s: non-function jmpslot",
sp->name);
nlp->nz_other = N_OTHER(bind, sp->aux);
nlp->nz_value =
rrs_sdt.sdt_plt + sp->jmpslot_offset;
}
} else
errx(1, "internal error: %s defined in mysterious way",
sp->name);
/* Set symbol's name */
nlp->nz_strx = offset;
strcpy(rrs_strtab + offset, sp->name);
offset += 1 + strlen(sp->name);
if (sp->alias) {
/*
* Write an extra symbol for indirections (possibly
* just a dummy).
*/
int t = (nlp->nz_type == N_INDR + N_EXT);
INCR_NLP(nlp);
nlp->nz_type = N_UNDF + (t ? N_EXT : 0);
nlp->nz_un.n_strx = offset;
nlp->nz_value = 0;
nlp->nz_other = 0;
nlp->nz_desc = 0;
nlp->nz_size = 0;
strcpy(rrs_strtab + offset, sp->alias->name);
offset += 1 + strlen(sp->alias->name);
}
INCR_NLP(nlp);
} END_EACH_SYMBOL;
if (MALIGN(offset) != rrs_strtab_size)
errx(1, "internal error: "
"inconsistent RRS string table length: %d, expected %d",
offset, rrs_strtab_size);
/* Write the symbol table */
if (rrs_symbol_size == sizeof(struct nlist))
md_swapout_symbols(rrs_symbols, number_of_rrs_symbols);
else
md_swapout_zsymbols(rrs_symbols, number_of_rrs_symbols);
mywrite(rrs_symbols, symsize, 1, outstream);
/* Write the strings */
mywrite(rrs_strtab, rrs_strtab_size, 1, outstream);
/*
* Write the names of the shared objects needed at run-time
*/
pos = rrs_sdt.sdt_sods + number_of_shobjs * sizeof(struct sod);
sodp = (struct sod *)alloca( number_of_shobjs * sizeof(struct sod));
for (i = 0, shp = rrs_shobjs; shp; i++, shp = shp->next) {
char *name = shp->entry->local_sym_name;
if (i >= number_of_shobjs)
errx(1, "internal error: # of link objects exceeds %d",
number_of_shobjs);
sodp[i].sod_name = pos;
sodp[i].sod_major = shp->entry->lib_major;
sodp[i].sod_minor = shp->entry->lib_minor;
if (*name == '-' && *(name+1) == 'l') {
name += 2;
sodp[i].sod_library = 1;
} else
sodp[i].sod_library = 0;
pos += 1 + strlen(name);
sodp[i].sod_next = (i == number_of_shobjs - 1) ? 0 :
(rrs_sdt.sdt_sods + (i+1)*sizeof(struct sod));
}
if (i < number_of_shobjs)
errx(1, "internal error: "
"# of link objects less then expected %d",
number_of_shobjs);
md_swapout_sod(sodp, number_of_shobjs);
mywrite(sodp, number_of_shobjs, sizeof(struct sod), outstream);
for (i = 0, shp = rrs_shobjs; shp; i++, shp = shp->next) {
char *name = shp->entry->local_sym_name;
if (*name == '-' && *(name+1) == 'l') {
name += 2;
}
mywrite(name, strlen(name) + 1, 1, outstream);
}
}
void
write_rrs()
{
/*
* First, do some consistency checks on the RRS segment.
*/
if (rrs_section_type == RRS_NONE) {
if (reserved_rrs_relocs > 1)
errx(1, "internal error: "
"RRS relocs in static program: %d",
reserved_rrs_relocs-1);
return;
}
#ifdef DEBUG
printf("rrs_relocs: reserved %d claimed %d discarded %d, gotslots %d jmpslots %d\n",
reserved_rrs_relocs, claimed_rrs_relocs, discarded_rrs_relocs,
number_of_gotslots-1, number_of_jmpslots-1);
#endif
/* Final consistency check */
if (claimed_rrs_relocs + discarded_rrs_relocs != reserved_rrs_relocs) {
errx(1, "internal error: "
"reserved relocs(%d) != claimed(%d) + discarded(%d)",
reserved_rrs_relocs,
claimed_rrs_relocs,
discarded_rrs_relocs);
}
/* Write the RRS segments. */
write_rrs_text ();
write_rrs_data ();
}