[BACK]Return to loader.c CVS log [TXT][DIR] Up to [local] / src / libexec / ld.so

File: [local] / src / libexec / ld.so / loader.c (download)

Revision 1.7, Mon Apr 2 23:11:20 2001 UTC (23 years, 2 months ago) by drahn
Branch: MAIN
CVS Tags: OPENBSD_2_9_BASE, OPENBSD_2_9
Changes since 1.6: +50 -50 lines

Cleanup for 64bit support.
Pieces by art, niklas and me.
Only tested on powerpc.

/*	$OpenBSD: loader.c,v 1.7 2001/04/02 23:11:20 drahn Exp $ */

/*
 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
 * 
 * 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 under OpenBSD by
 *	Per Fogelstrom, Opsycon AB, Sweden.
 * 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.
 *
 */

#define	_DYN_LOADER

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/exec.h>
#include <nlist.h>
#include <link.h>

#include "syscall.h"
#include "archdep.h"
#include "resolve.h"

/*
 *  Local decls.
 */
/* static */ char *_dl_getenv(const char *var, const char **env);

/*
 *   Static vars usable after bootsrapping.
 */
static void *_dl_malloc_base;
static void *_dl_malloc_pool = 0;
static long  *_dl_malloc_free = 0;

const char *_dl_progname;
int  _dl_pagesz;
int  _dl_trusted;

char *_dl_libpath;
char *_dl_preload;
char *_dl_bindnow;
char *_dl_traceld;
char *_dl_debug;
char *_dl_showmap;

struct r_debug *_dl_debug_map;
void _dl_unmaphints();

void
_dl_debug_state(void)
{
	/* Debugger stub */
}


#if 1
static inline void
put_x(unsigned int x)
{
	char string[8];
	char *pchr;
	unsigned int rem;
	int len = 0;
	string[19] = '\0';
	pchr = &string[7];
	do {
		rem = x % 16;
		x =   x / 16;
		if (rem < 10) {
		*pchr = rem + '0';
		} else  {
			*pchr = rem - 10 + 'a';
		}
		pchr--;
		len++;
	} while (len < 8);
	_dl_write(1, string, len);

}

static inline int
putstring(char *string, unsigned int off)
{
	int len = 0;
	char * str1;
	if ((unsigned long) string < 0x10000000) {
		string += off;
	}
	for ( str1 = string; len < 30 && *str1++ != '\0'; len++);
	return _dl_write(1, string, len);
	
}
static inline int
putc(char c)
{
	return _dl_write(1, &c, 1);
	
}
#endif

/*
 *  This is the dynamic loader entrypoint. When entering here, depending
 *  on architecture type, the stack and registers are set up according
 *  to the architectures ABI specification. The first thing required
 *  to do is to dig out all information we need to accomplish out task.
 */
