Patchwork [1/6] sysemu: add section_callback argument to ELF loader

login
register
mail settings
Submitter Nathan Froyd
Date Aug. 3, 2009, 2:45 p.m.
Message ID <1249310711-8873-2-git-send-email-froydnj@codesourcery.com>
Download mbox | patch
Permalink /patch/30596/
State Superseded
Headers show

Comments

Nathan Froyd - Aug. 3, 2009, 2:45 p.m.
Some targets indicate properties of the program with special sections in
the ELF file--MIPS in particular.  This infrastructure is useful for
grovelling through those sections.

Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
---
 elf_ops.h |   34 +++++++++++++++++++++++++++++++---
 loader.c  |   19 ++++++++++++++-----
 sysemu.h  |    5 +++++
 3 files changed, 50 insertions(+), 8 deletions(-)

Patch

diff --git a/elf_ops.h b/elf_ops.h
index 699651c..0acbcc7 100644
--- a/elf_ops.h
+++ b/elf_ops.h
@@ -98,7 +98,13 @@  static int glue(symcmp, SZ)(const void *s0, const void *s1)
         : ((sym0->st_value > sym1->st_value) ? 1 : 0);
 }
 
-static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
+/* Load function symbols for later groveling.  If SECTION_CALLBACK is
+   non-NULL, it will be called with information about each section in
+   the binary.  This interface enables to the caller to mine the binary
+   for useful information about the ABI, such as whether the CPU chosen is
+   compatible with the binary.  */
+static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
+                                  section_callback_t section_callback)
 {
     struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
     struct elf_sym *syms = NULL;
@@ -117,6 +123,27 @@  static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
         }
     }
 
+    /* Permit machines to grovel through the ELF file looking for
+       interesting bits of information.  */
+    if (section_callback && ehdr->e_shstrndx != SHN_UNDEF) {
+        char *shstr = NULL;
+        struct elf_shdr *shstrtab = &shdr_table[ehdr->e_shstrndx];
+
+        shstr = load_at(fd, shstrtab->sh_offset, shstrtab->sh_size);
+        if (!shstr)
+            goto fail_callback;
+
+        for (i = 0; i < ehdr->e_shnum; i++) {
+            struct elf_shdr *sh = shdr_table + i;
+
+            section_callback(fd, must_swab, sh->sh_size, sh->sh_offset,
+                             &shstr[sh->sh_name]);
+        }
+
+    fail_callback:
+        free (shstr);
+    }
+
     symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB);
     if (!symtab)
         goto fail;
@@ -179,7 +206,8 @@  static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
 
 static int glue(load_elf, SZ)(int fd, int64_t address_offset,
                               int must_swab, uint64_t *pentry,
-                              uint64_t *lowaddr, uint64_t *highaddr)
+                              uint64_t *lowaddr, uint64_t *highaddr,
+                              section_callback_t section_callback)
 {
     struct elfhdr ehdr;
     struct elf_phdr *phdr = NULL, *ph;
@@ -213,7 +241,7 @@  static int glue(load_elf, SZ)(int fd, int64_t address_offset,
     if (pentry)
    	*pentry = (uint64_t)(elf_sword)ehdr.e_entry;
 
-    glue(load_symbols, SZ)(&ehdr, fd, must_swab);
+    glue(load_symbols, SZ)(&ehdr, fd, must_swab, section_callback);
 
     size = ehdr.e_phnum * sizeof(phdr[0]);
     lseek(fd, ehdr.e_phoff, SEEK_SET);
diff --git a/loader.c b/loader.c
index 0cbcf9c..6f025a9 100644
--- a/loader.c
+++ b/loader.c
@@ -305,9 +305,10 @@  static void *load_at(int fd, int offset, int size)
 #define SZ		64
 #include "elf_ops.h"
 
-/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf(const char *filename, int64_t address_offset,
-             uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
+int load_elf_introspect(const char *filename, int64_t address_offset,
+                        uint64_t *pentry, uint64_t *lowaddr,
+                        uint64_t *highaddr,
+                        section_callback_t section_callback)
 {
     int fd, data_order, host_data_order, must_swab, ret;
     uint8_t e_ident[EI_NIDENT];
@@ -342,10 +343,10 @@  int load_elf(const char *filename, int64_t address_offset,
     lseek(fd, 0, SEEK_SET);
     if (e_ident[EI_CLASS] == ELFCLASS64) {
         ret = load_elf64(fd, address_offset, must_swab, pentry,
-                         lowaddr, highaddr);
+                         lowaddr, highaddr, section_callback);
     } else {
         ret = load_elf32(fd, address_offset, must_swab, pentry,
-                         lowaddr, highaddr);
+                         lowaddr, highaddr, section_callback);
     }
 
     close(fd);
@@ -356,6 +357,14 @@  int load_elf(const char *filename, int64_t address_offset,
     return -1;
 }
 
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, int64_t address_offset,
+             uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
+{
+    return load_elf_introspect (filename, address_offset, pentry, lowaddr,
+                                highaddr, NULL);
+}
+
 static void bswap_uboot_header(uboot_image_header_t *hdr)
 {
 #ifndef HOST_WORDS_BIGENDIAN
diff --git a/sysemu.h b/sysemu.h
index 6af88d8..b26cf40 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -241,6 +241,11 @@  int load_image(const char *filename, uint8_t *addr); /* deprecated */
 int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz);
 int load_elf(const char *filename, int64_t address_offset,
              uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr);
+typedef void (*section_callback_t)(int fd, int must_swab,
+                                   uint64_t size, uint64_t offset, char *name);
+int load_elf_introspect(const char *filename, int64_t address_offset,
+                        uint64_t *pentry, uint64_t *lowaddr,
+                        uint64_t *highaddr, section_callback_t callback);
 int load_aout(const char *filename, target_phys_addr_t addr, int max_sz);
 int load_uimage(const char *filename, target_ulong *ep, target_ulong *loadaddr,
                 int *is_linux);