예제 #1
0
/*
 * With the file (fd) open on the image, and (ehdr) containing
 * the Elf header, load the image at (off)
 */
static int
__elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
{
	Elf_Ehdr *hdr;
	Elf_Shdr *shdr, *cshdr, *lshdr;
	vm_offset_t firstaddr, lastaddr;
	int i, nsym, res, ret, shdrbytes, symstrindex;

	ret = 0;
	firstaddr = lastaddr = (vm_offset_t)off;
	hdr = &ef->hdr;
	ef->off = (vm_offset_t)off;

	/* Read in the section headers. */
	shdrbytes = hdr->e_shnum * hdr->e_shentsize;
	shdr = alloc_pread(ef->fd, (off_t)hdr->e_shoff, shdrbytes);
	if (shdr == NULL) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "_obj_loadimage: read section headers failed\n");
		goto out;
	}
	ef->e_shdr = shdr;

	/*
	 * Decide where to load everything, but don't read it yet.
	 * We store the load address as a non-zero sh_addr value.
	 * Start with the code/data and bss.
	 */
	for (i = 0; i < hdr->e_shnum; i++)
		shdr[i].sh_addr = 0;
	for (i = 0; i < hdr->e_shnum; i++) {
		if (shdr[i].sh_size == 0)
			continue;
		switch (shdr[i].sh_type) {
		case SHT_PROGBITS:
		case SHT_NOBITS:
			lastaddr = roundup(lastaddr, shdr[i].sh_addralign);
			shdr[i].sh_addr = (Elf_Addr)lastaddr;
			lastaddr += shdr[i].sh_size;
			break;
		}
	}

	/* Symbols. */
	nsym = 0;
	for (i = 0; i < hdr->e_shnum; i++) {
		switch (shdr[i].sh_type) {
		case SHT_SYMTAB:
			nsym++;
			ef->symtabindex = i;
			shdr[i].sh_addr = (Elf_Addr)lastaddr;
			lastaddr += shdr[i].sh_size;
			break;
		}
	}
	if (nsym != 1) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "_obj_loadimage: file has no valid symbol table\n");
		goto out;
	}
	lastaddr = roundup(lastaddr, shdr[ef->symtabindex].sh_addralign);
	shdr[ef->symtabindex].sh_addr = (Elf_Addr)lastaddr;
	lastaddr += shdr[ef->symtabindex].sh_size;

	symstrindex = shdr[ef->symtabindex].sh_link;
	if (symstrindex < 0 || symstrindex >= hdr->e_shnum ||
	    shdr[symstrindex].sh_type != SHT_STRTAB) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "_obj_loadimage: file has invalid symbol strings\n");
		goto out;
	}
	lastaddr = roundup(lastaddr, shdr[symstrindex].sh_addralign);
	shdr[symstrindex].sh_addr = (Elf_Addr)lastaddr;
	lastaddr += shdr[symstrindex].sh_size;

	/* Section names. */
	if (hdr->e_shstrndx == 0 || hdr->e_shstrndx >= hdr->e_shnum ||
	    shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "_obj_loadimage: file has no section names\n");
		goto out;
	}
	ef->shstrindex = hdr->e_shstrndx;
	lastaddr = roundup(lastaddr, shdr[ef->shstrindex].sh_addralign);
	shdr[ef->shstrindex].sh_addr = (Elf_Addr)lastaddr;
	lastaddr += shdr[ef->shstrindex].sh_size;

	/* Relocation tables. */
	for (i = 0; i < hdr->e_shnum; i++) {
		switch (shdr[i].sh_type) {
		case SHT_REL:
		case SHT_RELA:
			lastaddr = roundup(lastaddr, shdr[i].sh_addralign);
			shdr[i].sh_addr = (Elf_Addr)lastaddr;
			lastaddr += shdr[i].sh_size;
			break;
		}
	}

	/* Clear the whole area, including bss regions. */
	kern_bzero(firstaddr, lastaddr - firstaddr);

	/* Figure section with the lowest file offset we haven't loaded yet. */
	for (cshdr = NULL; /* none */; /* none */)
	{
		/*
		 * Find next section to load. The complexity of this loop is
		 * O(n^2), but with  the number of sections being typically
		 * small, we do not care.
		 */
		lshdr = cshdr;

		for (i = 0; i < hdr->e_shnum; i++) {
			if (shdr[i].sh_addr == 0 ||
			    shdr[i].sh_type == SHT_NOBITS)
				continue;
			/* Skip sections that were loaded already. */
			if (lshdr != NULL &&
			    lshdr->sh_offset >= shdr[i].sh_offset)
				continue;
			/* Find section with smallest offset. */
			if (cshdr == lshdr ||
			    cshdr->sh_offset > shdr[i].sh_offset)
				cshdr = &shdr[i];
		}

		if (cshdr == lshdr)
			break;

		if (kern_pread(ef->fd, (vm_offset_t)cshdr->sh_addr,
		    cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) {
			printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
			    "_obj_loadimage: read failed\n");
			goto out;
		}
	}

	file_addmetadata(fp, MODINFOMD_SHDR, shdrbytes, shdr);

	res = __elfN(obj_parse_modmetadata)(fp, ef);
	if (res != 0)
		goto out;

	ret = lastaddr - firstaddr;
	fp->f_addr = firstaddr;

	printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr);