unsigned long
_dl_boot(const char **argv, const char **envp, const long loff,
	Elf_Dyn *dynp, long *dl_data)
{
	int		n;
	int		brk_addr;
	Elf_Phdr	*phdp;
	char		*us = "";
	elf_object_t	*dynobj;
	struct elf_object  *exe_obj;	/* Pointer to executable object */
	struct elf_object  *dyn_obj;	/* Pointer to executable object */
	struct r_debug * debug_map;

	/*
	 *  Get paths to various things we are going to use.
	 */

	_dl_libpath = _dl_getenv("LD_LIBRARY_PATH", envp);
	_dl_preload = _dl_getenv("LD_PRELOAD", envp);
	_dl_bindnow = _dl_getenv("LD_BIND_NOW", envp);
	_dl_traceld = _dl_getenv("LD_TRACE_LOADED_OBJECTS", envp);
	_dl_debug   = _dl_getenv("LD_DEBUG", envp);

	_dl_progname = argv[0];
	if(dl_data[AUX_pagesz] != 0) {
		_dl_pagesz = dl_data[AUX_pagesz];
	}
	else {
		_dl_pagesz = 4096;
	}
	if(_dl_debug)
		_dl_printf("rtld loading: '%s'\n", _dl_progname);

	/*
	 *  Don't allow someone to change the search paths if he runs
	 *  a suid program without credentials high enough.
	 */
	if((_dl_trusted = !_dl_suid_ok())) {	/* Zap paths if s[ug]id... */
		if(_dl_preload) {
			*_dl_preload = '\0';
		}
		if(_dl_libpath) {
			*_dl_libpath = '\0';
		}
	}

	/*
	 *  Examine the user application and set up object information.
	 */
	phdp = (Elf_Phdr *) dl_data[AUX_phdr];
	for(n = 0; n < dl_data[AUX_phnum]; n++) {
		if(phdp->p_type == PT_LOAD) {				/*XXX*/
			if(phdp->p_vaddr + phdp->p_memsz > brk_addr)	/*XXX*/
				brk_addr = phdp->p_vaddr + phdp->p_memsz;
		}							/*XXX*/
		if(phdp->p_type == PT_DYNAMIC) {
			exe_obj = _dl_add_object("", (Elf_Dyn *)phdp->p_vaddr,
						   dl_data, OBJTYPE_EXE, 0, 0);
		}
		if(phdp->p_type == PT_INTERP) {
			us = (char *)_dl_malloc(_dl_strlen((char *)phdp->p_vaddr));
			_dl_strcpy(us, (char *)phdp->p_vaddr);
		}
		phdp++;
	}

	/*
	 *  Now, pick up and 'load' all libraries requierd. Start
	 *  With the first on the list and then do whatever gets
	 *  added along the tour.
	 */

	dynobj = _dl_objects;
	while(dynobj) {
		if(_dl_debug)
			_dl_printf("examining: '%s'\n", dynobj->load_name);
		for(dynp = dynobj->load_dyn; dynp->d_tag; dynp++) {
			if(dynp->d_tag == DT_NEEDED) {
				const char *libname;
				libname = dynobj->dyn.strtab;
				libname += dynp->d_un.d_val;
				if(_dl_debug) 
					_dl_printf("needs: '%s'\n", libname);
				if(_dl_load_shlib(libname, dynobj, OBJTYPE_LIB) == 0) {
					_dl_printf("%s: can't load library '%s'\n",
						_dl_progname, libname);
					_dl_exit(4);
				}
			}
		}
		dynobj = dynobj->next;
	}

	/*
	 *  Now add the dynamic loader itself last in the object list
	 *  so we can use the _dl_ code when serving dl.... calls.
	 */

	dynp = (Elf_Dyn *)((void *)_DYNAMIC);
	dyn_obj = _dl_add_object(us, dynp, 0, OBJTYPE_LDR, dl_data[AUX_base], loff);
	dyn_obj->status |= STAT_RELOC_DONE;

	/*
	 *  Everything should be in place now for doing the relocation
	 *  and binding. Call _dl_rtld to do the job. Fingers crossed.
	 */

	_dl_rtld(_dl_objects);
	/* the first object is the executable itself, 
	 * it is responsible for running it's ctors/dtors
	 * thus do NOT run the ctors for the executable, all of
	 * the shared libraries which follow.
	 */
	if (_dl_objects->next) {
		_dl_call_init(_dl_objects->next);
	}

	/*
	 *  Finally make something to help gdb when poking around in the code.
	 */
#ifdef __powerpc__
	{
		int done = 0;
		 
		debug_map = (struct r_debug *)_dl_malloc(sizeof(*debug_map));
		debug_map->r_version = 1;
		debug_map->r_map = (struct link_map *)_dl_objects;
		debug_map->r_brk = (Elf_Addr)_dl_debug_state;
		debug_map->r_state = RT_CONSISTENT;
		debug_map->r_ldbase = loff;
		_dl_debug_map = debug_map;

		/* picks up the first object, the executable itself */
		dynobj = _dl_objects;

		for(dynp = dynobj->load_dyn; dynp->d_tag; dynp++) {
			if (dynp->d_tag == DT_DEBUG) {
				dynp->d_un.d_ptr = (Elf_Addr) debug_map;
				done = 1;
				break;
			}
		}
		if (done == 0) {
			_dl_printf("failed to mark DTDEBUG\n");
		}
	}
#endif

#ifdef __mips__
	map_link = (struct r_debug **)(exe_obj->Dyn.info[DT_MIPS_RLD_MAP - DT_LOPROC + DT_NUM]);
	if(map_link) {
		debug_map = (struct r_debug *)_dl_malloc(sizeof(*debug_map));
		debug_map->r_version = 1;
		debug_map->r_map = (struct link_map *)_dl_objects;
		debug_map->r_brk = (Elf_Addr)_dl_debug_state;
		debug_map->r_state = RT_CONSISTENT;
		debug_map->r_ldbase = loff;
		_dl_debug_map = debug_map;
		*map_link = _dl_debug_map;
	}
#endif

	_dl_debug_state();

	if(_dl_debug || _dl_traceld) {
		void _dl_show_objects(); /* remove -Wall warning */
		_dl_show_objects();
		if (_dl_debug)
			_dl_printf("dynamic loading done.\n");
	}
	_dl_unmaphints();
	if (_dl_traceld) {
		_dl_exit(0);
	}
	return(dl_data[AUX_entry]);
}


