Ejemplo n.º 1
0
/*
 * Resolve a symbol at run-time.
 */
Elf_Addr
_dl_bind(elf_object_t *object, int reloff)
{
	Elf_RelA *rela;
	Elf_Addr *addr, ooff;
	const Elf_Sym *sym, *this;
	const char *symn;
	sigset_t savedmask;

	rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);

	addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
	if (object->plt_size != 0 && !(*addr >=  object->plt_start &&
	    *addr < (object->plt_start + object->plt_size ))) {
		/* something is broken, relocation has already occurred */
#if 0
		DL_DEB(("*addr doesn't point into plt %p obj %s\n", 
		    *addr, object->load_name));
#endif
		return *addr;
	}

	sym = object->dyn.symtab;
	sym += ELF64_R_SYM(rela->r_info);
	symn = object->dyn.strtab + sym->st_name;

	this = NULL;
	ooff = _dl_find_symbol(symn, &this,
	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
	    object, NULL);
	if (this == NULL) {
		_dl_printf("lazy binding failed!\n");
		*((int *)0) = 0;	/* XXX */
	}

	/* if PLT is protected, allow the write */
	if (object->plt_size != 0) {
		_dl_thread_bind_lock(0, &savedmask);
		_dl_mprotect(addr, sizeof(Elf_Addr),
		    PROT_READ|PROT_WRITE);
	}

	*addr = ooff + this->st_value + rela->r_addend;

	/* if PLT is (to be protected, change back to RO/X  */
	if (object->plt_size != 0) {
		_dl_mprotect(addr, sizeof(Elf_Addr),
		    PROT_READ);
		_dl_thread_bind_lock(1, &savedmask);
	}

	return *addr;
}
/*
 * Resolve a symbol at run-time.
 */
uint64_t
_dl_bind(elf_object_t *object, int reloff)
{
	const elf_object_t *sobj;
	const Elf_Sym *sym, *this;
	Elf_Addr *addr, ooff;
	const char *symn;
	Elf_Addr value;
	Elf_RelA *rela;
	sigset_t savedmask;

	rela = (Elf_RelA *)object->dyn.jmprel + reloff;

	sym = object->dyn.symtab;
	sym += ELF_R_SYM(rela->r_info);
	symn = object->dyn.strtab + sym->st_name;

	addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
	this = NULL;
	ooff = _dl_find_symbol(symn, &this,
	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
	if (this == NULL) {
		_dl_printf("lazy binding failed!\n");
		*(volatile int *)0 = 0;		/* XXX */
	}
	DL_DEB(("%s: %s\n", symn, sobj->load_name));

	value = ooff + this->st_value + rela->r_addend;

	if (sobj->traced && _dl_trace_plt(sobj, symn))
		return ((uint64_t)value << 32) | (Elf_Addr)sobj->dyn.pltgot;

	/* if PLT+GOT is protected, allow the write */
	if (object->got_size != 0) {
		_dl_thread_bind_lock(0, &savedmask);
		/* mprotect the actual modified region, not the whole plt */
		_dl_mprotect((void*)addr, sizeof (Elf_Addr) * 2,
		    PROT_READ|PROT_WRITE|PROT_EXEC);
	}

	addr[0] = value;
	addr[1] = (Elf_Addr)sobj->dyn.pltgot;

	/* if PLT is (to be protected, change back to RO */
	if (object->got_size != 0) {
		/* mprotect the actual modified region, not the whole plt */
		_dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3,
		    PROT_READ|PROT_EXEC);
		_dl_thread_bind_lock(1, &savedmask);
	}

	return ((uint64_t)addr[0] << 32) | addr[1];
}
Ejemplo n.º 3
0
Elf_Addr
_dl_bind(elf_object_t *object, int reloff)
{
	Elf_RelA *rel;
	Elf_Addr *r_addr, ooff, value;
	const Elf_Sym *sym, *this;
	const char *symn;
	const elf_object_t *sobj;
	sigset_t savedmask;

	rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);

	sym = object->dyn.symtab;
	sym += ELF_R_SYM(rel->r_info);
	symn = object->dyn.strtab + sym->st_name;

	r_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
	this = NULL;
	ooff = _dl_find_symbol(symn, &this,
	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
	if (this == NULL) {
		_dl_printf("lazy binding failed!\n");
		*(volatile int *)0 = 0;		/* XXX */
	}

	value = ooff + this->st_value + rel->r_addend;

	if (sobj->traced && _dl_trace_plt(sobj, symn))
		return value;

	/* if GOT is protected, allow the write */
	if (object->got_size != 0)  {
		_dl_thread_bind_lock(0, &savedmask);
		_dl_mprotect((void*)object->got_start, object->got_size,
		    PROT_READ|PROT_WRITE);
	}

	*r_addr = value;

	/* put the GOT back to RO */
	if (object->got_size != 0) {
		_dl_mprotect((void*)object->got_start, object->got_size,
		    PROT_READ);
		_dl_thread_bind_lock(1, &savedmask);
	}

	return (value);
}
Ejemplo n.º 4
0
/*
 * mprotect a segment to the indicated protection.  If 'addr' is non-zero,
 * then it's the start address, else the value of 'start_sym' is the start.
 * The value of 'end_sym' is the end address.  The start is rounded down
 * and the end is rounded up to page boundaries.  Returns 'addr' or the
 * address of the start symbol.
 */
