diff options
Diffstat (limited to 'libexec/rtld-aout')
-rw-r--r-- | libexec/rtld-aout/Makefile | 20 | ||||
-rw-r--r-- | libexec/rtld-aout/dynamic.h | 380 | ||||
-rw-r--r-- | libexec/rtld-aout/i386/md-static-funcs.c | 17 | ||||
-rw-r--r-- | libexec/rtld-aout/i386/md.c | 384 | ||||
-rw-r--r-- | libexec/rtld-aout/i386/md.h | 245 | ||||
-rw-r--r-- | libexec/rtld-aout/i386/mdprologue.S | 93 | ||||
-rw-r--r-- | libexec/rtld-aout/md-prologue.c | 39 | ||||
-rw-r--r-- | libexec/rtld-aout/rtld.1 | 224 | ||||
-rw-r--r-- | libexec/rtld-aout/rtld.1aout | 224 | ||||
-rw-r--r-- | libexec/rtld-aout/rtld.c | 2120 | ||||
-rw-r--r-- | libexec/rtld-aout/shlib.c | 342 | ||||
-rw-r--r-- | libexec/rtld-aout/shlib.h | 43 | ||||
-rw-r--r-- | libexec/rtld-aout/support.c | 86 | ||||
-rw-r--r-- | libexec/rtld-aout/support.h | 35 |
14 files changed, 4252 insertions, 0 deletions
diff --git a/libexec/rtld-aout/Makefile b/libexec/rtld-aout/Makefile new file mode 100644 index 0000000..4177f6b --- /dev/null +++ b/libexec/rtld-aout/Makefile @@ -0,0 +1,20 @@ +# $Id: Makefile,v 1.23 1997/02/22 15:46:46 peter Exp $ + +PROG= ld.so +SRCS= mdprologue.S rtld.c malloc.c shlib.c md.c support.c sbrk.c +MAN1= rtld.1 +LDDIR?= $(.CURDIR)/.. +# As there is relocation going on behind GCC's back, don't cache function addresses. +PICFLAG=-fpic -fno-function-cse +CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE) $(PICFLAG) -DRTLD +LDFLAGS+=-nostdlib -Wl,-Bshareable -Wl,-Bsymbolic -Wl,-assert -Wl,nosymbolic +ASFLAGS+=-k +DPADD+= ${LIBC:S/c.a/c_pic.a/} ${LIBC:S/c.a/gcc_pic.a/} +LDADD+= -lc_pic -lgcc_pic +BINDIR= /usr/libexec +INSTALLFLAGS+= -fschg -C # -C to install as atomically as possible +MLINKS= rtld.1 ld.so.1 + +.PATH: $(LDDIR) $(LDDIR)/$(MACHINE) + +.include <bsd.prog.mk> diff --git a/libexec/rtld-aout/dynamic.h b/libexec/rtld-aout/dynamic.h new file mode 100644 index 0000000..c1890db --- /dev/null +++ b/libexec/rtld-aout/dynamic.h @@ -0,0 +1,380 @@ +/* + * 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: dynamic.h,v 1.3 1997/02/22 15:46:18 peter Exp $ + */ + +#ifndef __DYNAMIC_H__ +#define __DYNAMIC_H__ + +#define SUN_COMPAT + +#include "md.h" +#include "link.h" + +#ifndef RELOC_JMPTAB_P + +#define RELOC_JMPTAB_P(r) ((r)->r_jmptable) +#define RELOC_BASEREL_P(r) ((r)->r_baserel) +#define RELOC_RELATIVE_P(r) ((r)->r_relative) +#define RELOC_COPY_P(r) ((r)->r_copy) +#define RELOC_LAZY_P(r) ((r)->r_jmptable) + +#define CHECK_GOT_RELOC(r) ((r)->r_pcrel) +#define RELOC_PIC_TYPE(r) ((r)->r_baserel? \ + PIC_TYPE_LARGE:PIC_TYPE_NONE) +#endif + +#ifndef RELOC_INIT_SEGMENT_RELOC +#define RELOC_INIT_SEGMENT_RELOC(r) +#endif + +#ifndef MAX_GOTOFF +#define MAX_GOTOFF(x) (LONG_MAX) +#endif + +#ifndef MIN_GOTOFF +#define MIN_GOTOFF(x) (LONG_MIN) +#endif + +/* + * Internal representation of relocation types + */ +#define RELTYPE_EXTERN 1 +#define RELTYPE_JMPSLOT 2 +#define RELTYPE_BASEREL 4 +#define RELTYPE_RELATIVE 8 +#define RELTYPE_COPY 16 + +#define N_ISWEAK(p) (N_BIND(p) & BIND_WEAK) + +typedef struct localsymbol { + struct nzlist nzlist; /* n[z]list from file */ + struct glosym *symbol; /* Corresponding global symbol, + if any */ + struct localsymbol *next; /* List of definitions */ + struct file_entry *entry; /* Backpointer to file */ + long gotslot_offset; /* Position in GOT, if any */ + int symbolnum; /* Position in output nlist */ + int flags; +#define LS_L_SYMBOL 1 /* Local symbol starts with an `L' */ +#define LS_WRITE 2 /* Symbol goes in output symtable */ +#define LS_RENAME 4 /* xlat name to `<file>.<name>' */ +#define LS_HASGOTSLOT 8 /* This symbol has a GOT entry */ +#define LS_WARNING 16 /* Second part of a N_WARNING duo */ +} localsymbol_t; + +/* + * Global symbol data is recorded in these structures, one for each global + * symbol. They are found via hashing in 'symtab', which points to a vector + * of buckets. Each bucket is a chain of these structures through the link + * field. + * + * Rewritten version to support extra info for dynamic linking. + */ + +struct glosym { + struct glosym *link; /* Next symbol hash bucket. */ + char *name; /* Name of this symbol. */ + long value; /* Value of this symbol */ + localsymbol_t *refs; /* Chain of local symbols from object + files pertaining to this global + symbol */ + localsymbol_t *sorefs;/* Same for local symbols from shared + object files. */ + + char *warning; /* message, from N_WARNING nlists */ + int common_size; /* Common size */ + int symbolnum; /* Symbol index in output symbol table */ + int rrs_symbolnum; /* Symbol index in RRS symbol table */ + + localsymbol_t *def_lsp; /* The local symbol that gave this + global symbol its definition */ + + char defined; /* Definition of this symbol */ + char so_defined; /* Definition of this symbol in a shared + object. These go into the RRS symbol table */ + u_char undef_refs; /* Count of number of "undefined" + messages printed for this symbol */ + u_char mult_defs; /* Same for "multiply defined" symbols */ + struct glosym *alias; /* For symbols of type N_INDR, this + points at the real symbol. */ + int setv_count; /* Number of elements in N_SETV symbols */ + int size; /* Size of this symbol (either from N_SIZE + symbols or a from shared object's RRS */ + int aux; /* Auxiliary type information conveyed in + the `n_other' field of nlists */ + + /* The offset into one of the RRS tables, -1 if not used */ + long jmpslot_offset; + long gotslot_offset; + + long flags; + +#define GS_DEFINED 0x1 /* Symbol has definition (notyetused)*/ +#define GS_REFERENCED 0x2 /* Symbol is referred to by something + interesting */ +#define GS_TRACE 0x4 /* Symbol will be traced */ +#define GS_HASJMPSLOT 0x8 /* */ +#define GS_HASGOTSLOT 0x10 /* Some state bits concerning */ +#define GS_CPYRELOCRESERVED 0x20 /* entries in GOT and PLT tables */ +#define GS_CPYRELOCCLAIMED 0x40 /* */ +#define GS_WEAK 0x80 /* Symbol is weakly defined */ + +}; +#ifndef __symbol_defined__ +#define __symbol_defined__ +typedef struct glosym symbol; +#endif + +/* The symbol hash table: a vector of SYMTABSIZE pointers to struct glosym. */ +extern symbol *symtab[]; +#define FOR_EACH_SYMBOL(i,sp) { \ + int i; \ + for (i = 0; i < SYMTABSIZE; i++) { \ + register symbol *sp; \ + for (sp = symtab[i]; sp; sp = sp->link) + +#define END_EACH_SYMBOL }} + +extern symbol *got_symbol; /* the symbol __GLOBAL_OFFSET_TABLE_ */ +extern symbol *dynamic_symbol; /* the symbol __DYNAMIC */ + +/* + * Each input file, and each library member ("subfile") being loaded, has a + * `file_entry' structure for it. + * + * For files specified by command args, these are contained in the vector which + * `file_table' points to. + * + * For library members, they are dynamically allocated, and chained through the + * `chain' field. The chain is found in the `subfiles' field of the + * `file_entry'. The `file_entry' objects for the members have `superfile' + * fields pointing to the one for the library. + * + * Rewritten version to support extra info for dynamic linking. + */ + +struct file_entry { + char *filename; /* Name of this file. */ + /* + * Name to use for the symbol giving address of text start Usually + * the same as filename, but for a file spec'd with -l this is the -l + * switch itself rather than the filename. + */ + char *local_sym_name; + struct exec header; /* The file's a.out header. */ + localsymbol_t *symbols; /* Symbol table of the file. */ + int nsymbols; /* Number of symbols in above array. */ + int string_size; /* Size in bytes of string table. */ + char *strings; /* Pointer to the string table when + in core, NULL otherwise */ + int strings_offset; /* Offset of string table, + (normally N_STROFF() + 4) */ + /* + * Next two used only if `relocatable_output' or if needed for + * output of undefined reference line numbers. + */ + struct relocation_info *textrel; /* Text relocations */ + int ntextrel; /* # of text relocations */ + struct relocation_info *datarel; /* Data relocations */ + int ndatarel; /* # of data relocations */ + + /* + * Relation of this file's segments to the output file. + */ + int text_start_address; /* Start of this file's text segment + in the output file core image. */ + int data_start_address; /* Start of this file's data segment + in the output file core image. */ + int bss_start_address; /* Start of this file's bss segment + in the output file core image. */ + struct file_entry *subfiles; /* For a library, points to chain of + entries for the library members. */ + struct file_entry *superfile; /* For library member, points to the + library's own entry. */ + struct file_entry *chain; /* For library member, points to next + entry for next member. */ + int starting_offset; /* For a library member, offset of the + member within the archive. Zero for + files that are not library members.*/ + int total_size; /* Size of contents of this file, + if library member. */ +#ifdef SUN_COMPAT + struct file_entry *silly_archive;/* For shared libraries which have + a .sa companion */ +#endif + int lib_major, lib_minor; /* Version numbers of a shared object */ + + int flags; +#define E_IS_LIBRARY 1 /* File is a an archive */ +#define E_HEADER_VALID 2 /* File's header has been read */ +#define E_SEARCH_DIRS 4 /* Search directories for file */ +#define E_SEARCH_DYNAMIC 8 /* Search for shared libs allowed */ +#define E_JUST_SYMS 0x10 /* File is used for incremental load */ +#define E_DYNAMIC 0x20 /* File is a shared object */ +#define E_SCRAPPED 0x40 /* Ignore this file */ +#define E_SYMBOLS_USED 0x80 /* Symbols from this entry were used */ +#define E_SECONDCLASS 0x100 /* Shared object is a subsidiary */ +}; + +/* + * Runtime Relocation Section (RRS). + * This describes the data structures that go into the output text and data + * segments to support the run-time linker. The RRS can be empty (plain old + * static linking), or can just exist of GOT and PLT entries (in case of + * statically linked PIC code). + */ +extern int rrs_section_type; /* What's in the RRS section */ +#define RRS_NONE 0 +#define RRS_PARTIAL 1 +#define RRS_FULL 2 +extern int rrs_text_size; /* Size of RRS text additions */ +extern int rrs_text_start; /* Location of above */ +extern int rrs_data_size; /* Size of RRS data additions */ +extern int rrs_data_start; /* Location of above */ +extern char *rrs_search_paths; /* `-L' RT paths */ + +/* Version number to put in __DYNAMIC (set by -V) */ +extern int soversion; +#ifndef DEFAULT_SOVERSION +#define DEFAULT_SOVERSION LD_VERSION_BSD +#endif + +extern int pc_relocation; /* Current PC reloc value */ + +extern int number_of_shobjs; /* # of shared objects linked in */ + +/* Current link mode */ +extern int link_mode; +#define DYNAMIC 1 /* Consider shared libraries */ +#define SYMBOLIC 2 /* Force symbolic resolution */ +#define FORCEARCHIVE 4 /* Force inclusion of all members + of archives */ +#define SHAREABLE 8 /* Build a shared object */ +#define SILLYARCHIVE 16 /* Process .sa companions, if any */ +#define FORCEDYNAMIC 32 /* Force dynamic output even if no + shared libraries included */ +#define WARNRRSTEXT 64 /* Warn about rrs in text */ + +extern FILE *outstream; /* Output file. */ +extern struct exec outheader; /* Output file header. */ +extern int magic; /* Output file magic. */ +extern int oldmagic; +extern int relocatable_output; +extern int pic_type; +#define PIC_TYPE_NONE 0 +#define PIC_TYPE_SMALL 1 +#define PIC_TYPE_LARGE 2 + +void read_header __P((int, struct file_entry *)); +void read_entry_symbols __P((int, struct file_entry *)); +void read_entry_strings __P((int, struct file_entry *)); +void read_entry_relocation __P((int, struct file_entry *)); +void enter_file_symbols __P((struct file_entry *)); +void read_file_symbols __P((struct file_entry *)); +int set_element_prefixed_p __P((char *)); +int text_offset __P((struct file_entry *)); +int file_open __P((struct file_entry *)); +void each_file __P((void (*)(), void *)); +void each_full_file __P((void (*)(), void *)); +unsigned long check_each_file __P((unsigned long (*)(), void *)); +void mywrite __P((void *, int, int, FILE *)); +void padfile __P((int, FILE *)); + +/* In warnings.c: */ +void perror_name __P((char *)); +void perror_file __P((struct file_entry *)); +void print_symbols __P((FILE *)); +char *get_file_name __P((struct file_entry *)); +void print_file_name __P((struct file_entry *, FILE *)); +void prline_file_name __P((struct file_entry *, FILE *)); +int do_warnings __P((FILE *)); + +/* In etc.c: */ +#include "support.h" + +/* In symbol.c: */ +void symtab_init __P((int)); +symbol *getsym __P((char *)), *getsym_soft __P((char *)); + +/* In lib.c: */ +void search_library __P((int, struct file_entry *)); +void read_shared_object __P((int, struct file_entry *)); +int findlib __P((struct file_entry *)); + +/* In shlib.c: */ +#include "shlib.h" + +/* In rrs.c: */ +void init_rrs __P((void)); +int rrs_add_shobj __P((struct file_entry *)); +void alloc_rrs_reloc __P((struct file_entry *, symbol *)); +void alloc_rrs_segment_reloc __P((struct file_entry *, struct relocation_info *)); +void alloc_rrs_jmpslot __P((struct file_entry *, symbol *)); +void alloc_rrs_gotslot __P((struct file_entry *, struct relocation_info *, localsymbol_t *)); +void alloc_rrs_cpy_reloc __P((struct file_entry *, symbol *)); + +int claim_rrs_reloc __P((struct file_entry *, struct relocation_info *, symbol *, long *)); +long claim_rrs_jmpslot __P((struct file_entry *, struct relocation_info *, symbol *, long)); +long claim_rrs_gotslot __P((struct file_entry *, struct relocation_info *, struct localsymbol *, long)); +long claim_rrs_internal_gotslot __P((struct file_entry *, struct relocation_info *, struct localsymbol *, long)); +void claim_rrs_cpy_reloc __P((struct file_entry *, struct relocation_info *, symbol *)); +void claim_rrs_segment_reloc __P((struct file_entry *, struct relocation_info *)); +void consider_rrs_section_lengths __P((void)); +void relocate_rrs_addresses __P((void)); +void write_rrs __P((void)); + +/* In <md>.c */ +void md_init_header __P((struct exec *, int, int)); +long md_get_addend __P((struct relocation_info *, unsigned char *)); +void md_relocate __P((struct relocation_info *, long, unsigned char *, int)); +void md_make_jmpslot __P((jmpslot_t *, long, long)); +void md_fix_jmpslot __P((jmpslot_t *, long, u_long)); +int md_make_reloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_jmpreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_gotreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_copyreloc __P((struct relocation_info *, struct relocation_info *)); +void md_set_breakpoint __P((long, long *)); + +#ifdef NEED_SWAP +/* In xbits.c: */ +void swap_longs __P((long *, int)); +void swap_symbols __P((struct nlist *, int)); +void swap_zsymbols __P((struct nzlist *, int)); +void swap_ranlib_hdr __P((struct ranlib *, int)); +void swap__dynamic __P((struct link_dynamic *)); +void swap_section_dispatch_table __P((struct section_dispatch_table *)); +void swap_so_debug __P((struct so_debug *)); +void swapin_sod __P((struct sod *, int)); +void swapout_sod __P((struct sod *, int)); +void swapout_fshash __P((struct fshash *, int)); +#endif + +#endif /* __DYNAMIC_H__ */ diff --git a/libexec/rtld-aout/i386/md-static-funcs.c b/libexec/rtld-aout/i386/md-static-funcs.c new file mode 100644 index 0000000..758a4b2 --- /dev/null +++ b/libexec/rtld-aout/i386/md-static-funcs.c @@ -0,0 +1,17 @@ +/* + * $Id$ + * + * Called by ld.so when onanating. + * This *must* be a static function, so it is not called through a jmpslot. + */ + +static void +md_relocate_simple(r, relocation, addr) +struct relocation_info *r; +long relocation; +char *addr; +{ + if (r->r_relative) + *(long *)addr += relocation; +} + diff --git a/libexec/rtld-aout/i386/md.c b/libexec/rtld-aout/i386/md.c new file mode 100644 index 0000000..8e945e8 --- /dev/null +++ b/libexec/rtld-aout/i386/md.c @@ -0,0 +1,384 @@ +/* + * 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$ + */ + +#include <sys/param.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <err.h> +#include <fcntl.h> +#include <a.out.h> +#include <stab.h> +#include <string.h> + +#include "dynamic.h" + +#if defined(RTLD) && defined(SUN_COMPAT) +#define REL_SIZE(r) (2) /* !!!!! Sun BUG compatible */ +#else +#define REL_SIZE(r) ((r)->r_length) +#endif + +/* + * Get relocation addend corresponding to relocation record RP + * from address ADDR + */ +long +md_get_addend(rp, addr) +struct relocation_info *rp; +unsigned char *addr; +{ + switch (REL_SIZE(rp)) { + case 0: + return get_byte(addr); + case 1: + return get_short(addr); + case 2: + return get_long(addr); + default: + errx(1, "Unsupported relocation size: %x", + REL_SIZE(rp)); + } +} + +/* + * Put RELOCATION at ADDR according to relocation record RP. + */ +void +md_relocate(rp, relocation, addr, relocatable_output) +struct relocation_info *rp; +long relocation; +unsigned char *addr; +int relocatable_output; +{ + switch (REL_SIZE(rp)) { + case 0: + put_byte(addr, relocation); + break; + case 1: + put_short(addr, relocation); + break; + case 2: + put_long(addr, relocation); + break; + default: + errx(1, "Unsupported relocation size: %x", + REL_SIZE(rp)); + } +} + +/* + * Machine dependent part of claim_rrs_reloc(). + * Set RRS relocation type. + */ +int +md_make_reloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + /* Relocation size */ + r->r_length = rp->r_length; + + if (rp->r_pcrel) + r->r_pcrel = 1; + + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + + if (type & RELTYPE_COPY) + r->r_copy = 1; + + return 0; +} + +/* + * Set up a transfer from jmpslot at OFFSET (relative to the PLT table) + * to the binder slot (which is at offset 0 of the PLT). + */ +void +md_make_jmpslot(sp, offset, index) +jmpslot_t *sp; +long offset; +long index; +{ + /* + * i386 PC-relative "fixed point" is located right after the + * instruction it pertains to. + */ + u_long fudge = - (sizeof(sp->opcode) + sizeof(sp->addr) + offset); + + sp->opcode = CALL; +#if 0 + sp->addr = fudge; +#else + sp->addr[0] = fudge & 0xffff; + sp->addr[1] = fudge >> 16; +#endif + sp->reloc_index = index; +} + +/* + * Set up a "direct" transfer (ie. not through the run-time binder) from + * jmpslot at OFFSET to ADDR. Used by `ld' when the SYMBOLIC flag is on, + * and by `ld.so' after resolving the symbol. + * On the i386, we use the JMP instruction which is PC relative, so no + * further RRS relocations will be necessary for such a jmpslot. + */ +void +md_fix_jmpslot(sp, offset, addr) +jmpslot_t *sp; +long offset; +u_long addr; +{ + u_long fudge = addr - (sizeof(sp->opcode) + sizeof(sp->addr) + offset); + + sp->opcode = JUMP; +#if 0 + sp->addr = fudge; +#else + sp->addr[0] = fudge & 0xffff; + sp->addr[1] = fudge >> 16; +#endif + sp->reloc_index = 0; +} + +/* + * Bind a jmpslot to its target address. TARGET is where the jmpslot + * should jump to, and WHERE is a pointer to the jmpslot's address field. + * This is called by the dynamic linker when LD_BIND_NOW is set in the + * environment. + */ +void +md_bind_jmpslot(target, where) +u_long target; +caddr_t where; +{ + jmpslot_t *sp = + (jmpslot_t *) (where - offsetof(jmpslot_t, addr[0])); + + md_fix_jmpslot(sp, (long) sp, target); +} + +/* + * Update the relocation record for a RRS jmpslot. + */ +void +md_make_jmpreloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + jmpslot_t *sp; + + /* + * Fix relocation address to point to the correct + * location within this jmpslot. + */ + r->r_address += sizeof(sp->opcode); + + /* Relocation size */ + r->r_length = 2; + + /* Set relocation type */ + r->r_jmptable = 1; + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + +} + +/* + * Set relocation type for a RRS GOT relocation. + */ +void +md_make_gotreloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + r->r_baserel = 1; + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + + /* Relocation size */ + r->r_length = 2; +} + +/* + * Set relocation type for a RRS copy operation. + */ +void +md_make_cpyreloc(rp, r) +struct relocation_info *rp, *r; +{ + /* Relocation size */ + r->r_length = 2; + + r->r_copy = 1; +} + +void +md_set_breakpoint(where, savep) +long where; +long *savep; +{ + *savep = *(long *)where; + *(char *)where = TRAP; +} + +#ifndef RTLD + +#ifdef __FreeBSD__ +int netzmagic; +#endif + +/* + * Initialize (output) exec header such that useful values are + * obtained from subsequent N_*() macro evaluations. + */ +void +md_init_header(hp, magic, flags) +struct exec *hp; +int magic, flags; +{ +#ifdef NetBSD + if (oldmagic || magic == QMAGIC) + hp->a_midmag = magic; + else + N_SETMAGIC((*hp), magic, MID_I386, flags); +#endif +#ifdef __FreeBSD__ + if (oldmagic) + hp->a_midmag = magic; + else if (netzmagic) + N_SETMAGIC_NET((*hp), magic, MID_I386, flags); + else + N_SETMAGIC((*hp), magic, MID_I386, flags); +#endif + + /* TEXT_START depends on the value of outheader.a_entry. */ + if (!(link_mode & SHAREABLE)) + hp->a_entry = PAGSIZ; +} +#endif /* RTLD */ + + +#ifdef NEED_SWAP +/* + * Byte swap routines for cross-linking. + */ + +void +md_swapin_exec_hdr(h) +struct exec *h; +{ + int skip = 0; + + if (!N_BADMAG(*h)) + skip = 1; + + swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip); +} + +void +md_swapout_exec_hdr(h) +struct exec *h; +{ + /* NetBSD: Always leave magic alone */ + int skip = 1; +#if 0 + if (N_GETMAGIC(*h) == OMAGIC) + skip = 0; +#endif + + swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip); +} + + +void +md_swapin_reloc(r, n) +struct relocation_info *r; +int n; +{ + int bits; + + for (; n; n--, r++) { + r->r_address = md_swap_long(r->r_address); + bits = ((int *)r)[1]; + r->r_symbolnum = md_swap_long(bits) & 0x00ffffff; + r->r_pcrel = (bits & 1); + r->r_length = (bits >> 1) & 3; + r->r_extern = (bits >> 3) & 1; + r->r_baserel = (bits >> 4) & 1; + r->r_jmptable = (bits >> 5) & 1; + r->r_relative = (bits >> 6) & 1; +#ifdef N_SIZE + r->r_copy = (bits >> 7) & 1; +#endif + } +} + +void +md_swapout_reloc(r, n) +struct relocation_info *r; +int n; +{ + int bits; + + for (; n; n--, r++) { + r->r_address = md_swap_long(r->r_address); + bits = md_swap_long(r->r_symbolnum) & 0xffffff00; + bits |= (r->r_pcrel & 1); + bits |= (r->r_length & 3) << 1; + bits |= (r->r_extern & 1) << 3; + bits |= (r->r_baserel & 1) << 4; + bits |= (r->r_jmptable & 1) << 5; + bits |= (r->r_relative & 1) << 6; +#ifdef N_SIZE + bits |= (r->r_copy & 1) << 7; +#endif + ((int *)r)[1] = bits; + } +} + +void +md_swapout_jmpslot(j, n) +jmpslot_t *j; +int n; +{ + for (; n; n--, j++) { + j->opcode = md_swap_short(j->opcode); + j->addr[0] = md_swap_short(j->addr[0]); + j->addr[1] = md_swap_short(j->addr[1]); + j->reloc_index = md_swap_short(j->reloc_index); + } +} + +#endif /* NEED_SWAP */ diff --git a/libexec/rtld-aout/i386/md.h b/libexec/rtld-aout/i386/md.h new file mode 100644 index 0000000..84785de --- /dev/null +++ b/libexec/rtld-aout/i386/md.h @@ -0,0 +1,245 @@ +/* + * 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: md.h,v 1.16 1997/02/22 15:46:34 peter Exp $ + */ + +#ifndef __MD_H__ +#define __MD_H__ + +#if defined(CROSS_LINKER) && defined(XHOST) && XHOST==sparc +#define NEED_SWAP +#endif + +#define MAX_ALIGNMENT (sizeof (long)) + +#ifdef NetBSD +#define PAGSIZ __LDPGSZ +#else +#define PAGSIZ 4096 +#endif + +#if defined(NetBSD) || defined(CROSS_LINKER) + +#define N_SET_FLAG(ex,f) (oldmagic || N_GETMAGIC(ex)==QMAGIC ? (0) : \ + N_SETMAGIC(ex, \ + N_GETMAGIC(ex), \ + MID_MACHINE, \ + N_GETFLAG(ex)|(f))) + +#define N_IS_DYNAMIC(ex) ((N_GETFLAG(ex) & EX_DYNAMIC)) + +#define N_BADMID(ex) \ + (N_GETMID(ex) != 0 && N_GETMID(ex) != MID_MACHINE) + +#endif + +/* + * FreeBSD does it differently + */ +#ifdef __FreeBSD__ +#define N_SET_FLAG(ex,f) (oldmagic ? (0) : \ + (netzmagic == 0 ? \ + N_SETMAGIC(ex, \ + N_GETMAGIC(ex), \ + MID_MACHINE, \ + N_GETFLAG(ex)|(f)) : \ + N_SETMAGIC_NET(ex, \ + N_GETMAGIC_NET(ex), \ + MID_MACHINE, \ + N_GETFLAG_NET(ex)|(f)) )) + +#define N_IS_DYNAMIC(ex) ((N_GETMAGIC_NET(ex) == ZMAGIC) ? \ + ((N_GETFLAG_NET(ex) & EX_DYNAMIC)) : \ + ((N_GETFLAG(ex) & EX_DYNAMIC) )) +#define N_BADMID(ex) 0 +#endif + +/* + * Should be handled by a.out.h ? + */ +#define N_ADJUST(ex) (((ex).a_entry < PAGSIZ) ? -PAGSIZ : 0) +#define TEXT_START(ex) (N_TXTADDR(ex) + N_ADJUST(ex)) +#define DATA_START(ex) (N_DATADDR(ex) + N_ADJUST(ex)) + +#define RELOC_STATICS_THROUGH_GOT_P(r) (0) +#define JMPSLOT_NEEDS_RELOC (0) + +#define md_got_reloc(r) (0) + +#define md_get_rt_segment_addend(r,a) md_get_addend(r,a) + +#define RELOC_INIT_SEGMENT_RELOC(r) ((r)->r_length = 2) + +/* Width of a Global Offset Table entry */ +#define GOT_ENTRY_SIZE 4 +typedef long got_t; + +typedef struct jmpslot { + u_short opcode; + u_short addr[2]; + u_short reloc_index; +#define JMPSLOT_RELOC_MASK 0xffff +} jmpslot_t; + +#define NOP 0x90 +#define CALL 0xe890 /* NOP + CALL opcode */ +#define JUMP 0xe990 /* NOP + JMP opcode */ +#define TRAP 0xcc /* INT 3 */ + +/* + * Byte swap defs for cross linking + */ + +#if !defined(NEED_SWAP) + +#define md_swapin_exec_hdr(h) +#define md_swapout_exec_hdr(h) +#define md_swapin_symbols(s,n) +#define md_swapout_symbols(s,n) +#define md_swapin_zsymbols(s,n) +#define md_swapout_zsymbols(s,n) +#define md_swapin_reloc(r,n) +#define md_swapout_reloc(r,n) +#define md_swapin__dynamic(l) +#define md_swapout__dynamic(l) +#define md_swapin_section_dispatch_table(l) +#define md_swapout_section_dispatch_table(l) +#define md_swapin_so_debug(d) +#define md_swapout_so_debug(d) +#define md_swapin_rrs_hash(f,n) +#define md_swapout_rrs_hash(f,n) +#define md_swapin_sod(l,n) +#define md_swapout_sod(l,n) +#define md_swapout_jmpslot(j,n) +#define md_swapout_got(g,n) +#define md_swapin_ranlib_hdr(h,n) +#define md_swapout_ranlib_hdr(h,n) + +#endif /* NEED_SWAP */ + +#ifdef CROSS_LINKER + +#define get_byte(p) ( ((unsigned char *)(p))[0] ) + +#define get_short(p) ( ( ((unsigned char *)(p))[0] << 8) | \ + ( ((unsigned char *)(p))[1] ) \ + ) + +#define get_long(p) ( ( ((unsigned char *)(p))[0] << 24) | \ + ( ((unsigned char *)(p))[1] << 16) | \ + ( ((unsigned char *)(p))[2] << 8 ) | \ + ( ((unsigned char *)(p))[3] ) \ + ) + +#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); } + +#define put_short(p, v) { ((unsigned char *)(p))[0] = \ + ((((unsigned long)(v)) >> 8) & 0xff); \ + ((unsigned char *)(p))[1] = \ + ((((unsigned long)(v)) ) & 0xff); } + +#define put_long(p, v) { ((unsigned char *)(p))[0] = \ + ((((unsigned long)(v)) >> 24) & 0xff); \ + ((unsigned char *)(p))[1] = \ + ((((unsigned long)(v)) >> 16) & 0xff); \ + ((unsigned char *)(p))[2] = \ + ((((unsigned long)(v)) >> 8) & 0xff); \ + ((unsigned char *)(p))[3] = \ + ((((unsigned long)(v)) ) & 0xff); } + +#ifdef NEED_SWAP + +/* Define IO byte swapping routines */ + +void md_swapin_exec_hdr __P((struct exec *)); +void md_swapout_exec_hdr __P((struct exec *)); +void md_swapin_reloc __P((struct relocation_info *, int)); +void md_swapout_reloc __P((struct relocation_info *, int)); +void md_swapout_jmpslot __P((jmpslot_t *, int)); + +#define md_swapin_symbols(s,n) swap_symbols(s,n) +#define md_swapout_symbols(s,n) swap_symbols(s,n) +#define md_swapin_zsymbols(s,n) swap_zsymbols(s,n) +#define md_swapout_zsymbols(s,n) swap_zsymbols(s,n) +#define md_swapin__dynamic(l) swap__dynamic(l) +#define md_swapout__dynamic(l) swap__dynamic(l) +#define md_swapin_section_dispatch_table(l) swap_section_dispatch_table(l) +#define md_swapout_section_dispatch_table(l) swap_section_dispatch_table(l) +#define md_swapin_so_debug(d) swap_so_debug(d) +#define md_swapout_so_debug(d) swap_so_debug(d) +#define md_swapin_rrs_hash(f,n) swap_rrs_hash(f,n) +#define md_swapout_rrs_hash(f,n) swap_rrs_hash(f,n) +#define md_swapin_sod(l,n) swapin_sod(l,n) +#define md_swapout_sod(l,n) swapout_sod(l,n) +#define md_swapout_got(g,n) swap_longs((long*)(g),n) +#define md_swapin_ranlib_hdr(h,n) swap_ranlib_hdr(h,n) +#define md_swapout_ranlib_hdr(h,n) swap_ranlib_hdr(h,n) + +#define md_swap_short(x) ( (((x) >> 8) & 0xff) | (((x) & 0xff) << 8) ) + +#define md_swap_long(x) ( (((x) >> 24) & 0xff ) | (((x) >> 8 ) & 0xff00 ) | \ + (((x) << 8 ) & 0xff0000) | (((x) << 24) & 0xff000000)) + +#else /* We need not swap, but must pay attention to alignment: */ + +#define md_swap_short(x) (x) +#define md_swap_long(x) (x) + +#endif /* NEED_SWAP */ + +#else /* Not a cross linker: use native */ + +#define md_swap_short(x) (x) +#define md_swap_long(x) (x) + +#define get_byte(where) (*(char *)(where)) +#define get_short(where) (*(short *)(where)) +#define get_long(where) (*(long *)(where)) + +#define put_byte(where,what) (*(char *)(where) = (what)) +#define put_short(where,what) (*(short *)(where) = (what)) +#define put_long(where,what) (*(long *)(where) = (what)) + +#endif /* CROSS_LINKER */ + +void md_init_header __P((struct exec *, int, int)); +long md_get_addend __P((struct relocation_info *, unsigned char *)); +void md_relocate __P((struct relocation_info *, long, unsigned char *, int)); +void md_make_jmpslot __P((jmpslot_t *, long, long)); +void md_fix_jmpslot __P((jmpslot_t *, long, u_long)); +void md_bind_jmpslot __P((u_long, caddr_t)); +int md_make_reloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_jmpreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_gotreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_copyreloc __P((struct relocation_info *, struct relocation_info *)); +void md_set_breakpoint __P((long, long *)); + + +#endif /* __MD_H__ */ diff --git a/libexec/rtld-aout/i386/mdprologue.S b/libexec/rtld-aout/i386/mdprologue.S new file mode 100644 index 0000000..091fe7f --- /dev/null +++ b/libexec/rtld-aout/i386/mdprologue.S @@ -0,0 +1,93 @@ +/* + * 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$ + */ + +/* + * i386 run-time link editor entry points. + */ + +#include <sys/syscall.h> + + .text + .globl _binder, _binder_entry + +/* + * _rtl(int version, struct crt_ldso *crtp) + */ + +_rtl: # crt0 calls us here + pushl %ebp # Allocate stack frame + movl %esp,%ebp + pushl %ebx + + call 1f # PIC function prologue +1: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+[.-1b],%ebx + + movl 12(%ebp),%eax # Extract data from interface structure + movl (%eax),%eax # base address of ld.so (first field) + # setup arguments for rtld() + movl (%ebx),%ecx # 1st entry in GOT is our __DYNAMIC + addl %eax,%ecx # add load address + pushl %ecx # 3rd arg + pushl 12(%ebp) # 2nd arg == &crt. + pushl 8(%ebp) # 1st arg == version + addl _rtld@GOT(%ebx),%eax # relocate address of function + call %eax # _rtld(version, crtp, DYNAMIC) + addl $12,%esp # pop arguments + + popl %ebx + leave # remove stack frame + ret + + # First call to a procedure generally comes through here for + # binding. + +_binder_entry: + pushl %ebp # setup a stack frame + movl %esp,%ebp + pusha # save all regs + + xorl %eax,%eax # clear + movl 4(%ebp),%esi # return address in PLT + movw (%esi),%ax # get hold of relocation number + subl $6,%esi # make it point to the jmpslot + + pushl %eax # pushd arguments + pushl %esi # + call _binder@PLT # _binder(rpc, index) + addl $8,%esp # pop arguments + movl %eax,4(%ebp) # return value from _binder() == actual + # address of function + popa # restore regs + leave # remove our stack frame + ret diff --git a/libexec/rtld-aout/md-prologue.c b/libexec/rtld-aout/md-prologue.c new file mode 100644 index 0000000..dae455e --- /dev/null +++ b/libexec/rtld-aout/md-prologue.c @@ -0,0 +1,39 @@ +/* + * rtld entry pseudo code - turn into assembler and tweak it + */ + +#include <sys/types.h> +#include <sys/types.h> +#include <a.out.h> +#include "link.h" +#include "md.h" + +extern long _GOT_[]; +extern void (*rtld)(); +extern void (*binder())(); + +void +rtld_entry(version, crtp) +int version; +struct crt *crtp; +{ + register struct link_dynamic *dp; + register void (*f)(); + + /* __DYNAMIC is first entry in GOT */ + dp = (struct link_dynamic *) (_GOT_[0]+crtp->crt_ba); + + f = (void (*)())((long)rtld + crtp->crt_ba); + (*f)(version, crtp, dp); +} + +void +binder_entry() +{ + extern int PC; + struct jmpslot *sp; + void (*func)(); + + func = binder(PC, sp->reloc_index & 0x003fffff); + (*func)(); +} diff --git a/libexec/rtld-aout/rtld.1 b/libexec/rtld-aout/rtld.1 new file mode 100644 index 0000000..dbd4dde --- /dev/null +++ b/libexec/rtld-aout/rtld.1 @@ -0,0 +1,224 @@ +.\" $Id: rtld.1,v 1.13 1997/02/22 15:46:47 peter Exp $ +.\" +.\" Copyright (c) 1995 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 acknowledgment: +.\" This product includes software developed by Paul Kranenburg. +.\" 3. 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. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been successfully loaded, +.Nm +proceeds to resolve external references from both the main program and +all objects loaded. A mechanism is provided for initialization routines +to be called, on a per-object basis, giving a shared object an opportunity +to perform any extra set-up, before execution of the program proper begins. +This is useful for C++ libraries that contain static constructors. +.Pp +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other than 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_IGNORE_MISSING_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +This is ignored for set-user-ID and set-group-ID programs. +.It Ev LD_PRELOAD +A colon separated list of shared libraries, to be linked in before any +other shared libraries. If the directory is not specified then +the directories specified by LD_LIBRARY_PATH will be searched first +followed by the set of built-in standard directories. +This is ignored for set-user-ID and set-group-ID programs. +.It Ev LD_BIND_NOW +When set to a nonempty string, causes +.Nm +to relocate all external function calls before starting execution of the +program. Normally, function calls are bound lazily, at the first call +of each function. +.Ev LD_BIND_NOW +increases the start-up time of a program, but it avoids run-time +surprises caused by unexpectedly undefined functions. +.It Ev LD_WARN_NON_PURE_CODE +When set to a nonempty string, issue a warning whenever a link-editing +operation requires modification of the text segment of some loaded +object. This is usually indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set to a nonempty string, no warning messages of any kind are +issued. Normally, a warning is given if satisfactorily versioned +library could not be found. +.It Ev LD_IGNORE_MISSING_OBJECTS +When set to a nonempty string, makes it a nonfatal condition if +one or more required shared objects cannot be loaded. +Loading and execution proceeds using the objects that are +available. +A warning is produced for each missing object, unless the environment +variable +.Ev LD_SUPPRESS_WARNINGS +is set to a nonempty string. +.Pp +This is ignored for set-user-ID and set-group-ID programs. +.Pp +Missing shared objects can be ignored without errors if all the +following conditions hold: +.Bl -bullet +.It +They do not supply definitions for any required data symbols. +.It +No functions defined by them are called during program execution. +.It +The environment variable +.Ev LD_BIND_NOW +is unset or is set to the empty string. +.El +.It Ev LD_TRACE_LOADED_OBJECTS +When set to a nonempty string, causes +.Nm +to exit after loading the shared objects and printing a summary which includes +the absolute pathnames of all objects, to standard output. +.It Ev LD_TRACE_LOADED_OBJECTS_FMT1 +.It Ev LD_TRACE_LOADED_OBJECTS_FMT2 +When set, these variables are interpreted as format strings a la +.Xr printf 3 +to customize the trace output and are used by +.Xr ldd 1 's +.Fl f +option and allows +.Xr ldd 1 +to be operated as a filter more conveniently. +The following conversions can be used: +.Bl -tag -indent "LD_TRACE_LOADED_OBJECTS_FMT1 " -width "xxxx" +.It \&%a +The main program's name +.Po also known as +.Dq __progname +.Pc . +.It \&%A +The value of the environment variable +.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME +.It \&%o +The library name. +.It \&%m +The library's major version number. +.It \&%n +The library's minor version number. +.It \&%p +The full pathname as determined by +.Nm rtld Ns 's +library search rules. +.It \&%x +The library's load address. +.El +.Pp +Additionally, +.Sy \en +and +.Sy \et +are recognised and have their usual meaning. +.\" .It Ev LD_NO_INTERN_SEARCH +.\" When set, +.\" .Nm +.\" does not process any internal search paths that were recorded in the +.\" executable. +.\" .It Ev LD_NOSTD_PATH +.\" When set, do not include a set of built-in standard directory paths for +.\" searching. This might be useful when running on a system with a completely +.\" non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 , +.Xr link 5 , +.Xr ldconfig 8 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/rtld-aout/rtld.1aout b/libexec/rtld-aout/rtld.1aout new file mode 100644 index 0000000..dbd4dde --- /dev/null +++ b/libexec/rtld-aout/rtld.1aout @@ -0,0 +1,224 @@ +.\" $Id: rtld.1,v 1.13 1997/02/22 15:46:47 peter Exp $ +.\" +.\" Copyright (c) 1995 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 acknowledgment: +.\" This product includes software developed by Paul Kranenburg. +.\" 3. 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. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been successfully loaded, +.Nm +proceeds to resolve external references from both the main program and +all objects loaded. A mechanism is provided for initialization routines +to be called, on a per-object basis, giving a shared object an opportunity +to perform any extra set-up, before execution of the program proper begins. +This is useful for C++ libraries that contain static constructors. +.Pp +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other than 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_IGNORE_MISSING_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +This is ignored for set-user-ID and set-group-ID programs. +.It Ev LD_PRELOAD +A colon separated list of shared libraries, to be linked in before any +other shared libraries. If the directory is not specified then +the directories specified by LD_LIBRARY_PATH will be searched first +followed by the set of built-in standard directories. +This is ignored for set-user-ID and set-group-ID programs. +.It Ev LD_BIND_NOW +When set to a nonempty string, causes +.Nm +to relocate all external function calls before starting execution of the +program. Normally, function calls are bound lazily, at the first call +of each function. +.Ev LD_BIND_NOW +increases the start-up time of a program, but it avoids run-time +surprises caused by unexpectedly undefined functions. +.It Ev LD_WARN_NON_PURE_CODE +When set to a nonempty string, issue a warning whenever a link-editing +operation requires modification of the text segment of some loaded +object. This is usually indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set to a nonempty string, no warning messages of any kind are +issued. Normally, a warning is given if satisfactorily versioned +library could not be found. +.It Ev LD_IGNORE_MISSING_OBJECTS +When set to a nonempty string, makes it a nonfatal condition if +one or more required shared objects cannot be loaded. +Loading and execution proceeds using the objects that are +available. +A warning is produced for each missing object, unless the environment +variable +.Ev LD_SUPPRESS_WARNINGS +is set to a nonempty string. +.Pp +This is ignored for set-user-ID and set-group-ID programs. +.Pp +Missing shared objects can be ignored without errors if all the +following conditions hold: +.Bl -bullet +.It +They do not supply definitions for any required data symbols. +.It +No functions defined by them are called during program execution. +.It +The environment variable +.Ev LD_BIND_NOW +is unset or is set to the empty string. +.El +.It Ev LD_TRACE_LOADED_OBJECTS +When set to a nonempty string, causes +.Nm +to exit after loading the shared objects and printing a summary which includes +the absolute pathnames of all objects, to standard output. +.It Ev LD_TRACE_LOADED_OBJECTS_FMT1 +.It Ev LD_TRACE_LOADED_OBJECTS_FMT2 +When set, these variables are interpreted as format strings a la +.Xr printf 3 +to customize the trace output and are used by +.Xr ldd 1 's +.Fl f +option and allows +.Xr ldd 1 +to be operated as a filter more conveniently. +The following conversions can be used: +.Bl -tag -indent "LD_TRACE_LOADED_OBJECTS_FMT1 " -width "xxxx" +.It \&%a +The main program's name +.Po also known as +.Dq __progname +.Pc . +.It \&%A +The value of the environment variable +.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME +.It \&%o +The library name. +.It \&%m +The library's major version number. +.It \&%n +The library's minor version number. +.It \&%p +The full pathname as determined by +.Nm rtld Ns 's +library search rules. +.It \&%x +The library's load address. +.El +.Pp +Additionally, +.Sy \en +and +.Sy \et +are recognised and have their usual meaning. +.\" .It Ev LD_NO_INTERN_SEARCH +.\" When set, +.\" .Nm +.\" does not process any internal search paths that were recorded in the +.\" executable. +.\" .It Ev LD_NOSTD_PATH +.\" When set, do not include a set of built-in standard directory paths for +.\" searching. This might be useful when running on a system with a completely +.\" non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 , +.Xr link 5 , +.Xr ldconfig 8 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/rtld-aout/rtld.c b/libexec/rtld-aout/rtld.c new file mode 100644 index 0000000..ea33fe8 --- /dev/null +++ b/libexec/rtld-aout/rtld.c @@ -0,0 +1,2120 @@ +/* + * 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: rtld.c,v 1.46 1997/02/22 15:46:48 peter 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 <sys/errno.h> +#include <sys/mman.h> +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE +#endif +#include <dlfcn.h> +#include <err.h> +#include <fcntl.h> +#include <a.out.h> +#include <stab.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <link.h> + +#include "md.h" +#include "shlib.h" +#include "support.h" +#include "dynamic.h" + +#ifndef MAP_ANON +#define MAP_ANON 0 +#define anon_open() do { \ + if ((anon_fd = open("/dev/zero", O_RDWR, 0)) == -1) \ + err("open: %s", "/dev/zero"); \ +} while (0) +#define anon_close() do { \ + (void)close(anon_fd); \ + anon_fd = -1; \ +} while (0) +#else +#define anon_open() +#define anon_close() +#endif + +/* + * Structure for building a list of shared objects. + */ +struct so_list { + struct so_map *sol_map; /* Link map for shared object */ + struct so_list *sol_next; /* Next entry in the list */ +}; + +/* + * Loader private data, hung off <so_map>->som_spd + */ +struct somap_private { + int spd_version; + struct so_map *spd_parent; + struct so_list *spd_children; + struct so_map *spd_prev; + dev_t spd_dev; + ino_t spd_ino; + int spd_refcount; + int spd_flags; +#define RTLD_MAIN 0x01 +#define RTLD_RTLD 0x02 +#define RTLD_DL 0x04 +#define RTLD_INIT 0x08 + unsigned long a_text; /* text size, if known */ + unsigned long a_data; /* initialized data size */ + unsigned long a_bss; /* uninitialized data size */ + +#ifdef SUN_COMPAT + long spd_offset; /* Correction for Sun main programs */ +#endif +}; + +#define LM_PRIVATE(smp) ((struct somap_private *)(smp)->som_spd) + +#ifdef SUN_COMPAT +#define LM_OFFSET(smp) (LM_PRIVATE(smp)->spd_offset) +#else +#define LM_OFFSET(smp) (0) +#endif + +/* Base address for section_dispatch_table entries */ +#define LM_LDBASE(smp) (smp->som_addr + LM_OFFSET(smp)) + +/* Start of text segment */ +#define LM_TXTADDR(smp) (smp->som_addr == (caddr_t)0 ? PAGSIZ : 0) + +/* Start of run-time relocation_info */ +#define LM_REL(smp) ((struct relocation_info *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_REL((smp)->som_dynamic))) + +/* Start of symbols */ +#define LM_SYMBOL(smp, i) ((struct nzlist *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_SYMBOL((smp)->som_dynamic) + \ + i * (LD_VERSION_NZLIST_P(smp->som_dynamic->d_version) ? \ + sizeof(struct nzlist) : sizeof(struct nlist)))) + +/* Start of hash table */ +#define LM_HASH(smp) ((struct rrs_hash *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_HASH((smp)->som_dynamic))) + +/* Start of strings */ +#define LM_STRINGS(smp) ((char *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_STRINGS((smp)->som_dynamic))) + +/* Start of search paths */ +#define LM_PATHS(smp) ((char *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_PATHS((smp)->som_dynamic))) + +/* End of text */ +#define LM_ETEXT(smp) ((char *) \ + ((smp)->som_addr + LM_TXTADDR(smp) + LD_TEXTSZ((smp)->som_dynamic))) + +/* Needed shared objects */ +#define LM_NEED(smp) ((struct sod *) \ + ((smp)->som_addr + LM_TXTADDR(smp) + LD_NEED((smp)->som_dynamic))) + +/* PLT is in data segment, so don't use LM_OFFSET here */ +#define LM_PLT(smp) ((jmpslot_t *) \ + ((smp)->som_addr + LD_PLT((smp)->som_dynamic))) + +/* Parent of link map */ +#define LM_PARENT(smp) (LM_PRIVATE(smp)->spd_parent) + +#ifndef RELOC_EXTERN_P +#define RELOC_EXTERN_P(s) ((s)->r_extern) +#endif + +#ifndef RELOC_SYMBOL +#define RELOC_SYMBOL(s) ((s)->r_symbolnum) +#endif + +#ifndef RELOC_PCREL_P +#define RELOC_PCREL_P(s) ((s)->r_pcrel) +#endif + +static char __main_progname[] = "main"; +static char *main_progname = __main_progname; +static char us[] = "/usr/libexec/ld.so"; + +char **environ; +char *__progname; +int errno; + +static uid_t uid, euid; +static gid_t gid, egid; +static int careful; +static int anon_fd = -1; + +static char *ld_bind_now; +static char *ld_ignore_missing_objects; +static char *ld_library_path; +static char *ld_preload; +static char *ld_tracing; +static char *ld_suppress_warnings; +static char *ld_warn_non_pure_code; + +struct so_map *link_map_head; +struct so_map *link_map_tail; +struct rt_symbol *rt_symbol_head; + +static void *__dlopen __P((char *, int)); +static int __dlclose __P((void *)); +static void *__dlsym __P((void *, char *)); +static char *__dlerror __P((void)); +static void __dlexit __P((void)); +static void *__dlsym3 __P((void *, char *, void *)); + +static struct ld_entry ld_entry = { + __dlopen, __dlclose, __dlsym, __dlerror, __dlexit, __dlsym3 +}; + + void xprintf __P((char *, ...)); +static struct so_map *map_object __P(( char *, + struct sod *, + struct so_map *)); +static int map_preload __P((void)); +static int map_sods __P((struct so_map *)); +static int reloc_and_init __P((struct so_map *, int)); +static void unmap_object __P((struct so_map *, int)); +static struct so_map *alloc_link_map __P(( char *, struct sod *, + struct so_map *, caddr_t, + struct _dynamic *)); +static void free_link_map __P((struct so_map *)); +static inline int check_text_reloc __P(( struct relocation_info *, + struct so_map *, + caddr_t)); +static int reloc_map __P((struct so_map *, int)); +static void reloc_copy __P((struct so_map *)); +static void init_object __P((struct so_map *)); +static void init_sods __P((struct so_list *)); +static int call_map __P((struct so_map *, char *)); +static char *findhint __P((char *, int, int *)); +static char *rtfindlib __P((char *, int, int)); +static char *rtfindfile __P((char *)); +void binder_entry __P((void)); +long binder __P((jmpslot_t *)); +static struct nzlist *lookup __P((char *, struct so_map **, int)); +static inline struct rt_symbol *lookup_rts __P((char *)); +static struct rt_symbol *enter_rts __P((char *, long, int, caddr_t, + long, struct so_map *)); +static void die __P((void)); +static void generror __P((char *, ...)); +static int maphints __P((void)); +static void unmaphints __P((void)); +static void ld_trace __P((struct so_map *)); +static void rt_readenv __P((void)); +static int hinthash __P((char *, int)); +int rtld __P((int, struct crt_ldso *, struct _dynamic *)); + +static inline int +strcmp (register const char *s1, register const char *s2) +{ + while (*s1 == *s2++) + if (*s1++ == 0) + return (0); + return (*(unsigned char *)s1 - *(unsigned char *)--s2); +} + +#include "md-static-funcs.c" + +/* + * Called from assembler stub that has set up crtp (passed from crt0) + * and dp (our __DYNAMIC). + */ +int +rtld(version, crtp, dp) +int version; +struct crt_ldso *crtp; +struct _dynamic *dp; +{ + struct relocation_info *reloc; + struct relocation_info *reloc_limit; /* End+1 of relocation */ + struct so_debug *ddp; + struct so_map *main_map; + struct so_map *smp; + char *add_paths; + + /* Check version */ + if (version != CRT_VERSION_BSD_2 && + version != CRT_VERSION_BSD_3 && + version != CRT_VERSION_BSD_4 && + version != CRT_VERSION_SUN) + return -1; + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += crtp->crt_ba; + + /* Relocate ourselves */ + reloc = (struct relocation_info *) (LD_REL(dp) + crtp->crt_ba); + reloc_limit = + (struct relocation_info *) ((char *) reloc + LD_RELSZ(dp)); + while(reloc < reloc_limit) { + /* + * Objects linked with "-Bsymbolic" (in particular, ld.so + * itself) can end up having unused relocation entries at + * the end. These can be detected by the fact that they + * have an address of 0. + */ + if(reloc->r_address == 0) /* We're done */ + break; + md_relocate_simple(reloc, crtp->crt_ba, + reloc->r_address + crtp->crt_ba); + ++reloc; + } + + if (version >= CRT_VERSION_BSD_4) + __progname = crtp->crt_ldso; + if (version >= CRT_VERSION_BSD_3) + main_progname = crtp->crt_prog; + + /* Some buggy versions of crt0.o have crt_ldso filled in as NULL. */ + if (__progname == NULL) + __progname = us; + + /* Fill in some fields in _DYNAMIC or crt structure */ + if (version >= CRT_VERSION_BSD_4) + crtp->crt_ldentry = &ld_entry; /* crt */ + else + crtp->crt_dp->d_entry = &ld_entry; /* _DYNAMIC */ + + /* Setup out (private) environ variable */ + environ = crtp->crt_ep; + + /* Get user and group identifiers */ + uid = getuid(); euid = geteuid(); + gid = getgid(); egid = getegid(); + + careful = (uid != euid) || (gid != egid); + + rt_readenv(); + + anon_open(); + + /* Make a link map entry for the main program */ + main_map = alloc_link_map(main_progname, + (struct sod *) NULL, (struct so_map *) NULL, + (caddr_t) 0, crtp->crt_dp); + LM_PRIVATE(main_map)->spd_refcount++; + LM_PRIVATE(main_map)->spd_flags |= RTLD_MAIN; + + /* Make a link map entry for ourselves */ + smp = alloc_link_map(us, + (struct sod *) NULL, (struct so_map *) NULL, + (caddr_t) crtp->crt_ba, dp); + LM_PRIVATE(smp)->spd_refcount++; + LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD; + + /* + * Setup the executable's run path + */ + if (version >= CRT_VERSION_BSD_4) { + add_paths = LM_PATHS(main_map); + if (add_paths) + add_search_path(add_paths); + } + + /* + * Setup the directory search list for findshlib. We use only + * the standard search path. Any extra directories from + * LD_LIBRARY_PATH are searched explicitly, in rtfindlib. + */ + std_search_path(); + + /* Map in LD_PRELOADs before the main program's shared objects so we + can intercept those calls */ + if (ld_preload != NULL) { + if(map_preload() == -1) /* Failed */ + die(); + } + + /* Map all the shared objects that the main program depends upon */ + if(map_sods(main_map) == -1) + die(); + + if(ld_tracing) { /* We're done */ + ld_trace(link_map_head); + exit(0); + } + + crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next; + + /* Relocate and initialize all mapped objects */ + if(reloc_and_init(main_map, ld_bind_now != NULL) == -1) /* Failed */ + die(); + + ddp = crtp->crt_dp->d_debug; + ddp->dd_cc = rt_symbol_head; + if (ddp->dd_in_debugger) { + caddr_t addr = (caddr_t)((long)crtp->crt_bp & (~(PAGSIZ - 1))); + + /* Set breakpoint for the benefit of debuggers */ + if (mprotect(addr, PAGSIZ, + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + err(1, "Cannot set breakpoint (%s)", main_progname); + } + md_set_breakpoint((long)crtp->crt_bp, (long *)&ddp->dd_bpt_shadow); + if (mprotect(addr, PAGSIZ, PROT_READ|PROT_EXEC) == -1) { + err(1, "Cannot re-protect breakpoint (%s)", + main_progname); + } + + ddp->dd_bpt_addr = crtp->crt_bp; + if (link_map_head) + ddp->dd_sym_loaded = 1; + } + + /* Close the hints file */ + unmaphints(); + + /* Close our file descriptor */ + (void)close(crtp->crt_ldfd); + anon_close(); + + return LDSO_VERSION_HAS_DLSYM3; +} + +void +ld_trace(smp) + struct so_map *smp; +{ + char *fmt1, *fmt2, *fmt, *main_local; + int c; + + if ((main_local = getenv("LD_TRACE_LOADED_OBJECTS_PROGNAME")) == NULL) + main_local = ""; + + if ((fmt1 = getenv("LD_TRACE_LOADED_OBJECTS_FMT1")) == NULL) + fmt1 = "\t-l%o.%m => %p (%x)\n"; + + if ((fmt2 = getenv("LD_TRACE_LOADED_OBJECTS_FMT2")) == NULL) + fmt2 = "\t%o (%x)\n"; + + for (; smp; smp = smp->som_next) { + struct sod *sodp; + char *name, *path; + + if ((sodp = smp->som_sod) == NULL) + continue; + + name = (char *)sodp->sod_name; + if (LM_PARENT(smp)) + name += (long)LM_LDBASE(LM_PARENT(smp)); + + if ((path = smp->som_path) == NULL) + path = "not found"; + + fmt = sodp->sod_library ? fmt1 : fmt2; + while ((c = *fmt++) != '\0') { + switch (c) { + default: + putchar(c); + continue; + case '\\': + switch (c = *fmt) { + case '\0': + continue; + case 'n': + putchar('\n'); + break; + case 't': + putchar('\t'); + break; + } + break; + case '%': + switch (c = *fmt) { + case '\0': + continue; + case '%': + default: + putchar(c); + break; + case 'A': + printf("%s", main_local); + break; + case 'a': + printf("%s", main_progname); + break; + case 'o': + printf("%s", name); + break; + case 'm': + printf("%d", sodp->sod_major); + break; + case 'n': + printf("%d", sodp->sod_minor); + break; + case 'p': + printf("%s", path); + break; + case 'x': + printf("%p", smp->som_addr); + break; + } + break; + } + ++fmt; + } + } +} + +/* + * Allocate a new link map and return a pointer to it. + * + * PATH is the pathname of the shared object. + * + * SODP is a pointer to the shared object dependency structure responsible + * for causing the new object to be loaded. PARENT is the shared object + * into which SODP points. Both can be NULL if the new object is not + * being loaded as a result of a shared object dependency. + * + * ADDR is the address at which the object has been mapped. DP is a pointer + * to its _dynamic structure. + */ + static struct so_map * +alloc_link_map(path, sodp, parent, addr, dp) + char *path; + struct sod *sodp; + struct so_map *parent; + caddr_t addr; + struct _dynamic *dp; +{ + struct so_map *smp; + struct somap_private *smpp; + size_t smp_size; + +#ifdef DEBUG /* { */ + xprintf("alloc_link_map: \"%s\" at %p\n", path, addr); +#endif /* } */ + + /* + * Allocate so_map and private area with a single malloc. Round + * up the size of so_map so the private area is aligned. + */ + smp_size = ((((sizeof(struct so_map)) + sizeof (void *) - 1) / + sizeof (void *)) * sizeof (void *)); + + smp = (struct so_map *)xmalloc(smp_size + + sizeof (struct somap_private)); + smpp = (struct somap_private *) (((caddr_t) smp) + smp_size); + + /* Link the new entry into the list of link maps */ + smp->som_next = NULL; + smpp->spd_prev = link_map_tail; + if(link_map_tail == NULL) /* First link map entered into list */ + link_map_head = link_map_tail = smp; + else { /* Append to end of list */ + link_map_tail->som_next = smp; + link_map_tail = smp; + } + + smp->som_addr = addr; + smp->som_path = path ? strdup(path) : NULL; + smp->som_sod = sodp; + smp->som_dynamic = dp; + smp->som_spd = (caddr_t)smpp; + + smpp->spd_refcount = 0; + smpp->spd_flags = 0; + smpp->spd_parent = parent; + smpp->spd_children = NULL; + smpp->a_text = 0; + smpp->a_data = 0; + smpp->a_bss = 0; +#ifdef SUN_COMPAT + smpp->spd_offset = + (addr==0 && dp && dp->d_version==LD_VERSION_SUN) ? PAGSIZ : 0; +#endif + return smp; +} + +/* + * Remove the specified link map entry from the list of link maps, and free + * the associated storage. + */ + static void +free_link_map(smp) + struct so_map *smp; +{ + struct somap_private *smpp = LM_PRIVATE(smp); + +#ifdef DEBUG /* { */ + xprintf("free_link_map: \"%s\"\n", smp->som_path); +#endif /* } */ + + if(smpp->spd_prev == NULL) /* Removing first entry in list */ + link_map_head = smp->som_next; + else /* Update link of previous entry */ + smpp->spd_prev->som_next = smp->som_next; + + if(smp->som_next == NULL) /* Removing last entry in list */ + link_map_tail = smpp->spd_prev; + else /* Update back link of next entry */ + LM_PRIVATE(smp->som_next)->spd_prev = smpp->spd_prev; + + free(smp->som_path); + free(smp); +} + +/* + * Map the shared object specified by PATH into memory, if it is not + * already mapped. Increment the object's reference count, and return a + * pointer to its link map. + * + * As a special case, if PATH is NULL, it is taken to refer to the main + * program. + * + * SODP is a pointer to the shared object dependency structure that caused + * this object to be requested. PARENT is a pointer to the link map of + * the shared object containing that structure. For a shared object not + * being mapped as a result of a shared object dependency, these pointers + * should be NULL. An example of this is a shared object that is explicitly + * loaded via dlopen(). + * + * The return value is a pointer to the link map for the requested object. + * If the operation failed, the return value is NULL. In that case, an + * error message can be retrieved by calling dlerror(). + */ + static struct so_map * +map_object(path, sodp, parent) + char *path; + struct sod *sodp; + struct so_map *parent; +{ + struct so_map *smp; + struct stat statbuf; + + if(path == NULL) /* Special case for the main program itself */ + smp = link_map_head; + else { + /* + * Check whether the shared object is already mapped. + * We check first for an exact match by pathname. That + * will detect the usual case. If no match is found by + * pathname, then stat the file, and check for a match by + * device and inode. That will detect the less common case + * involving multiple links to the same library. + */ + for(smp = link_map_head; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & (RTLD_MAIN|RTLD_RTLD)) + && smp->som_path != NULL + && strcmp(smp->som_path, path) == 0) + break; + } + if(smp == NULL) { /* Check for a match by device and inode */ + if (stat(path, &statbuf) == -1) { + generror ("cannot stat \"%s\" : %s", + path, strerror(errno)); + return NULL; + } + for (smp = link_map_head; smp != NULL; + smp = smp->som_next) { + struct somap_private *smpp = LM_PRIVATE(smp); + + if (!(smpp->spd_flags & (RTLD_MAIN | RTLD_RTLD)) + && smpp->spd_ino == statbuf.st_ino + && smpp->spd_dev == statbuf.st_dev) + break; + } + } + } + + if (smp == NULL) { /* We must map the object */ + struct _dynamic *dp; + int fd; + caddr_t addr; + struct exec hdr; + struct somap_private *smpp; + + if ((fd = open(path, O_RDONLY, 0)) == -1) { + generror ("open failed for \"%s\" : %s", + path, strerror (errno)); + return NULL; + } + + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + generror ("header read failed for \"%s\"", path); + (void)close(fd); + return NULL; + } + + if (N_BADMAG(hdr)) { + generror ("bad magic number in \"%s\"", path); + (void)close(fd); + return NULL; + } + + /* + * Map the entire address space of the object. It is + * tempting to map just the text segment at first, in + * order to avoid having to use mprotect to change the + * protections of the data segment. But that would not + * be correct. Mmap might find a group of free pages + * large enough to hold the text segment, but not large + * enough for the entire object. When we then mapped + * in the data and BSS segments, they would either be + * non-contiguous with the text segment (if we didn't + * specify MAP_FIXED), or they would map over some + * previously mapped region (if we did use MAP_FIXED). + * The only way we can be sure of getting a contigous + * region that is large enough is to map the entire + * region at once. + */ + if ((addr = mmap(0, hdr.a_text + hdr.a_data + hdr.a_bss, + PROT_READ|PROT_EXEC, + MAP_COPY, fd, 0)) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)close(fd); + return NULL; + } + + (void)close(fd); + + /* Change the data segment to writable */ + if (mprotect(addr + hdr.a_text, hdr.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { + generror ("mprotect failed for \"%s\" : %s", + path, strerror (errno)); + (void)munmap(addr, hdr.a_text + hdr.a_data + hdr.a_bss); + return NULL; + } + + /* Map in pages of zeros for the BSS segment */ + if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_ANON|MAP_COPY|MAP_FIXED, + anon_fd, 0) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)munmap(addr, hdr.a_text + hdr.a_data + hdr.a_bss); + return NULL; + } + + /* Assume _DYNAMIC is the first data item */ + dp = (struct _dynamic *)(addr+hdr.a_text); + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += (long)addr; + + smp = alloc_link_map(path, sodp, parent, addr, dp); + + /* save segment sizes for unmap. */ + smpp = LM_PRIVATE(smp); + smpp->a_text = hdr.a_text; + smpp->a_data = hdr.a_data; + smpp->a_bss = hdr.a_bss; + + /* + * Save the device and inode, so we can detect multiple links + * to the same library. Note, if we reach this point, then + * statbuf is guaranteed to have been filled in. + */ + smpp->spd_dev = statbuf.st_dev; + smpp->spd_ino = statbuf.st_ino; + } + + LM_PRIVATE(smp)->spd_refcount++; + if(LM_PRIVATE(smp)->spd_refcount == 1) { /* First use of object */ + /* + * Recursively map all of the shared objects that this + * one depends upon. + */ + if(map_sods(smp) == -1) { /* Failed */ + unmap_object(smp, 0); /* Clean up */ + return NULL; + } + } + + return smp; +} + +/* + * Map all the shared libraries named in the LD_PRELOAD environment + * variable. + * + * Returns 0 on success, -1 on failure. On failure, an error message can + * be gotten via dlerror(). + */ + static int +map_preload __P((void)) { + char *ld_name = ld_preload; + char *name; + + while ((name = strsep(&ld_name, ":")) != NULL) { + char *path = NULL; + struct so_map *smp = NULL; + + if (*name != '\0') { + path = (strchr(name, '/') != NULL) ? strdup(name) : + rtfindfile(name); + } + if (path == NULL) { + generror("Can't find LD_PRELOAD shared" + " library \"%s\"", name); + } else { + smp = map_object(path, (struct sod *) NULL, + (struct so_map *) NULL); + free(path); + } + if (ld_name != NULL) + *(ld_name - 1) = ':'; + if (smp == NULL) { + /* + * We don't bother to unmap already-loaded libraries + * on failure, because in that case the program is + * about to die anyway. + */ + return -1; + } + } + return 0; +} + +/* + * Map all of the shared objects that a given object depends upon. PARENT is + * a pointer to the link map for the shared object whose dependencies are + * to be mapped. + * + * Returns 0 on success. Returns -1 on failure. In that case, an error + * message can be retrieved by calling dlerror(). + */ + static int +map_sods(parent) + struct so_map *parent; +{ + struct somap_private *parpp = LM_PRIVATE(parent); + struct so_list **soltail = &parpp->spd_children; + long next = LD_NEED(parent->som_dynamic); + + while(next != 0) { + struct sod *sodp = + (struct sod *) (LM_LDBASE(parent) + next); + char *name = + (char *) (LM_LDBASE(parent) + sodp->sod_name); + char *path = NULL; + struct so_map *smp = NULL; + + if(sodp->sod_library) { + path = rtfindlib(name, sodp->sod_major, + sodp->sod_minor); + if(path == NULL && !ld_tracing) { + generror ("Can't find shared library" + " \"lib%s.so.%d.%d\"", name, + sodp->sod_major, sodp->sod_minor); + } + } else { + if(careful && name[0] != '/') { + generror("Shared library path must start" + " with \"/\" for \"%s\"", name); + } else + path = strdup(name); + } + + if(path != NULL) { + smp = map_object(path, sodp, parent); + free(path); + } + + if(smp != NULL) { + struct so_list *solp = (struct so_list *) + xmalloc(sizeof(struct so_list)); + solp->sol_map = smp; + solp->sol_next = NULL; + *soltail = solp; + soltail = &solp->sol_next; + } else if(ld_tracing) { + /* + * Allocate a dummy map entry so that we will get the + * "not found" message. + */ + (void)alloc_link_map(NULL, sodp, parent, 0, 0); + } else if (ld_ignore_missing_objects) { + char *msg; + /* + * Call __dlerror() even it we're not going to use + * the message, in order to clear the saved message. + */ + msg = __dlerror(); /* Should never be NULL */ + if (!ld_suppress_warnings) + warnx("warning: %s", msg); + } else /* Give up */ + break; + + next = sodp->sod_next; + } + + if(next != 0) { + /* + * Oh drat, we have to clean up a mess. + * + * We failed to load a shared object that we depend upon. + * So now we have to unload any dependencies that we had + * already successfully loaded prior to the error. + * + * Cleaning up doesn't matter so much for the initial + * loading of the program, since any failure is going to + * terminate the program anyway. But it is very important + * to clean up properly when something is being loaded + * via dlopen(). + */ + struct so_list *solp; + + while((solp = parpp->spd_children) != NULL) { + unmap_object(solp->sol_map, 0); + parpp->spd_children = solp->sol_next; + free(solp); + } + + return -1; + } + + return 0; +} + +/* + * Relocate and initialize the tree of shared objects rooted at the given + * link map entry. Returns 0 on success, or -1 on failure. On failure, + * an error message can be retrieved via dlerror(). + */ + static int +reloc_and_init(root, bind_now) + struct so_map *root; + int bind_now; +{ + struct so_map *smp; + + /* + * Relocate all newly-loaded objects. We avoid recursion for this + * step by taking advantage of a few facts. This function is called + * only when there are in fact some newly-loaded objects to process. + * Furthermore, all newly-loaded objects will have their link map + * entries at the end of the link map list. And, the root of the + * tree of objects just loaded will have been the first to be loaded + * and therefore the first new object in the link map list. Finally, + * we take advantage of the fact that we can relocate the newly-loaded + * objects in any order. + * + * All these facts conspire to let us simply loop over the tail + * portion of the link map list, relocating each object so + * encountered. + */ + for(smp = root; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)) { + if(reloc_map(smp, bind_now) < 0) + return -1; + } + } + + /* + * Copy any relocated initialized data. Again, we can just loop + * over the appropriate portion of the link map list. + */ + for(smp = root; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)) + reloc_copy(smp); + } + + /* + * Call any object initialization routines. + * + * Here, the order is very important, and we cannot simply loop + * over the newly-loaded objects as we did before. Rather, we + * have to initialize the tree of new objects depth-first, and + * process the sibling objects at each level in reverse order + * relative to the dependency list. + * + * Here is the reason we initialize depth-first. If an object + * depends on one or more other objects, then the objects it + * depends on should be initialized first, before the parent + * object itself. For it is possible that the parent's + * initialization routine will need the services provided by the + * objects it depends on -- and those objects had better already + * be initialized. + * + * We initialize the objects at each level of the tree in reverse + * order for a similar reason. When an object is linked with + * several libraries, it is common for routines in the earlier + * libraries to call routines in the later libraries. So, again, + * the later libraries need to be initialized first. + * + * The upshot of these rules is that we have to use recursion to + * get the libraries initialized in the best order. But the + * recursion is never likely to be very deep. + */ + init_object(root); + + return 0; +} + +/* + * Remove a reference to the shared object specified by SMP. If no + * references remain, unmap the object and, recursively, its descendents. + * This function also takes care of calling the finalization routines for + * objects that are removed. + * + * If KEEP is true, then the actual calls to munmap() are skipped, + * and the object is kept in memory. That is used only for finalization, + * from dlexit(), when the program is exiting. There are two reasons + * for it. First, the program is exiting and there is no point in + * spending the time to explicitly unmap its shared objects. Second, + * even after dlexit() has been called, there are still a couple of + * calls that are made to functions in libc. (This is really a bug + * in crt0.) So libc and the main program, at least, must remain + * mapped in that situation. + * + * Under no reasonable circumstances should this function fail. If + * anything goes wrong, we consider it an internal error, and report + * it with err(). + */ + static void +unmap_object(smp, keep) + struct so_map *smp; + int keep; +{ + struct somap_private *smpp = LM_PRIVATE(smp); + + smpp->spd_refcount--; + if(smpp->spd_refcount == 0) { /* Finished with this object */ + struct so_list *solp; + + if(smpp->spd_flags & RTLD_INIT) { /* Was initialized */ + /* + * Call the object's finalization routine. For + * backward compatibility, we first try to call + * ".fini". If that does not exist, we call + * "__fini". + */ + if(call_map(smp, ".fini") == -1) + call_map(smp, "__fini"); + } + + /* Recursively unreference the object's descendents */ + while((solp = smpp->spd_children) != NULL) { + unmap_object(solp->sol_map, keep); + smpp->spd_children = solp->sol_next; + free(solp); + } + + if(!keep) { /* Unmap the object from memory */ + if(munmap(smp->som_addr, + smpp->a_text + smpp->a_data + smpp->a_bss) < 0) + err(1, "internal error 1: munmap failed"); + + /* Unlink and free the object's link map entry */ + free_link_map(smp); + } + } +} + +static inline int +check_text_reloc(r, smp, addr) +struct relocation_info *r; +struct so_map *smp; +caddr_t addr; +{ + char *sym; + + if (addr >= LM_ETEXT(smp)) + return 0; + + if (RELOC_EXTERN_P(r)) + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp, RELOC_SYMBOL(r))->nz_strx; + else + sym = ""; + + if (!ld_suppress_warnings && ld_warn_non_pure_code) + warnx("warning: non pure code in %s at %x (%s)", + smp->som_path, r->r_address, sym); + + if (smp->som_write == 0 && + mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + generror ("mprotect failed for \"%s\" : %s", + smp->som_path, strerror (errno)); + return -1; + } + + smp->som_write = 1; + return 0; +} + +static int +reloc_map(smp, bind_now) + struct so_map *smp; + int bind_now; +{ + /* + * Caching structure for reducing the number of calls to + * lookup() during relocation. + * + * While relocating a given shared object, the dynamic linker + * maintains a caching vector that is directly indexed by + * the symbol number in the relocation entry. The first time + * a given symbol is looked up, the caching vector is + * filled in with a pointer to the symbol table entry, and + * a pointer to the so_map of the shared object in which the + * symbol was defined. On subsequent uses of the same symbol, + * that information is retrieved directly from the caching + * vector, without calling lookup() again. + * + * A symbol that is referenced in a relocation entry is + * typically referenced in many relocation entries, so this + * caching reduces the number of calls to lookup() + * dramatically. The overall improvement in the speed of + * dynamic linking is also dramatic -- as much as a factor + * of three for programs that use many shared libaries. + */ + struct cacheent { + struct nzlist *np; /* Pointer to symbol entry */ + struct so_map *src_map; /* Shared object that defined symbol */ + }; + + struct _dynamic *dp = smp->som_dynamic; + struct relocation_info *r = LM_REL(smp); + struct relocation_info *rend = r + LD_RELSZ(dp)/sizeof(*r); + long symbolbase = (long)LM_SYMBOL(smp, 0); + char *stringbase = LM_STRINGS(smp); + int symsize = LD_VERSION_NZLIST_P(dp->d_version) ? + sizeof(struct nzlist) : + sizeof(struct nlist); + long numsyms = LD_STABSZ(dp) / symsize; + size_t cachebytes = numsyms * sizeof(struct cacheent); + struct cacheent *symcache = + (struct cacheent *) alloca(cachebytes); + + if(symcache == NULL) { + generror("Cannot allocate symbol caching vector for %s", + smp->som_path); + return -1; + } + bzero(symcache, cachebytes); + + if (LD_PLTSZ(dp)) + md_fix_jmpslot(LM_PLT(smp), + (long)LM_PLT(smp), (long)binder_entry); + + for (; r < rend; r++) { + char *sym; + caddr_t addr; + + /* + * Objects linked with "-Bsymbolic" can end up having unused + * relocation entries at the end. These can be detected by + * the fact that they have an address of 0. + */ + if(r->r_address == 0) /* Finished relocating this object */ + break; + + addr = smp->som_addr + r->r_address; + if (check_text_reloc(r, smp, addr) < 0) + return -1; + + if (RELOC_EXTERN_P(r)) { + struct so_map *src_map = NULL; + struct nzlist *p, *np; + long relocation; + + if (RELOC_JMPTAB_P(r) && !bind_now) + continue; + + p = (struct nzlist *) + (symbolbase + symsize * RELOC_SYMBOL(r)); + + if (p->nz_type == (N_SETV + N_EXT)) + src_map = smp; + + sym = stringbase + p->nz_strx; + + /* + * Look up the symbol, checking the caching + * vector first. + */ + np = symcache[RELOC_SYMBOL(r)].np; + if(np != NULL) /* Symbol already cached */ + src_map = symcache[RELOC_SYMBOL(r)].src_map; + else { /* Symbol not cached yet */ + np = lookup(sym, &src_map, RELOC_JMPTAB_P(r)); + /* + * Record the needed information about + * the symbol in the caching vector, + * so that we won't have to call + * lookup the next time we encounter + * the symbol. + */ + symcache[RELOC_SYMBOL(r)].np = np; + symcache[RELOC_SYMBOL(r)].src_map = src_map; + } + + if (np == NULL) { + generror ("Undefined symbol \"%s\" in %s:%s", + sym, main_progname, smp->som_path); + return -1; + } + + /* + * Found symbol definition. + * If it's in a link map, adjust value + * according to the load address of that map. + * Otherwise it's a run-time allocated common + * whose value is already up-to-date. + */ + relocation = np->nz_value; + if (src_map) + relocation += (long)src_map->som_addr; + + if (RELOC_JMPTAB_P(r)) { + md_bind_jmpslot(relocation, addr); + continue; + } + + relocation += md_get_addend(r, addr); + + if (RELOC_PCREL_P(r)) + relocation -= (long)smp->som_addr; + + if (RELOC_COPY_P(r) && src_map) { + (void)enter_rts(sym, + (long)addr, + N_DATA + N_EXT, + src_map->som_addr + np->nz_value, + np->nz_size, src_map); + continue; + } + + md_relocate(r, relocation, addr, 0); + } else { + md_relocate(r, +#ifdef SUN_COMPAT + md_get_rt_segment_addend(r, addr) +#else + md_get_addend(r, addr) +#endif + + (long)smp->som_addr, addr, 0); + } + + } + + if (smp->som_write) { + if (mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_EXEC) == -1) { + generror ("mprotect failed for \"%s\" : %s", + smp->som_path, strerror (errno)); + return -1; + } + smp->som_write = 0; + } + return 0; +} + + static void +reloc_copy(smp) + struct so_map *smp; +{ + struct rt_symbol *rtsp; + + for (rtsp = rt_symbol_head; rtsp; rtsp = rtsp->rt_next) + if ((rtsp->rt_smp == NULL || rtsp->rt_smp == smp) && + rtsp->rt_sp->nz_type == N_DATA + N_EXT) { + bcopy(rtsp->rt_srcaddr, (caddr_t)rtsp->rt_sp->nz_value, + rtsp->rt_sp->nz_size); + } +} + + static void +init_object(smp) + struct so_map *smp; +{ + struct somap_private *smpp = LM_PRIVATE(smp); + + if(!(smpp->spd_flags & RTLD_INIT)) { /* Not initialized yet */ + smpp->spd_flags |= RTLD_INIT; + + /* Make sure all the children are initialized */ + if(smpp->spd_children != NULL) + init_sods(smpp->spd_children); + + if(call_map(smp, ".init") == -1) + call_map(smp, "__init"); + } +} + + static void +init_sods(solp) + struct so_list *solp; +{ + /* Recursively initialize the rest of the list */ + if(solp->sol_next != NULL) + init_sods(solp->sol_next); + + /* Initialize the first element of the list */ + init_object(solp->sol_map); +} + + +/* + * Call a function in a given shared object. SMP is the shared object, and + * SYM is the name of the function. + * + * Returns 0 on success, or -1 if the symbol was not found. Failure is not + * necessarily an error condition, so no error message is generated. + */ + static int +call_map(smp, sym) + struct so_map *smp; + char *sym; +{ + struct so_map *src_map = smp; + struct nzlist *np; + + np = lookup(sym, &src_map, 1); + if (np) { + (*(void (*)())(src_map->som_addr + np->nz_value))(); + return 0; + } + + return -1; +} + +/* + * Run-time common symbol table. + */ + +#define RTC_TABSIZE 57 +static struct rt_symbol *rt_symtab[RTC_TABSIZE]; + +/* + * Compute hash value for run-time symbol table + */ + static inline int +hash_string(key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* + * Lookup KEY in the run-time common symbol table. + */ + + static inline struct rt_symbol * +lookup_rts(key) + char *key; +{ + register int hashval; + register struct rt_symbol *rtsp; + + /* Determine which bucket. */ + + hashval = hash_string(key) % RTC_TABSIZE; + + /* Search the bucket. */ + + for (rtsp = rt_symtab[hashval]; rtsp; rtsp = rtsp->rt_link) + if (strcmp(key, rtsp->rt_sp->nz_name) == 0) + return rtsp; + + return NULL; +} + + static struct rt_symbol * +enter_rts(name, value, type, srcaddr, size, smp) + char *name; + long value; + int type; + caddr_t srcaddr; + long size; + struct so_map *smp; +{ + register int hashval; + register struct rt_symbol *rtsp, **rpp; + + /* Determine which bucket */ + hashval = hash_string(name) % RTC_TABSIZE; + + /* Find end of bucket */ + for (rpp = &rt_symtab[hashval]; *rpp; rpp = &(*rpp)->rt_link) + continue; + + /* Allocate new common symbol */ + rtsp = (struct rt_symbol *)malloc(sizeof(struct rt_symbol)); + rtsp->rt_sp = (struct nzlist *)malloc(sizeof(struct nzlist)); + rtsp->rt_sp->nz_name = strdup(name); + rtsp->rt_sp->nz_value = value; + rtsp->rt_sp->nz_type = type; + rtsp->rt_sp->nz_size = size; + rtsp->rt_srcaddr = srcaddr; + rtsp->rt_smp = smp; + rtsp->rt_link = NULL; + + /* Link onto linear list as well */ + rtsp->rt_next = rt_symbol_head; + rt_symbol_head = rtsp; + + *rpp = rtsp; + + return rtsp; +} + + +/* + * Lookup NAME in the link maps. The link map producing a definition + * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search is + * confined to that map. If STRONG is set, the symbol returned must + * have a proper type (used by binder()). + */ + static struct nzlist * +lookup(name, src_map, strong) + char *name; + struct so_map **src_map; /* IN/OUT */ + int strong; +{ + long common_size = 0; + struct so_map *smp; + struct rt_symbol *rtsp; + + if ((rtsp = lookup_rts(name)) != NULL) + return rtsp->rt_sp; + + /* + * Search all maps for a definition of NAME + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + int buckets; + long hashval; + struct rrs_hash *hp; + char *cp; + struct nzlist *np; + + /* Some local caching */ + long symbolbase; + struct rrs_hash *hashbase; + char *stringbase; + int symsize; + + if (*src_map && smp != *src_map) + continue; + + if ((buckets = LD_BUCKETS(smp->som_dynamic)) == 0) + continue; + + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + +restart: + /* + * Compute bucket in which the symbol might be found. + */ + for (hashval = 0, cp = name; *cp; cp++) + hashval = (hashval << 1) + *cp; + + hashval = (hashval & 0x7fffffff) % buckets; + + hashbase = LM_HASH(smp); + hp = hashbase + hashval; + if (hp->rh_symbolnum == -1) + /* Nothing in this bucket */ + continue; + + symbolbase = (long)LM_SYMBOL(smp, 0); + stringbase = LM_STRINGS(smp); + symsize = LD_VERSION_NZLIST_P(smp->som_dynamic->d_version)? + sizeof(struct nzlist) : + sizeof(struct nlist); + while (hp) { + np = (struct nzlist *) + (symbolbase + hp->rh_symbolnum * symsize); + cp = stringbase + np->nz_strx; + if (strcmp(cp, name) == 0) + break; + if (hp->rh_next == 0) + hp = NULL; + else + hp = hashbase + hp->rh_next; + } + if (hp == NULL) + /* Nothing in this bucket */ + continue; + + /* + * We have a symbol with the name we're looking for. + */ + if (np->nz_type == N_INDR+N_EXT) { + /* + * Next symbol gives the aliased name. Restart + * search with new name and confine to this map. + */ + name = stringbase + (++np)->nz_strx; + *src_map = smp; + goto restart; + } + + if (np->nz_value == 0) + /* It's not a definition */ + continue; + + if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) { + if (np->nz_other == AUX_FUNC) { + /* It's a weak function definition */ + if (strong) + continue; + } else { + /* It's a common, note value and continue search */ + if (common_size < np->nz_value) + common_size = np->nz_value; + continue; + } + } + + *src_map = smp; + return np; + } + + if (common_size == 0) + /* Not found */ + return NULL; + + /* + * It's a common, enter into run-time common symbol table. + */ + rtsp = enter_rts(name, (long)calloc(1, common_size), + N_UNDF + N_EXT, 0, common_size, NULL); + +#if DEBUG + xprintf("Allocating common: %s size %d at %#x\n", name, common_size, + rtsp->rt_sp->nz_value); +#endif + + return rtsp->rt_sp; +} + +/* + * This routine is called from the jumptable to resolve + * procedure calls to shared objects. + */ + long +binder(jsp) + jmpslot_t *jsp; +{ + struct so_map *smp, *src_map = NULL; + long addr; + char *sym; + struct nzlist *np; + int index; + + /* + * Find the PLT map that contains JSP. + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PLT(smp) < jsp && + jsp < LM_PLT(smp) + LD_PLTSZ(smp->som_dynamic)/sizeof(*jsp)) + break; + } + + if (smp == NULL) + errx(1, "Call to binder from unknown location: %#x\n", jsp); + + index = jsp->reloc_index & JMPSLOT_RELOC_MASK; + + /* Get the local symbol this jmpslot refers to */ + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp,RELOC_SYMBOL(&LM_REL(smp)[index]))->nz_strx; + + np = lookup(sym, &src_map, 1); + if (np == NULL) + errx(1, "Undefined symbol \"%s\" called from %s:%s at %#x", + sym, main_progname, smp->som_path, jsp); + + /* Fixup jmpslot so future calls transfer directly to target */ + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + md_fix_jmpslot(jsp, (long)jsp, addr); + +#if DEBUG + xprintf(" BINDER: %s located at = %#x in %s\n", sym, addr, + src_map->som_path); +#endif + return addr; +} + +static struct hints_header *hheader; /* NULL means not mapped */ +static struct hints_bucket *hbuckets; +static char *hstrtab; + +/* + * Map the hints file into memory, if it is not already mapped. Returns + * 0 on success, or -1 on failure. + */ + static int +maphints __P((void)) +{ + static int hints_bad; /* TRUE if hints are unusable */ + static int paths_added; + int hfd; + struct hints_header hdr; + caddr_t addr; + + if (hheader != NULL) /* Already mapped */ + return 0; + + if (hints_bad) /* Known to be corrupt or unavailable */ + return -1; + + if ((hfd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { + hints_bad = 1; + return -1; + } + + /* Read the header and check it */ + + if (read(hfd, &hdr, sizeof hdr) != sizeof hdr || + HH_BADMAG(hdr) || + (hdr.hh_version != LD_HINTS_VERSION_1 && + hdr.hh_version != LD_HINTS_VERSION_2)) { + close(hfd); + hints_bad = 1; + return -1; + } + + /* Map the hints into memory */ + + addr = mmap(0, hdr.hh_ehints, PROT_READ, MAP_SHARED, hfd, 0); + if (addr == (caddr_t)-1) { + close(hfd); + hints_bad = 1; + return -1; + } + + close(hfd); + + hheader = (struct hints_header *)addr; + hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab); + hstrtab = (char *)(addr + hheader->hh_strtab); + /* pluck out the system ldconfig path */ + if (hheader->hh_version >= LD_HINTS_VERSION_2 && !paths_added) { + add_search_path(hstrtab + hheader->hh_dirlist); + paths_added = 1; + } + + return 0; +} + +/* + * Unmap the hints file, if it is currently mapped. + */ + static void +unmaphints() +{ + if (hheader != NULL) { + munmap((caddr_t)hheader, hheader->hh_ehints); + hheader = NULL; + } +} + + int +hinthash(cp, vmajor) + char *cp; + int vmajor; +{ + int k = 0; + + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; + + return k; +} + +#undef major +#undef minor + +/* + * Search for a library in the hints generated by ldconfig. On success, + * returns the full pathname of the matching library. This string is + * always dynamically allocated on the heap. + * + * Returns the minor number of the matching library via the pointer + * argument MINORP. + * + * Returns NULL if the library cannot be found. + */ + static char * +findhint(name, major, minorp) + char *name; + int major; + int *minorp; +{ + struct hints_bucket *bp = + hbuckets + (hinthash(name, major) % hheader->hh_nbucket); + + while (1) { + /* Sanity check */ + if (bp->hi_namex >= hheader->hh_strtab_sz) { + warnx("Bad name index: %#x\n", bp->hi_namex); + break; + } + if (bp->hi_pathx >= hheader->hh_strtab_sz) { + warnx("Bad path index: %#x\n", bp->hi_pathx); + break; + } + + /* + * We accept the current hints entry if its name matches + * and its major number matches. We don't have to search + * for the best minor number, because that was already + * done by "ldconfig" when it built the hints file. + */ + if (strcmp(name, hstrtab + bp->hi_namex) == 0 && + bp->hi_major == major) { + struct stat s; + + if (stat(hstrtab + bp->hi_pathx, &s) == -1) + return NULL; /* Doesn't actually exist */ + *minorp = bp->hi_ndewey >= 2 ? bp->hi_minor : -1; + return strdup(hstrtab + bp->hi_pathx); + } + + if (bp->hi_next == -1) + break; + + /* Move on to next in bucket */ + bp = &hbuckets[bp->hi_next]; + } + + /* No hints available for name */ + return NULL; +} + +/* + * Search for the given shared library. On success, returns a string + * containing the full pathname for the library. This string is always + * dynamically allocated on the heap. + * + * Returns NULL if the library cannot be found. + */ + static char * +rtfindlib(name, major, minor) + char *name; + int major, minor; +{ + char *ld_path = ld_library_path; + char *path = NULL; + int realminor = -1; + + if (ld_path != NULL) { /* First, search the directories in ld_path */ + /* + * There is no point in trying to use the hints file for this. + */ + char *dir; + + while (path == NULL && (dir = strsep(&ld_path, ":")) != NULL) { + path = search_lib_dir(dir, name, &major, &realminor, 0); + if (ld_path != NULL) + *(ld_path - 1) = ':'; + } + } + + if (path == NULL && maphints() == 0) /* Search the hints file */ + path = findhint(name, major, &realminor); + + if (path == NULL) /* Search the standard directories */ + path = findshlib(name, &major, &realminor, 0); + + if (path != NULL && realminor < minor && !ld_suppress_warnings) { + warnx("warning: %s: minor version %d" + " older than expected %d, using it anyway", + path, realminor, minor); + } + + return path; +} + +/* + * Search for the given shared library file. This is similar to rtfindlib, + * except that the argument is the actual name of the desired library file. + * Thus there is no need to worry about version numbers. The return value + * is a string containing the full pathname for the library. This string + * is always dynamically allocated on the heap. + * + * Returns NULL if the library cannot be found. + */ + static char * +rtfindfile(name) + char *name; +{ + char *ld_path = ld_library_path; + char *path = NULL; + + if (ld_path != NULL) { /* First, search the directories in ld_path */ + char *dir; + + while (path == NULL && (dir = strsep(&ld_path, ":")) != NULL) { + struct stat sb; + + path = concat(dir, "/", name); + if (lstat(path, &sb) == -1) { /* Does not exist */ + free(path); + path = NULL; + } + if (ld_path != NULL) + *(ld_path - 1) = ':'; + } + } + + /* + * We don't search the hints file. It is organized around major + * and minor version numbers, so it is not suitable for finding + * a specific file name. + */ + + if (path == NULL) /* Search the standard directories */ + path = find_lib_file(name); + + return path; +} + +/* + * Buffer for error messages and a pointer that is set to point to the buffer + * when a error occurs. It acts as a last error flag, being set to NULL + * after an error is returned. + */ +#define DLERROR_BUF_SIZE 512 +static char dlerror_buf [DLERROR_BUF_SIZE]; +static char *dlerror_msg = NULL; + + + static void * +__dlopen(path, mode) + char *path; + int mode; +{ + struct so_map *old_tail = link_map_tail; + struct so_map *smp; + int bind_now = mode == RTLD_NOW; + + /* + * path == NULL is handled by map_object() + */ + + anon_open(); + + /* Map the object, and the objects on which it depends */ + smp = map_object(path, (struct sod *) NULL, (struct so_map *) NULL); + if(smp == NULL) /* Failed */ + return NULL; + LM_PRIVATE(smp)->spd_flags |= RTLD_DL; + + /* Relocate and initialize all newly-mapped objects */ + if(link_map_tail != old_tail) { /* We have mapped some new objects */ + if(reloc_and_init(smp, bind_now) == -1) /* Failed */ + return NULL; + } + + unmaphints(); + anon_close(); + + return smp; +} + + static int +__dlclose(fd) + void *fd; +{ + struct so_map *smp = (struct so_map *)fd; + struct so_map *scanp; + +#ifdef DEBUG + xprintf("dlclose(%s): refcount = %d\n", smp->som_path, + LM_PRIVATE(smp)->spd_refcount); +#endif + /* Check the argument for validity */ + for(scanp = link_map_head; scanp != NULL; scanp = scanp->som_next) + if(scanp == smp) /* We found the map in the list */ + break; + if(scanp == NULL || !(LM_PRIVATE(smp)->spd_flags & RTLD_DL)) { + generror("Invalid argument to dlclose"); + return -1; + } + + unmap_object(smp, 0); + + return 0; +} + +/* + * This form of dlsym is obsolete. Current versions of crt0 don't call + * it. It can still be called by old executables that were linked with + * old versions of crt0. + */ + static void * +__dlsym(fd, sym) + void *fd; + char *sym; +{ + if (fd == RTLD_NEXT) { + generror("RTLD_NEXT not supported by this version of" + " /usr/lib/crt0.o"); + return NULL; + } + return __dlsym3(fd, sym, NULL); +} + + static void * +__dlsym3(fd, sym, retaddr) + void *fd; + char *sym; + void *retaddr; +{ + struct so_map *smp; + struct so_map *src_map; + struct nzlist *np; + long addr; + + if (fd == RTLD_NEXT) { + /* Find the shared object that contains the caller. */ + for (smp = link_map_head; smp != NULL; smp = smp->som_next) { + void *textbase = smp->som_addr + LM_TXTADDR(smp); + void *textlimit = LM_ETEXT(smp); + + if (textbase <= retaddr && retaddr < textlimit) + break; + } + if (smp == NULL) { + generror("Cannot determine caller's shared object"); + return NULL; + } + smp = smp->som_next; + if (smp != NULL && LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + smp = smp->som_next; + if (smp == NULL) { + generror("No next shared object for RTLD_NEXT"); + return NULL; + } + do { + src_map = smp; + np = lookup(sym, &src_map, 1); + } while (np == NULL && (smp = smp->som_next) != NULL); + } else { + smp = (struct so_map *)fd; + src_map = NULL; + + /* + * Restrict search to passed map if dlopen()ed. + */ + if (smp != NULL && LM_PRIVATE(smp)->spd_flags & RTLD_DL) + src_map = smp; + + np = lookup(sym, &src_map, 1); + } + + if (np == NULL) { + generror("Undefined symbol"); + return NULL; + } + + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + return (void *)addr; +} + + static char * +__dlerror __P((void)) +{ + char *err; + + err = dlerror_msg; + dlerror_msg = NULL; /* Next call will return NULL */ + + return err; +} + + static void +__dlexit __P((void)) +{ +#ifdef DEBUG +xprintf("__dlexit called\n"); +#endif + + unmap_object(link_map_head, 1); +} + +/* + * Print the current error message and exit with failure status. + */ +static void +die __P((void)) +{ + char *msg; + + fprintf(stderr, "ld.so failed"); + if ((msg = __dlerror()) != NULL) + fprintf(stderr, ": %s", msg); + putc('\n', stderr); + _exit(1); +} + + +/* + * Generate an error message that can be later be retrieved via dlerror. + */ +static void +#if __STDC__ +generror(char *fmt, ...) +#else +generror(fmt, va_alist) +char *fmt; +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsnprintf (dlerror_buf, DLERROR_BUF_SIZE, fmt, ap); + dlerror_msg = dlerror_buf; + + va_end(ap); +} + +void +#if __STDC__ +xprintf(char *fmt, ...) +#else +xprintf(fmt, va_alist) +char *fmt; +#endif +{ + char buf[256]; + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + vsnprintf(buf, sizeof(buf), fmt, ap); + (void)write(1, buf, strlen(buf)); + va_end(ap); +} + +/* + * rt_readenv() etc. + * + * Do a sweep over the environment once only, pick up what + * looks interesting. + * + * This is pretty obscure, but is relatively simple. Simply + * look at each environment variable, if it starts with "LD_" then + * look closer at it. If it's in our table, set the variable + * listed. effectively, this is like: + * ld_preload = careful ? NULL : getenv("LD_PRELOAD"); + * except that the environment is scanned once only to pick up all + * known variables, rather than scanned multiple times for each + * variable. + * + * If an environment variable of interest is set to the empty string, we + * treat it as if it were unset. + */ + +#define L(n, u, v) { n, sizeof(n) - 1, u, v }, +struct env_scan_tab { + char *name; + int len; + int unsafe; + char **value; +} scan_tab[] = { + L("LD_LIBRARY_PATH=", 1, &ld_library_path) + L("LD_PRELOAD=", 1, &ld_preload) + L("LD_IGNORE_MISSING_OBJECTS=", 1, &ld_ignore_missing_objects) + L("LD_TRACE_LOADED_OBJECTS=", 0, &ld_tracing) + L("LD_BIND_NOW=", 0, &ld_bind_now) + L("LD_SUPPRESS_WARNINGS=", 0, &ld_suppress_warnings) + L("LD_WARN_NON_PURE_CODE=", 0, &ld_warn_non_pure_code) + { NULL, 0, NULL } +}; +#undef L + +static void +rt_readenv() +{ + char **p = environ; + char *v; + struct env_scan_tab *t; + + /* for each string in the environment... */ + while ((v = *p++)) { + + /* check for LD_xxx */ + if (v[0] != 'L' || v[1] != 'D' || v[2] != '_') + continue; + + for (t = scan_tab; t->name; t++) { + if (careful && t->unsafe) + continue; /* skip for set[ug]id */ + if (strncmp(t->name, v, t->len) == 0) { + if (*(v + t->len) != '\0') /* Not empty */ + *t->value = v + t->len; + break; + } + } + } +} diff --git a/libexec/rtld-aout/shlib.c b/libexec/rtld-aout/shlib.c new file mode 100644 index 0000000..efb0bb6 --- /dev/null +++ b/libexec/rtld-aout/shlib.c @@ -0,0 +1,342 @@ +/* + * 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$ + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <a.out.h> +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <link.h> +#include "shlib.h" +#include "support.h" + +/* + * Standard directories to search for files specified by -l. + */ +#ifndef STANDARD_SEARCH_DIRS +#define STANDARD_SEARCH_DIRS "/usr/lib" +#endif + +/* + * Actual vector of library search directories, + * including `-L'ed and LD_LIBRARY_PATH spec'd ones. + */ +char **search_dirs; +int n_search_dirs; + +char *standard_search_dirs[] = { + STANDARD_SEARCH_DIRS +}; + + +void +add_search_dir(name) + char *name; +{ + int n; + + for (n = 0; n < n_search_dirs; n++) + if (strcmp(search_dirs[n], name) == 0) + return; + n_search_dirs++; + search_dirs = (char **) + xrealloc(search_dirs, n_search_dirs * sizeof search_dirs[0]); + search_dirs[n_search_dirs - 1] = strdup(name); +} + +void +add_search_path(path) +char *path; +{ + register char *cp, *dup; + + if (path == NULL) + return; + + /* Add search directories from `path' */ + path = dup = strdup(path); + while ((cp = strsep(&path, ":")) != NULL) + add_search_dir(cp); + free(dup); +} + +void +std_search_path() +{ + int i, n; + + /* Append standard search directories */ + n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; + for (i = 0; i < n; i++) + add_search_dir(standard_search_dirs[i]); +} + +/* + * Return true if CP points to a valid dewey number. + * Decode and leave the result in the array DEWEY. + * Return the number of decoded entries in DEWEY. + */ + +int +getdewey(dewey, cp) +int dewey[]; +char *cp; +{ + int i, n; + + for (n = 0, i = 0; i < MAXDEWEY; i++) { + if (*cp == '\0') + break; + + if (*cp == '.') cp++; + if (!isdigit(*cp)) + return 0; + + dewey[n++] = strtol(cp, &cp, 10); + } + + return n; +} + +/* + * Compare two dewey arrays. + * Return -1 if `d1' represents a smaller value than `d2'. + * Return 1 if `d1' represents a greater value than `d2'. + * Return 0 if equal. + */ +int +cmpndewey(d1, n1, d2, n2) +int d1[], d2[]; +int n1, n2; +{ + register int i; + + for (i = 0; i < n1 && i < n2; i++) { + if (d1[i] < d2[i]) + return -1; + if (d1[i] > d2[i]) + return 1; + } + + if (n1 == n2) + return 0; + + if (i == n1) + return -1; + + if (i == n2) + return 1; + + errx(1, "cmpndewey: cant happen"); + return 0; +} + +/* + * Search directories for a shared library matching the given + * major and minor version numbers. See search_lib_dir() below for + * the detailed matching rules. + * + * As soon as a directory with an acceptable match is found, the search + * terminates. Subsequent directories are not searched for a better + * match. This is in conformance with the SunOS searching rules. Also, + * it avoids a lot of directory searches that are virtually guaranteed to + * be fruitless. + * + * The return value is a full pathname to the matching library. The + * string is dynamically allocated. If no matching library is found, the + * function returns NULL. + */ + +char * +findshlib(name, majorp, minorp, do_dot_a) +char *name; +int *majorp, *minorp; +int do_dot_a; +{ + int i; + + for (i = 0; i < n_search_dirs; i++) { + char *path; + + path = search_lib_dir(search_dirs[i], name, majorp, minorp, + do_dot_a); + if(path != NULL) + return path; + } + + return NULL; +} + +/* + * Search library directories for a file with the given name. The + * return value is a full pathname to the matching file. The string + * is dynamically allocated. If no matching file is found, the function + * returns NULL. + */ + +char * +find_lib_file(name) + char *name; +{ + int i; + + for (i = 0; i < n_search_dirs; i++) { + char *path = concat(search_dirs[i], "/", name); + struct stat sb; + + if (lstat(path, &sb) != -1) /* We found it */ + return path; + + free(path); + } + + return NULL; +} + +/* + * Search a given directory for a library (preferably shared) satisfying + * the given criteria. + * + * The matching rules are as follows: + * + * if(*majorp == -1) + * find the library with the highest major version; + * else + * insist on a major version identical to *majorp; + * + * Always find the library with the highest minor version; + * if(*minorp != -1) + * insist on a minor version >= *minorp; + * + * It is invalid to specify a specific minor number while wildcarding + * the major number. + * + * The actual major and minor numbers found are returned via the pointer + * arguments. + * + * A suitable shared library is always preferred over a static (.a) library. + * If do_dot_a is false, then a static library will not be accepted in + * any case. + * + * The return value is a full pathname to the matching library. The + * string is dynamically allocated. If no matching library is found, the + * function returns NULL. + */ + +char * +search_lib_dir(dir, name, majorp, minorp, do_dot_a) + char *dir; + char *name; + int *majorp; + int *minorp; + int do_dot_a; +{ + int namelen; + DIR *dd; + struct dirent *dp; + int best_dewey[MAXDEWEY]; + int best_ndewey; + char dot_a_name[MAXNAMLEN+1]; + char dot_so_name[MAXNAMLEN+1]; + + if((dd = opendir(dir)) == NULL) + return NULL; + + namelen = strlen(name); + best_ndewey = 0; + dot_a_name[0] = '\0'; + dot_so_name[0] = '\0'; + + while((dp = readdir(dd)) != NULL) { + char *extension; + + if(strlen(dp->d_name) < 3 + namelen + 2 || /* lib+xxx+.a */ + strncmp(dp->d_name, "lib", 3) != 0 || + strncmp(dp->d_name + 3, name, namelen) != 0 || + dp->d_name[3+namelen] != '.') + continue; + + extension = dp->d_name + 3 + namelen + 1; /* a or so.* */ + + if(strncmp(extension, "so.", 3) == 0) { + int cur_dewey[MAXDEWEY]; + int cur_ndewey; + + cur_ndewey = getdewey(cur_dewey, extension+3); + if(cur_ndewey == 0) /* No version number */ + continue; + + if(*majorp != -1) { /* Need exact match on major */ + if(cur_dewey[0] != *majorp) + continue; + if(*minorp != -1) { /* Need minor >= minimum */ + if(cur_ndewey < 2 || + cur_dewey[1] < *minorp) + continue; + } + } + + if(cmpndewey(cur_dewey, cur_ndewey, best_dewey, + best_ndewey) <= 0) /* No better than prior match */ + continue; + + /* We found a better match */ + strcpy(dot_so_name, dp->d_name); + bcopy(cur_dewey, best_dewey, + cur_ndewey * sizeof best_dewey[0]); + best_ndewey = cur_ndewey; + } else if(do_dot_a && strcmp(extension, "a") == 0) + strcpy(dot_a_name, dp->d_name); + } + closedir(dd); + + if(dot_so_name[0] != '\0') { + *majorp = best_dewey[0]; + if(best_ndewey >= 2) + *minorp = best_dewey[1]; + return concat(dir, "/", dot_so_name); + } + + if(dot_a_name[0] != '\0') + return concat(dir, "/", dot_a_name); + + return NULL; +} diff --git a/libexec/rtld-aout/shlib.h b/libexec/rtld-aout/shlib.h new file mode 100644 index 0000000..796d37e --- /dev/null +++ b/libexec/rtld-aout/shlib.h @@ -0,0 +1,43 @@ +/*- + * Copyright (C) 1996 + * Peter Wemm. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ + */ + +/* + * prototypes for shlib.c. Big deal. + */ + +extern char **search_dirs; +extern int n_search_dirs; + +void add_search_dir __P((char *)); +void add_search_path __P((char *)); +void std_search_path __P((void)); +int getdewey __P((int[], char *)); +int cmpndewey __P((int[], int, int[], int)); +char *findshlib __P((char *, int *, int *, int)); +char *find_lib_file __P((char *)); +char *search_lib_dir __P((char *, char *, int *, int *, int)); diff --git a/libexec/rtld-aout/support.c b/libexec/rtld-aout/support.c new file mode 100644 index 0000000..0d9df2a --- /dev/null +++ b/libexec/rtld-aout/support.c @@ -0,0 +1,86 @@ +/* + * Generic "support" routines to replace those obtained from libiberty for ld. + * + * I've collected these from random bits of (published) code I've written + * over the years, not that they are a big deal. peter@freebsd.org + *- + * Copyright (C) 1996 + * Peter Wemm. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ + */ +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <err.h> + +#include "support.h" + +char * +concat(s1, s2, s3) + const char *s1, *s2, *s3; +{ + int len = 1; + char *s; + if (s1) + len += strlen(s1); + if (s2) + len += strlen(s2); + if (s3) + len += strlen(s3); + s = xmalloc(len); + s[0] = '\0'; + if (s1) + strcat(s, s1); + if (s2) + strcat(s, s2); + if (s3) + strcat(s, s3); + return s; +} + +void * +xmalloc(n) + size_t n; +{ + char *p = malloc(n); + + if (p == NULL) + errx(1, "Could not allocate memory"); + + return p; +} + +void * +xrealloc(p, n) + void *p; + size_t n; +{ + p = realloc(p, n); + + if (p == NULL) + errx(1, "Could not allocate memory"); + + return p; +} diff --git a/libexec/rtld-aout/support.h b/libexec/rtld-aout/support.h new file mode 100644 index 0000000..5be1e31 --- /dev/null +++ b/libexec/rtld-aout/support.h @@ -0,0 +1,35 @@ +/*- + * Copyright (C) 1996 + * Peter Wemm. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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$ + */ + +/* + * prototypes for support.c. Big deal. + */ + +void *xmalloc __P((size_t)); +void *xrealloc __P((void *, size_t)); +char *concat __P((const char *, const char *, const char *)); |