void
_dl_boot_bind(const long sp, const long loff,  int argc, const char **argv,
	const char **envp, Elf_Dyn *dynamicp, long *dl_data)
{
	Elf_Dyn	*dynp;
	int		n;
	long		*stack;
	AuxInfo		*auxstack;

	struct elf_object  dynld;	/* Resolver data for the loader */
#ifdef __mips__
	struct r_debug	   *debug_map;	/* Dynamic objects map for gdb */
	struct r_debug	   **map_link;	/* Where to put pointer for gdb */
#endif /* __mips__ */

	/*
	 * Scan argument and environment vectors. Find dynamic
	 * data vector put after them.
	 */
#ifdef _mips_
	stack = (long *)sp;
	argc = *stack++;
	argv = (const char **)stack;
	envp = &argv[argc + 1];
#endif /* _mips_ */
	stack = (long *)envp;
	while(*stack++ != NULL) {};

	/*
	 * Dig out auxiliary data set up by exec call. Move all known
	 * tags to an indexed local table for easy access.
	 */

	auxstack = (AuxInfo *)stack;

	while(auxstack->au_id != AUX_null) {
		if(auxstack->au_id <= AUX_entry) {
			dl_data[auxstack->au_id] = auxstack->au_v;
		}
		auxstack++;
	}

	/*
	 *  We need to do 'selfreloc' in case the code were'nt
	 *  loaded at the address it was linked to.
	 *
	 *  Scan the DYNAMIC section for the loader.
	 *  Cache the data for easier access.
	 */

#if defined(__powerpc__) || defined(__alpha__)
	dynp = dynamicp;
#else
	dynp = (Elf_Dyn *)((long)_DYNAMIC + loff);
#endif
	while(dynp != NULL && dynp->d_tag != DT_NULL) {
		if(dynp->d_tag < DT_LOPROC) {
			dynld.Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
		}
		else if(dynp->d_tag >= DT_LOPROC && dynp->d_tag < DT_LOPROC + DT_NUM) {
			dynld.Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] = dynp->d_un.d_val;
		}
		if(dynp->d_tag == DT_TEXTREL)
			dynld.dyn.textrel = 1;
		dynp++;
	}

	/*
	 *  Do the 'bootstrap relocation'. This is really only needed if
	 *  the code was loaded at another location than it was linked to.
	 *  We don't do undefined symbols resolving (to difficult..)
	 */

	/* "relocate" dyn.X values if they represent addresses */
	{
		int i, val;
		/* must be code, not pic data */
		int table[20]; 
		i = 0;
		table[i++] = DT_PLTGOT;
		table[i++] = DT_HASH;
		table[i++] = DT_STRTAB;
		table[i++] = DT_SYMTAB;
		table[i++] = DT_RELA;
		table[i++] = DT_INIT;
		table[i++] = DT_FINI;
		table[i++] = DT_REL;
		table[i++] = DT_JMPREL;
		/* other processors insert there extras here */
		table[i++] = DT_NULL;
#if 0
		= {
		DT_PLTGOT,
		DT_HASH,
		DT_STRTAB,
		DT_SYMTAB,
		DT_RELA,
		DT_INIT,
		DT_FINI,
		DT_REL,
		DT_JMPREL,
		/* other processors insert there extras here */
		DT_NULL
		};
#endif
		for (i = 0; table[i] != DT_NULL; i++)
		{
			val = table[i];
			if ( val > DT_HIPROC) {
				/* ??? */
				continue;
			}
			if ( val > DT_LOPROC) {
				val -= DT_LOPROC + DT_NUM;
			}
			if ( dynld.Dyn.info[val] != 0 ) {
				dynld.Dyn.info[val] += loff;
			}
		}

	}

	{
		int	  i;
		u_int32_t rs;
		Elf_Rel  *rp;

		rp = (Elf_Rel *)(dynld.Dyn.info[DT_REL]);
		rs = dynld.dyn.relsz;

		for(i = 0; i < rs; i += sizeof (Elf_Rel)) {
			Elf_Addr *ra;
			const Elf_Sym  *sp;

			sp = dynld.dyn.symtab;
			sp += ELF_R_SYM(rp->r_info);
#if 1
			putstring("reloc  ", loff);
			putstring(((char *)dynld.dyn.strtab) + sp->st_name, 0);
			putstring(" ", loff);
#endif

			if(ELF_R_SYM(rp->r_info) && sp->st_value == 0) {
#if 0
/* cannot printf in this function */
				_dl_wrstderr("Dynamic loader failure: self bootstrapping impossible.\n");
				_dl_wrstderr("Undefined symbol: ");
				_dl_wrstderr((char *)dynld.dyn.strtab
					+ sp->st_name);
#endif
				_dl_exit(5);
			}

			ra = (Elf_Addr *)(rp->r_offset + loff);
#if 0
			put_x((unsigned int)ra);
			putstring("\n", loff);
#endif
			/*
			RELOC_REL(rp, sp, ra, loff);
			*/
			rp++;
		}

	}
	for(n = 0; n < 2; n++) {
		int	  i;
		u_int32_t rs;
		Elf_RelA  *rp;

		switch (n) {
		case 0:
			rp = (Elf_RelA *)(dynld.Dyn.info[DT_JMPREL]);
			rs = dynld.dyn.pltrelsz;
			break;
		case 1:
			rp = (Elf_RelA *)(dynld.Dyn.info[DT_RELA]);
			rs = dynld.dyn.relasz;

			break;
		default:
			rp = NULL;
			rs = 0;
			;
		}
		for(i = 0; i < rs; i += sizeof (Elf_RelA)) {
			Elf_Addr *ra;
			const Elf_Sym  *sp;

			sp = dynld.dyn.symtab;
			sp += ELF_R_SYM(rp->r_info);
			if(ELF_R_SYM(rp->r_info) && sp->st_value == 0) {
#if 0
				_dl_wrstderr("Dynamic loader failure: self bootstrapping impossible.\n");
				_dl_wrstderr("Undefined symbol: ");
				_dl_wrstderr((char *)dynld.dyn.strtab
					+ sp->st_name);
#endif
				_dl_exit(6);
			}

			ra = (Elf_Addr *)(rp->r_offset + loff);

			RELOC_RELA(rp, sp, ra, loff);
			/*
			*/

			/*
			*/
			rp++;
		}

	}
	/* we have been fully relocated here, so most things no longer
	 * need the loff adjustment
	 */
	return;
}