out:
	printf("\n");
	return ret;
}
예제 #2
0
파일: load_elf.c 프로젝트: alek-p/openzfs
/*
 * With the file (fd) open on the image, and (ehdr) containing
 * the Elf header, load the image at (off)
 */
static int
__elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
{
    int 	i;
    u_int	j;
    Elf_Ehdr	*ehdr;
    Elf_Phdr	*phdr, *php;
    Elf_Shdr	*shdr;
    char	*shstr;
    int		ret;
    vm_offset_t firstaddr;
    vm_offset_t lastaddr;
    size_t	chunk;
    ssize_t	result;
    Elf_Addr	ssym, esym;
    Elf_Dyn	*dp;
    Elf_Addr	adp;
    Elf_Addr	ctors;
    int		ndp;
    int		symstrindex;
    int		symtabindex;
    Elf_Size	size;
    u_int	fpcopy;
    Elf_Sym	sym;
    Elf_Addr	p_start, p_end;

    dp = NULL;
    shdr = NULL;
    ret = 0;
    firstaddr = lastaddr = 0;
    ehdr = ef->ehdr;
    if (ehdr->e_type == ET_EXEC) {
#if defined(__i386__) || defined(__amd64__)
#if __ELF_WORD_SIZE == 64
	off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */
#else
	off = - (off & 0xff000000u);	/* i386 relocates after locore */
#endif
#elif defined(__powerpc__)
	/*
	 * On the purely virtual memory machines like e500, the kernel is
	 * linked against its final VA range, which is most often not
	 * available at the loader stage, but only after kernel initializes
	 * and completes its VM settings. In such cases we cannot use p_vaddr
	 * field directly to load ELF segments, but put them at some
	 * 'load-time' locations.
	 */
	if (off & 0xf0000000u) {
	    off = -(off & 0xf0000000u);
	    /*
	     * XXX the physical load address should not be hardcoded. Note
	     * that the Book-E kernel assumes that it's loaded at a 16MB
	     * boundary for now...
	     */
	    off += 0x01000000;
	    ehdr->e_entry += off;
#ifdef ELF_VERBOSE
	    printf("Converted entry 0x%08x\n", ehdr->e_entry);
#endif
	} else
	    off = 0;
#elif defined(__arm__) && !defined(EFI)
	/*
	 * The elf headers in arm kernels specify virtual addresses in all
	 * header fields, even the ones that should be physical addresses.
	 * We assume the entry point is in the first page, and masking the page
	 * offset will leave us with the virtual address the kernel was linked
	 * at.  We subtract that from the load offset, making 'off' into the
	 * value which, when added to a virtual address in an elf header,
	 * translates it to a physical address.  We do the va->pa conversion on
	 * the entry point address in the header now, so that later we can
	 * launch the kernel by just jumping to that address.
	 *
	 * When booting from UEFI the copyin and copyout functions handle
	 * adjusting the location relative to the first virtual address.
	 * Because of this there is no need to adjust the offset or entry
	 * point address as these will both be handled by the efi code.
	 */
	off -= ehdr->e_entry & ~PAGE_MASK;
	ehdr->e_entry += off;
#ifdef ELF_VERBOSE
	printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off);
#endif
#else
	off = 0;		/* other archs use direct mapped kernels */
#endif
    }
    ef->off = off;

    if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
	/* use entry address from header */
	fp->f_addr = ehdr->e_entry;
    }

    if (ef->kernel)
	__elfN(relocation_offset) = off;

    if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) {
	printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n");
	goto out;
    }
    phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff);

    for (i = 0; i < ehdr->e_phnum; i++) {
	/* We want to load PT_LOAD segments only.. */
	if (phdr[i].p_type != PT_LOAD)
	    continue;

#ifdef ELF_VERBOSE
	if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
	    printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
		(long)phdr[i].p_filesz, (long)phdr[i].p_offset,
		(long)(phdr[i].p_paddr + off),
		(long)(phdr[i].p_paddr + off + phdr[i].p_memsz - 1));
	} else {
	    printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
		(long)phdr[i].p_filesz, (long)phdr[i].p_offset,
		(long)(phdr[i].p_vaddr + off),
		(long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
	}
#else
	if ((phdr[i].p_flags & PF_W) == 0) {
	    printf("text=0x%lx ", (long)phdr[i].p_filesz);
	} else {
	    printf("data=0x%lx", (long)phdr[i].p_filesz);
	    if (phdr[i].p_filesz < phdr[i].p_memsz)
		printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz));
	    printf(" ");
	}