void *
_dl_protect_segment(elf_object_t *object, Elf_Addr addr,
    const char *start_sym, const char *end_sym, int prot)
{
	const Elf_Sym *this;
	Elf_Addr ooff, start, end;

	if (addr == 0) {
		this = NULL;
		ooff = _dl_find_symbol(start_sym, &this,
		    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL,
		    object, NULL);
		/* If not found, nothing to do */
		if (this == NULL)
			return (NULL);
		addr = ooff + this->st_value;
	}

	this = NULL;
	ooff = _dl_find_symbol(end_sym, &this,
	    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
	if (this == NULL)
		addr = 0;
	else {
		end = ooff + this->st_value;
		if (addr < end) {
			start = ELF_TRUNC(addr, _dl_pagesz);
			end = ELF_ROUND(end, _dl_pagesz);
			_dl_mprotect((void *)start, end - start, prot);
		}
	}

	return ((void *)addr);
}
Ejemplo n.º 5
0
Elf_Addr
_dl_bind(elf_object_t *object, int symidx)
{
	Elf_Addr *gotp = object->dyn.pltgot;
	Elf_Addr *addr, ooff;
	const Elf_Sym *sym, *this;
	const char *symn;
	sigset_t savedmask;
	int n;

	sym = object->dyn.symtab;
	sym += symidx;
	symn = object->dyn.strtab + sym->st_name;
	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] -
	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];

	this = NULL;
	ooff = _dl_find_symbol(symn, &this,
	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL);
	if (this == NULL) {
		_dl_printf("lazy binding failed\n");
		*((int *)0) = 0;	/* XXX */
	}

	addr = &gotp[n + symidx];

	/* if GOT is protected, allow the write */
	if (object->got_size != 0) {
		_dl_thread_bind_lock(0, &savedmask);
		_dl_mprotect(addr, sizeof(Elf_Addr), PROT_READ|PROT_WRITE);
	}

	*addr = ooff + this->st_value;

	/* if GOT is (to be protected, change back to RO */
	if (object->got_size != 0) {
		_dl_mprotect(addr, sizeof (Elf_Addr), PROT_READ);
		_dl_thread_bind_lock(1, &savedmask);
	}

	return *addr;
}
Ejemplo n.º 6
0
void
_dl_protect_relro (struct elf_resolve *l)
{
	ElfW(Addr) base = (ElfW(Addr)) DL_RELOC_ADDR(l->loadaddr, l->relro_addr);
	ElfW(Addr) start = (base & PAGE_ALIGN);
	ElfW(Addr) end = ((base + l->relro_size) & PAGE_ALIGN);
	_dl_if_debug_dprint("RELRO protecting %s:  start:%x, end:%x\n", l->libname, start, end);
	if (start != end &&
	    _dl_mprotect ((void *) start, end - start, PROT_READ) < 0) {
		_dl_dprintf(2, "%s: cannot apply additional memory protection after relocation", l->libname);
		_dl_exit(0);
	}
}
Ejemplo n.º 7
0
int
_dl_md_reloc(elf_object_t *object, int rel, int relasz)
{
	long	i;
	long	numrela;
	int	fails = 0;
	Elf64_Addr loff;
	Elf64_Rela  *relas;
	struct load_list *llist;

	loff = object->obj_base;
	numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela);
	relas = (Elf64_Rela *)(object->Dyn.info[rel]);

	if (relas == NULL)
		return(0);

	/*
	 * unprotect some segments if we need it.
	 * XXX - we unprotect way to much. only the text can have cow
	 * relocations.
	 */
	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
		for (llist = object->load_list; llist != NULL; llist = llist->next) {
			if (!(llist->prot & PROT_WRITE)) {
				_dl_mprotect(llist->start, llist->size,
				    llist->prot|PROT_WRITE);
			}
		}
	}

	for (i = 0; i < numrela; i++, relas++) {
		Elf64_Addr *r_addr;
		Elf64_Addr ooff;
		const Elf64_Sym *sym, *this;
		const char *symn;

		r_addr = (Elf64_Addr *)(relas->r_offset + loff);

		if (ELF64_R_SYM(relas->r_info) == 0xffffffff)
			continue;


		sym = object->dyn.symtab;
		sym += ELF64_R_SYM(relas->r_info);
		symn = object->dyn.strtab + sym->st_name;

		this = NULL;
		switch (ELF64_R_TYPE(relas->r_info)) {
		case R_TYPE(REFQUAD):
			ooff =  _dl_find_symbol_bysym(object,
			    ELF64_R_SYM(relas->r_info), &this,
			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
			    sym, NULL);
			if (this == NULL)
				goto resolve_failed;
			*r_addr += ooff + this->st_value + relas->r_addend;
			break;
		case R_TYPE(RELATIVE):
			/*
			 * There is a lot of unaligned RELATIVE
			 * relocs generated by gcc in the exception handlers.
			 */
			if ((((Elf_Addr) r_addr) & 0x7) != 0) {
				Elf_Addr tmp;
#if 0
_dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr,
    ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff);
#endif
				_dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
				tmp += loff;
				_dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
			} else
				*r_addr += loff;
			break;
		case R_TYPE(JMP_SLOT):
			ooff = _dl_find_symbol(symn, &this,
			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
			    sym, object, NULL);
			if (this == NULL)
				goto resolve_failed;
			*r_addr = ooff + this->st_value + relas->r_addend;
			break;
		case R_TYPE(GLOB_DAT):
			ooff =  _dl_find_symbol_bysym(object,
			    ELF64_R_SYM(relas->r_info), &this,
			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
			    sym, NULL);
			if (this == NULL)
				goto resolve_failed;
			*r_addr = ooff + this->st_value + relas->r_addend;
			break;
		case R_TYPE(NONE):
			break;
		default:
			_dl_printf("%s:"
			    " %s: unsupported relocation '%s' %d at %lx\n",
			    _dl_progname, object->load_name, symn,
			    ELF64_R_TYPE(relas->r_info), r_addr );
			_dl_exit(1);
		}
		continue;
