Example #1
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);
}
Example #2
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);
}
Example #4
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);
}
Example #5
0
/*
 * Load a file (interpreter/library) pointed to by path [stolen from
 * coff_load_shlib()]. Made slightly generic so it might be used externally.
 */
int
ELFNAME(load_file)(struct proc *p, char *path, struct exec_package *epp,
	struct elf_args *ap, Elf_Addr *last)
{
	int error, i;
	struct nameidata nd;
	Elf_Ehdr eh;
	Elf_Phdr *ph = NULL;
	u_long phsize;
	char *bp = NULL;
	Elf_Addr addr;
	struct vnode *vp;
	u_int8_t os;			/* Just a dummy in this routine */
	Elf_Phdr *base_ph = NULL;
	struct interp_ld_sec {
		Elf_Addr vaddr;
		u_long memsz;
	} loadmap[ELF_MAX_VALID_PHDR];
	int nload, idx = 0;
	Elf_Addr pos = *last;
	int file_align;

	bp = path;
	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, path, p);
	if ((error = namei(&nd)) != 0) {
		return (error);
	}
	vp = nd.ni_vp;
	if (vp->v_type != VREG) {
		error = EACCES;
		goto bad;
	}
	if ((error = VOP_GETATTR(vp, epp->ep_vap, p->p_ucred, p)) != 0)
		goto bad;
	if (vp->v_mount->mnt_flag & MNT_NOEXEC) {
		error = EACCES;
		goto bad;
	}
	if ((error = VOP_ACCESS(vp, VREAD, p->p_ucred, p)) != 0)
		goto bad1;
	if ((error = ELFNAME(read_from)(p, nd.ni_vp, 0,
				    (caddr_t)&eh, sizeof(eh))) != 0)
		goto bad1;

	if (ELFNAME(check_header)(&eh, ET_DYN) &&
	    ELFNAME(olf_check_header)(&eh, ET_DYN, &os)) {
		error = ENOEXEC;
		goto bad1;
	}

	phsize = eh.e_phnum * sizeof(Elf_Phdr);
	ph = (Elf_Phdr *)malloc(phsize, M_TEMP, M_WAITOK);

	if ((error = ELFNAME(read_from)(p, nd.ni_vp, eh.e_phoff, (caddr_t)ph,
	    phsize)) != 0)
		goto bad1;

	for (i = 0; i < eh.e_phnum; i++) {
		if (ph[i].p_type == PT_LOAD) {
			loadmap[idx].vaddr = trunc_page(ph[i].p_vaddr);
			loadmap[idx].memsz = round_page (ph[i].p_vaddr +
			    ph[i].p_memsz - loadmap[idx].vaddr);
			file_align = ph[i].p_align;
			idx++;
		}
	}
	nload = idx;

	/*
	 * If no position to load the interpreter was set by a probe
	 * function, pick the same address that a non-fixed mmap(0, ..)
	 * would (i.e. something safely out of the way).
	 */
	if (pos == ELFDEFNNAME(NO_ADDR)) {
		pos = uvm_map_hint(p, VM_PROT_EXECUTE);
	}

	pos = ELF_ROUND(pos, file_align);
	*last = epp->ep_interp_pos = pos;
	for (i = 0; i < nload;/**/) {
		vaddr_t	addr;
		struct	uvm_object *uobj;
		off_t	uoff;
		size_t	size;

#ifdef this_needs_fixing
		if (i == 0) {
			uobj = &vp->v_uvm.u_obj;
			/* need to fix uoff */
		} else {
#endif
			uobj = NULL;
			uoff = 0;
#ifdef this_needs_fixing
		}
#endif

		addr = trunc_page(pos + loadmap[i].vaddr);
		size =  round_page(addr + loadmap[i].memsz) - addr;

		/* CRAP - map_findspace does not avoid daddr+MAXDSIZ */
		if ((addr + size > (vaddr_t)p->p_vmspace->vm_daddr) &&
		    (addr < (vaddr_t)p->p_vmspace->vm_daddr + MAXDSIZ))
			addr = round_page((vaddr_t)p->p_vmspace->vm_daddr +
			    MAXDSIZ);

		if (uvm_map_findspace(&p->p_vmspace->vm_map, addr, size,
		    &addr, uobj, uoff, 0, UVM_FLAG_FIXED) == NULL) {
			if (uvm_map_findspace(&p->p_vmspace->vm_map, addr, size,
			    &addr, uobj, uoff, 0, 0) == NULL) {
				error = ENOMEM; /* XXX */
				goto bad1;
			}
		} 
		if (addr != pos + loadmap[i].vaddr) {
			/* base changed. */
			pos = addr - trunc_page(loadmap[i].vaddr);
			pos = ELF_ROUND(pos,file_align);
			epp->ep_interp_pos = *last = pos;
			i = 0;
			continue;
		}

		i++;
	}

	/*
	 * Load all the necessary sections
	 */
	for (i = 0; i < eh.e_phnum; i++) {
		Elf_Addr size = 0;
		int prot = 0;
		int flags;

		switch (ph[i].p_type) {
		case PT_LOAD:
			if (base_ph == NULL) {
				flags = VMCMD_BASE;
				addr = *last;
				base_ph = &ph[i];
			} else {
				flags = VMCMD_RELATIVE;
				addr = ph[i].p_vaddr - base_ph->p_vaddr;
			}
			ELFNAME(load_psection)(&epp->ep_vmcmds, nd.ni_vp,
			    &ph[i], &addr, &size, &prot, flags);
			/* If entry is within this section it must be text */
			if (eh.e_entry >= ph[i].p_vaddr &&
			    eh.e_entry < (ph[i].p_vaddr + size)) {
 				epp->ep_entry = addr + eh.e_entry -
				    ELF_TRUNC(ph[i].p_vaddr,ph[i].p_align);
				ap->arg_interp = addr;
			}
			addr += size;
			break;

		case PT_DYNAMIC:
		case PT_PHDR:
		case PT_NOTE:
			break;

		default:
			break;
		}
	}

	vn_marktext(nd.ni_vp);