#endif
	fpcopy = 0;
	if (ef->firstlen > phdr[i].p_offset) {
	    fpcopy = ef->firstlen - phdr[i].p_offset;
	    if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
		archsw.arch_copyin(ef->firstpage + phdr[i].p_offset,
		    phdr[i].p_paddr + off, fpcopy);
	    } else {
		archsw.arch_copyin(ef->firstpage + phdr[i].p_offset,
		    phdr[i].p_vaddr + off, fpcopy);
	    }
	}
	if (phdr[i].p_filesz > fpcopy) {
	    if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
		if (kern_pread(ef->fd, phdr[i].p_paddr + off + fpcopy,
		    phdr[i].p_filesz - fpcopy,
		    phdr[i].p_offset + fpcopy) != 0) {
			printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
			    "_loadimage: read failed\n");
			goto out;
		}
	    } else {
		if (kern_pread(ef->fd, phdr[i].p_vaddr + off + fpcopy,
		    phdr[i].p_filesz - fpcopy,
		    phdr[i].p_offset + fpcopy) != 0) {
			printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
			    "_loadimage: read failed\n");
			goto out;
		}
	    }
	}
	/* clear space from oversized segments; eg: bss */
	if (phdr[i].p_filesz < phdr[i].p_memsz) {
#ifdef ELF_VERBOSE
	    if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
		printf(" (bss: 0x%lx-0x%lx)",
		    (long)(phdr[i].p_paddr + off + phdr[i].p_filesz),
		    (long)(phdr[i].p_paddr + off + phdr[i].p_memsz - 1));
	    } else {
		printf(" (bss: 0x%lx-0x%lx)",
		    (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
		    (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
	    }
#endif

	    if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
		kern_bzero(phdr[i].p_paddr + off + phdr[i].p_filesz,
		    phdr[i].p_memsz - phdr[i].p_filesz);
	    } else {
		kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz,
		    phdr[i].p_memsz - phdr[i].p_filesz);
	    }
	}
#ifdef ELF_VERBOSE
	printf("\n");
