diff mbox

[11/14] linux-user: Extract load_elf_image from load_elf_interp.

Message ID 2b07469b1fc4cecf13e34c6493605c9eb00555e5.1272483393.git.rth@twiddle.net
State New
Headers show

Commit Message

Richard Henderson April 5, 2010, 12:46 a.m. UTC
Moving toward a single copy of the elf binary loading code.
Fill in the details of the loaded image into a struct image_info.

Adjust create_elf_tables to read from such structures instead
of from a collection of passed arguments.  Don't return error
values from load_elf_interp; always exit(-1) with a message to
stderr.  Collect elf_interpreter handling in load_elf_binary
to a common spot.

Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 linux-user/elfload.c |  341 ++++++++++++++++++++++++-------------------------
 1 files changed, 167 insertions(+), 174 deletions(-)
diff mbox

Patch

diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 2f15b9c..c58387a 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1078,11 +1078,9 @@  static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot)
 }
 
 static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
-                                   struct elfhdr * exec,
-                                   abi_ulong load_addr,
-                                   abi_ulong load_bias,
-                                   abi_ulong interp_load_addr,
-                                   struct image_info *info)
+                                   struct elfhdr *exec,
+                                   struct image_info *info,
+                                   struct image_info *interp_info)
 {
     abi_ulong sp;
     int size;
@@ -1127,13 +1125,13 @@  static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
     NEW_AUX_ENT (AT_NULL, 0);
 
     /* There must be exactly DLINFO_ITEMS entries here.  */
-    NEW_AUX_ENT(AT_PHDR, (abi_ulong)(load_addr + exec->e_phoff));
+    NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff));
     NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
     NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
     NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE));
-    NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_load_addr));
+    NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info->load_addr));
     NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0);
-    NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry);
+    NEW_AUX_ENT(AT_ENTRY, info->entry);
     NEW_AUX_ENT(AT_UID, (abi_ulong) getuid());
     NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
     NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
@@ -1157,51 +1155,60 @@  static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
     return sp;
 }
 
