bsd-user: Refactor load_elf_sections and is_target_elf_binary

Factor out load_elf_sections and is_target_elf_binary out of
load_elf_interp.

Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com>
Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Reviewed-by: Kyle Evans <kevans@FreeBSD.org>
This commit is contained in:
Warner Losh 2021-08-06 17:12:24 -06:00 committed by Warner Losh
parent 0456a1772b
commit c09f12feba
1 changed files with 156 additions and 184 deletions

View File

@ -36,6 +36,8 @@ abi_ulong target_stksiz;
abi_ulong target_stkbas;
static int elf_core_dump(int signr, CPUArchState *env);
static int load_elf_sections(const struct elfhdr *hdr, struct elf_phdr *phdr,
int fd, abi_ulong rbase, abi_ulong *baddrp);
static inline void memcpy_fromfs(void *to, const void *from, unsigned long n)
{
@ -271,16 +273,10 @@ static abi_ulong load_elf_interp(struct elfhdr *interp_elf_ex,
abi_ulong *interp_load_addr)
{
struct elf_phdr *elf_phdata = NULL;
struct elf_phdr *eppnt;
abi_ulong load_addr = 0;
int load_addr_set = 0;
abi_ulong rbase;
int retval;
abi_ulong last_bss, elf_bss;
abi_ulong error;
int i;
abi_ulong baddr, error;
elf_bss = 0;
last_bss = 0;
error = 0;
bswap_ehdr(interp_elf_ex);
@ -325,93 +321,33 @@ static abi_ulong load_elf_interp(struct elfhdr *interp_elf_ex,
}
bswap_phdr(elf_phdata, interp_elf_ex->e_phnum);
rbase = 0;
if (interp_elf_ex->e_type == ET_DYN) {
/*
* In order to avoid hardcoding the interpreter load
* address in qemu, we allocate a big enough memory zone.
*/
error = target_mmap(0, INTERP_MAP_SIZE, PROT_NONE,
rbase = target_mmap(0, INTERP_MAP_SIZE, PROT_NONE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (error == -1) {
if (rbase == -1) {
perror("mmap");
exit(-1);
}
load_addr = error;
load_addr_set = 1;
}
eppnt = elf_phdata;
for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++)
if (eppnt->p_type == PT_LOAD) {
int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
int elf_prot = 0;
abi_ulong vaddr = 0;
abi_ulong k;
if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) {
elf_type |= MAP_FIXED;
vaddr = eppnt->p_vaddr;
}
error = target_mmap(load_addr + TARGET_ELF_PAGESTART(vaddr),
eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr),
elf_prot,
elf_type,
interpreter_fd,
eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr));
if (error == -1) {
/* Real error */
close(interpreter_fd);
free(elf_phdata);
return ~((abi_ulong)0UL);
}
if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
load_addr = error;
load_addr_set = 1;
}
/*
* Find the end of the file mapping for this phdr, and keep
* track of the largest address we see for this.
*/
k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
if (k > elf_bss) elf_bss = k;
/*
* Do the same thing for the memory mapping - between
* elf_bss and last_bss is the bss section.
*/
k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
if (k > last_bss) last_bss = k;
}
error = load_elf_sections(interp_elf_ex, elf_phdata, interpreter_fd, rbase,
&baddr);
if (error != 0) {
perror("load_elf_sections");
exit(-1);
}
/* Now use mmap to map the library into memory. */
close(interpreter_fd);
/*
* Now fill out the bss section. First pad the last page up
* to the page boundary, and then perform a mmap to make sure
* that there are zeromapped pages up to and including the last
* bss page.
*/
padzero(elf_bss, last_bss);
elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */
/* Map the last of the bss segment */
if (last_bss > elf_bss) {
target_mmap(elf_bss, last_bss - elf_bss,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
}
free(elf_phdata);
*interp_load_addr = load_addr;
return ((abi_ulong) interp_elf_ex->e_entry) + load_addr;
*interp_load_addr = baddr;
return ((abi_ulong) interp_elf_ex->e_entry) + rbase;
}
static int symfind(const void *s0, const void *s1)
@ -521,6 +457,10 @@ found:
}
continue;
}
#if defined(TARGET_ARM) || defined(TARGET_MIPS)
/* The bottom address bit marks a Thumb or MIPS16 symbol. */
syms[i].st_value &= ~(target_ulong)1;
#endif
i++;
}
@ -560,26 +500,119 @@ found:
syminfos = s;
}
/* Check the elf header and see if this a target elf binary. */
int is_target_elf_binary(int fd)
{
uint8_t buf[128];
struct elfhdr elf_ex;
if (lseek(fd, 0L, SEEK_SET) < 0) {
return 0;
}
if (read(fd, buf, sizeof(buf)) < 0) {
return 0;
}
elf_ex = *((struct elfhdr *)buf);
bswap_ehdr(&elf_ex);
if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
(!elf_check_arch(elf_ex.e_machine))) {
return 0;
} else {
return 1;
}
}
static int
load_elf_sections(const struct elfhdr *hdr, struct elf_phdr *phdr, int fd,
abi_ulong rbase, abi_ulong *baddrp)
{
struct elf_phdr *elf_ppnt;
abi_ulong baddr;
int i;
bool first;
/*
* Now we do a little grungy work by mmaping the ELF image into
* the correct location in memory. At this point, we assume that
* the image should be loaded at fixed address, not at a variable
* address.
*/
first = true;
for (i = 0, elf_ppnt = phdr; i < hdr->e_phnum; i++, elf_ppnt++) {
int elf_prot = 0;
abi_ulong error;
/* XXX Skip memsz == 0. */
if (elf_ppnt->p_type != PT_LOAD) {
continue;
}
if (elf_ppnt->p_flags & PF_R) {
elf_prot |= PROT_READ;
}
if (elf_ppnt->p_flags & PF_W) {
elf_prot |= PROT_WRITE;
}
if (elf_ppnt->p_flags & PF_X) {
elf_prot |= PROT_EXEC;
}
error = target_mmap(TARGET_ELF_PAGESTART(rbase + elf_ppnt->p_vaddr),
(elf_ppnt->p_filesz +
TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
elf_prot,
(MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
fd,
(elf_ppnt->p_offset -
TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
if (error == -1) {
perror("mmap");
exit(-1);
} else if (elf_ppnt->p_memsz != elf_ppnt->p_filesz) {
abi_ulong start_bss, end_bss;
start_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
end_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
/*
* Calling set_brk effectively mmaps the pages that we need for the
* bss and break sections.
*/
set_brk(start_bss, end_bss);
padzero(start_bss, end_bss);
}
if (first) {
baddr = TARGET_ELF_PAGESTART(rbase + elf_ppnt->p_vaddr);
first = false;
}
}
if (baddrp != NULL) {
*baddrp = baddr;
}
return 0;
}
int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
struct image_info *info)
{
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
int interpreter_fd = -1; /* avoid warning */
abi_ulong load_addr, load_bias;
int load_addr_set = 0;
abi_ulong load_addr;
int i;
struct elf_phdr * elf_ppnt;
struct elf_phdr *elf_ppnt;
struct elf_phdr *elf_phdata;
abi_ulong elf_bss, k, elf_brk;
int retval;
char * elf_interpreter;
abi_ulong elf_entry, interp_load_addr = 0;
abi_ulong start_code, end_code, start_data, end_data;
abi_ulong elf_brk;
int error, retval;
char *elf_interpreter;
abi_ulong baddr, elf_entry, et_dyn_addr, interp_load_addr = 0;
abi_ulong reloc_func_desc = 0;
load_addr = 0;
load_bias = 0;
elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
bswap_ehdr(&elf_ex);
@ -618,16 +651,10 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
bswap_phdr(elf_phdata, elf_ex.e_phnum);
elf_ppnt = elf_phdata;
elf_bss = 0;
elf_brk = 0;
elf_interpreter = NULL;
start_code = ~((abi_ulong)0UL);
end_code = 0;
start_data = 0;
end_data = 0;
for (i = 0; i < elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
if (elf_interpreter != NULL) {
@ -714,94 +741,45 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
info->mmap = 0;
elf_entry = (abi_ulong) elf_ex.e_entry;
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
/* XXX Join this with PT_INTERP search? */
baddr = 0;
for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
if (elf_ppnt->p_type != PT_LOAD) {
continue;
}
baddr = elf_ppnt->p_vaddr;
break;
}
et_dyn_addr = 0;
if (elf_ex.e_type == ET_DYN && baddr == 0) {
et_dyn_addr = ELF_ET_DYN_LOAD_ADDR;
}
/*
* Do this so that we can load the interpreter, if need be. We will
* change some of these later
*/
info->rss = 0;
setup_arg_pages(bprm, info, &bprm->p, &bprm->stringp);
info->start_stack = bprm->p;
/* Now we do a little grungy work by mmaping the ELF image into
* the correct location in memory. At this point, we assume that
* the image should be loaded at fixed address, not at a variable
* address.
*/
info->elf_flags = elf_ex.e_flags;
error = load_elf_sections(&elf_ex, elf_phdata, bprm->fd, et_dyn_addr,
&load_addr);
for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
int elf_prot = 0;
int elf_flags = 0;
abi_ulong error;
if (elf_ppnt->p_type != PT_LOAD)
if (elf_ppnt->p_type != PT_LOAD) {
continue;
if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
elf_flags = MAP_PRIVATE | MAP_DENYWRITE;
if (elf_ex.e_type == ET_EXEC || load_addr_set) {
elf_flags |= MAP_FIXED;
} else if (elf_ex.e_type == ET_DYN) {
/* Try and get dynamic programs out of the way of the default mmap
base, as well as whatever program they might try to exec. This
is because the brk will follow the loader, and is not movable. */
/* NOTE: for qemu, we do a big mmap to get enough space
without hardcoding any address */
error = target_mmap(0, ET_DYN_MAP_SIZE,
PROT_NONE, MAP_PRIVATE | MAP_ANON,
-1, 0);
if (error == -1) {
perror("mmap");
exit(-1);
}
load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr);
}
error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr),
(elf_ppnt->p_filesz +
TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
elf_prot,
(MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
bprm->fd,
(elf_ppnt->p_offset -
TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
if (error == -1) {
perror("mmap");
exit(-1);
}
if (!load_addr_set) {
load_addr_set = 1;
load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
if (elf_ex.e_type == ET_DYN) {
load_bias += error -
TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr);
load_addr += load_bias;
reloc_func_desc = load_bias;
}
}
k = elf_ppnt->p_vaddr;
if (k < start_code)
start_code = k;
if (start_data < k)
start_data = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
if (k > elf_bss)
elf_bss = k;
if ((elf_ppnt->p_flags & PF_X) && end_code < k)
end_code = k;
if (end_data < k)
end_data = k;
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
if (k > elf_brk) elf_brk = k;
if (elf_ppnt->p_memsz > elf_ppnt->p_filesz)
elf_brk = MAX(elf_brk, et_dyn_addr + elf_ppnt->p_vaddr +
elf_ppnt->p_memsz);
}
if (error != 0) {
perror("load_elf_sections");
exit(-1);
}
elf_entry += load_bias;
elf_bss += load_bias;
elf_brk += load_bias;
start_code += load_bias;
end_code += load_bias;
start_data += load_bias;
end_data += load_bias;
if (elf_interpreter) {
elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd,
@ -817,6 +795,9 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
exit(-1);
return 0;
}
} else {
interp_load_addr = et_dyn_addr;
elf_entry += interp_load_addr;
}
free(elf_phdata);
@ -829,20 +810,11 @@ int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs,
bprm->p = target_create_elf_tables(bprm->p, bprm->argc, bprm->envc,
bprm->stringp, &elf_ex, load_addr,
load_bias, interp_load_addr, info);
et_dyn_addr, interp_load_addr, info);
info->load_addr = reloc_func_desc;
info->start_brk = info->brk = elf_brk;
info->end_code = end_code;
info->start_code = start_code;
info->start_data = start_data;
info->end_data = end_data;
info->start_stack = bprm->p;
/* Calling set_brk effectively mmaps the pages that we need for the bss and break
sections */
set_brk(elf_bss, elf_brk);
padzero(elf_bss, elf_brk);
info->load_bias = 0;
info->entry = elf_entry;