#endif

	if (archsw.arch_loadseg != NULL)
	    archsw.arch_loadseg(ehdr, phdr + i, off);

	if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS) {
		if (firstaddr == 0 || firstaddr > (phdr[i].p_paddr + off))
		    firstaddr = phdr[i].p_paddr + off;
		if (lastaddr == 0 ||
		    lastaddr < (phdr[i].p_paddr + off + phdr[i].p_memsz))
		    lastaddr = phdr[i].p_paddr + off + phdr[i].p_memsz;
	} else {
		if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off))
		    firstaddr = phdr[i].p_vaddr + off;
		if (lastaddr == 0 ||
		    lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
		    lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
	}
    }
    lastaddr = roundup(lastaddr, sizeof(long));

    /*
     * Get the section headers.  We need this for finding the .ctors
     * section as well as for loading any symbols.  Both may be hard
     * to do if reading from a .gz file as it involves seeking.  I
     * think the rule is going to have to be that you must strip a
     * file to remove symbols before gzipping it.
     */
    chunk = ehdr->e_shnum * ehdr->e_shentsize;
    if (chunk == 0 || ehdr->e_shoff == 0)
	goto nosyms;
    shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk);
    if (shdr == NULL) {
	printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
	    "_loadimage: failed to read section headers");
	goto nosyms;
    }
    file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr);

    /*
     * Read the section string table and look for the .ctors section.
     * We need to tell the kernel where it is so that it can call the
     * ctors.
     */
    chunk = shdr[ehdr->e_shstrndx].sh_size;
    if (chunk) {
	shstr = alloc_pread(ef->fd, shdr[ehdr->e_shstrndx].sh_offset, chunk);
	if (shstr) {
	    for (i = 0; i < ehdr->e_shnum; i++) {
		if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0)
		    continue;
		ctors = shdr[i].sh_addr;
		file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors),
		    &ctors);
		size = shdr[i].sh_size;
		file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size),
		    &size);
		break;
	    }
	    free(shstr);
	}
    }

    /*
     * Now load any symbols.
     */
    symtabindex = -1;
    symstrindex = -1;
    for (i = 0; i < ehdr->e_shnum; i++) {
	if (shdr[i].sh_type != SHT_SYMTAB)
	    continue;
	for (j = 0; j < ehdr->e_phnum; j++) {
	    if (phdr[j].p_type != PT_LOAD)
		continue;
	    if (shdr[i].sh_offset >= phdr[j].p_offset &&
		(shdr[i].sh_offset + shdr[i].sh_size <=
		 phdr[j].p_offset + phdr[j].p_filesz)) {
		shdr[i].sh_offset = 0;
		shdr[i].sh_size = 0;
		break;
	    }
	}
	if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
	    continue;		/* alread loaded in a PT_LOAD above */
	/* Save it for loading below */
	symtabindex = i;
	symstrindex = shdr[i].sh_link;
    }
    if (symtabindex < 0 || symstrindex < 0)
	goto nosyms;

    /* Ok, committed to a load. */
#ifndef ELF_VERBOSE
    printf("syms=[");
#endif
    ssym = lastaddr;
    for (i = symtabindex; i >= 0; i = symstrindex) {
#ifdef ELF_VERBOSE
	char	*secname;

	switch(shdr[i].sh_type) {
	    case SHT_SYMTAB:		/* Symbol table */
		secname = "symtab";
		break;
	    case SHT_STRTAB:		/* String table */
		secname = "strtab";
		break;
	    default:
		secname = "WHOA!!";
		break;
	}
#endif

	size = shdr[i].sh_size;
	archsw.arch_copyin(&size, lastaddr, sizeof(size));
	lastaddr += sizeof(size);

#ifdef ELF_VERBOSE
	printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname,
	    (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset,
	    (uintmax_t)lastaddr, (uintmax_t)(lastaddr + shdr[i].sh_size));
#else
	if (i == symstrindex)
	    printf("+");
	printf("0x%lx+0x%lx", (long)sizeof(size), (long)size);
#endif

	if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) {
	    printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!");
	    lastaddr = ssym;
	    ssym = 0;
	    goto nosyms;
	}
	result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size);
	if (result < 0 || (size_t)result != shdr[i].sh_size) {
	    printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! (%ju != %ju)", (uintmax_t)result,
		(uintmax_t)shdr[i].sh_size);
	    lastaddr = ssym;
	    ssym = 0;
	    goto nosyms;
	}
	/* Reset offsets relative to ssym */
	lastaddr += shdr[i].sh_size;
	lastaddr = roundup(lastaddr, sizeof(size));
	if (i == symtabindex)
	    symtabindex = -1;
	else if (i == symstrindex)
	    symstrindex = -1;
    }
    esym = lastaddr;