void
_dl_rtld(elf_object_t *object)
{
	if(object->next) {
		_dl_rtld(object->next);
	}

	/*
	 *  Do relocation information first, then GOT.
	 */
	_dl_md_reloc(object, DT_REL, DT_RELSZ);
	_dl_md_reloc(object, DT_RELA, DT_RELASZ);
	/*
	_dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
	*/
	if(_dl_bindnow) {	/* XXX Perhaps more checking ? */
		_dl_md_reloc_got(object, 1);
	}
	else {
		_dl_md_reloc_got(object, 0);
	}
}

void
_dl_call_init(elf_object_t *object)
{
	Elf_Addr ooff;
	const Elf_Sym  *sym;
	static void (*_dl_atexit)(Elf_Addr) = NULL;

	if(object->next) {
		_dl_call_init(object->next);
	}

	if(object->status & STAT_INIT_DONE) {
		return;
	}


#ifndef __mips__
	if(object->dyn.init) {
		(*object->dyn.init)();
	}
#endif
#ifdef __mips__
/* XXX We perform relocation of DTOR/CTOR. This is a ld bug problem
 * XXX that should be fixed.
 */
	sym = 0;
	ooff = _dl_find_symbol("__CTOR_LIST__", object, &sym, 1, 1);
	if(sym) {
		int i = *(int *)(sym->st_value + ooff);
		while(i--) {
			*(int *)(sym->st_value + ooff + 4 + 4 * i) += ooff;
		}
	}
	sym = 0;
	ooff = _dl_find_symbol("__DTOR_LIST__", object, &sym, 1, 1);
	if(sym) {
		int i = *(int *)(sym->st_value + ooff);
		while(i--) {
			*(int *)(sym->st_value + ooff + 4 + 4 * i) += ooff;
		}
	}

/* XXX We should really call any code which resides in the .init segment
 * XXX but at the moment this functionality is not provided by the toolchain.
 * XXX Instead we rely on a symbol named '.init' and call it if it exists.
 */
	sym = 0;
	ooff = _dl_find_symbol(".init", object, &sym, 1, 1);
	if(sym) {
		if(_dl_debug)
			_dl_printf("calling .init in '%s'\n",object->load_name);
		(*(void(*)(void))(sym->st_value + ooff))();
	}
#if 0 /*XXX*/
	if(object->dyn.init) {
		(*object->dyn.init)();
	}
#endif
#endif /* __mips__ */
	object->status |= STAT_INIT_DONE;
}