bad1:
	VOP_CLOSE(nd.ni_vp, FREAD, p->p_ucred, p);
bad:
	if (ph != NULL)
		free((char *)ph, M_TEMP);

	*last = addr;
	vput(nd.ni_vp);
	return (error);
}
Example #6
0
/*
 * Load a psection at the appropriate address
 */
void
ELFNAME(load_psection)(struct exec_vmcmd_set *vcset, struct vnode *vp,
	Elf_Phdr *ph, Elf_Addr *addr, Elf_Addr *size, int *prot, int flags)
{
	u_long uaddr, msize, lsize, psize, rm, rf;
	long diff, offset, bdiff;
	Elf_Addr base;

	/*
	 * If the user specified an address, then we load there.
	 */
	if (*addr != ELFDEFNNAME(NO_ADDR)) {
		if (ph->p_align > 1) {
			*addr = ELF_TRUNC(*addr, ph->p_align);
			diff = ph->p_vaddr - ELF_TRUNC(ph->p_vaddr, ph->p_align);
			/* page align vaddr */
			base = *addr + trunc_page(ph->p_vaddr) 
			    - ELF_TRUNC(ph->p_vaddr, ph->p_align);

			bdiff = ph->p_vaddr - trunc_page(ph->p_vaddr);

		} else
			diff = 0;
	} else {
		*addr = uaddr = ph->p_vaddr;
		if (ph->p_align > 1)
			*addr = ELF_TRUNC(uaddr, ph->p_align);
		base = trunc_page(uaddr);
		bdiff = uaddr - base;
		diff = uaddr - *addr;
	}

	*prot |= (ph->p_flags & PF_R) ? VM_PROT_READ : 0;
	*prot |= (ph->p_flags & PF_W) ? VM_PROT_WRITE : 0;
	*prot |= (ph->p_flags & PF_X) ? VM_PROT_EXECUTE : 0;

	msize = ph->p_memsz + diff;
	offset = ph->p_offset - bdiff;
	lsize = ph->p_filesz + bdiff;
	psize = round_page(lsize);

	/*
	 * Because the pagedvn pager can't handle zero fill of the last
	 * data page if it's not page aligned we map the last page readvn.
	 */
	if (ph->p_flags & PF_W) {
		psize = trunc_page(lsize);
		if (psize > 0)
			NEW_VMCMD2(vcset, vmcmd_map_pagedvn, psize, base, vp,
			    offset, *prot, flags);
		if (psize != lsize) {
			NEW_VMCMD2(vcset, vmcmd_map_readvn, lsize - psize,
			    base + psize, vp, offset + psize, *prot, flags);
		}
	} else {
		NEW_VMCMD2(vcset, vmcmd_map_pagedvn, psize, base, vp, offset,
		    *prot, flags);
	}

	/*
	 * Check if we need to extend the size of the segment
	 */
	rm = round_page(*addr + ph->p_memsz + diff);
	rf = round_page(*addr + ph->p_filesz + diff);

	if (rm != rf) {
		NEW_VMCMD2(vcset, vmcmd_map_zero, rm - rf, rf, NULLVP, 0,
		    *prot, flags);
	}
	*size = msize;
}