#ifndef ELF_VERBOSE
    printf("]");
#endif

    file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
    file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym);

nosyms:
    printf("\n");

    ret = lastaddr - firstaddr;
    if (ehdr->e_ident[EI_OSABI] != ELFOSABI_SOLARIS)
	fp->f_addr = firstaddr;

    php = NULL;
    for (i = 0; i < ehdr->e_phnum; i++) {
	if (phdr[i].p_type == PT_DYNAMIC) {
	    php = phdr + i;
	    adp = php->p_vaddr;
	    file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp);
	    break;
	}
    }

    if (php == NULL)	/* this is bad, we cannot get to symbols or _DYNAMIC */
	goto out;

    ndp = php->p_filesz / sizeof(Elf_Dyn);
    if (ndp == 0)
	goto out;
    dp = malloc(php->p_filesz);
    if (dp == NULL)
	goto out;
    if (ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
	archsw.arch_copyout(php->p_paddr + off, dp, php->p_filesz);
    else
	archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz);

    ef->strsz = 0;
    for (i = 0; i < ndp; i++) {
	if (dp[i].d_tag == 0)
	    break;
	switch (dp[i].d_tag) {
	case DT_HASH:
	    ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_STRTAB:
	    ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_STRSZ:
	    ef->strsz = dp[i].d_un.d_val;
	    break;
	case DT_SYMTAB:
	    ef->symtab = (Elf_Sym*)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_REL:
	    ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_RELSZ:
	    ef->relsz = dp[i].d_un.d_val;
	    break;
	case DT_RELA:
	    ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_RELASZ:
	    ef->relasz = dp[i].d_un.d_val;
	    break;
	default:
	    break;
	}
    }
    if (ef->hashtab == NULL || ef->symtab == NULL ||
	ef->strtab == NULL || ef->strsz == 0)
	goto out;
    COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets));
    COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains));
    ef->buckets = ef->hashtab + 2;
    ef->chains = ef->buckets + ef->nbuckets;

    if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0)
	return 0;
    p_start = sym.st_value + ef->off;
    if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0)
	return ENOENT;
    p_end = sym.st_value + ef->off;

    if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0)
	goto out;

    if (ef->kernel)			/* kernel must not depend on anything */
	goto out;

out:
    if (dp)
	free(dp);
    if (shdr)
	free(shdr);
    return ret;
}
예제 #3
0
파일: load_elf.c 프로젝트: alek-p/openzfs
int
__elfN(load_modmetadata)(struct preloaded_file *fp, u_int64_t dest)
{
	struct elf_file		 ef;
	int			 err, i, j;
	Elf_Shdr		*sh_meta, *shdr = NULL;
	Elf_Shdr		*sh_data[2];
	char			*shstrtab = NULL;
	size_t			 size;
	Elf_Addr		 p_start, p_end;

	bzero(&ef, sizeof(struct elf_file));
	ef.fd = -1;

	err = __elfN(load_elf_header)(fp->f_name, &ef);
	if (err != 0)
		goto out;

	if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) {
		ef.kernel = 1;
	} else if (ef.ehdr->e_type != ET_DYN) {
		err = EFTYPE;
		goto out;
	}

	size = ef.ehdr->e_shnum * ef.ehdr->e_shentsize;
	shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size);
	if (shdr == NULL) {
		err = ENOMEM;
		goto out;
	}

	/* Load shstrtab. */
	shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset,
	    shdr[ef.ehdr->e_shstrndx].sh_size);
	if (shstrtab == NULL) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "load_modmetadata: unable to load shstrtab\n");
		err = EFTYPE;
		goto out;
	}

	/* Find set_modmetadata_set and data sections. */
	sh_data[0] = sh_data[1] = sh_meta = NULL;
	for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) {
		if (strcmp(&shstrtab[shdr[i].sh_name],
		    "set_modmetadata_set") == 0) {
			sh_meta = &shdr[i];
		}
		if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) ||
		    (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) {
			sh_data[j++] = &shdr[i];
		}
	}
	if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
    "load_modmetadata: unable to find set_modmetadata_set or data sections\n");
		err = EFTYPE;
		goto out;
	}

	/* Load set_modmetadata_set into memory */
	err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset);
	if (err != 0) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
    "load_modmetadata: unable to load set_modmetadata_set: %d\n", err);
		goto out;
	}
	p_start = dest;
	p_end = dest + sh_meta->sh_size;
	dest += sh_meta->sh_size;

	/* Load data sections into memory. */
	err = kern_pread(ef.fd, dest, sh_data[0]->sh_size,
	    sh_data[0]->sh_offset);
	if (err != 0) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "load_modmetadata: unable to load data: %d\n", err);
		goto out;
	}

	/*
	 * We have to increment the dest, so that the offset is the same into
	 * both the .rodata and .data sections.
	 */
	ef.off = -(sh_data[0]->sh_addr - dest);
	dest +=	(sh_data[1]->sh_addr - sh_data[0]->sh_addr);

	err = kern_pread(ef.fd, dest, sh_data[1]->sh_size,
	    sh_data[1]->sh_offset);
	if (err != 0) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "load_modmetadata: unable to load data: %d\n", err);
		goto out;
	}

	err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end);
	if (err != 0) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "load_modmetadata: unable to parse metadata: %d\n", err);
		goto out;
	}

