summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-aout
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/rtld-aout')
-rw-r--r--libexec/rtld-aout/Makefile9
-rw-r--r--libexec/rtld-aout/rtld.c732
2 files changed, 474 insertions, 267 deletions
diff --git a/libexec/rtld-aout/Makefile b/libexec/rtld-aout/Makefile
index 4177f6b..13c8de8 100644
--- a/libexec/rtld-aout/Makefile
+++ b/libexec/rtld-aout/Makefile
@@ -1,13 +1,12 @@
-# $Id: Makefile,v 1.23 1997/02/22 15:46:46 peter Exp $
-
+# $Id: Makefile,v 1.24 1997/04/16 11:31:32 bde Exp $
PROG= ld.so
-SRCS= mdprologue.S rtld.c malloc.c shlib.c md.c support.c sbrk.c
+SRCS= mdprologue.S rtld.c shlib.c md.c support.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
+CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE) $(PICFLAG) -DRTLD -Wall
+LDFLAGS+=-nostdlib -Wl,-Bshareable,-Bsymbolic,-assert,nosymbolic
ASFLAGS+=-k
DPADD+= ${LIBC:S/c.a/c_pic.a/} ${LIBC:S/c.a/gcc_pic.a/}
LDADD+= -lc_pic -lgcc_pic
diff --git a/libexec/rtld-aout/rtld.c b/libexec/rtld-aout/rtld.c
index e3debe5..efe912b 100644
--- a/libexec/rtld-aout/rtld.c
+++ b/libexec/rtld-aout/rtld.c
@@ -27,7 +27,7 @@
* (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.48 1997/08/19 23:33:45 nate Exp $
+ * $Id: rtld.c,v 1.49 1997/09/18 13:55:45 phk Exp $
*/
#include <sys/param.h>
@@ -199,26 +199,31 @@ 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 void *__dlopen __P((const char *, int));
static int __dlclose __P((void *));
-static void *__dlsym __P((void *, char *));
-static char *__dlerror __P((void));
+static void *__dlsym __P((void *, const char *));
+static const char *__dlerror __P((void));
static void __dlexit __P((void));
-static void *__dlsym3 __P((void *, char *, void *));
+static void *__dlsym3 __P((void *, const 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 *,
+static struct so_map *map_object __P(( const 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 int reloc_dag __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 *,
+static struct so_map *alloc_link_map __P(( const char *, struct sod *,
+ struct so_map *, caddr_t,
+ struct _dynamic *));
+static void init_link_map __P(( struct so_map *,
+ struct somap_private *,
+ const char *, struct sod *,
struct so_map *, caddr_t,
struct _dynamic *));
static void free_link_map __P((struct so_map *));
@@ -227,8 +232,10 @@ static inline int check_text_reloc __P(( struct relocation_info *,
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_dag __P((struct so_map *));
static void init_sods __P((struct so_list *));
+static void init_internal_malloc __P((struct _dynamic *));
+static void init_external_malloc __P((void));
static int call_map __P((struct so_map *, char *));
static char *findhint __P((char *, int, int *));
static char *rtfindlib __P((char *, int, int));
@@ -236,9 +243,12 @@ 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 inline struct rt_symbol *lookup_rts __P((char *, unsigned long));
+static struct nzlist *lookup_in_obj __P((char *, unsigned long,
+ struct so_map *, int));
+static struct rt_symbol *enter_rts __P((char *, unsigned long, long, int,
+ caddr_t, long, struct so_map *));
+static void *sym_addr __P((char *));
static void die __P((void));
static void generror __P((char *, ...));
static int maphints __P((void));
@@ -248,6 +258,23 @@ static void rt_readenv __P((void));
static int hinthash __P((char *, int));
int rtld __P((int, struct crt_ldso *, struct _dynamic *));
+/*
+ * Compute a hash value for symbol tables. Don't change this -- the
+ * algorithm is dictated by the way the linker builds the symbol
+ * tables in the shared objects.
+ */
+static inline unsigned long
+sym_hash(s)
+ const char *s;
+{
+ unsigned long h;
+
+ h = 0;
+ while (*s != '\0')
+ h = (h << 1) + *s++;
+ return h & 0x7fffffffUL;
+}
+
static inline int
strcmp (register const char *s1, register const char *s2)
{
@@ -319,6 +346,9 @@ struct _dynamic *dp;
else
crtp->crt_dp->d_entry = &ld_entry; /* _DYNAMIC */
+ /* Initialize our internal malloc package. */
+ init_internal_malloc(crtp->crt_dp);
+
/* Setup out (private) environ variable */
environ = crtp->crt_ep;
@@ -380,10 +410,21 @@ struct _dynamic *dp;
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 */
+ /* Relocate all mapped objects. */
+ if(reloc_dag(main_map, ld_bind_now != NULL) == -1) /* Failed */
die();
+ /*
+ * Switch to the same malloc that the program uses. We do
+ * this before initializing the loaded objects, because their
+ * initialization functions may well call malloc, and it won't
+ * work right until we have set it up.
+ */
+ init_external_malloc();
+
+ /* Initialize all mapped objects. */
+ init_dag(main_map);
+
ddp = crtp->crt_dp->d_debug;
ddp->dd_cc = rt_symbol_head;
if (ddp->dd_in_debugger) {
@@ -513,9 +554,9 @@ ld_trace(smp)
* ADDR is the address at which the object has been mapped. DP is a pointer
* to its _dynamic structure.
*/
- static struct so_map *
+static struct so_map *
alloc_link_map(path, sodp, parent, addr, dp)
- char *path;
+ const char *path;
struct sod *sodp;
struct so_map *parent;
caddr_t addr;
@@ -523,25 +564,16 @@ alloc_link_map(path, sodp, parent, addr, 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);
+ smp = (struct so_map *)xmalloc(sizeof(struct so_map));
+ smpp = (struct somap_private *)xmalloc(sizeof(struct somap_private));
+ init_link_map(smp, smpp, path, sodp, parent, addr, dp);
/* 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;
@@ -550,31 +582,41 @@ alloc_link_map(path, sodp, parent, addr, dp)
link_map_tail = smp;
}
+ return smp;
+}
+
+/*
+ * Initialize a link map entry that has already been allocated.
+ */
+static void
+init_link_map(smp, smpp, path, sodp, parent, addr, dp)
+ struct so_map *smp;
+ struct somap_private *smpp;
+ const char *path;
+ struct sod *sodp;
+ struct so_map *parent;
+ caddr_t addr;
+ struct _dynamic *dp;
+{
+ memset(smp, 0, sizeof *smp);
+ memset(smpp, 0, sizeof *smpp);
+ smp->som_spd = (caddr_t)smpp;
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
+static void
free_link_map(smp)
struct so_map *smp;
{
@@ -594,7 +636,9 @@ free_link_map(smp)
else /* Update back link of next entry */
LM_PRIVATE(smp->som_next)->spd_prev = smpp->spd_prev;
- free(smp->som_path);
+ if (smp->som_path != NULL)
+ free(smp->som_path);
+ free(smpp);
free(smp);
}
@@ -617,9 +661,9 @@ free_link_map(smp)
* 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 *
+static struct so_map *
map_object(path, sodp, parent)
- char *path;
+ const char *path;
struct sod *sodp;
struct so_map *parent;
{
@@ -821,7 +865,7 @@ map_preload __P((void)) {
* Returns 0 on success. Returns -1 on failure. In that case, an error
* message can be retrieved by calling dlerror().
*/
- static int
+static int
map_sods(parent)
struct so_map *parent;
{
@@ -872,7 +916,7 @@ map_sods(parent)
*/
(void)alloc_link_map(NULL, sodp, parent, 0, 0);
} else if (ld_ignore_missing_objects) {
- char *msg;
+ const char *msg;
/*
* Call __dlerror() even it we're not going to use
* the message, in order to clear the saved message.
@@ -915,12 +959,12 @@ map_sods(parent)
}
/*
- * 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().
+ * Relocate the DAG 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)
+static int
+reloc_dag(root, bind_now)
struct so_map *root;
int bind_now;
{
@@ -957,35 +1001,6 @@ reloc_and_init(root, bind_now)
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;
}
@@ -1009,7 +1024,7 @@ reloc_and_init(root, bind_now)
* anything goes wrong, we consider it an internal error, and report
* it with err().
*/
- static void
+static void
unmap_object(smp, keep)
struct so_map *smp;
int keep;
@@ -1218,7 +1233,7 @@ reloc_map(smp, bind_now)
relocation -= (long)smp->som_addr;
if (RELOC_COPY_P(r) && src_map) {
- (void)enter_rts(sym,
+ (void)enter_rts(sym, sym_hash(sym),
(long)addr,
N_DATA + N_EXT,
src_map->som_addr + np->nz_value,
@@ -1252,7 +1267,7 @@ reloc_map(smp, bind_now)
return 0;
}
- static void
+static void
reloc_copy(smp)
struct so_map *smp;
{
@@ -1266,8 +1281,11 @@ reloc_copy(smp)
}
}
- static void
-init_object(smp)
+/*
+ * Initialize the DAG of shared objects rooted at the given object.
+ */
+static void
+init_dag(smp)
struct so_map *smp;
{
struct somap_private *smpp = LM_PRIVATE(smp);
@@ -1284,7 +1302,7 @@ init_object(smp)
}
}
- static void
+static void
init_sods(solp)
struct so_list *solp;
{
@@ -1293,7 +1311,7 @@ init_sods(solp)
init_sods(solp->sol_next);
/* Initialize the first element of the list */
- init_object(solp->sol_map);
+ init_dag(solp->sol_map);
}
@@ -1304,7 +1322,7 @@ init_sods(solp)
* 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
+static int
call_map(smp, sym)
struct so_map *smp;
char *sym;
@@ -1329,69 +1347,46 @@ call_map(smp, sym)
static struct rt_symbol *rt_symtab[RTC_TABSIZE];
/*
- * Compute hash value for run-time symbol table
+ * Look up a symbol in the run-time common symbol table. For efficiency,
+ * the symbol's hash value must be passed in too.
*/
- 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;
+static inline struct rt_symbol *
+lookup_rts(name, hash)
+ char *name;
+ unsigned long hash;
{
- 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)
+ for (rtsp = rt_symtab[hash % RTC_TABSIZE]; rtsp; rtsp = rtsp->rt_link)
+ if (strcmp(name, rtsp->rt_sp->nz_name) == 0)
return rtsp;
return NULL;
}
- static struct rt_symbol *
-enter_rts(name, value, type, srcaddr, size, smp)
+/*
+ * Enter a symbol into the run-time common symbol table. For efficiency,
+ * the symbol's hash value must be passed in too.
+ */
+static struct rt_symbol *
+enter_rts(name, hash, value, type, srcaddr, size, smp)
char *name;
- long value;
- int type;
- caddr_t srcaddr;
- long size;
+ unsigned long hash;
+ 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)
+ for (rpp = &rt_symtab[hash % RTC_TABSIZE]; *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 = (struct rt_symbol *)xmalloc(sizeof(struct rt_symbol));
+ rtsp->rt_sp = (struct nzlist *)xmalloc(sizeof(struct nzlist));
rtsp->rt_sp->nz_name = strdup(name);
rtsp->rt_sp->nz_value = value;
rtsp->rt_sp->nz_type = type;
@@ -1412,140 +1407,193 @@ enter_rts(name, value, type, srcaddr, size, smp)
/*
* 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()).
+ * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search
+ * is confined to that map.
+ *
+ * REAL_DEF_ONLY is a boolean which specifies whether certain special
+ * symbols for functions should satisfy the lookup or not. The
+ * reasons behind it are somewhat complicated. They are motivated
+ * by the scenario in which the address of a single function is
+ * taken from several shared objects. The address should come out
+ * the same in all cases, because the application code might decide
+ * to use it in comparisons. To make this work, the linker creates
+ * a symbol entry for the function in the main executable, with a
+ * type of N_UNDF+N_EXT, an N_AUX of AUX_FUNC, and a value that
+ * refers to the PLT entry for the function in the main executable.
+ * If REAL_DEF_ONLY is false, then this kind of special symbol is
+ * considered a "definition" when lookup up the symbol. Since the
+ * main executable is at the beginning of the shared object search
+ * list, the result is that references from all shared objects will
+ * resolve to the main program's PLT entry, and thus the function
+ * addresses will compare equal as they should.
+ *
+ * When relocating the PLT entry itself, we obviously must match
+ * only the true defining symbol for the function. In that case, we
+ * set REAL_DEF_ONLY to true, which disables matching the special
+ * N_UNDF+N_EXT entries.
+ *
+ * It is not so clear how to set this flag for a lookup done from
+ * dlsym. If the lookup specifies a particular shared object other
+ * than the main executable, the flag makes no difference -- only the
+ * true definition will be matched. (That is because the special
+ * symbols are only present in the main executable, which will not
+ * be searched.) But when the lookup is over all the shared objects
+ * (i.e., dlsym's "fd" parameter is NULL), then the flag does have an
+ * effect. We elect to match only the true definition even in that
+ * case.
+ *
+ * The upshot of all this is the following rule of thumb: Set
+ * REAL_DEF_ONLY in all cases except when processing a non-PLT
+ * relocation.
*/
- static struct nzlist *
-lookup(name, src_map, strong)
+static struct nzlist *
+lookup(name, src_map, real_def_only)
char *name;
struct so_map **src_map; /* IN/OUT */
- int strong;
+ int real_def_only;
{
- long common_size = 0;
- struct so_map *smp;
- struct rt_symbol *rtsp;
+ unsigned long hash;
- if ((rtsp = lookup_rts(name)) != NULL)
- return rtsp->rt_sp;
+ hash = sym_hash(name);
- /*
- * 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 (*src_map != NULL) /* Look in just one specific object */
+ return lookup_in_obj(name, hash, *src_map, real_def_only);
+ else { /* Search runtime symbols and all loaded objects */
+ unsigned long common_size;
+ struct so_map *smp;
+ struct rt_symbol *rtsp;
+ struct nzlist *np;
- if ((buckets = LD_BUCKETS(smp->som_dynamic)) == 0)
- continue;
-
- if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
- continue;
+ if ((rtsp = lookup_rts(name, hash)) != NULL)
+ return rtsp->rt_sp;
-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 */
+ common_size = 0;
+ for (smp = link_map_head; smp; smp = smp->som_next) {
+ if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)
+ continue;
+ np = lookup_in_obj(name, hash, smp, real_def_only);
+ if (np == NULL)
+ continue;
+ /* We know that np->nz_value > 0 at this point. */
+ if (np->nz_type == N_UNDF+N_EXT &&
+ N_AUX(&np->nlist) != AUX_FUNC) { /* Common */
if (common_size < np->nz_value)
common_size = np->nz_value;
continue;
}
+
+ /* We found the symbol definition. */
+ *src_map = smp;
+ return np;
}
+ if (common_size > 0) { /* It is a common symbol. */
+ void *mem;
- *src_map = smp;
- return np;
+ mem = memset(xmalloc(common_size), 0, common_size);
+ rtsp = enter_rts(name, hash, (long)mem, N_UNDF + N_EXT,
+ 0, common_size, NULL);
+ return rtsp->rt_sp;
+ }
+
+ /* No definition was found for the symbol. */
+ return NULL;
}
+}
- if (common_size == 0)
- /* Not found */
+/*
+ * Lookup a symbol in one specific shared object. The hash
+ * value is passed in for efficiency. For an explanation of the
+ * "real_def_only" flag, see the comment preceding the "lookup"
+ * function.
+ */
+static struct nzlist *
+lookup_in_obj(name, hash, smp, real_def_only)
+ char *name;
+ unsigned long hash;
+ struct so_map *smp;
+ int real_def_only;
+{
+ unsigned long buckets;
+ struct rrs_hash *hp;
+ char *cp;
+ struct nzlist *np;
+ char *symbolbase;
+ struct rrs_hash *hashbase;
+ char *stringbase;
+ size_t symsize;
+
+ if ((buckets = LD_BUCKETS(smp->som_dynamic)) == 0)
+ return NULL;
+
+ hashbase = LM_HASH(smp);
+
+restart:
+ hp = &hashbase[hash % buckets];
+ if (hp->rh_symbolnum == -1)
return NULL;
+ symbolbase = (char *)LM_SYMBOL(smp, 0);
+ stringbase = LM_STRINGS(smp);
+ symsize = LD_VERSION_NZLIST_P(smp->som_dynamic->d_version)?
+ sizeof(struct nzlist) : sizeof(struct nlist);
+ for ( ; ; ) {
+ np = (struct nzlist *)(symbolbase + hp->rh_symbolnum*symsize);
+ cp = stringbase + np->nz_strx;
+ if (strcmp(cp, name) == 0)
+ break;
+ if (hp->rh_next == 0) /* End of hash chain */
+ return NULL;
+ hp = hashbase + hp->rh_next;
+ }
+
/*
- * It's a common, enter into run-time common symbol table.
+ * We have a symbol with the name we're looking for.
*/
- rtsp = enter_rts(name, (long)calloc(1, common_size),
- N_UNDF + N_EXT, 0, common_size, NULL);
+ if (np->nz_type == N_INDR+N_EXT) {
+ /*
+ * Next symbol gives the aliased name. Restart
+ * search with new name.
+ */
+ name = stringbase + (++np)->nz_strx;
+ hash = sym_hash(name);
+ goto restart;
+ }
-#if DEBUG
- xprintf("Allocating common: %s size %d at %#x\n", name, common_size,
- rtsp->rt_sp->nz_value);
-#endif
+ if (np->nz_value == 0) /* It's not a definition */
+ return NULL;
+
+ if (real_def_only) /* Don't match special function symbols. */
+ if (np->nz_type == N_UNDF+N_EXT &&
+ N_AUX(&np->nlist) == AUX_FUNC)
+ return NULL;
+
+ return np;
+}
- return rtsp->rt_sp;
+/*
+ * Return the value of a symbol in the user's program. This is used
+ * internally for a few symbols which must exist. If the requested
+ * symbol is not found, this simply exits with a fatal error.
+ */
+static void *
+sym_addr(name)
+ char *name;
+{
+ struct so_map *smp;
+ struct nzlist *np;
+
+ smp = NULL;
+ np = lookup(name, &smp, 1);
+ if (np == NULL)
+ errx(1, "Program has no symbol \"%s\"", name);
+ return ((smp == NULL) ? NULL : smp->som_addr) + np->nz_value;
}
/*
* This routine is called from the jumptable to resolve
* procedure calls to shared objects.
*/
- long
+long
binder(jsp)
jmpslot_t *jsp;
{
@@ -1565,7 +1613,7 @@ binder(jsp)
}
if (smp == NULL)
- errx(1, "Call to binder from unknown location: %#x\n", jsp);
+ errx(1, "Call to binder from unknown location: %p\n", jsp);
index = jsp->reloc_index & JMPSLOT_RELOC_MASK;
@@ -1575,7 +1623,7 @@ binder(jsp)
np = lookup(sym, &src_map, 1);
if (np == NULL)
- errx(1, "Undefined symbol \"%s\" called from %s:%s at %#x",
+ errx(1, "Undefined symbol \"%s\" called from %s:%s at %p",
sym, main_progname, smp->som_path, jsp);
/* Fixup jmpslot so future calls transfer directly to target */
@@ -1600,7 +1648,7 @@ 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
+static int
maphints __P((void))
{
static int hints_bad; /* TRUE if hints are unusable */
@@ -1657,7 +1705,7 @@ maphints __P((void))
/*
* Unmap the hints file, if it is currently mapped.
*/
- static void
+static void
unmaphints()
{
if (hheader != NULL) {
@@ -1666,7 +1714,7 @@ unmaphints()
}
}
- int
+int
hinthash(cp, vmajor)
char *cp;
int vmajor;
@@ -1694,7 +1742,7 @@ hinthash(cp, vmajor)
*
* Returns NULL if the library cannot be found.
*/
- static char *
+static char *
findhint(name, major, minorp)
char *name;
int major;
@@ -1748,7 +1796,7 @@ findhint(name, major, minorp)
*
* Returns NULL if the library cannot be found.
*/
- static char *
+static char *
rtfindlib(name, major, minor)
char *name;
int major, minor;
@@ -1794,7 +1842,7 @@ rtfindlib(name, major, minor)
*
* Returns NULL if the library cannot be found.
*/
- static char *
+static char *
rtfindfile(name)
char *name;
{
@@ -1839,10 +1887,10 @@ static char dlerror_buf [DLERROR_BUF_SIZE];
static char *dlerror_msg = NULL;
- static void *
+static void *
__dlopen(path, mode)
- char *path;
- int mode;
+ const char *path;
+ int mode;
{
struct so_map *old_tail = link_map_tail;
struct so_map *smp;
@@ -1862,8 +1910,9 @@ __dlopen(path, mode)
/* 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 */
+ if(reloc_dag(smp, bind_now) == -1) /* Failed */
return NULL;
+ init_dag(smp);
}
unmaphints();
@@ -1872,7 +1921,7 @@ __dlopen(path, mode)
return smp;
}
- static int
+static int
__dlclose(fd)
void *fd;
{
@@ -1902,10 +1951,10 @@ __dlclose(fd)
* it. It can still be called by old executables that were linked with
* old versions of crt0.
*/
- static void *
+static void *
__dlsym(fd, sym)
- void *fd;
- char *sym;
+ void *fd;
+ const char *sym;
{
if (fd == RTLD_NEXT) {
generror("RTLD_NEXT not supported by this version of"
@@ -1915,7 +1964,7 @@ __dlsym(fd, sym)
return __dlsym3(fd, sym, NULL);
}
- static void *
+static void *
resolvesym(fd, sym, retaddr)
void *fd;
char *sym;
@@ -1975,11 +2024,11 @@ resolvesym(fd, sym, retaddr)
return (void *)addr;
}
- static void *
+static void *
__dlsym3(fd, sym, retaddr)
- void *fd;
- char *sym;
- void *retaddr;
+ void *fd;
+ const char *sym;
+ void *retaddr;
{
void *result;
@@ -1993,7 +2042,7 @@ __dlsym3(fd, sym, retaddr)
*/
if (result == NULL) {
/* Prepend an underscore and try again */
- char *newsym = malloc(strlen(sym) + 2);
+ char *newsym = xmalloc(strlen(sym) + 2);
newsym[0] = '_';
strcpy(&newsym[1], sym);
@@ -2003,10 +2052,10 @@ __dlsym3(fd, sym, retaddr)
return result;
}
- static char *
+static const char *
__dlerror __P((void))
{
- char *err;
+ const char *err;
err = dlerror_msg;
dlerror_msg = NULL; /* Next call will return NULL */
@@ -2014,7 +2063,7 @@ __dlerror __P((void))
return err;
}
- static void
+static void
__dlexit __P((void))
{
#ifdef DEBUG
@@ -2030,7 +2079,7 @@ xprintf("__dlexit called\n");
static void
die __P((void))
{
- char *msg;
+ const char *msg;
fprintf(stderr, "ld.so failed");
if ((msg = __dlerror()) != NULL)
@@ -2146,3 +2195,162 @@ rt_readenv()
}
}
}
+
+/*
+ * Malloc implementation for use within the dynamic linker. At first
+ * we do a simple allocation using sbrk. After the user's program
+ * has been loaded, we switch to using whatever malloc functions are
+ * defined there.
+ */
+
+/* Symbols related to the sbrk and brk implementations. */
+#define CURBRK_SYM "curbrk"
+#define MINBRK_SYM "minbrk"
+#define END_SYM "_end"
+
+/* Symbols related to malloc. */
+#define FREE_SYM "_free"
+#define MALLOC_SYM "_malloc"
+#define REALLOC_SYM "_realloc"
+
+/* Hooks into the implementation of sbrk and brk. */
+extern char *curbrk __asm__(CURBRK_SYM);
+extern char *minbrk __asm__(MINBRK_SYM);
+
+/* Pointers to the user program's malloc functions. */
+static void *(*p_malloc) __P((size_t));
+static void *(*p_realloc) __P((void *, size_t));
+static void (*p_free) __P((void *));
+
+/* Upper limit of the memory allocated by our internal malloc. */
+static char *rtld_alloc_lev;
+
+/*
+ * Set up the internal malloc so that it will take its memory from the
+ * main program's sbrk arena.
+ */
+static void
+init_internal_malloc(dp)
+ struct _dynamic *dp;
+{
+ struct so_map tmp_map;
+ struct somap_private map_private;
+ struct nzlist *np;
+
+ /*
+ * Before anything calls sbrk or brk, we have to initialize
+ * its idea of the current break level to the main program's
+ * "_end" symbol, rather than that of the dynamic linker. In
+ * order to do that, we need to look up the value of the main
+ * program's "_end" symbol. We set up a temporary link map
+ * entry for the main program so that we can do the lookup.
+ */
+ init_link_map(&tmp_map, &map_private, NULL, NULL, NULL, NULL, dp);
+ np = lookup_in_obj(END_SYM, sym_hash(END_SYM), &tmp_map, 1);
+ if (np == NULL)
+ errx(1, "Main program has no symbol \"%s\"", END_SYM);
+ rtld_alloc_lev = curbrk = minbrk = (char *)np->nz_value;
+}
+
+/*
+ * Set things up so that the dynamic linker can use the program's
+ * malloc functions.
+ */
+static void
+init_external_malloc __P((void))
+{
+ /*
+ * Patch the program's idea of the current break address to
+ * what it really is as a result of the allocations we have
+ * already done.
+ */
+ *(char **)(sym_addr(CURBRK_SYM)) = curbrk;
+
+ /*
+ * Set up pointers to the program's allocation functions, so
+ * that we can use them from now on.
+ */
+ p_malloc = (void *(*)(size_t))(sym_addr(MALLOC_SYM));
+ p_free = (void (*)(void *))(sym_addr(FREE_SYM));
+ p_realloc = (void *(*)(void *, size_t))(sym_addr(REALLOC_SYM));
+}
+
+void *
+malloc(size)
+ size_t size;
+{
+ char *p;
+
+ /* If we are far enough along, we can use the system malloc. */
+ if (p_malloc != NULL)
+ return (*p_malloc)(size);
+
+ /*
+ * Otherwise we use our simple built-in malloc. We get the
+ * memory from brk() in increments of one page. We store the
+ * allocated size in the first word, so that realloc can be
+ * made to work.
+ */
+ if (rtld_alloc_lev == NULL)
+ errx(1, "Internal error: internal malloc called before"
+ " being initialized");
+
+ p = (char *)ALIGN(rtld_alloc_lev);
+ rtld_alloc_lev = p + sizeof(size_t) + size;
+
+ if (rtld_alloc_lev > curbrk) { /* Get memory from system */
+ char *newbrk;
+
+ newbrk = (char *)
+ roundup2((unsigned long)rtld_alloc_lev, PAGSIZ);
+ if (brk(newbrk) == (char *)-1)
+ return NULL;
+ }
+
+ *(size_t *)p = size;
+ return p + sizeof(size_t);
+}
+
+void *
+realloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ size_t old_size;
+ void *new_ptr;
+
+ if (ptr == NULL)
+ return malloc(size);
+
+ /*
+ * If we are far enough along, and if the memory originally came
+ * from the system malloc, we can use the system realloc.
+ */
+ if (p_realloc != NULL && (char *)ptr >= rtld_alloc_lev)
+ return (*p_realloc)(ptr, size);
+
+ old_size = *((size_t *)ptr - 1);
+ if (old_size >= size) /* Not expanding the region */
+ return ptr;
+
+ new_ptr = malloc(size);
+ if (new_ptr != NULL)
+ memcpy(new_ptr, ptr, old_size);
+ return new_ptr;
+}
+
+void
+free(ptr)
+ void *ptr;
+{
+ if (ptr == NULL)
+ return;
+
+ /*
+ * If we are far enough along, and if the memory originally came
+ * from the system malloc, we can use the system free. Otherwise
+ * we can't free the memory and we just let it go to waste.
+ */
+ if (p_free != NULL && (char *)ptr >= rtld_alloc_lev)
+ (*p_free)(ptr);
+}
OpenPOWER on IntegriCloud