/* $OpenBSD: rrs.c,v 1.11 2005/09/29 20:40:35 deraadt Exp $*/ /* * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; char *rrs_search_paths; /* `-L' RT search */ static int rrs_search_paths_size; 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 max_got_offset; static int min_got_offset; static int got_origin; 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_hash | | | hash buckets | | | +-------------------+ <-- .sdt_nzlist | | | symbols | | | +-------------------+ <-- .sdt_strings | | | strings | | | +-------------------+ <-- .sdt_sods | | | shobjs | | | +-------------------+ | | | shobjs strings | <-- .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 | | | +-------------------+ */ static int dlopen_is_used(void) { symbol *sym; struct localsymbol *lsp; #ifdef nounderscore sym = getsym_soft("dlopen"); #else sym = getsym_soft("_dlopen"); #endif if (!sym) return 0; /* * A use is characterized as being an entry on the "refs" list * that is not in the text section, because such an entry is the * definition. */ for (lsp = sym->refs; lsp; lsp = lsp->next) if (!(lsp->nzlist.nlist.n_type & N_TEXT)) return 1; return 0; } /* * Add NAME to the list of needed run-time objects. * Return 1 if ENTRY was added to the list. */ int rrs_add_shobj(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(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(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(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(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(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(void) { 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; } /* don't warn more than n times for text relocations */ #define TEXT_RELOC_THRESHOLD 3 static unsigned long total_text_relocs = 0; /* called at cleanup time */ void rrs_summarize_warnings(void) { if (total_text_relocs >= TEXT_RELOC_THRESHOLD) warnx("Total: %lu RRS text relocations", total_text_relocs); } /* * 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(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 && ++total_text_relocs < TEXT_RELOC_THRESHOLD) 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(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(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_got_offset) 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(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 %#lx 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_got_offset) 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(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(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. */ static void rrs_insert_hash(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(void) { int n; struct shobj *shp; #ifdef notyet struct shobj **shpp; /* 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 && !dlopen_is_used() /*&& !(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; rrs_search_paths_size = rrs_search_paths ? strlen(rrs_search_paths) + 1 : 0; rrs_search_paths_size = MALIGN(rrs_search_paths_size); rrs_text_size += rrs_search_paths_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(void) { int gotsize; 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; max_got_offset = MAX_GOTOFF(pic_type); min_got_offset = MIN_GOTOFF(pic_type); gotsize = number_of_gotslots * sizeof(got_t); if (gotsize + min_got_offset - (int)sizeof(got_t) > max_got_offset) warnx("Global Offset Table overflow (use `-fPIC')"); if (gotsize > max_got_offset) /* Position at "two-complements" origin */ current_got_offset += min_got_offset; 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_paths = rrs_search_paths ? rrs_sdt.sdt_strings + rrs_strtab_size : 0; rrs_sdt.sdt_sods = rrs_shobjs ? rrs_sdt.sdt_strings + rrs_strtab_size + rrs_search_paths_size : 0; rrs_sdt.sdt_filler2 = 0; 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); /* * 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; } static void write_rrs_data(void) { 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); } static void write_rrs_text(void) { 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; strlcpy(rrs_strtab + offset, dynamic_symbol->name, rrs_strtab_size - offset); 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; strlcpy(rrs_strtab + offset, sp->warning, rrs_strtab_size - offset); 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; strlcpy(rrs_strtab + offset, sp->name, rrs_strtab_size - offset); 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; strlcpy(rrs_strtab + offset, sp->name, rrs_strtab_size - offset); 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; strlcpy(rrs_strtab + offset, sp->alias->name, rrs_strtab_size - offset); 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 RT search path */ mywrite(rrs_search_paths, rrs_search_paths_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(void) { /* * 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 (); }