diff mbox

[v2,08/13] xsplice: Implement payload loading (v2)

Message ID 1452808031-706-9-git-send-email-konrad.wilk@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Konrad Rzeszutek Wilk Jan. 14, 2016, 9:47 p.m. UTC
From: Ross Lagerwall <ross.lagerwall@citrix.com>

Add support for loading xsplice payloads. This is somewhat similar to
the Linux kernel module loader, implementing the following steps:
- Verify the elf file.
- Parse the elf file.
- Allocate a region of memory mapped within a free area of
  [xen_virt_end, XEN_VIRT_END].
- Copy allocated sections into the new region.
- Resolve section symbols. All other symbols must be absolute addresses.
- Perform relocations.

Note that the structure 'xsplice_patch_func' differs a bit from the design
by usurping 8 bytes from the padding. We use that for our own uses.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
v2: - Change the 'xsplice_patch_func' structure layout/size.
    - Add more error checking. Fix memory leak.
    - Move elf_resolve and elf_perform relocs in elf file.
    - Print the payload address and pages in keyhandler.
v3:
    - Make it build under ARM
---
 xen/arch/arm/Makefile             |   1 +
 xen/arch/arm/xsplice.c            |  23 ++++
 xen/arch/x86/Makefile             |   1 +
 xen/arch/x86/setup.c              |   7 ++
 xen/arch/x86/xsplice.c            | 106 +++++++++++++++++
 xen/common/xsplice.c              | 239 +++++++++++++++++++++++++++++++++++++-
 xen/common/xsplice_elf.c          |  84 ++++++++++++++
 xen/include/asm-arm/config.h      |   2 +
 xen/include/asm-x86/x86_64/page.h |   2 +
 xen/include/xen/xsplice.h         |  12 ++
 xen/include/xen/xsplice_elf.h     |   7 +-
 11 files changed, 481 insertions(+), 3 deletions(-)
 create mode 100644 xen/arch/arm/xsplice.c
 create mode 100644 xen/arch/x86/xsplice.c

Comments

Ross Lagerwall Jan. 19, 2016, 2:34 p.m. UTC | #1
On 01/14/2016 09:47 PM, Konrad Rzeszutek Wilk wrote:
> From: Ross Lagerwall <ross.lagerwall@citrix.com>
>
> Add support for loading xsplice payloads. This is somewhat similar to
> the Linux kernel module loader, implementing the following steps:
> - Verify the elf file.
> - Parse the elf file.
> - Allocate a region of memory mapped within a free area of
>    [xen_virt_end, XEN_VIRT_END].
> - Copy allocated sections into the new region.
> - Resolve section symbols. All other symbols must be absolute addresses.
> - Perform relocations.
>
> Note that the structure 'xsplice_patch_func' differs a bit from the design
> by usurping 8 bytes from the padding. We use that for our own uses.
>
> Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> ---
> v2: - Change the 'xsplice_patch_func' structure layout/size.
>      - Add more error checking. Fix memory leak.
>      - Move elf_resolve and elf_perform relocs in elf file.
>      - Print the payload address and pages in keyhandler.
> v3:
>      - Make it build under ARM
snip
>
> +static void find_hole(ssize_t pages, unsigned long *hole_start,
> +                      unsigned long *hole_end)
> +{
> +    struct payload *data, *data2;
> +
> +    spin_lock(&payload_list_lock);
> +    list_for_each_entry ( data, &payload_list, list )
> +    {
> +        list_for_each_entry ( data2, &payload_list, list )
> +        {
> +            unsigned long start, end;
> +
> +            start = (unsigned long)data2->payload_address;
> +            end = start + data2->payload_pages * PAGE_SIZE;
> +            if ( *hole_end > start && *hole_start < end )
> +            {
> +                *hole_start = end;
> +                *hole_end = *hole_start + pages * PAGE_SIZE;
> +                break;
> +            }
> +        }
> +        if ( &data2->list == &payload_list )
> +            break;
> +    }
> +    spin_unlock(&payload_list_lock);
> +}

This function above should go down into the CONFIG_X86 section below.

