Files
xen/tools/libxc/xc_load_elf.c
T

345 lines
9.6 KiB
C

/******************************************************************************
* xc_elf_load.c
*/
#include "xg_private.h"
#if defined(__i386__)
#define ELFSIZE 32
#endif
#if defined(__x86_64__) || defined(__ia64__)
#define ELFSIZE 64
#endif
#include "xc_elf.h"
#include <stdlib.h>
#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
#define round_pgdown(_p) ((_p)&PAGE_MASK)
static int
parseelfimage(
const char *image, unsigned long image_size,
struct domain_setup_info *dsi);
static int
loadelfimage(
const char *image, unsigned long image_size, int xch, uint32_t dom,
unsigned long *parray, struct domain_setup_info *dsi);
static int
loadelfsymtab(
const char *image, int xch, uint32_t dom, unsigned long *parray,
struct domain_setup_info *dsi);
int probe_elf(const char *image,
unsigned long image_size,
struct load_funcs *load_funcs)
{
Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
if ( !IS_ELF(*ehdr) )
return -EINVAL;
load_funcs->parseimage = parseelfimage;
load_funcs->loadimage = loadelfimage;
return 0;
}
static inline int is_loadable_phdr(Elf_Phdr *phdr)
{
return ((phdr->p_type == PT_LOAD) &&
((phdr->p_flags & (PF_W|PF_X)) != 0));
}
static int parseelfimage(const char *image,
unsigned long elfsize,
struct domain_setup_info *dsi)
{
Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
Elf_Phdr *phdr;
Elf_Shdr *shdr;
unsigned long kernstart = ~0UL, kernend=0UL;
const char *shstrtab;
char *guestinfo=NULL, *p;
int h;
if ( !IS_ELF(*ehdr) )
{
ERROR("Kernel image does not have an ELF header.");
return -EINVAL;
}
if (
#if defined(__i386__)
(ehdr->e_ident[EI_CLASS] != ELFCLASS32) ||
(ehdr->e_machine != EM_386) ||
#elif defined(__x86_64__)
(ehdr->e_ident[EI_CLASS] != ELFCLASS64) ||
(ehdr->e_machine != EM_X86_64) ||
#endif
(ehdr->e_ident[EI_DATA] != ELFDATA2LSB) ||
(ehdr->e_type != ET_EXEC) )
{
ERROR("Kernel not a Xen-compatible Elf image.");
return -EINVAL;
}
if ( (ehdr->e_phoff + (ehdr->e_phnum * ehdr->e_phentsize)) > elfsize )
{
ERROR("ELF program headers extend beyond end of image.");
return -EINVAL;
}
if ( (ehdr->e_shoff + (ehdr->e_shnum * ehdr->e_shentsize)) > elfsize )
{
ERROR("ELF section headers extend beyond end of image.");
return -EINVAL;
}
/* Find the section-header strings table. */
if ( ehdr->e_shstrndx == SHN_UNDEF )
{
ERROR("ELF image has no section-header strings table (shstrtab).");
return -EINVAL;
}
shdr = (Elf_Shdr *)(image + ehdr->e_shoff +
(ehdr->e_shstrndx*ehdr->e_shentsize));
shstrtab = image + shdr->sh_offset;
/* Find the special '__xen_guest' section and check its contents. */
for ( h = 0; h < ehdr->e_shnum; h++ )
{
shdr = (Elf_Shdr *)(image + ehdr->e_shoff + (h*ehdr->e_shentsize));
if ( strcmp(&shstrtab[shdr->sh_name], "__xen_guest") != 0 )
continue;
guestinfo = (char *)image + shdr->sh_offset;
if ( (strstr(guestinfo, "LOADER=generic") == NULL) &&
(strstr(guestinfo, "GUEST_OS=linux") == NULL) )
{
ERROR("Will only load images built for the generic loader "
"or Linux images");
ERROR("Actually saw: '%s'", guestinfo);
return -EINVAL;
}
if ( (strstr(guestinfo, "XEN_VER=xen-3.0") == NULL) )
{
ERROR("Will only load images built for Xen v3.0");
ERROR("Actually saw: '%s'", guestinfo);
return -EINVAL;
}
if ( (strstr(guestinfo, "PAE=yes") != NULL) )
dsi->pae_kernel = 1;
break;
}
if ( guestinfo == NULL )
{
#ifdef __ia64__
guestinfo = "";
#else
ERROR("Not a Xen-ELF image: '__xen_guest' section not found.");
return -EINVAL;
#endif
}
dsi->xen_guest_string = guestinfo;
for ( h = 0; h < ehdr->e_phnum; h++ )
{
phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
if ( !is_loadable_phdr(phdr) )
continue;
if ( phdr->p_paddr < kernstart )
kernstart = phdr->p_paddr;
if ( (phdr->p_paddr + phdr->p_memsz) > kernend )
kernend = phdr->p_paddr + phdr->p_memsz;
}
if ( (kernstart > kernend) ||
(ehdr->e_entry < kernstart) ||
(ehdr->e_entry > kernend) )
{
ERROR("Malformed ELF image.");
return -EINVAL;
}
dsi->v_start = kernstart;
if ( (p = strstr(guestinfo, "VIRT_BASE=")) != NULL )
dsi->v_start = strtoul(p+10, &p, 0);
if ( (p = strstr(guestinfo, "BSD_SYMTAB")) != NULL )
dsi->load_symtab = 1;
dsi->v_kernstart = kernstart;
dsi->v_kernend = kernend;
dsi->v_kernentry = ehdr->e_entry;
dsi->v_end = dsi->v_kernend;
loadelfsymtab(image, 0, 0, NULL, dsi);
return 0;
}
static int
loadelfimage(
const char *image, unsigned long elfsize, int xch, uint32_t dom,
unsigned long *parray, struct domain_setup_info *dsi)
{
Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
Elf_Phdr *phdr;
int h;
char *va;
unsigned long pa, done, chunksz;
for ( h = 0; h < ehdr->e_phnum; h++ )
{
phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
if ( !is_loadable_phdr(phdr) )
continue;
for ( done = 0; done < phdr->p_filesz; done += chunksz )
{
pa = (phdr->p_paddr + done) - dsi->v_start;
va = xc_map_foreign_range(
xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
chunksz = phdr->p_filesz - done;
if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
memcpy(va + (pa & (PAGE_SIZE-1)),
image + phdr->p_offset + done, chunksz);
munmap(va, PAGE_SIZE);
}
for ( ; done < phdr->p_memsz; done += chunksz )
{
pa = (phdr->p_paddr + done) - dsi->v_start;
va = xc_map_foreign_range(
xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
chunksz = phdr->p_memsz - done;
if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
memset(va + (pa & (PAGE_SIZE-1)), 0, chunksz);
munmap(va, PAGE_SIZE);
}
}
loadelfsymtab(image, xch, dom, parray, dsi);
return 0;
}
#define ELFROUND (ELFSIZE / 8)
static int
loadelfsymtab(
const char *image, int xch, uint32_t dom, unsigned long *parray,
struct domain_setup_info *dsi)
{
Elf_Ehdr *ehdr = (Elf_Ehdr *)image, *sym_ehdr;
Elf_Shdr *shdr;
unsigned long maxva, symva;
char *p;
int h, i;
if ( !dsi->load_symtab )
return 0;
p = malloc(sizeof(int) + sizeof(Elf_Ehdr) +
ehdr->e_shnum * sizeof(Elf_Shdr));
if (p == NULL)
return 0;
maxva = (dsi->v_kernend + ELFROUND - 1) & ~(ELFROUND - 1);
symva = maxva;
maxva += sizeof(int);
dsi->symtab_addr = maxva;
dsi->symtab_len = 0;
maxva += sizeof(Elf_Ehdr) + ehdr->e_shnum * sizeof(Elf_Shdr);
maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
shdr = (Elf_Shdr *)(p + sizeof(int) + sizeof(Elf_Ehdr));
memcpy(shdr, image + ehdr->e_shoff, ehdr->e_shnum * sizeof(Elf_Shdr));
for ( h = 0; h < ehdr->e_shnum; h++ )
{
if ( shdr[h].sh_type == SHT_STRTAB )
{
/* Look for a strtab @i linked to symtab @h. */
for ( i = 0; i < ehdr->e_shnum; i++ )
if ( (shdr[i].sh_type == SHT_SYMTAB) &&
(shdr[i].sh_link == h) )
break;
/* Skip symtab @h if we found no corresponding strtab @i. */
if ( i == ehdr->e_shnum )
{
shdr[h].sh_offset = 0;
continue;
}
}
if ( (shdr[h].sh_type == SHT_STRTAB) ||
(shdr[h].sh_type == SHT_SYMTAB) )
{
if ( parray != NULL )
xc_map_memcpy(maxva, image + shdr[h].sh_offset,
shdr[h].sh_size,
xch, dom, parray, dsi->v_start);
/* Mangled to be based on ELF header location. */
shdr[h].sh_offset = maxva - dsi->symtab_addr;
dsi->symtab_len += shdr[h].sh_size;
maxva += shdr[h].sh_size;
maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
}
shdr[h].sh_name = 0; /* Name is NULL. */
}
if ( dsi->symtab_len == 0 )
{
dsi->symtab_addr = 0;
goto out;
}
if ( parray != NULL )
{
*(int *)p = maxva - dsi->symtab_addr;
sym_ehdr = (Elf_Ehdr *)(p + sizeof(int));
memcpy(sym_ehdr, ehdr, sizeof(Elf_Ehdr));
sym_ehdr->e_phoff = 0;
sym_ehdr->e_shoff = sizeof(Elf_Ehdr);
sym_ehdr->e_phentsize = 0;
sym_ehdr->e_phnum = 0;
sym_ehdr->e_shstrndx = SHN_UNDEF;
/* Copy total length, crafted ELF header and section header table */
xc_map_memcpy(symva, p, sizeof(int) + sizeof(Elf_Ehdr) +
ehdr->e_shnum * sizeof(Elf_Shdr), xch, dom, parray,
dsi->v_start);
}
dsi->symtab_len = maxva - dsi->symtab_addr;
dsi->v_end = round_pgup(maxva);
out:
free(p);
return 0;
}
/*
* Local variables:
* mode: C
* c-set-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/