+/* Load an ELF image into the address space.
 
-static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
-                                 int interpreter_fd,
-                                 abi_ulong *interp_load_addr,
-                                 char bprm_buf[BPRM_BUF_SIZE])
+   IMAGE_NAME is the filename of the image, to use in error messages.
+   IMAGE_FD is the open file descriptor for the image.
+
+   BUFPAGE is a copy of the first page of the file; this of course
+   contains the elf file header at offset 0.  It is assumed that this
+   buffer is sufficiently aligned to present no problems to the host
+   in accessing data at aligned offsets within the buffer.
+
+   On return: INFO values will be filled in, as necessary or available.  */
+
+static void load_elf_image(const char *image_name, int image_fd,
+                           struct image_info *info,
+                           char bprm_buf[BPRM_BUF_SIZE])
 {
-    struct elf_phdr *elf_phdata  =  NULL;
-    abi_ulong load_addr, load_bias, loaddr, hiaddr;
-    int retval;
-    abi_ulong error;
-    int i;
+    struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
+    struct elf_phdr *phdr;
+    abi_ulong load_addr, load_bias, loaddr, hiaddr, error;
+    int i, retval;
+    const char *errmsg;
 
-    bswap_ehdr(interp_elf_ex);
-    if (!elf_check_ehdr(interp_elf_ex)) {
-        return ~((abi_ulong)0UL);
+    /* First of all, some simple consistency checks */
+    errmsg = "Invalid ELF image for this architecture";
+    if (!elf_check_ident(ehdr)) {
+        goto exit_errmsg;
+    }
+    bswap_ehdr(ehdr);
+    if (!elf_check_ehdr(ehdr)) {
+        goto exit_errmsg;
     }
 
-    /* Now read in all of the header information */
-    elf_phdata =  (struct elf_phdr *)
-        malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
-    if (!elf_phdata)
-        return ~((abi_ulong)0UL);
-
-    i = interp_elf_ex->e_phnum * sizeof(struct elf_phdr);
-    if (interp_elf_ex->e_phoff + i <= BPRM_BUF_SIZE) {
-        memcpy(elf_phdata, bprm_buf + interp_elf_ex->e_phoff, i);
+    i = ehdr->e_phnum * sizeof(struct elf_phdr);
+    if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) {
+        phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff);
     } else {
-        retval = pread(interpreter_fd, elf_phdata, i, interp_elf_ex->e_phoff);
+        phdr = (struct elf_phdr *) alloca(i);
+        retval = pread(image_fd, phdr, i, ehdr->e_phoff);
         if (retval != i) {
-            perror("load_elf_interp");
-            exit(-1);
+            goto exit_read;
         }
     }
-    bswap_phdr(elf_phdata, interp_elf_ex->e_phnum);
+    bswap_phdr(phdr, ehdr->e_phnum);
 
     /* Find the maximum size of the image and allocate an appropriate
        amount of memory to handle that.  */
     loaddr = -1, hiaddr = 0;
-    for (i = 0; i < interp_elf_ex->e_phnum; ++i) {
-        if (elf_phdata[i].p_type == PT_LOAD) {
-            abi_ulong a = elf_phdata[i].p_vaddr;
+    for (i = 0; i < ehdr->e_phnum; ++i) {
+        if (phdr[i].p_type == PT_LOAD) {
+            abi_ulong a = phdr[i].p_vaddr;
             if (a < loaddr) {
                 loaddr = a;
             }
-            a += elf_phdata[i].p_memsz;
+            a += phdr[i].p_memsz;
             if (a > hiaddr) {
                 hiaddr = a;
             }
@@ -1209,7 +1216,7 @@  static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
     }
 
     load_addr = loaddr;
-    if (interp_elf_ex->e_type == ET_DYN) {
+    if (ehdr->e_type == ET_DYN) {
         /* The image indicates that it can be loaded anywhere.  Find a
            location that can hold the memory space required.  If the
            image is pre-linked, LOADDR will be non-zero.  Since we do
@@ -1219,14 +1226,22 @@  static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
                                 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
                                 -1, 0);
         if (load_addr == -1) {
-            perror("mmap");
-            exit(-1);
+            goto exit_perror;
         }
     }
     load_bias = load_addr - loaddr;
 
-    for (i = 0; i < interp_elf_ex->e_phnum; i++) {
-        struct elf_phdr *eppnt = elf_phdata + i;
+    info->load_bias = load_bias;
+    info->load_addr = load_addr;
+    info->entry = ehdr->e_entry + load_bias;
+    info->start_code = -1;
+    info->end_code = 0;
+    info->start_data = -1;
+    info->end_data = 0;
+    info->brk = 0;
+
+    for (i = 0; i < ehdr->e_phnum; i++) {
+        struct elf_phdr *eppnt = phdr + i;
         if (eppnt->p_type == PT_LOAD) {
 	    abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em;
 	    int elf_prot = 0;
@@ -1241,12 +1256,9 @@  static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
 
 	    error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po,
                                 elf_prot, MAP_PRIVATE | MAP_FIXED,
-                                interpreter_fd, eppnt->p_offset - vaddr_po);
+                                image_fd, eppnt->p_offset - vaddr_po);
 	    if (error == -1) {
-                /* Real error */
-                close(interpreter_fd);
-                free(elf_phdata);
-                return ~((abi_ulong)0UL);
+                goto exit_perror;
 	    }
 
             vaddr_ef = vaddr + eppnt->p_filesz;
@@ -1256,18 +1268,79 @@  static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
             if (vaddr_ef < vaddr_em) {
                 zero_bss(vaddr_ef, vaddr_em, elf_prot);
             }
+
+            /* Find the full program boundaries.  */
+            if (elf_prot & PROT_EXEC) {
+                if (vaddr < info->start_code) {
+                    info->start_code = vaddr;
+                }
+                if (vaddr_ef > info->end_code) {
+                    info->end_code = vaddr_ef;
+                }
+            }
+            if (elf_prot & PROT_WRITE) {
+                if (vaddr < info->start_data) {
+                    info->start_data = vaddr;
+                }
+                if (vaddr_ef > info->end_data) {
+                    info->end_data = vaddr_ef;
+                }
+                if (vaddr_em > info->brk) {
+                    info->brk = vaddr_em;
+                }
+            }
         }
     }
 
+    if (info->end_data == 0) {
+        info->start_data = info->end_code;
+        info->end_data = info->end_code;
+        info->brk = info->end_code;
+    }
+
     if (qemu_log_enabled()) {
-	load_symbols(interp_elf_ex, interpreter_fd, load_bias);
+	load_symbols(ehdr, image_fd, load_bias);
     }
 
-    close(interpreter_fd);
-    free(elf_phdata);
+    close(image_fd);
+    return;
+
+ exit_read:
+    if (retval >= 0) {
+        errmsg = "Incomplete read of file header";
+        goto exit_errmsg;
+    }
+ exit_perror:
+    errmsg = strerror(errno);
+ exit_errmsg:
+    fprintf(stderr, "%s: %s\n", image_name, errmsg);
+    exit(-1);
+}
+
+static void load_elf_interp(const char *filename, struct image_info *info,
+                            char bprm_buf[BPRM_BUF_SIZE])
+{
+    int fd, retval;
+
+    fd = open(path(filename), O_RDONLY);
+    if (fd < 0) {
+        goto exit_perror;
+    }
 
-    *interp_load_addr = load_addr;
-    return ((abi_ulong) interp_elf_ex->e_entry) + load_bias;
+    retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
+    if (retval < 0) {
+        goto exit_perror;
+    }
+    if (retval < BPRM_BUF_SIZE) {
+        memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
+    }
+
+    load_elf_image(filename, fd, info, bprm_buf);
+    return;
+
+ exit_perror:
+    fprintf(stderr, "%s: %s\n", filename, strerror(errno));
+    exit(-1);
 }
 
 static int symfind(const void *s0, const void *s1)
@@ -1404,26 +1477,21 @@  static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
 int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
                     struct image_info * info)
 {
+    struct image_info interp_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;
-    unsigned char ibcs2_interpreter;
     int i;
-    abi_ulong mapped_addr;
     struct elf_phdr * elf_ppnt;
     struct elf_phdr *elf_phdata;
     abi_ulong k, elf_brk;
     int retval;
-    char * elf_interpreter;
-    abi_ulong elf_entry, interp_load_addr = 0;
+    char *elf_interpreter = NULL;
+    abi_ulong elf_entry;
     int status;
     abi_ulong start_code, end_code, start_data, end_data;
-    abi_ulong reloc_func_desc = 0;
     abi_ulong elf_stack;
 
-    ibcs2_interpreter = 0;
     status = 0;
     load_addr = 0;
     load_bias = 0;
@@ -1466,7 +1534,6 @@  int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
 
     elf_brk = 0;
     elf_stack = ~((abi_ulong)0UL);
-    elf_interpreter = NULL;
     start_code = ~((abi_ulong)0UL);
     end_code = 0;
     start_data = 0;
@@ -1475,82 +1542,21 @@  int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
     elf_ppnt = elf_phdata;
     for(i=0;i < elf_ex.e_phnum; i++) {
 	if (elf_ppnt->p_type == PT_INTERP) {
-	    if ( elf_interpreter != NULL )
-	    {
-		free (elf_phdata);
-		free(elf_interpreter);
-		close(bprm->fd);
-		return -EINVAL;
-	    }
-
-	    /* This is the program interpreter used for
-	     * shared libraries - for now assume that this
-	     * is an a.out format binary
-	     */
-
-	    elf_interpreter = (char *)malloc(elf_ppnt->p_filesz);
-
-	    if (elf_interpreter == NULL) {
-		free (elf_phdata);
-		close(bprm->fd);
-		return -ENOMEM;
-	    }
-
             if (elf_ppnt->p_offset + elf_ppnt->p_filesz <= BPRM_BUF_SIZE) {
-                memcpy(elf_interpreter, bprm->buf + elf_ppnt->p_offset,
-                       elf_ppnt->p_filesz);
+                elf_interpreter = bprm->buf + elf_ppnt->p_offset;
             } else {
+                elf_interpreter = alloca(elf_ppnt->p_filesz);
 		retval = pread(bprm->fd, elf_interpreter, elf_ppnt->p_filesz,
                                elf_ppnt->p_offset);
                 if (retval != elf_ppnt->p_filesz) {
-                    perror("load_elf_binary2");
+                    perror("load_elf_binary");
                     exit(-1);
                 }
 	    }
-
-	    /* If the program interpreter is one of these two,
-	       then assume an iBCS2 image. Otherwise assume
-	       a native linux image. */
-
-	    /* JRP - Need to add X86 lib dir stuff here... */
-
-	    if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
-		strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) {
-                ibcs2_interpreter = 1;
-	    }
-
-            retval = open(path(elf_interpreter), O_RDONLY);
-            if (retval < 0) {
-                perror(elf_interpreter);
-                exit(-1);
-            }
-            interpreter_fd = retval;
-
-            retval = read(interpreter_fd, bprm->buf, BPRM_BUF_SIZE);
-            if (retval < 0) {
-		perror("load_elf_binary3");
-		exit(-1);
-	    }
-            if (retval < BPRM_BUF_SIZE) {
-                memset(bprm->buf, 0, BPRM_BUF_SIZE - retval);
-            }
-
-            interp_elf_ex = *((struct elfhdr *) bprm->buf);
 	}
 	elf_ppnt++;
     }
 
-    /* Some simple consistency checks for the interpreter */
-    if (elf_interpreter) {
-        if (!elf_check_ident(&interp_elf_ex)) {
-	    free(elf_interpreter);
-	    free(elf_phdata);
-	    close(bprm->fd);
-            close(interpreter_fd);
-	    return -ELIBBAD;
-	}
-    }
-
     /* OK, This is the point of no return */
     info->end_data = 0;
     info->end_code = 0;
@@ -1648,7 +1654,6 @@  int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
                 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;
@@ -1681,19 +1686,15 @@  int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
     start_data += load_bias;
     end_data += load_bias;
 
-    if (elf_interpreter) {
-        elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd,
-                                    &interp_load_addr, bprm->buf);
-        reloc_func_desc = interp_load_addr;
-	free(elf_interpreter);
-
-	if (elf_entry == ~((abi_ulong)0UL)) {
-	    printf("Unable to load interpreter\n");
-	    free(elf_phdata);
-	    exit(-1);
-	    return 0;
-	}
-    }
+    info->load_bias = load_bias;
+    info->load_addr = load_addr;
+    info->entry = elf_entry;
+    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->personality = PER_LINUX;
 
     free(elf_phdata);
 