> +
> +/*
> + * The following functions prepare an xSplice payload to be executed by
> + * allocating space, loading the allocated sections, resolving symbols,
> + * performing relocations, etc.
> + */
> +#ifdef CONFIG_X86
> +static void *alloc_payload(size_t size)
> +{
> +    mfn_t *mfn, *mfn_ptr;
> +    size_t pages, i;
> +    struct page_info *pg;
> +    unsigned long hole_start, hole_end, cur;
> +
> +    ASSERT(size);
> +
> +    /*
> +     * Copied from vmalloc which allocates pages and then maps them to an
> +     * arbitrary virtual address with PAGE_HYPERVISOR. We need specific
> +     * virtual address with PAGE_HYPERVISOR_RWX.
> +     */
> +    pages = PFN_UP(size);
> +    mfn = xmalloc_array(mfn_t, pages);
> +    if ( mfn == NULL )
> +        return NULL;
> +
> +    for ( i = 0; i < pages; i++ )
> +    {
> +        pg = alloc_domheap_page(NULL, 0);
> +        if ( pg == NULL )
> +            goto error;
> +        mfn[i] = _mfn(page_to_mfn(pg));
snip
> diff --git a/xen/common/xsplice_elf.c b/xen/common/xsplice_elf.c
> index a5e9d63..ea7eb73 100644
> --- a/xen/common/xsplice_elf.c
> +++ b/xen/common/xsplice_elf.c
> @@ -199,3 +199,87 @@ void xsplice_elf_free(struct xsplice_elf *elf)
>       elf->name = NULL;
>       elf->len = 0;
>   }
> +
> +int xsplice_elf_resolve_symbols(struct xsplice_elf *elf)
> +{
> +    unsigned int i;
> +
> +    /*
> +     * The first entry of an ELF symbol table is the "undefined symbol index".
> +     * aka reserved so we skip it.
> +     */
> +    ASSERT( elf->sym );
> +    for ( i = 1; i < elf->nsym; i++ )
> +    {
> +        switch ( elf->sym[i].sym->st_shndx )
> +        {
> +            case SHN_COMMON:
> +                printk(XENLOG_ERR "%s: Unexpected common symbol: %s\n",
> +                       elf->name, elf->sym[i].name);
> +                return_(-EINVAL);
> +                break;
> +            case SHN_UNDEF:
> +                printk(XENLOG_ERR "%s: Unknown symbol: %s\n", elf->name,
> +                       elf->sym[i].name);
> +                return_(-ENOENT);
> +                break;
> +            case SHN_ABS:
> +                printk(XENLOG_DEBUG "%s: Absolute symbol: %s => 0x%p\n",
> +                      elf->name, elf->sym[i].name,
> +                      (void *)elf->sym[i].sym->st_value);
> +                break;
> +            default:
> +                if ( elf->sec[elf->sym[i].sym->st_shndx].sec->sh_flags & SHF_ALLOC )
> +                {
> +                    elf->sym[i].sym->st_value +=
> +                        (unsigned long)elf->sec[elf->sym[i].sym->st_shndx].load_addr;
> +                    printk(XENLOG_DEBUG "%s: Symbol resolved: %s => 0x%p\n",
> +                           elf->name, elf->sym[i].name,
> +                           (void *)elf->sym[i].sym->st_value);
> +                }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +int xsplice_elf_perform_relocs(struct xsplice_elf *elf)
> +{
> +    struct xsplice_elf_sec *rela, *base;
> +    unsigned int i;
> +    int rc;
> +
> +    /*
> +     * The first entry of an ELF symbol table is the "undefined symbol index".
> +     * aka reserved so we skip it.
> +     */
> +    ASSERT( elf->sym );
> +    for ( i = 1; i < elf->hdr->e_shnum; i++ )
> +    {
> +        rela = &elf->sec[i];
> +
> +        if ( (rela->sec->sh_type != SHT_RELA ) &&
> +             (rela->sec->sh_type != SHT_REL ) )
> +            continue;
> +
> +         /* Is it a valid relocation section? */
> +         if ( rela->sec->sh_info >= elf->hdr->e_shnum )
> +            continue;
> +
> +         base = &elf->sec[rela->sec->sh_info];
> +
> +         /* Don't relocate non-allocated sections. */
> +         if ( !(base->sec->sh_flags & SHF_ALLOC) )
> +            continue;
> +
> +        if ( elf->sec[i].sec->sh_type == SHT_RELA )
> +            rc = xsplice_perform_rela(elf, base, rela);
> +        else /* SHT_REL */
> +            rc = xsplice_perform_rel(elf, base, rela);
> +
> +        if ( rc )
> +            return rc;
> +    }
> +
> +    return 0;
> +}

Is there a reason the above two functions weren't put in the previous patch?

> diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
> index bd832df..4ea66bf 100644
> --- a/xen/include/asm-arm/config.h
> +++ b/xen/include/asm-arm/config.h
> @@ -15,8 +15,10 @@
>
>   #if defined(CONFIG_ARM_64)
>   # define LONG_BYTEORDER 3
> +# define ELFSIZE 64
>   #else
>   # define LONG_BYTEORDER 2
> +# define ELFSIZE 32
>   #endif

What does this do?

(And perhaps it should also be in the previous patch since it's 
mentioned in the previous patch's changelog?)
Konrad Rzeszutek Wilk Jan. 19, 2016, 4:59 p.m. UTC | #2
> >+static void find_hole(ssize_t pages, unsigned long *hole_start,
> >+                      unsigned long *hole_end)
> >+{
> >+    struct payload *data, *data2;
> >+
> >+    spin_lock(&payload_list_lock);
> >+    list_for_each_entry ( data, &payload_list, list )
> >+    {
> >+        list_for_each_entry ( data2, &payload_list, list )
> >+        {
> >+            unsigned long start, end;
> >+
> >+            start = (unsigned long)data2->payload_address;
> >+            end = start + data2->payload_pages * PAGE_SIZE;
> >+            if ( *hole_end > start && *hole_start < end )
> >+            {
> >+                *hole_start = end;
> >+                *hole_end = *hole_start + pages * PAGE_SIZE;
> >+                break;
> >+            }
> >+        }
> >+        if ( &data2->list == &payload_list )
> >+            break;
> >+    }
> >+    spin_unlock(&payload_list_lock);
> >+}
> 
> This function above should go down into the CONFIG_X86 section below.

Odd. I have it in my tree. Ah right I - I had the patch not committed in. <sigh>
.. snip..
> >+int xsplice_elf_resolve_symbols(struct xsplice_elf *elf)
.. snip..
> >+int xsplice_elf_perform_relocs(struct xsplice_elf *elf)
.. snip..
> 
> Is there a reason the above two functions weren't put in the previous patch?

Historical. I will move them there. Thanks!
> 
> >diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
> >index bd832df..4ea66bf 100644
> >--- a/xen/include/asm-arm/config.h
> >+++ b/xen/include/asm-arm/config.h
> >@@ -15,8 +15,10 @@
> >
> >  #if defined(CONFIG_ARM_64)
> >  # define LONG_BYTEORDER 3
> >+# define ELFSIZE 64
> >  #else
> >  # define LONG_BYTEORDER 2
> >+# define ELFSIZE 32
> >  #endif
> 
> What does this do?

Make Elf_Note and all the ELf_* macros actually work.
> 
> (And perhaps it should also be in the previous patch since it's mentioned in
> the previous patch's changelog?)

I kind of lost where it was added. 

I could spin it out as a seperate patch - or make it part of the previous
patch? Thoughts?
> 
> -- 
> Ross Lagerwall
Ross Lagerwall Jan. 25, 2016, 11:21 a.m. UTC | #3
On 01/19/2016 04:59 PM, Konrad Rzeszutek Wilk wrote:
snip
>>
>> (And perhaps it should also be in the previous patch since it's mentioned in
>> the previous patch's changelog?)
>
> I kind of lost where it was added.
>
> I could spin it out as a seperate patch - or make it part of the previous
> patch? Thoughts?
>>

I think a separate patch would be better.
diff mbox

Patch

diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 2f050f5..c0f16b0 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -40,6 +40,7 @@  obj-y += device.o
 obj-y += decode.o
 obj-y += processor.o
 obj-y += smc.o
+obj-$(CONFIG_XSPLICE) += xsplice.o
 
 #obj-bin-y += ....o
 
diff --git a/xen/arch/arm/xsplice.c b/xen/arch/arm/xsplice.c
new file mode 100644
index 0000000..8d85fa9
--- /dev/null
+++ b/xen/arch/arm/xsplice.c
@@ -0,0 +1,23 @@ 
+#include <xen/lib.h>
+#include <xen/errno.h>
+#include <xen/xsplice_elf.h>
+#include <xen/xsplice.h>
+
+int xsplice_verify_elf(uint8_t *data, ssize_t len)
+{
+    return -ENOSYS;
+}
+
+int xsplice_perform_rel(struct xsplice_elf *elf,
+                        struct xsplice_elf_sec *base,
+                        struct xsplice_elf_sec *rela)
+{
+    return -ENOSYS;
+}
+
+int xsplice_perform_rela(struct xsplice_elf *elf,
+                         struct xsplice_elf_sec *base,
+                         struct xsplice_elf_sec *rela)
+{
+    return -ENOSYS;
+}
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 8e6e901..f7d3e39 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -63,6 +63,7 @@  obj-y += vm_event.o
 obj-y += xstate.o
 
 obj-$(crash_debug) += gdbstub.o
+obj-$(CONFIG_XSPLICE) += xsplice.o
 
 x86_emulate.o: x86_emulate/x86_emulate.c x86_emulate/x86_emulate.h
 
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index 76c7b0f..fb35005 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -99,6 +99,9 @@  unsigned long __read_mostly xen_phys_start;
 
 unsigned long __read_mostly xen_virt_end;
 
+unsigned long __read_mostly module_virt_start;
+unsigned long __read_mostly module_virt_end;
+
 DEFINE_PER_CPU(struct tss_struct, init_tss);
 
 char __section(".bss.stack_aligned") cpu0_stack[STACK_SIZE];
@@ -1146,6 +1149,10 @@  void __init noreturn __start_xen(unsigned long mbi_p)
                    ~((1UL << L2_PAGETABLE_SHIFT) - 1);
     destroy_xen_mappings(xen_virt_end, XEN_VIRT_START + BOOTSTRAP_MAP_BASE);
 
+    module_virt_start = xen_virt_end;
+    module_virt_end = XEN_VIRT_END - NR_CPUS * PAGE_SIZE;
+    BUG_ON(module_virt_end <= module_virt_start);
+
     memguard_init();
 
     nr_pages = 0;
diff --git a/xen/arch/x86/xsplice.c b/xen/arch/x86/xsplice.c
new file mode 100644
index 0000000..7b13511
--- /dev/null
+++ b/xen/arch/x86/xsplice.c
@@ -0,0 +1,106 @@ 
+#include <xen/errno.h>
+#include <xen/lib.h>
+#include <xen/xsplice_elf.h>
+#include <xen/xsplice.h>
+
+int xsplice_verify_elf(struct xsplice_elf *elf, uint8_t *data)
+{
+
+    Elf_Ehdr *hdr = (Elf_Ehdr *)data;
+
+    if ( elf->len < (sizeof *hdr) ||
+         !IS_ELF(*hdr) ||
+         hdr->e_ident[EI_CLASS] != ELFCLASS64 ||
+         hdr->e_ident[EI_DATA] != ELFDATA2LSB ||
+         hdr->e_ident[EI_OSABI] != ELFOSABI_SYSV ||
+         hdr->e_machine != EM_X86_64 ||
+         hdr->e_type != ET_REL ||
+         hdr->e_phnum != 0 )
+    {
+        printk(XENLOG_ERR "%s: Invalid ELF file.\n", elf->name);
+        return -EOPNOTSUPP;
+    }
+
+    return 0;
+}
+
+int xsplice_perform_rel(struct xsplice_elf *elf,
+                        struct xsplice_elf_sec *base,
+                        struct xsplice_elf_sec *rela)
+{
+    printk(XENLOG_ERR "%s: SHR_REL relocation unsupported\n", elf->name);
+    return -ENOSYS;
+}
+
+int xsplice_perform_rela(struct xsplice_elf *elf,
+                         struct xsplice_elf_sec *base,
+                         struct xsplice_elf_sec *rela)
+{
+    Elf_RelA *r;
+    unsigned int symndx, i;
+    uint64_t val;
+    uint8_t *dest;
+
+    if ( !rela->sec->sh_entsize || !rela->sec->sh_size )
+        return -EINVAL;
+
+    if ( rela->sec->sh_entsize != sizeof(Elf_RelA) )
+        return -EINVAL;
+
+    for ( i = 0; i < (rela->sec->sh_size / rela->sec->sh_entsize); i++ )
+    {
+        r = (Elf_RelA *)(rela->data + i * rela->sec->sh_entsize);
+        if ( (unsigned long)r > (unsigned long)(elf->hdr + elf->len) )
+            return -EINVAL;
+
+        symndx = ELF64_R_SYM(r->r_info);
+        if ( symndx > elf->nsym )
+            return -EINVAL;
+
+        dest = base->load_addr + r->r_offset;
+        val = r->r_addend + elf->sym[symndx].sym->st_value;
+
+        switch ( ELF64_R_TYPE(r->r_info) )
+        {
+            case R_X86_64_NONE:
+                break;
+            case R_X86_64_64:
+                *(uint64_t *)dest = val;
+                break;
+            case R_X86_64_32:
+                *(uint32_t *)dest = val;
+                if (val != *(uint32_t *)dest)
+                    goto overflow;
+                break;
+            case R_X86_64_32S:
+                *(int32_t *)dest = val;
+                if ((int64_t)val != *(int32_t *)dest)
+                    goto overflow;
+                break;
+            case R_X86_64_PLT32:
+                /*
+                 * Xen uses -fpic which normally uses PLT relocations
+                 * except that it sets visibility to hidden which means
+                 * that they are not used.  However, when gcc cannot
+                 * inline memcpy it emits memcpy with default visibility
+                 * which then creates a PLT relocation.  It can just be
+                 * treated the same as R_X86_64_PC32.
+                 */
+                /* Fall through */
+            case R_X86_64_PC32:
+                *(uint32_t *)dest = val - (uint64_t)dest;
+                break;
+            default:
+                printk(XENLOG_ERR "%s: Unhandled relocation %lu\n",
+                       elf->name, ELF64_R_TYPE(r->r_info));
+                return -EINVAL;
+        }
+    }
+
+    return 0;
+
+ overflow:
+    printk(XENLOG_ERR "%s: Overflow in relocation %d in %s for %s\n",
+           elf->name, i, rela->name, base->name);
+    return -EOVERFLOW;
+}
diff --git a/xen/common/xsplice.c b/xen/common/xsplice.c
index 3c6acc3..67f6fc7 100644
--- a/xen/common/xsplice.c
+++ b/xen/common/xsplice.c
@@ -11,6 +11,7 @@ 
 #include <xen/sched.h>
 #include <xen/smp.h>
 #include <xen/spinlock.h>
+#include <xen/xsplice_elf.h>
 #include <xen/xsplice.h>
 
 #include <asm/event.h>
@@ -26,9 +27,15 @@  struct payload {
     int32_t state;                       /* One of the XSPLICE_STATE_*. */
     int32_t rc;                          /* 0 or -XEN_EXX. */
     struct list_head list;               /* Linked to 'payload_list'. */
+    void *payload_address;               /* Virtual address mapped. */
+    size_t payload_pages;                /* Nr of the pages. */
+
     char name[XEN_XSPLICE_NAME_SIZE + 1];/* Name of it. */
 };
 
+static int load_payload_data(struct payload *payload, uint8_t *raw, ssize_t len);
+static void free_payload_data(struct payload *payload);
+
 static const char *state2str(int32_t state)
 {
 #define STATE(x) [XSPLICE_STATE_##x] = #x
@@ -58,8 +65,9 @@  static void xsplice_printall(unsigned char key)
     spin_lock(&payload_list_lock);
 
     list_for_each_entry ( data, &payload_list, list )
-        printk(" name=%s state=%s(%d)\n", data->name,
-               state2str(data->state), data->state);
+        printk(" name=%s state=%s(%d) %p using %zu pages.\n", data->name,
+               state2str(data->state), data->state, data->payload_address,
+               data->payload_pages);
 
     spin_unlock(&payload_list_lock);
 }
@@ -136,6 +144,7 @@  static void free_payload(struct payload *data)
     list_del(&data->list);
     payload_cnt--;
     payload_version++;
+    free_payload_data(data);
     xfree(data);
 }
 
@@ -174,6 +183,10 @@  static int xsplice_upload(xen_sysctl_xsplice_upload_t *upload)
     if ( copy_from_guest(raw_data, upload->payload, upload->size) )
         goto err_raw;
 
+    rc = load_payload_data(data, raw_data, upload->size);
+    if ( rc )
+        goto err_raw;
+
     data->state = XSPLICE_STATE_LOADED;
     data->rc = 0;
     INIT_LIST_HEAD(&data->list);
@@ -378,6 +391,228 @@  int xsplice_control(xen_sysctl_xsplice_op_t *xsplice)
     return rc;
 }
 
+static void find_hole(ssize_t pages, unsigned long *hole_start,
+                      unsigned long *hole_end)
+{
+    struct payload *data, *data2;
+
+    spin_lock(&payload_list_lock);
+    list_for_each_entry ( data, &payload_list, list )
+    {
+        list_for_each_entry ( data2, &payload_list, list )
+        {
+            unsigned long start, end;
+
+            start = (unsigned long)data2->payload_address;
+            end = start + data2->payload_pages * PAGE_SIZE;
+            if ( *hole_end > start && *hole_start < end )
+            {
+                *hole_start = end;
+                *hole_end = *hole_start + pages * PAGE_SIZE;
+                break;
+            }
+        }
+        if ( &data2->list == &payload_list )
+            break;
+    }
+    spin_unlock(&payload_list_lock);
+}
+
+/*
+ * The following functions prepare an xSplice payload to be executed by
+ * allocating space, loading the allocated sections, resolving symbols,
+ * performing relocations, etc.
+ */
+#ifdef CONFIG_X86
+static void *alloc_payload(size_t size)
+{
+    mfn_t *mfn, *mfn_ptr;
+    size_t pages, i;
+    struct page_info *pg;
+    unsigned long hole_start, hole_end, cur;
+
+    ASSERT(size);
+
+    /*
+     * Copied from vmalloc which allocates pages and then maps them to an
+     * arbitrary virtual address with PAGE_HYPERVISOR. We need specific
+     * virtual address with PAGE_HYPERVISOR_RWX.
+     */
+    pages = PFN_UP(size);
+    mfn = xmalloc_array(mfn_t, pages);
+    if ( mfn == NULL )
+        return NULL;
+
+    for ( i = 0; i < pages; i++ )
+    {
+        pg = alloc_domheap_page(NULL, 0);
+        if ( pg == NULL )
+            goto error;
+        mfn[i] = _mfn(page_to_mfn(pg));
+    }
+
+    hole_start = (unsigned long)module_virt_start;
+    hole_end = hole_start + pages * PAGE_SIZE;
+    find_hole(pages, &hole_start, &hole_end);
+
+    if ( hole_end >= module_virt_end )
+        goto error;
+
+    for ( cur = hole_start, mfn_ptr = mfn; pages--; ++mfn_ptr, cur += PAGE_SIZE )
+    {
+        if ( map_pages_to_xen(cur, mfn_x(*mfn_ptr), 1, PAGE_HYPERVISOR_RWX) )
+        {
+            if ( cur != hole_start )
+                destroy_xen_mappings(hole_start, cur);
+            goto error;
+        }
+    }
+    xfree(mfn);
+    return (void *)hole_start;
+
+ error:
+    while ( i-- )
+        free_domheap_page(mfn_to_page(mfn_x(mfn[i])));
+    xfree(mfn);
+    return NULL;
+}
+#else
+static void *alloc_payload(size_t size)
+{
+    return NULL;
+}
+#endif
+
+static void free_payload_data(struct payload *payload)
+{
+    unsigned int i;
+    struct page_info *pg;
+    PAGE_LIST_HEAD(pg_list);
+    void *va = payload->payload_address;
+    unsigned long addr = (unsigned long)va;
+
+    if ( !va )
+        return;
+
+    payload->payload_address = NULL;
+
+    for ( i = 0; i < payload->payload_pages; i++ )
+        page_list_add(vmap_to_page(va + i * PAGE_SIZE), &pg_list);
+
+    destroy_xen_mappings(addr, addr + payload->payload_pages * PAGE_SIZE);
+
+    while ( (pg = page_list_remove_head(&pg_list)) != NULL )
+        free_domheap_page(pg);
+
+    payload->payload_pages = 0;
+}
+
+static void calc_section(struct xsplice_elf_sec *sec, size_t *core_size)
+{
+    size_t align_size = ROUNDUP(*core_size, sec->sec->sh_addralign);
+    sec->sec->sh_entsize = align_size;
+    *core_size = sec->sec->sh_size + align_size;
+}
+
+static int move_payload(struct payload *payload, struct xsplice_elf *elf)
+{
+    uint8_t *buf;
+    unsigned int i;
+    size_t core_size = 0;
+
+    /* Compute text regions */
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( (elf->sec[i].sec->sh_flags & (SHF_ALLOC|SHF_EXECINSTR)) ==
+             (SHF_ALLOC|SHF_EXECINSTR) )
+            calc_section(&elf->sec[i], &core_size);
+    }
+
+    /* Compute rw data */
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( (elf->sec[i].sec->sh_flags & SHF_ALLOC) &&
+             !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
+             (elf->sec[i].sec->sh_flags & SHF_WRITE) )
+            calc_section(&elf->sec[i], &core_size);
+    }
+
+    /* Compute ro data */
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( (elf->sec[i].sec->sh_flags & SHF_ALLOC) &&
+             !(elf->sec[i].sec->sh_flags & SHF_EXECINSTR) &&
+             !(elf->sec[i].sec->sh_flags & SHF_WRITE) )
+            calc_section(&elf->sec[i], &core_size);
+    }
+
+    buf = alloc_payload(core_size);
+    if ( !buf ) {
+        printk(XENLOG_ERR "%s: Could not allocate memory for module\n",
+               elf->name);
+        return -ENOMEM;
+    }
+    memset(buf, 0, core_size);
+
+    for ( i = 0; i < elf->hdr->e_shnum; i++ )
+    {
+        if ( elf->sec[i].sec->sh_flags & SHF_ALLOC )
+        {
+            elf->sec[i].load_addr = buf + elf->sec[i].sec->sh_entsize;
+            memcpy(elf->sec[i].load_addr, elf->sec[i].data,
+                   elf->sec[i].sec->sh_size);
+            printk(XENLOG_DEBUG "%s: Loaded %s at 0x%p\n",
+                   elf->name, elf->sec[i].name, elf->sec[i].load_addr);
+        }
+    }
+
+    payload->payload_address = buf;
+    payload->payload_pages = PFN_UP(core_size);
+
+    return 0;
+}
+
+static int load_payload_data(struct payload *payload, uint8_t *raw, ssize_t len)
+{
+    struct xsplice_elf elf;
+    int rc = 0;
+
+    memset(&elf, 0, sizeof(elf));
+    elf.name = payload->name;
+    elf.len = len;
+
+    rc = xsplice_verify_elf(&elf, raw);
+    if ( rc )
+        return rc;
+
+    rc = xsplice_elf_load(&elf, raw);
+    if ( rc )
+        goto err_elf;
+
+    rc = move_payload(payload, &elf);
+    if ( rc )
+        goto err_elf;
+
+    rc = xsplice_elf_resolve_symbols(&elf);
+    if ( rc )
+        goto err_payload;
+
+    rc = xsplice_elf_perform_relocs(&elf);
+    if ( rc )
+        goto err_payload;
+
+    /* Free our temporary data structure. */
+    xsplice_elf_free(&elf);
+    return 0;
+
+ err_payload:
+    free_payload_data(payload);
+ err_elf:
+    xsplice_elf_free(&elf);
+
+    return rc;
+}
+
 static int __init xsplice_init(void)
 {
     register_keyhandler('x', xsplice_printall, "print xsplicing info", 1);
diff --git a/xen/common/xsplice_elf.c b/xen/common/xsplice_elf.c
index a5e9d63..ea7eb73 100644
--- a/xen/common/xsplice_elf.c
+++ b/xen/common/xsplice_elf.c
@@ -199,3 +199,87 @@  void xsplice_elf_free(struct xsplice_elf *elf)
     elf->name = NULL;
     elf->len = 0;
 }
+
+int xsplice_elf_resolve_symbols(struct xsplice_elf *elf)
+{
+    unsigned int i;
+
+    /*
+     * The first entry of an ELF symbol table is the "undefined symbol index".
+     * aka reserved so we skip it.
+     */
+    ASSERT( elf->sym );
+    for ( i = 1; i < elf->nsym; i++ )
+    {
+        switch ( elf->sym[i].sym->st_shndx )
+        {
+            case SHN_COMMON:
+                printk(XENLOG_ERR "%s: Unexpected common symbol: %s\n",
+                       elf->name, elf->sym[i].name);
+                return_(-EINVAL);
+                break;
+            case SHN_UNDEF:
+                printk(XENLOG_ERR "%s: Unknown symbol: %s\n", elf->name,
+                       elf->sym[i].name);
+                return_(-ENOENT);
+                break;
+            case SHN_ABS:
+                printk(XENLOG_DEBUG "%s: Absolute symbol: %s => 0x%p\n",
+                      elf->name, elf->sym[i].name,
+                      (void *)elf->sym[i].sym->st_value);
+                break;
+            default:
+                if ( elf->sec[elf->sym[i].sym->st_shndx].sec->sh_flags & SHF_ALLOC )
+                {
+                    elf->sym[i].sym->st_value +=
+                        (unsigned long)elf->sec[elf->sym[i].sym->st_shndx].load_addr;
+                    printk(XENLOG_DEBUG "%s: Symbol resolved: %s => 0x%p\n",
+                           elf->name, elf->sym[i].name,
+                           (void *)elf->sym[i].sym->st_value);
+                }
+        }
+    }
+
+    return 0;
+}
+
+int xsplice_elf_perform_relocs(struct xsplice_elf *elf)
+{
+    struct xsplice_elf_sec *rela, *base;
+    unsigned int i;
+    int rc;
+
+    /*
+     * The first entry of an ELF symbol table is the "undefined symbol index".
+     * aka reserved so we skip it.
+     */
+    ASSERT( elf->sym );
+    for ( i = 1; i < elf->hdr->e_shnum; i++ )
+    {
+        rela = &elf->sec[i];
+
+        if ( (rela->sec->sh_type != SHT_RELA ) &&
+             (rela->sec->sh_type != SHT_REL ) )
+            continue;
+
+         /* Is it a valid relocation section? */
+         if ( rela->sec->sh_info >= elf->hdr->e_shnum )
+            continue;
+
+         base = &elf->sec[rela->sec->sh_info];
+
+         /* Don't relocate non-allocated sections. */
+         if ( !(base->sec->sh_flags & SHF_ALLOC) )
+            continue;
+
+        if ( elf->sec[i].sec->sh_type == SHT_RELA )
+            rc = xsplice_perform_rela(elf, base, rela);
+        else /* SHT_REL */
+            rc = xsplice_perform_rel(elf, base, rela);
+
+        if ( rc )
+            return rc;
+    }
+
+    return 0;
+}
diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
index bd832df..4ea66bf 100644
--- a/xen/include/asm-arm/config.h
+++ b/xen/include/asm-arm/config.h
@@ -15,8 +15,10 @@ 
 
 #if defined(CONFIG_ARM_64)
 # define LONG_BYTEORDER 3
+# define ELFSIZE 64
 #else
 # define LONG_BYTEORDER 2
+# define ELFSIZE 32
 #endif
 
 #define BYTES_PER_LONG (1 << LONG_BYTEORDER)
diff --git a/xen/include/asm-x86/x86_64/page.h b/xen/include/asm-x86/x86_64/page.h
index 19ab4d0..e6f08e9 100644
--- a/xen/include/asm-x86/x86_64/page.h
+++ b/xen/include/asm-x86/x86_64/page.h
@@ -38,6 +38,8 @@ 
 #include <xen/pdx.h>
 
 extern unsigned long xen_virt_end;
+extern unsigned long module_virt_start;
+extern unsigned long module_virt_end;
 
 #define spage_to_pdx(spg) (((spg) - spage_table)<<(SUPERPAGE_SHIFT-PAGE_SHIFT))
 #define pdx_to_spage(pdx) (spage_table + ((pdx)>>(SUPERPAGE_SHIFT-PAGE_SHIFT)))
diff --git a/xen/include/xen/xsplice.h b/xen/include/xen/xsplice.h
index 2cb2035..b90742f 100644
--- a/xen/include/xen/xsplice.h
+++ b/xen/include/xen/xsplice.h
@@ -1,7 +1,19 @@ 
 #ifndef __XEN_XSPLICE_H__
 #define __XEN_XSPLICE_H__
 
+struct xsplice_elf;
+struct xsplice_elf_sec;
+struct xsplice_elf_sym;
 struct xen_sysctl_xsplice_op;
+
 int xsplice_control(struct xen_sysctl_xsplice_op *);
 
+/* Arch hooks */
+int xsplice_verify_elf(struct xsplice_elf *elf, uint8_t *data);
+int xsplice_perform_rel(struct xsplice_elf *elf,
+                        struct xsplice_elf_sec *base,
+                        struct xsplice_elf_sec *rela);
+int xsplice_perform_rela(struct xsplice_elf *elf,
+                         struct xsplice_elf_sec *base,
+                         struct xsplice_elf_sec *rela);
 #endif /* __XEN_XSPLICE_H__ */
diff --git a/xen/include/xen/xsplice_elf.h b/xen/include/xen/xsplice_elf.h
index 60c932b..229c11f 100644
--- a/xen/include/xen/xsplice_elf.h
+++ b/xen/include/xen/xsplice_elf.h
@@ -9,8 +9,10 @@  struct xsplice_elf_sec {
     Elf_Shdr *sec;                 /* Hooked up in elf_resolve_sections. */
     const char *name;              /* Human readable name hooked in
                                       elf_resolve_section_names. */
-    const char uint8_t *data;      /* Pointer to the section (done by
+    const uint8_t *data;           /* Pointer to the section (done by
                                       elf_resolve_sections). */
+    uint8_t *load_addr;            /* A pointer to the allocated destination.
+                                      Done by load_payload_data. */
 };
 
 struct xsplice_elf_sym {
@@ -34,4 +36,7 @@  struct xsplice_elf_sec *xsplice_elf_sec_by_name(const struct xsplice_elf *elf,
 int xsplice_elf_load(struct xsplice_elf *elf, uint8_t *data);
 void xsplice_elf_free(struct xsplice_elf *elf);
 
+int xsplice_elf_resolve_symbols(struct xsplice_elf *elf);
+int xsplice_elf_perform_relocs(struct xsplice_elf *elf);
+
 #endif /* __XEN_XSPLICE_ELF_H__ */