resolve_failed:
		if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
			fails++;
	}
	__asm __volatile("imb" : : : "memory");

	/* reprotect the unprotected segments */
	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
		for (llist = object->load_list; llist != NULL; llist = llist->next) {
			if (!(llist->prot & PROT_WRITE))
				_dl_mprotect(llist->start, llist->size,
				    llist->prot);
		}
	}
	return (fails);
}
Ejemplo n.º 8
0
/*
 *	Relocate the Global Offset Table (GOT).
 */
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
	int	fails = 0;
	Elf_Addr *pltgot;
	extern void _dl_bind_start(void);	/* XXX */
	Elf_Addr ooff;
	Elf_Addr plt_addr;
	const Elf_Sym *this;


	pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];

	object->got_addr = 0;
	object->got_size = 0;
	this = NULL;
	ooff = _dl_find_symbol("__got_start", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->got_addr = ooff + this->st_value;

	this = NULL;
	ooff = _dl_find_symbol("__got_end", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->got_size = ooff + this->st_value  - object->got_addr;

	plt_addr = 0;
	object->plt_size = 0;
	this = NULL;
	ooff = _dl_find_symbol("__plt_start", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		plt_addr = ooff + this->st_value;

	this = NULL;
	ooff = _dl_find_symbol("__plt_end", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->plt_size = ooff + this->st_value  - plt_addr;

	if (object->got_addr == 0)
		object->got_start = 0;
	else {
		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
		object->got_size += object->got_addr - object->got_start;
		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
	}
	if (plt_addr == 0)
		object->plt_start = 0;
	else {
		object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
		object->plt_size += plt_addr - object->plt_start;
		object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
	}

	if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) {
		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
	} else {
		if (object->obj_base != 0) {
			int i, size;
			Elf_Addr *addr;
			Elf_RelA *rela;

			size = object->Dyn.info[DT_PLTRELSZ] /
			    sizeof(Elf_RelA);
			rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);

			for (i = 0; i < size; i++) {
				addr = (Elf_Addr *)(object->obj_base +
				    rela[i].r_offset);
				*addr += object->obj_base;
			}
		}
	}
	if (pltgot != NULL) {
		pltgot[2] = (Elf_Addr)_dl_bind_start;
		pltgot[3] = (Elf_Addr)object;
	}
	if (object->got_size != 0)
		_dl_mprotect((void*)object->got_start, object->got_size,
		    PROT_READ);
	if (object->plt_size != 0)
		_dl_mprotect((void*)object->plt_start, object->plt_size,
		    PROT_READ|PROT_EXEC);

	return (fails);
}
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
	Elf_RelA *rela;
	Elf_Addr  ooff;
	int	i, numrela, fails = 0;
	const Elf_Sym *this;

	if (object->dyn.pltrel != DT_RELA)
		return (0);

	object->got_addr = 0;
	object->got_size = 0;
	this = NULL;
	ooff = _dl_find_symbol("__got_start", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL );
	if (this != NULL)
		object->got_addr = ooff + this->st_value;

	this = NULL;
	ooff = _dl_find_symbol("__got_end", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->got_size = ooff + this->st_value  - object->got_addr;

	if (object->got_addr == 0)
		object->got_start = 0;
	else {
		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
		object->got_size += object->got_addr - object->got_start;
		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
	}

	if (object->traced)
		lazy = 1;

	if (!lazy) {
		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
	} else {
		register Elf_Addr ltp __asm ("%r19");
		Elf_Addr *got = NULL;

		rela = (Elf_RelA *)(object->dyn.jmprel);
		numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
		ooff = object->obj_base;

		/*
		 * Find the PLT stub by looking at all the
		 * relocations.  The PLT stub should be at the end of
		 * the .plt section so we start with the last
		 * relocation, since the linker should have emitted
		 * them in order.
		 */
		for (i = numrela - 1; i >= 0; i--) {
			got = (Elf_Addr *)(ooff + rela[i].r_offset +
			    PLT_ENTRY_SIZE + PLT_STUB_SIZE);
			if (got[-2] == PLT_STUB_MAGIC1 ||
			    got[-1] == PLT_STUB_MAGIC2)
				break;
			got = NULL;
		}
		if (got == NULL)
			return (1);

		/*
		 * Patch up the PLT stub such that it doesn't clobber
		 * %r22, which is used to pass on the errno values
		 * from failed system calls to __cerrno() in libc.
		 */
		got[-7] = PLT_STUB_INSN1;
		got[-6] = PLT_STUB_INSN2;
		__asm volatile("fdc 0(%0)" :: "r" (&got[-7]));
		__asm volatile("fdc 0(%0)" :: "r" (&got[-6]));
		__asm volatile("sync");
		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-7]));
		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-6]));
		__asm volatile("sync");

		/*
		 * Fill in the PLT stub such that it invokes the
		 * _dl_bind_start() trampoline to fix up the
		 * relocation.
		 */
		got[1] = (Elf_Addr)object;
		got[-2] = (Elf_Addr)&_dl_bind_start;
		got[-1] = ltp;
		/*
		 * Even though we didn't modify any instructions it
		 * seems we still need to syncronize the caches.
		 * There may be instructions in the same cache line
		 * and they end up being corrupted otherwise.
		 */
		__asm volatile("fdc 0(%0)" :: "r" (&got[-2]));
		__asm volatile("fdc 0(%0)" :: "r" (&got[-1]));
		__asm volatile("sync");
		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-2]));
		__asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-1]));
		__asm volatile("sync");
		for (i = 0; i < numrela; i++, rela++) {
			Elf_Addr *r_addr = (Elf_Addr *)(ooff + rela->r_offset);

			if (ELF_R_TYPE(rela->r_info) != RELOC_IPLT) {
				_dl_printf("unexpected reloc 0x%x\n",
				    ELF_R_TYPE(rela->r_info));
				return (1);
			}

			if (ELF_R_SYM(rela->r_info)) {
				r_addr[0] = (Elf_Addr)got - PLT_STUB_GOTOFF;
				r_addr[1] = (Elf_Addr) (rela -
				    (Elf_RelA *)object->dyn.jmprel);
			} else {
				r_addr[0] = ooff + rela->r_addend;
				r_addr[1] = (Elf_Addr)object->dyn.pltgot;
			}
		}
	}
	if (object->got_size != 0)
		_dl_mprotect((void *)object->got_start, object->got_size,
		    GOT_PERMS|PROT_EXEC);

	return (fails);
}
Ejemplo n.º 10
0
int
_dl_md_reloc(elf_object_t *object, int rel, int relsz)
{
	int	i;
	int	numrel;
	int	fails = 0;
	struct load_list *load_list;
	Elf64_Addr loff;
	Elf64_Addr ooff;
	Elf64_Addr got_start, got_end;
	Elf64_Rel  *relocs;
	const Elf64_Sym *sym, *this;

	loff = object->obj_base;
	numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel);
	relocs = (Elf64_Rel *)(object->Dyn.info[rel]);

	if (relocs == NULL)
		return(0);

	/*
	 * Change protection of all write protected segments in the
	 * object so we can do relocations in the .rodata section.
	 * After relocation restore protection.
	 */
	load_list = object->load_list;
	while (load_list != NULL) {
		if ((load_list->prot & PROT_WRITE) == 0)
			_dl_mprotect(load_list->start, load_list->size,
			    load_list->prot|PROT_WRITE);
		load_list = load_list->next;
	}

	/* XXX We need the got limits to know if reloc is in got. */
	/* XXX Relocs against the got should not include the STUB address! */
	this = NULL;
	got_start = 0;
	got_end = 0;
	ooff = _dl_find_symbol("__got_start", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		got_start = ooff + this->st_value;
	this = NULL;
	ooff = _dl_find_symbol("__got_end", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		got_end = ooff + this->st_value;

	DL_DEB(("relocating %d\n", numrel));
	for (i = 0; i < numrel; i++, relocs++) {
		Elf64_Addr r_addr = relocs->r_offset + loff;
		const char *symn;
		int type;

		if (ELF64_R_SYM(relocs->r_info) == 0xffffff)
			continue;

		ooff = 0;
		sym = object->dyn.symtab;
		sym += ELF64_R_SYM(relocs->r_info);
		symn = object->dyn.strtab + sym->st_name;
		type = ELF64_R_TYPE(relocs->r_info);

		this = NULL;
		if (ELF64_R_SYM(relocs->r_info) &&
		    !(ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
		    ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
			ooff = _dl_find_symbol(symn, &this,
			SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT,
			sym, object, NULL);

			if (this == NULL) {
				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
					fails++;
				continue;
			}
		}

		switch (ELF64_R_TYPE(relocs->r_info)) {
			/* XXX Handle non aligned relocs. .eh_frame
			 * XXX in libstdc++ seems to have them... */
			u_int64_t robj;

		case R_MIPS_REL32_64:
			if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
			    (ELF64_ST_TYPE(sym->st_info) == STT_SECTION ||
			    ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
				if ((long)r_addr & 7) {
					_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
					robj += loff + sym->st_value;
					_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
				} else {
					*(u_int64_t *)r_addr += loff + sym->st_value;
				}
			} else if (this && ((long)r_addr & 7)) {
				_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
				robj += this->st_value + ooff;
				_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
			} else if (this) {
				*(u_int64_t *)r_addr += this->st_value + ooff;
			}
			break;

		case R_MIPS_NONE:
			break;

		default:
			_dl_printf("%s: unsupported relocation '%d'\n",
			    _dl_progname, ELF64_R_TYPE(relocs->r_info));
			_dl_exit(1);
		}
	}
	DL_DEB(("done %d fails\n", fails));
	load_list = object->load_list;
	while (load_list != NULL) {
		if ((load_list->prot & PROT_WRITE) == 0)
			_dl_mprotect(load_list->start, load_list->size,
			    load_list->prot);
		load_list = load_list->next;
	}
	return(fails);
}
Ejemplo n.º 11
0
/*
 *	Relocate the Global Offset Table (GOT). Currently we don't
 *	do lazy evaluation here because the GNU linker doesn't
 *	follow the ABI spec which says that if an external symbol
 *	is referenced by other relocations than CALL16 and 26 it
 *	should not be given a stub and have a zero value in the
 *	symbol table. By not doing so, we can't use pointers to
 *	external functions and use them in comparisons...
 */
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
	int	i, n;
	Elf64_Addr loff;
	Elf64_Addr ooff;
	Elf64_Addr *gotp;
	const Elf64_Sym  *symp;
	const Elf64_Sym  *this;
	const char *strt;

	if (object->status & STAT_GOT_DONE)
		return (0);

	loff = object->obj_base;
	strt = object->dyn.strtab;
	gotp = object->dyn.pltgot;
	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];

	DL_DEB(("loff: '%p'\n", loff));
	/*
	 *  Set up pointers for run time (lazy) resolving.
	 */
	gotp[0] = (long)_dl_bind_start;
	gotp[1] = (long)object;

	/*  First do all local references. */
	for (i = 2; i < n; i++) {
		gotp[i] += loff;
	}

	gotp += n;

	symp =  object->dyn.symtab;
	symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
	n =  object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] -
	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];

	this = NULL;
	object->plt_size = 0;
	object->got_size = 0;
	ooff = _dl_find_symbol("__got_start", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->got_start = ooff + this->st_value;

	this = NULL;
	ooff = _dl_find_symbol("__got_end", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->got_size = ooff + this->st_value  - object->got_start;

	/*
	 *  Then do all global references according to the ABI.
	 *  Quickstart is not yet implemented.
	 */
	while (n--) {
		if (symp->st_shndx == SHN_UNDEF &&
		    ELF64_ST_TYPE(symp->st_info) == STT_FUNC) {
			if (symp->st_value == 0 || !lazy) {
				this = NULL;
				ooff = _dl_find_symbol(strt + symp->st_name,
				    &this,
				    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
				    symp, object, NULL);
				if (this)
					*gotp = this->st_value + ooff;
			} else
				*gotp = symp->st_value + loff;
		} else if (symp->st_shndx == SHN_COMMON ||
			symp->st_shndx == SHN_UNDEF) {
			this = NULL;
			ooff = _dl_find_symbol(strt + symp->st_name, &this,
			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
			    symp, object, NULL);
			if (this)
				*gotp = this->st_value + ooff;
		} else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC &&
			symp->st_value != *gotp) {
			*gotp += loff;
		} else {	/* Resolve all others immediately */
			this = NULL;
			ooff = _dl_find_symbol(strt + symp->st_name, &this,
			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
			    symp, object, NULL);
			if (this)
				*gotp = this->st_value + ooff;
			else
				*gotp = symp->st_value + loff;
		}
		gotp++;
		symp++;
	}
	object->status |= STAT_GOT_DONE;

	DL_DEB(("got: %x, %x\n", object->got_start, object->got_size));
	if (object->got_size != 0)
		_dl_mprotect((void*)object->got_start, object->got_size,
		    PROT_READ);

	return (0);
}
Ejemplo n.º 12
0
int
_dl_md_reloc(elf_object_t *object, int rel, int relasz)
{
	int	i;
	int	numrela;
	int	relrel;
	int	fails = 0;
	struct load_list *llist;
	Elf32_Addr loff;
	Elf32_Rela  *relas;

	loff = object->obj_base;
	numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
	relrel = rel == DT_RELA ? object->relacount : 0;
	relas = (Elf32_Rela *)(object->Dyn.info[rel]);

#ifdef DL_PRINTF_DEBUG
	_dl_printf("object relocation size %x, numrela %x\n",
	    object->Dyn.info[relasz], numrela);
#endif

	if (relas == NULL)
		return(0);

	if (relrel > numrela) {
		_dl_printf("relacount > numrela: %ld > %ld\n", relrel, numrela);
		_dl_exit(20);
	}

	/*
	 * Change protection of all write protected segments in the object
	 * so we can do relocations such as PC32. After relocation,
	 * restore protection.
	 */
	if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) {
		for (llist = object->load_list; llist != NULL;
		    llist = llist->next) {
			if (!(llist->prot & PROT_WRITE)) {
				_dl_mprotect(llist->start, llist->size,
				    llist->prot|PROT_WRITE);
			}
		}
	}

	/* tight loop for leading RELATIVE relocs */
	for (i = 0; i < relrel; i++, relas++) {
		Elf32_Addr *r_addr;

#ifdef DEBUG
		if (ELF_R_TYPE(relas->r_info) != R_68K_RELATIVE) {
			_dl_printf("RELACOUNT wrong\n");
			_dl_exit(20);
		}
#endif
		r_addr = (Elf32_Addr *)(relas->r_offset + loff);
		*r_addr = relas->r_addend + loff;
	}
	for (; i < numrela; i++, relas++) {
		Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
		Elf32_Addr ooff, addend, newval;
		const Elf32_Sym *sym, *this;
		const char *symn;
		int type;
		Elf32_Addr prev_value = 0, prev_ooff = 0;
		const Elf32_Sym *prev_sym = NULL;

		type = ELF32_R_TYPE(relas->r_info);

		if (type == R_68K_JMP_SLOT && rel != DT_JMPREL)
			continue;

		if (type == R_68K_NONE)
			continue;

		sym = object->dyn.symtab;
		sym += ELF32_R_SYM(relas->r_info);
		symn = object->dyn.strtab + sym->st_name;

		if (type == R_68K_COPY) {
			/*
			 * we need to find a symbol, that is not in the current
			 * object, start looking at the beginning of the list,
			 * searching all objects but _not_ the current object,
			 * first one found wins.
			 */
			const Elf32_Sym *cpysrc = NULL;
			Elf32_Addr src_loff;
			int size;

			src_loff = 0;
			src_loff = _dl_find_symbol(symn, &cpysrc,
			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
			    sym, object, NULL);
			if (cpysrc != NULL) {
				size = sym->st_size;
				if (sym->st_size != cpysrc->st_size) {
					/* _dl_find_symbol() has warned
					   about this already */
					size = sym->st_size < cpysrc->st_size ?
					    sym->st_size : cpysrc->st_size;
				}
				_dl_bcopy((void *)(src_loff + cpysrc->st_value),
				    r_addr, size);
			} else
				fails++;

			continue;
		}

		if (ELF32_R_SYM(relas->r_info) &&
		    !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
		    ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
		    sym != prev_sym) {
			this = NULL;
			ooff = _dl_find_symbol_bysym(object,
			    ELF32_R_SYM(relas->r_info), &this,
			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
			    ((type == R_68K_JMP_SLOT) ? SYM_PLT:SYM_NOTPLT),
			    sym, NULL);

			if (this == NULL) {
				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
					fails++;
				continue;
			}
			prev_sym = sym;
			prev_value = this->st_value;
			prev_ooff = ooff;
		}

		if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
		    (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
		    ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE))
			addend = relas->r_addend;
		else
			addend = prev_value + relas->r_addend;

		switch (type) {
		case R_68K_PC32:
			newval = prev_ooff + addend;
			newval -= (Elf_Addr)r_addr;
			*r_addr = newval;
			break;
		case R_68K_32:
		case R_68K_GLOB_DAT:
		case R_68K_JMP_SLOT:
			newval = prev_ooff + addend;
			*r_addr = newval;
			break;
		case R_68K_RELATIVE:
			newval = loff + addend;
			*r_addr = newval;
			break;
		default:
			_dl_printf("%s:"
			    " %s: unsupported relocation '%s' %d at %x\n",
			    _dl_progname, object->load_name, symn,
			    ELF32_R_TYPE(relas->r_info), r_addr);
			_dl_exit(1);
		}
	}

	/* reprotect the unprotected segments */
	if (object->dyn.textrel == 1 && (rel == DT_REL || rel == DT_RELA)) {
		for (llist = object->load_list; llist != NULL;
		    llist = llist->next) {
			if (!(llist->prot & PROT_WRITE))
				_dl_mprotect(llist->start, llist->size,
				    llist->prot);
		}
	}

	return(fails);
}
Ejemplo n.º 13
0
/*
 *	Relocate the Global Offset Table (GOT).
 *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
 *	otherwise the lazy binding plt initialization is performed.
 */
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
	extern void _dl_bind_start(void);	/* XXX */
	int	fails = 0;
	Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
	Elf_Addr ooff;
	const Elf_Sym *this;

	if (pltgot == NULL)
		return (0);

	pltgot[1] = (Elf_Addr)object;
	pltgot[2] = (Elf_Addr)_dl_bind_start;

	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
		return (0);

	object->got_addr = 0;
	object->got_size = 0;
	this = NULL;
	ooff = _dl_find_symbol("__got_start", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->got_addr = ooff + this->st_value;

	this = NULL;
	ooff = _dl_find_symbol("__got_end", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->got_size = ooff + this->st_value  - object->got_addr;

#if 0
	plt_addr = 0;
	object->plt_size = 0;
	this = NULL;
	ooff = _dl_find_symbol("__plt_start", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		plt_addr = ooff + this->st_value;

	this = NULL;
	ooff = _dl_find_symbol("__plt_end", &this,
	    SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
	if (this != NULL)
		object->plt_size = ooff + this->st_value  - plt_addr;
#endif

	if (object->got_addr == 0)
		object->got_start = 0;
	else {
		object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
		object->got_size += object->got_addr - object->got_start;
		object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
	}
#if 0
	if (plt_addr == 0)
		object->plt_start = 0;
	else {
		object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
		object->plt_size += plt_addr - object->plt_start;
		object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
	}
#endif

	if (object->traced)
		lazy = 1;

	if (!lazy) {
		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
	} else {
		if (object->obj_base != 0) {
			int i, size;
			Elf_Addr *addr;
			Elf_RelA *rela;

			size = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
			rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];

			for (i = 0; i < size; i++) {
				addr = (Elf_Addr *)(object->obj_base +
				    rela[i].r_offset);
				*addr += object->obj_base;
			}
		}
	}
	if (object->got_size != 0) {
		_dl_mprotect((void*)object->got_start, object->got_size,
		    PROT_READ);
	}
	/* PLT is already RO, no point in mprotecting it, just do GOT */
#if 0
	if (object->plt_size != 0)
		_dl_mprotect((void*)object->plt_start, object->plt_size,
		    PROT_READ|PROT_EXEC);
#endif

	return (fails);
}
Ejemplo n.º 14
0
int
_dl_md_reloc(elf_object_t *object, int rel, int relsz)
{
	long	i;
	long	numrel;
	long	relrel;
	int	fails = 0;
	Elf_Addr loff;
	Elf_Addr prev_value = 0;
	const Elf_Sym *prev_sym = NULL;
	Elf_RelA *rels;
	struct load_list *llist;

	loff = object->obj_base;
	numrel = object->Dyn.info[relsz] / sizeof(Elf_RelA);
	relrel = rel == DT_RELA ? object->relacount : 0;
	rels = (Elf_RelA *)(object->Dyn.info[rel]);
	if (rels == NULL)
		return(0);

	if (relrel > numrel) {
		_dl_printf("relacount > numrel: %ld > %ld\n", relrel, numrel);
		_dl_exit(20);
	}

	/*
	 * unprotect some segments if we need it.
	 */
	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
		for (llist = object->load_list; llist != NULL; llist = llist->next) {
			if (!(llist->prot & PROT_WRITE))
				_dl_mprotect(llist->start, llist->size,
				    PROT_READ | PROT_WRITE);
		}
	}

	/* tight loop for leading RELATIVE relocs */
	for (i = 0; i < relrel; i++, rels++) {
		Elf_Addr *where;

#ifdef DEBUG
		if (ELF_R_TYPE(rels->r_info) != R_TYPE(RELATIVE)) {
			_dl_printf("RELACOUNT wrong\n");
			_dl_exit(20);
		}
#endif
		where = (Elf_Addr *)(rels->r_offset + loff);
		*where = rels->r_addend + loff;
	}
	for (; i < numrel; i++, rels++) {
		Elf_Addr *where, value, ooff, mask;
		Elf_Word type;
		const Elf_Sym *sym, *this;
		const char *symn;

		type = ELF_R_TYPE(rels->r_info);

		if (RELOC_ERROR(type)) {
			_dl_printf("relocation error %d idx %d\n", type, i);
			_dl_exit(20);
		}

		if (type == R_TYPE(NONE))
			continue;

		if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
			continue;

		where = (Elf_Addr *)(rels->r_offset + loff);

		if (RELOC_USE_ADDEND(type))
			value = rels->r_addend;
		else
			value = 0;

		sym = NULL;
		symn = NULL;
		if (RELOC_RESOLVE_SYMBOL(type)) {
			sym = object->dyn.symtab;
			sym += ELF_R_SYM(rels->r_info);
			symn = object->dyn.strtab + sym->st_name;

			if (sym->st_shndx != SHN_UNDEF &&
			    ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
				value += loff;
			} else if (sym == prev_sym) {
				value += prev_value;
			} else {
				this = NULL;
				ooff = _dl_find_symbol_bysym(object,
				    ELF_R_SYM(rels->r_info), &this,
				    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
				    ((type == R_TYPE(JUMP_SLOT))?
					SYM_PLT:SYM_NOTPLT),
				    sym, NULL);
				if (this == NULL) {
resolve_failed:
					if (ELF_ST_BIND(sym->st_info) !=
					    STB_WEAK)
						fails++;
					continue;
				}
				prev_sym = sym;
				prev_value = (Elf_Addr)(ooff + this->st_value);
				value += prev_value;
			}
		}

		if (type == R_TYPE(JUMP_SLOT)) {
			_dl_reloc_plt(where, value);
			continue;
		}

		if (type == R_TYPE(COPY)) {
			void *dstaddr = where;
			const void *srcaddr;
			const Elf_Sym *dstsym = sym, *srcsym = NULL;
			Elf_Addr soff;

			soff = _dl_find_symbol(symn, &srcsym,
			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
			    dstsym, object, NULL);
			if (srcsym == NULL)
				goto resolve_failed;

			srcaddr = (void *)(soff + srcsym->st_value);
			_dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
			continue;
		}

		if (RELOC_PC_RELATIVE(type))
			value -= (Elf_Addr)where;
		if (RELOC_BASE_RELATIVE(type))
			value += loff;

		mask = RELOC_VALUE_BITMASK(type);
		value >>= RELOC_VALUE_RIGHTSHIFT(type);
		value &= mask;

		if (RELOC_TARGET_SIZE(type) > 32) {
			*where &= ~mask;
			*where |= value;
		} else {
			Elf32_Addr *where32 = (Elf32_Addr *)where;

			*where32 &= ~mask;
			*where32 |= value;
		}
	}

	/* reprotect the unprotected segments */
	if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
		for (llist = object->load_list; llist != NULL; llist = llist->next) {
			if (!(llist->prot & PROT_WRITE))
				_dl_mprotect(llist->start, llist->size,
				    llist->prot);
		}
	}

	return (fails);
}