$NetBSD: $

--- xen/common/libelf/libelf-loader.c.orig	2007-05-18 16:45:21.000000000 +0200
+++ xen/common/libelf/libelf-loader.c	2007-08-16 21:43:18.000000000 +0200
@@ -20,11 +20,15 @@
     memset(elf, 0, sizeof(*elf));
     elf->image = image;
     elf->size = size;
-    elf->ehdr = (elf_ehdr *) image;
+    elf->ehdr = (elf_ehdr *)image;
     elf->class = elf->ehdr->e32.e_ident[EI_CLASS];
     elf->data = elf->ehdr->e32.e_ident[EI_DATA];
 
-    /* sanity check phdr */
+#ifdef VERBOSE
+    elf_set_verbose(elf);
+#endif
+
+    /* Sanity check phdr. */
     offset = elf_uval(elf, elf->ehdr, e_phoff) +
         elf_uval(elf, elf->ehdr, e_phentsize) * elf_phdr_count(elf);
     if ( offset > elf->size )
@@ -34,7 +38,7 @@
         return -1;
     }
 
-    /* sanity check shdr */
+    /* Sanity check shdr. */
     offset = elf_uval(elf, elf->ehdr, e_shoff) +
         elf_uval(elf, elf->ehdr, e_shentsize) * elf_shdr_count(elf);
     if ( offset > elf->size )
@@ -44,13 +48,13 @@
         return -1;
     }
 
-    /* find section string table */
+    /* Find section string table. */
     section = elf_uval(elf, elf->ehdr, e_shstrndx);
     shdr = elf_shdr_by_index(elf, section);
     if ( shdr != NULL )
         elf->sec_strtab = elf_section_start(elf, shdr);
 
-    /* find symbol table, symbol string table */
+    /* Find symbol table and symbol string table. */
     count = elf_shdr_count(elf);
     for ( i = 0; i < count; i++ )
     {
@@ -67,6 +71,7 @@
         elf->sym_strtab = elf_section_start(elf, shdr);
         break;
     }
+
     return 0;
 }
 
@@ -83,6 +88,101 @@
 }
 #endif
 
+/* Calculate the required additional kernel space for the elf image */
+void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart)
+{
+    uint64_t sz;
+    const elf_shdr *shdr;
+    int i, type;
+
+    if ( !elf->sym_tab )
+        return;
+
+    pstart = elf_round_up(elf, pstart);
+
+    /* Space to store the size of the elf image */
+    sz = sizeof(uint32_t);
+
+    /* Space for the elf and elf section headers */
+    sz += (elf_uval(elf, elf->ehdr, e_ehsize) +
+           elf_shdr_count(elf) * elf_uval(elf, elf->ehdr, e_shentsize));
+    sz = elf_round_up(elf, sz);
+
+    /* Space for the symbol and string tables. */
+    for ( i = 0; i < elf_shdr_count(elf); i++ )
+    {
+        shdr = elf_shdr_by_index(elf, i);
+        type = elf_uval(elf, (elf_shdr *)shdr, sh_type);
+        if ( (type == SHT_STRTAB) || (type == SHT_SYMTAB) )
+            sz = elf_round_up(elf, sz + elf_uval(elf, shdr, sh_size));
+    }
+
+    elf->bsd_symtab_pstart = pstart;
+    elf->bsd_symtab_pend   = pstart + sz;
+}
+
+static void elf_load_bsdsyms(struct elf_binary *elf)
+{
+    elf_ehdr *sym_ehdr;
+    unsigned long sz;
+    char *maxva, *symbase, *symtab_addr;
+    elf_shdr *shdr;
+    int i, type;
+
+    if ( !elf->bsd_symtab_pstart )
+        return;
+
+#define elf_hdr_elm(_elf, _hdr, _elm, _val)     \
+do {                                            \
+    if ( elf_64bit(_elf) )                      \
+        (_hdr)->e64._elm = _val;                \
+    else                                        \
+        (_hdr)->e32._elm = _val;                \
+} while ( 0 )
+
+    symbase = elf_get_ptr(elf, elf->bsd_symtab_pstart);
+    symtab_addr = maxva = symbase + sizeof(uint32_t);
+
+    /* Set up Elf header. */
+    sym_ehdr = (elf_ehdr *)symtab_addr;
+    sz = elf_uval(elf, elf->ehdr, e_ehsize);
+    memcpy(sym_ehdr, elf->ehdr, sz);
+    maxva += sz; /* no round up */
+
+    elf_hdr_elm(elf, sym_ehdr, e_phoff, 0);
+    elf_hdr_elm(elf, sym_ehdr, e_shoff, elf_uval(elf, elf->ehdr, e_ehsize));
+    elf_hdr_elm(elf, sym_ehdr, e_phentsize, 0);
+    elf_hdr_elm(elf, sym_ehdr, e_phnum, 0);
+
+    /* Copy Elf section headers. */
+    shdr = (elf_shdr *)maxva;
+    sz = elf_shdr_count(elf) * elf_uval(elf, elf->ehdr, e_shentsize);
+    memcpy(shdr, elf->image + elf_uval(elf, elf->ehdr, e_shoff), sz);
+    maxva = (char *)(long)elf_round_up(elf, (long)maxva + sz);
+
+    for ( i = 0; i < elf_shdr_count(elf); i++ )
+    {
+        type = elf_uval(elf, shdr, sh_type);
+        if ( (type == SHT_STRTAB) || (type == SHT_SYMTAB) )
+        {
+             elf_msg(elf, "%s: shdr %i at 0x%p -> 0x%p\n", __func__, i,
+                     elf_section_start(elf, shdr), maxva);
+             sz = elf_uval(elf, shdr, sh_size);
+             memcpy(maxva, elf_section_start(elf, shdr), sz);
+             /* Mangled to be based on ELF header location. */
+             elf_hdr_elm(elf, shdr, sh_offset, maxva - symtab_addr);
+             maxva = (char *)(long)elf_round_up(elf, (long)maxva + sz);
+        }
+        shdr = (elf_shdr *)((long)shdr +
+                            (long)elf_uval(elf, elf->ehdr, e_shentsize));
+    }
+
+    /* Write down the actual sym size. */
+    *(uint32_t *)symbase = maxva - symtab_addr;
+
+#undef elf_ehdr_elm
+}
+
 void elf_parse_binary(struct elf_binary *elf)
 {
     const elf_phdr *phdr;
@@ -133,6 +233,8 @@
         memcpy(dest, elf->image + offset, filesz);
         memset(dest + filesz, 0, memsz - filesz);
     }
+
+    elf_load_bsdsyms(elf);
 }
 
 void *elf_get_ptr(struct elf_binary *elf, unsigned long addr)