@@ -1702,46 +1703,38 @@  int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
     }
 
     close(bprm->fd);
-    info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
 
-#ifdef LOW_ELF_STACK
-    info->start_stack = bprm->p = elf_stack - 4;
-#endif
-    bprm->p = create_elf_tables(bprm->p,
-                                bprm->argc,
-                                bprm->envc,
-                                &elf_ex,
-                                load_addr, load_bias,
-                                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;
+    if (elf_interpreter) {
+        load_elf_interp(elf_interpreter, &interp_info, bprm->buf);
 
-#if 0
-    printf("(start_brk) %x\n" , info->start_brk);
-    printf("(end_code) %x\n" , info->end_code);
-    printf("(start_code) %x\n" , info->start_code);
-    printf("(end_data) %x\n" , info->end_data);
-    printf("(start_stack) %x\n" , info->start_stack);
-    printf("(brk) %x\n" , info->brk);
-#endif
+        /* If the program interpreter is one of these two, then assume
+           an iBCS2 image.  Otherwise assume a native linux image.  */
 
-    if ( info->personality == PER_SVR4 )
-    {
-        /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
-           and some applications "depend" upon this behavior.
-           Since we do not have the power to recompile these, we
-           emulate the SVr4 behavior.  Sigh.  */
-        mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
-                                  MAP_FIXED | MAP_PRIVATE, -1, 0);
+        if (strcmp(elf_interpreter, "/usr/lib/libc.so.1") == 0
+            || strcmp(elf_interpreter, "/usr/lib/ld.so.1") == 0) {
+            info->personality = PER_SVR4;
+
+            /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
+               and some applications "depend" upon this behavior.  Since
+               we do not have the power to recompile these, we emulate
+               the SVr4 behavior.  Sigh.  */
+            target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
+                        MAP_FIXED | MAP_PRIVATE, -1, 0);
+        }
     }
 
-    info->entry = elf_entry;
+    bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
+                                info, (elf_interpreter ? &interp_info : NULL));
+    info->start_stack = bprm->p;
+
+    /* If we have an interpreter, set that as the program's entry point.
+       Copy the load_addr as well, to help PPC64 interpret the entry
+       point as a function descriptor.  Do this after creating elf tables
+       so that we copy the original program entry point into the AUXV.  */
+    if (elf_interpreter) {
+        info->load_addr = interp_info.load_addr;
+        info->entry = interp_info.entry;
+    }
 
 #ifdef USE_ELF_CORE_DUMP
     bprm->core_dump = &elf_core_dump;