out:
	if (shstrtab != NULL)
		free(shstrtab);
	if (shdr != NULL)
		free(shdr);
	if (ef.firstpage != NULL)
		free(ef.firstpage);
	if (ef.fd != -1)
		close(ef.fd);
	return (err);
}
예제 #4
0
/*
 * With the file (fd) open on the image, and (ehdr) containing
 * the Elf header, load the image at (off)
 */
static int
__elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, u_int64_t off)
{
    int 	i;
    u_int	j;
    Elf_Ehdr	*ehdr;
    Elf_Phdr	*phdr, *php;
    Elf_Shdr	*shdr;
    int		ret;
    vm_offset_t firstaddr;
    vm_offset_t lastaddr;
    size_t	chunk;
    ssize_t	result;
    Elf_Addr	ssym, esym;
    Elf_Dyn	*dp;
    Elf_Addr	adp;
    int		ndp;
    int		symstrindex;
    int		symtabindex;
    Elf_Size	size;
    u_int	fpcopy;

    dp = NULL;
    shdr = NULL;
    ret = 0;
    firstaddr = lastaddr = 0;
    ehdr = ef->ehdr;
    if (ef->kernel) {
#if defined(__i386__) || defined(__amd64__)
#if __ELF_WORD_SIZE == 64
	off = - (off & 0xffffffffff000000ull);/* x86_64 relocates after locore */
#else
	off = - (off & 0xff000000u);	/* i386 relocates after locore */
#endif
#elif defined(__powerpc__)
	/*
	 * On the purely virtual memory machines like e500, the kernel is
	 * linked against its final VA range, which is most often not
	 * available at the loader stage, but only after kernel initializes
	 * and completes its VM settings. In such cases we cannot use p_vaddr
	 * field directly to load ELF segments, but put them at some
	 * 'load-time' locations.
	 */
	if (off & 0xf0000000u) {
	    off = -(off & 0xf0000000u);
	    /*
	     * XXX the physical load address should not be hardcoded. Note
	     * that the Book-E kernel assumes that it's loaded at a 16MB
	     * boundary for now...
	     */
	    off += 0x01000000;
	    ehdr->e_entry += off;
#ifdef ELF_VERBOSE
	    printf("Converted entry 0x%08x\n", ehdr->e_entry);
#endif
	} else
	    off = 0;
#elif defined(__arm__)
	/*
	 * The elf headers in some kernels specify virtual addresses in all
	 * header fields.  More recently, the e_entry and p_paddr fields are the
	 * proper physical addresses.  Even when the p_paddr fields are correct,
	 * the MI code below uses the p_vaddr fields with an offset added for
	 * loading (doing so is arguably wrong).  To make loading work, we need
	 * an offset that represents the difference between physical and virtual
	 * addressing.  ARM kernels are always linked at 0xCnnnnnnn.  Depending
	 * on the headers, the offset value passed in may be physical or virtual
	 * (because it typically comes from e_entry), but we always replace
	 * whatever is passed in with the va<->pa offset.  On the other hand, we
	 * always remove the high-order part of the entry address whether it's
	 * physical or virtual, because it will be adjusted later for the actual
	 * physical entry point based on where the image gets loaded.
	 */
	off = -0xc0000000;
	ehdr->e_entry &= ~0xf0000000;
#ifdef ELF_VERBOSE
	printf("ehdr->e_entry 0x%08x, va<->pa off %llx\n", ehdr->e_entry, off);
#endif
#else
	off = 0;		/* other archs use direct mapped kernels */
#endif
	__elfN(relocation_offset) = off;
    }
    ef->off = off;

    if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) {
	printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n");
	goto out;
    }
    phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff);

    for (i = 0; i < ehdr->e_phnum; i++) {
	/* We want to load PT_LOAD segments only.. */
	if (phdr[i].p_type != PT_LOAD)
	    continue;

#ifdef ELF_VERBOSE
	printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx",
	    (long)phdr[i].p_filesz, (long)phdr[i].p_offset,
	    (long)(phdr[i].p_vaddr + off),
	    (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
#else
	if ((phdr[i].p_flags & PF_W) == 0) {
	    printf("text=0x%lx ", (long)phdr[i].p_filesz);
	} else {
	    printf("data=0x%lx", (long)phdr[i].p_filesz);
	    if (phdr[i].p_filesz < phdr[i].p_memsz)
		printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz));
	    printf(" ");
	}