/* static */ char *
_dl_getenv(const char *var, const char **env)
{
	const char *ep;

	while((ep = *env++)) {
		const char *vp = var;
		while(*vp && *vp == *ep) {
			vp++;
			ep++;
		}
		if(*vp == '\0' && *ep++ == '=') {
			return((char *)ep);
		}
	}
	return(0);
}


/*
 *  The following malloc/free code is a very simplified implementation
 *  of a malloc function. However, we do not need to be very complex here
 *  because we only free memory when 'dlclose()' is called and we can
 *  reuse at least the memory allocated for the object descriptor. We have
 *  one dynamic string allocated, the library name and it is likely that
 *  we can reuse that one to without a lot of complex colapsing code.
 */

void *
_dl_malloc(int size)
{
	long *p;
	long *t, *n;

	size = (size + 8 + DL_MALLOC_ALIGN - 1) & ~(DL_MALLOC_ALIGN - 1);

	if((t = _dl_malloc_free) != 0) {	/* Try free list first */
		n = (long *)&_dl_malloc_free;
		while(t && t[-1] < size) {
			n = t;
			t = (long *)*t;
		}
		if(t) {
			*n = *t;
			_dl_memset(t, 0, t[-1] - 4);
			return((void *)t);
		}
	}
	if((_dl_malloc_pool == 0) ||
	   (_dl_malloc_pool + size > _dl_malloc_base + 4096)) {
		_dl_malloc_pool = (void *)_dl_mmap((void *)0, 4096,
						PROT_READ|PROT_WRITE,
						MAP_ANON|MAP_COPY, -1, 0);
		if(_dl_malloc_pool == 0 ||
			_dl_malloc_pool == MAP_FAILED )
		{
			_dl_printf("Dynamic loader failure: malloc.\n");
			_dl_exit(7);
		}
		_dl_malloc_base = _dl_malloc_pool;
	}
	p = _dl_malloc_pool;
	_dl_malloc_pool += size;
	_dl_memset(p, 0, size);
	*p = size;
	return((void *)(p + 1));
}

void
_dl_free(void *p)
{
	long *t = (long *)p;

	*t = (long)_dl_malloc_free;
	_dl_malloc_free = p;
}