#endif
	fpcopy = 0;
	if (ef->firstlen > phdr[i].p_offset) {
	    fpcopy = ef->firstlen - phdr[i].p_offset;
	    archsw.arch_copyin(ef->firstpage + phdr[i].p_offset,
			       phdr[i].p_vaddr + off, fpcopy);
	}
	if (phdr[i].p_filesz > fpcopy) {
	    if (kern_pread(ef->fd, phdr[i].p_vaddr + off + fpcopy,
		phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) {
		printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
		    "_loadimage: read failed\n");
		goto out;
	    }
	}
	/* clear space from oversized segments; eg: bss */
	if (phdr[i].p_filesz < phdr[i].p_memsz) {
#ifdef ELF_VERBOSE
	    printf(" (bss: 0x%lx-0x%lx)",
		(long)(phdr[i].p_vaddr + off + phdr[i].p_filesz),
		(long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1));
#endif

	    kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz,
		phdr[i].p_memsz - phdr[i].p_filesz);
	}
#ifdef ELF_VERBOSE
	printf("\n");
#endif

	if (archsw.arch_loadseg != NULL)
	    archsw.arch_loadseg(ehdr, phdr + i, off);

	if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off))
	    firstaddr = phdr[i].p_vaddr + off;
	if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz))
	    lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz;
    }
    lastaddr = roundup(lastaddr, sizeof(long));

    /*
     * Now grab the symbol tables.  This isn't easy if we're reading a
     * .gz file.  I think the rule is going to have to be that you must
     * strip a file to remove symbols before gzipping it so that we do not
     * try to lseek() on it.
     */
    chunk = ehdr->e_shnum * ehdr->e_shentsize;
    if (chunk == 0 || ehdr->e_shoff == 0)
	goto nosyms;
    shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk);
    if (shdr == NULL) {
	printf("\nelf" __XSTRING(__ELF_WORD_SIZE)
	    "_loadimage: failed to read section headers");
	goto nosyms;
    }
    file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr);

    symtabindex = -1;
    symstrindex = -1;
    for (i = 0; i < ehdr->e_shnum; i++) {
	if (shdr[i].sh_type != SHT_SYMTAB)
	    continue;
	for (j = 0; j < ehdr->e_phnum; j++) {
	    if (phdr[j].p_type != PT_LOAD)
		continue;
	    if (shdr[i].sh_offset >= phdr[j].p_offset &&
		(shdr[i].sh_offset + shdr[i].sh_size <=
		 phdr[j].p_offset + phdr[j].p_filesz)) {
		shdr[i].sh_offset = 0;
		shdr[i].sh_size = 0;
		break;
	    }
	}
	if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0)
	    continue;		/* alread loaded in a PT_LOAD above */
	/* Save it for loading below */
	symtabindex = i;
	symstrindex = shdr[i].sh_link;
    }
    if (symtabindex < 0 || symstrindex < 0)
	goto nosyms;

    /* Ok, committed to a load. */
#ifndef ELF_VERBOSE
    printf("syms=[");
#endif
    ssym = lastaddr;
    for (i = symtabindex; i >= 0; i = symstrindex) {
#ifdef ELF_VERBOSE
	char	*secname;

	switch(shdr[i].sh_type) {
	    case SHT_SYMTAB:		/* Symbol table */
		secname = "symtab";
		break;
	    case SHT_STRTAB:		/* String table */
		secname = "strtab";
		break;
	    default:
		secname = "WHOA!!";
		break;
	}
#endif

	size = shdr[i].sh_size;
	archsw.arch_copyin(&size, lastaddr, sizeof(size));
	lastaddr += sizeof(size);

#ifdef ELF_VERBOSE
	printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname,
	    (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset,
	    (uintmax_t)lastaddr, (uintmax_t)(lastaddr + shdr[i].sh_size));
#else
	if (i == symstrindex)
	    printf("+");
	printf("0x%lx+0x%lx", (long)sizeof(size), (long)size);
#endif

	if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) {
	    printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!");
	    lastaddr = ssym;
	    ssym = 0;
	    goto nosyms;
	}
	result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size);
	if (result < 0 || (size_t)result != shdr[i].sh_size) {
	    printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! (%ju != %ju)", (uintmax_t)result,
		(uintmax_t)shdr[i].sh_size);
	    lastaddr = ssym;
	    ssym = 0;
	    goto nosyms;
	}
	/* Reset offsets relative to ssym */
	lastaddr += shdr[i].sh_size;
	lastaddr = roundup(lastaddr, sizeof(size));
	if (i == symtabindex)
	    symtabindex = -1;
	else if (i == symstrindex)
	    symstrindex = -1;
    }
    esym = lastaddr;
#ifndef ELF_VERBOSE
    printf("]");
#endif

    file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym);
    file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym);

nosyms:
    printf("\n");

    ret = lastaddr - firstaddr;
    fp->f_addr = firstaddr;

    php = NULL;
    for (i = 0; i < ehdr->e_phnum; i++) {
	if (phdr[i].p_type == PT_DYNAMIC) {
	    php = phdr + i;
	    adp = php->p_vaddr;
	    file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp);
	    break;
	}
    }

    if (php == NULL)	/* this is bad, we cannot get to symbols or _DYNAMIC */
	goto out;

    ndp = php->p_filesz / sizeof(Elf_Dyn);
    if (ndp == 0)
	goto out;
    dp = malloc(php->p_filesz);
    if (dp == NULL)
	goto out;
    archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz);

    ef->strsz = 0;
    for (i = 0; i < ndp; i++) {
	if (dp[i].d_tag == 0)
	    break;
	switch (dp[i].d_tag) {
	case DT_HASH:
	    ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_STRTAB:
	    ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_STRSZ:
	    ef->strsz = dp[i].d_un.d_val;
	    break;
	case DT_SYMTAB:
	    ef->symtab = (Elf_Sym*)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_REL:
	    ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_RELSZ:
	    ef->relsz = dp[i].d_un.d_val;
	    break;
	case DT_RELA:
	    ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off);
	    break;
	case DT_RELASZ:
	    ef->relasz = dp[i].d_un.d_val;
	    break;
	default:
	    break;
	}
    }
    if (ef->hashtab == NULL || ef->symtab == NULL ||
	ef->strtab == NULL || ef->strsz == 0)
	goto out;
    COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets));
    COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains));
    ef->buckets = ef->hashtab + 2;
    ef->chains = ef->buckets + ef->nbuckets;
    if (__elfN(parse_modmetadata)(fp, ef) == 0)
	goto out;

    if (ef->kernel)			/* kernel must not depend on anything */
	goto out;

out:
    if (dp)
	free(dp);
    if (shdr)
	free(shdr);
    return ret;
}