diff options
author | Emmanuel Ackaouy <ack@xensource.com> | 2007-01-25 22:16:52 +0000 |
---|---|---|
committer | Emmanuel Ackaouy <ack@xensource.com> | 2007-01-25 22:16:52 +0000 |
commit | eca96c65d99317513bd10804b641b3da7049b56c (patch) | |
tree | 47c08e6587c11eee5361492285461b1cda8f9de9 /xen/common | |
parent | 690dd07a38f366d71b64c1ddc518b6992601e9da (diff) | |
download | xen-eca96c65d99317513bd10804b641b3da7049b56c.tar.gz xen-eca96c65d99317513bd10804b641b3da7049b56c.tar.bz2 xen-eca96c65d99317513bd10804b641b3da7049b56c.zip |
add libelf: an ELF binary parser library.
This patch adds a library with a small collection of helper functions
to parse and load elf binaries. The library handles endianess and
elfsize at runtime.
The patch also shuffles around the include files a bit. Now there is
*one* include file holding all the elf structures
(xen/include/public/elfstructs.h) which is included by everyone who
needs them.
It's dead code with this patch only, putting the code into use happens
in followup patches.
Signed-off-by: Gerd Hoffmann <kraxel@suse.de>
---
tools/libxc/xc_elf.h | 525 -----------------------------------
xen/arch/x86/boot/mkelf32.c | 2
xen/common/Makefile | 2
xen/common/libelf/Makefile | 4
xen/common/libelf/README | 1
xen/common/libelf/libelf-dominfo.c | 420 ++++++++++++++++++++++++++++
xen/common/libelf/libelf-loader.c | 156 ++++++++++
xen/common/libelf/libelf-private.h | 51 +++
xen/common/libelf/libelf-relocate.c | 345 +++++++++++++++++++++++
xen/common/libelf/libelf-tools.c | 225 +++++++++++++++
xen/include/public/elfstructs.h | 527 ++++++++++++++++++++++++++++++++++++
xen/include/public/libelf.h | 238 ++++++++++++++++
xen/include/xen/elf.h | 490 ---------------------------------
13 files changed, 1972 insertions(+), 1014 deletions(-)
Diffstat (limited to 'xen/common')
-rw-r--r-- | xen/common/Makefile | 2 | ||||
-rw-r--r-- | xen/common/libelf/Makefile | 4 | ||||
-rw-r--r-- | xen/common/libelf/README | 1 | ||||
-rw-r--r-- | xen/common/libelf/libelf-dominfo.c | 420 | ||||
-rw-r--r-- | xen/common/libelf/libelf-loader.c | 156 | ||||
-rw-r--r-- | xen/common/libelf/libelf-private.h | 51 | ||||
-rw-r--r-- | xen/common/libelf/libelf-relocate.c | 345 | ||||
-rw-r--r-- | xen/common/libelf/libelf-tools.c | 225 |
8 files changed, 1204 insertions, 0 deletions
diff --git a/xen/common/Makefile b/xen/common/Makefile index 0c9b9b2333..7e3b03daaf 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -37,6 +37,8 @@ obj-$(CONFIG_XENCOMM) += xencomm.o subdir-$(CONFIG_COMPAT) += compat +subdir-y += libelf + # Object file contains changeset and compiler information. version.o: $(BASEDIR)/include/xen/compile.h diff --git a/xen/common/libelf/Makefile b/xen/common/libelf/Makefile new file mode 100644 index 0000000000..a586e1ae12 --- /dev/null +++ b/xen/common/libelf/Makefile @@ -0,0 +1,4 @@ +obj-y += libelf-tools.o +obj-y += libelf-loader.o +obj-y += libelf-dominfo.o +#obj-y += libelf-relocate.o diff --git a/xen/common/libelf/README b/xen/common/libelf/README new file mode 100644 index 0000000000..c54a6933e1 --- /dev/null +++ b/xen/common/libelf/README @@ -0,0 +1 @@ +Take care, this code is used by both xen and tools ... diff --git a/xen/common/libelf/libelf-dominfo.c b/xen/common/libelf/libelf-dominfo.c new file mode 100644 index 0000000000..c19339ab01 --- /dev/null +++ b/xen/common/libelf/libelf-dominfo.c @@ -0,0 +1,420 @@ +/* + * parse xen-specific informations out of elf kernel binaries. + */ + +#include "libelf-private.h" + +/* ------------------------------------------------------------------------ */ +/* xen features */ + +const char *elf_xen_feature_names[] = { + [XENFEAT_writable_page_tables] = "writable_page_tables", + [XENFEAT_writable_descriptor_tables] = "writable_descriptor_tables", + [XENFEAT_auto_translated_physmap] = "auto_translated_physmap", + [XENFEAT_supervisor_mode_kernel] = "supervisor_mode_kernel", + [XENFEAT_pae_pgdir_above_4gb] = "pae_pgdir_above_4gb" +}; +const int elf_xen_features = + sizeof(elf_xen_feature_names) / sizeof(elf_xen_feature_names[0]); + +int elf_xen_parse_features(const char *features, + uint32_t *supported, + uint32_t *required) +{ + char feature[64]; + int pos, len, i; + + if (NULL == features) + return 0; + for (pos = 0; features[pos] != '\0'; pos += len) + { + memset(feature, 0, sizeof(feature)); + for (len = 0;; len++) + { + if (len >= sizeof(feature)-1) + break; + if (features[pos + len] == '\0') + break; + if (features[pos + len] == '|') + { + len++; + break; + } + feature[len] = features[pos + len]; + } + + for (i = 0; i < elf_xen_features; i++) + { + if (!elf_xen_feature_names[i]) + continue; + if (NULL != required && feature[0] == '!') + { + /* required */ + if (0 == strcmp(feature + 1, elf_xen_feature_names[i])) + { + elf_xen_feature_set(i, supported); + elf_xen_feature_set(i, required); + break; + } + } + else + { + /* supported */ + if (0 == strcmp(feature, elf_xen_feature_names[i])) + { + elf_xen_feature_set(i, supported); + break; + } + } + } + if (i == elf_xen_features) + return -1; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* xen elf notes */ + +int elf_xen_parse_note(struct elf_binary *elf, + struct elf_dom_parms *parms, + const elf_note *note) +{ +/* *INDENT-OFF* */ + static const struct { + char *name; + int str; + } note_desc[] = { + [XEN_ELFNOTE_ENTRY] = { "ENTRY", 0}, + [XEN_ELFNOTE_HYPERCALL_PAGE] = { "HYPERCALL_PAGE", 0}, + [XEN_ELFNOTE_VIRT_BASE] = { "VIRT_BASE", 0}, + [XEN_ELFNOTE_PADDR_OFFSET] = { "PADDR_OFFSET", 0}, + [XEN_ELFNOTE_HV_START_LOW] = { "HV_START_LOW", 0}, + [XEN_ELFNOTE_XEN_VERSION] = { "XEN_VERSION", 1}, + [XEN_ELFNOTE_GUEST_OS] = { "GUEST_OS", 1}, + [XEN_ELFNOTE_GUEST_VERSION] = { "GUEST_VERSION", 1}, + [XEN_ELFNOTE_LOADER] = { "LOADER", 1}, + [XEN_ELFNOTE_PAE_MODE] = { "PAE_MODE", 1}, + [XEN_ELFNOTE_FEATURES] = { "FEATURES", 1}, + [XEN_ELFNOTE_BSD_SYMTAB] = { "BSD_SYMTAB", 1}, + }; +/* *INDENT-ON* */ + + const char *str = NULL; + uint64_t val = 0; + int type = elf_uval(elf, note, type); + + if ((type >= sizeof(note_desc) / sizeof(note_desc[0])) || + (NULL == note_desc[type].name)) + { + elf_err(elf, "%s: unknown xen elf note (0x%x)\n", + __FUNCTION__, type); + return -1; + } + + if (note_desc[type].str) + { + str = elf_note_desc(elf, note); + elf_msg(elf, "%s: %s = \"%s\"\n", __FUNCTION__, + note_desc[type].name, str); + } + else + { + val = elf_note_numeric(elf, note); + elf_msg(elf, "%s: %s = 0x%" PRIx64 "\n", __FUNCTION__, + note_desc[type].name, val); + } + + switch (type) + { + case XEN_ELFNOTE_LOADER: + strncpy(parms->loader, str, sizeof(parms->loader)); + break; + case XEN_ELFNOTE_GUEST_OS: + strncpy(parms->guest_os, str, sizeof(parms->guest_os)); + break; + case XEN_ELFNOTE_GUEST_VERSION: + strncpy(parms->guest_ver, str, sizeof(parms->guest_ver)); + break; + case XEN_ELFNOTE_XEN_VERSION: + strncpy(parms->xen_ver, str, sizeof(parms->xen_ver)); + break; + case XEN_ELFNOTE_PAE_MODE: + if (0 == strcmp(str, "yes")) + parms->pae = 2 /* extended_cr3 */; + if (strstr(str, "bimodal")) + parms->pae = 3 /* bimodal */; + break; + case XEN_ELFNOTE_BSD_SYMTAB: + if (0 == strcmp(str, "yes")) + parms->bsd_symtab = 1; + break; + + case XEN_ELFNOTE_VIRT_BASE: + parms->virt_base = val; + break; + case XEN_ELFNOTE_ENTRY: + parms->virt_entry = val; + break; + case XEN_ELFNOTE_PADDR_OFFSET: + parms->elf_paddr_offset = val; + break; + case XEN_ELFNOTE_HYPERCALL_PAGE: + parms->virt_hypercall = val; + break; + case XEN_ELFNOTE_HV_START_LOW: + parms->virt_hv_start_low = val; + break; + + case XEN_ELFNOTE_FEATURES: + if (0 != elf_xen_parse_features(str, parms->f_supported, + parms->f_required)) + return -1; + break; + + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* __xen_guest section */ + +int elf_xen_parse_guest_info(struct elf_binary *elf, + struct elf_dom_parms *parms) +{ + const char *h; + char name[32], value[128]; + int len; + + h = parms->guest_info; + while (*h) + { + memset(name, 0, sizeof(name)); + memset(value, 0, sizeof(value)); + for (len = 0;; len++, h++) { + if (len >= sizeof(name)-1) + break; + if (*h == '\0') + break; + if (*h == ',') + { + h++; + break; + } + if (*h == '=') + { + h++; + for (len = 0;; len++, h++) { + if (len >= sizeof(value)-1) + break; + if (*h == '\0') + break; + if (*h == ',') + { + h++; + break; + } + value[len] = *h; + } + break; + } + name[len] = *h; + } + elf_msg(elf, "%s: %s=\"%s\"\n", __FUNCTION__, name, value); + + /* strings */ + if (0 == strcmp(name, "LOADER")) + strncpy(parms->loader, value, sizeof(parms->loader)); + if (0 == strcmp(name, "GUEST_OS")) + strncpy(parms->guest_os, value, sizeof(parms->guest_os)); + if (0 == strcmp(name, "GUEST_VER")) + strncpy(parms->guest_ver, value, sizeof(parms->guest_ver)); + if (0 == strcmp(name, "XEN_VER")) + strncpy(parms->xen_ver, value, sizeof(parms->xen_ver)); + if (0 == strcmp(name, "PAE")) + { + if (0 == strcmp(value, "yes[extended-cr3]")) + parms->pae = 2 /* extended_cr3 */; + else if (0 == strncmp(value, "yes", 3)) + parms->pae = 1 /* yes */; + } + if (0 == strcmp(name, "BSD_SYMTAB")) + parms->bsd_symtab = 1; + + /* longs */ + if (0 == strcmp(name, "VIRT_BASE")) + parms->virt_base = strtoull(value, NULL, 0); + if (0 == strcmp(name, "VIRT_ENTRY")) + parms->virt_entry = strtoull(value, NULL, 0); + if (0 == strcmp(name, "ELF_PADDR_OFFSET")) + parms->elf_paddr_offset = strtoull(value, NULL, 0); + if (0 == strcmp(name, "HYPERCALL_PAGE")) + parms->virt_hypercall = (strtoull(value, NULL, 0) << 12) + + parms->virt_base; + + /* other */ + if (0 == strcmp(name, "FEATURES")) + if (0 != elf_xen_parse_features(value, parms->f_supported, + parms->f_required)) + return -1; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* sanity checks */ + +static int elf_xen_note_check(struct elf_binary *elf, + struct elf_dom_parms *parms) +{ + if (NULL == parms->elf_note_start && NULL == parms->guest_info) { + int machine = elf_uval(elf, elf->ehdr, e_machine); + if (EM_386 == machine || EM_X86_64 == machine) { + elf_err(elf, "%s: ERROR: Not a Xen-ELF image: " + "No ELF notes or '__xen_guest' section found.\n", + __FUNCTION__); + return -1; + } + return 0; + } + + /* Check the contents of the Xen notes or guest string. */ + if ( ( 0 == strlen(parms->loader) || strncmp(parms->loader, "generic", 7) ) && + ( 0 == strlen(parms->guest_os) || strncmp(parms->guest_os, "linux", 5) ) ) + { + elf_err(elf, "%s: ERROR: Will only load images built for the generic " + "loader or Linux images", __FUNCTION__); + return -1; + } + + if ( 0 == strlen(parms->xen_ver) || strncmp(parms->xen_ver, "xen-3.0", 7) ) + { + elf_err(elf, "%s: ERROR: Xen will only load images built for Xen v3.0\n", + __FUNCTION__); + return -1; + } + return 0; +} + +static int elf_xen_addr_calc_check(struct elf_binary *elf, + struct elf_dom_parms *parms) +{ + if (UNSET_ADDR != parms->elf_paddr_offset && + UNSET_ADDR == parms->virt_base ) + { + elf_err(elf, "%s: ERROR: ELF_PADDR_OFFSET set, VIRT_BASE unset\n", + __FUNCTION__); + return -1; + } + + /* Initial guess for virt_base is 0 if it is not explicitly defined. */ + if (UNSET_ADDR == parms->virt_base) + { + parms->virt_base = 0; + elf_msg(elf, "%s: VIRT_BASE unset, using 0x%" PRIx64 "\n", + __FUNCTION__, parms->virt_base); + } + + /* + * If we are using the legacy __xen_guest section then elf_pa_off + * defaults to v_start in order to maintain compatibility with + * older hypervisors which set padd in the ELF header to + * virt_base. + * + * If we are using the modern ELF notes interface then the default + * is 0. + */ + if (UNSET_ADDR == parms->elf_paddr_offset) + { + if (parms->elf_note_start) + parms->elf_paddr_offset = 0; + else + parms->elf_paddr_offset = parms->virt_base; + elf_msg(elf, "%s: ELF_PADDR_OFFSET unset, using 0x%" PRIx64 "\n", + __FUNCTION__, parms->elf_paddr_offset); + } + + parms->virt_offset = parms->virt_base - parms->elf_paddr_offset; + parms->virt_kstart = elf->pstart + parms->virt_offset; + parms->virt_kend = elf->pend + parms->virt_offset; + + if (UNSET_ADDR == parms->virt_entry) + parms->virt_entry = elf_uval(elf, elf->ehdr, e_entry); + + elf_msg(elf, "%s: addresses:\n", __FUNCTION__); + elf_msg(elf, " virt_base = 0x%" PRIx64 "\n", parms->virt_base); + elf_msg(elf, " elf_paddr_offset = 0x%" PRIx64 "\n", parms->elf_paddr_offset); + elf_msg(elf, " virt_offset = 0x%" PRIx64 "\n", parms->virt_offset); + elf_msg(elf, " virt_kstart = 0x%" PRIx64 "\n", parms->virt_kstart); + elf_msg(elf, " virt_kend = 0x%" PRIx64 "\n", parms->virt_kend); + elf_msg(elf, " virt_entry = 0x%" PRIx64 "\n", parms->virt_entry); + + if ( (parms->virt_kstart > parms->virt_kend) || + (parms->virt_entry < parms->virt_kstart) || + (parms->virt_entry > parms->virt_kend) || + (parms->virt_base > parms->virt_kstart) ) + { + elf_err(elf, "%s: ERROR: ELF start or entries are out of bounds.\n", + __FUNCTION__); + return -1; + } + + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* glue it all together ... */ + +int elf_xen_parse(struct elf_binary *elf, + struct elf_dom_parms *parms) +{ + const elf_note *note; + const elf_shdr *shdr; + int xen_elfnotes = 0; + int i, count; + + memset(parms, 0, sizeof(*parms)); + parms->virt_base = UNSET_ADDR; + parms->virt_entry = UNSET_ADDR; + parms->virt_hypercall = UNSET_ADDR; + parms->virt_hv_start_low = UNSET_ADDR; + parms->elf_paddr_offset = UNSET_ADDR; + + /* find and parse elf notes */ + count = elf_shdr_count(elf); + for (i = 0; i < count; i++) + { + shdr = elf_shdr_by_index(elf, i); + if (0 == strcmp(elf_section_name(elf, shdr), "__xen_guest")) + parms->guest_info = elf_section_start(elf, shdr); + if (elf_uval(elf, shdr, sh_type) != SHT_NOTE) + continue; + parms->elf_note_start = elf_section_start(elf, shdr); + parms->elf_note_end = elf_section_end(elf, shdr); + for (note = parms->elf_note_start; + (void *)note < parms->elf_note_end; + note = elf_note_next(elf, note)) + { + if (0 != strcmp(elf_note_name(elf, note), "Xen")) + continue; + if (0 != elf_xen_parse_note(elf, parms, note)) + return -1; + xen_elfnotes++; + } + } + + if (!xen_elfnotes && parms->guest_info) + { + parms->elf_note_start = NULL; + parms->elf_note_end = NULL; + elf_msg(elf, "%s: __xen_guest: \"%s\"\n", __FUNCTION__, + parms->guest_info); + elf_xen_parse_guest_info(elf, parms); + } + + if (0 != elf_xen_note_check(elf, parms)) + return -1; + if (0 != elf_xen_addr_calc_check(elf, parms)) + return -1; + return 0; +} diff --git a/xen/common/libelf/libelf-loader.c b/xen/common/libelf/libelf-loader.c new file mode 100644 index 0000000000..381c90b5cc --- /dev/null +++ b/xen/common/libelf/libelf-loader.c @@ -0,0 +1,156 @@ +/* + * parse and load elf binaries + */ + +#include "libelf-private.h" + +/* ------------------------------------------------------------------------ */ + +int elf_init(struct elf_binary *elf, const char *image, size_t size) +{ + const elf_shdr *shdr; + uint64_t i, count, section, offset; + + if (!elf_is_elfbinary(image)) + { + elf_err(elf, "%s: not an ELF binary\n", __FUNCTION__); + return -1; + } + + memset(elf, 0, sizeof(*elf)); + elf->image = image; + elf->size = size; + 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 */ + offset = elf_uval(elf, elf->ehdr, e_phoff) + + elf_uval(elf, elf->ehdr, e_phentsize) * elf_phdr_count(elf); + if (offset > elf->size) + { + elf_err(elf, "%s: phdr overflow (off %" PRIx64 " > size %lx)\n", + __FUNCTION__, offset, (unsigned long)elf->size); + return -1; + } + + /* 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) + { + elf_err(elf, "%s: shdr overflow (off %" PRIx64 " > size %lx)\n", + __FUNCTION__, offset, (unsigned long)elf->size); + return -1; + } + + /* find section string table */ + section = elf_uval(elf, elf->ehdr, e_shstrndx); + shdr = elf_shdr_by_index(elf, section); + if (NULL != shdr) + elf->sec_strtab = elf_section_start(elf, shdr); + + /* find symbol table, symbol string table */ + count = elf_shdr_count(elf); + for (i = 0; i < count; i++) + { + shdr = elf_shdr_by_index(elf, i); + if (elf_uval(elf, shdr, sh_type) != SHT_SYMTAB) + continue; + elf->sym_tab = shdr; + shdr = elf_shdr_by_index(elf, elf_uval(elf, shdr, sh_link)); + if (NULL == shdr) + { + elf->sym_tab = NULL; + continue; + } + elf->sym_strtab = elf_section_start(elf, shdr); + break; + } + return 0; +} + +#ifndef __XEN__ +void elf_set_logfile(struct elf_binary *elf, FILE * log, int verbose) +{ + elf->log = log; + elf->verbose = verbose; +} +#else +void elf_set_verbose(struct elf_binary *elf) +{ + elf->verbose = 1; +} +#endif + +void elf_parse_binary(struct elf_binary *elf) +{ + const elf_phdr *phdr; + uint64_t low = -1; + uint64_t high = 0; + uint64_t i, count, paddr, memsz; + + count = elf_uval(elf, elf->ehdr, e_phnum); + for (i = 0; i < count; i++) + { + phdr = elf_phdr_by_index(elf, i); + if (!elf_phdr_is_loadable(elf, phdr)) + continue; + paddr = elf_uval(elf, phdr, p_paddr); + memsz = elf_uval(elf, phdr, p_memsz); + elf_msg(elf, "%s: phdr: paddr=0x%" PRIx64 + " memsz=0x%" PRIx64 "\n", __FUNCTION__, paddr, memsz); + if (low > paddr) + low = paddr; + if (high < paddr + memsz) + high = paddr + memsz; + } + elf->pstart = low; + elf->pend = high; + elf_msg(elf, "%s: memory: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", + __FUNCTION__, elf->pstart, elf->pend); +} + +void elf_load_binary(struct elf_binary *elf) +{ + const elf_phdr *phdr; + uint64_t i, count, paddr, offset, filesz, memsz; + char *dest; + + count = elf_uval(elf, elf->ehdr, e_phnum); + for (i = 0; i < count; i++) + { + phdr = elf_phdr_by_index(elf, i); + if (!elf_phdr_is_loadable(elf, phdr)) + continue; + paddr = elf_uval(elf, phdr, p_paddr); + offset = elf_uval(elf, phdr, p_offset); + filesz = elf_uval(elf, phdr, p_filesz); + memsz = elf_uval(elf, phdr, p_memsz); + dest = elf_get_ptr(elf, paddr); + memcpy(dest, elf->image + offset, filesz); + memset(dest + filesz, 0, memsz - filesz); + } +} + +void *elf_get_ptr(struct elf_binary *elf, unsigned long addr) +{ + return elf->dest + addr - elf->pstart; +} + +uint64_t elf_lookup_addr(struct elf_binary * elf, const char *symbol) +{ + const elf_sym *sym; + uint64_t value; + + sym = elf_sym_by_name(elf, symbol); + if (NULL == sym) + { + elf_err(elf, "%s: not found: %s\n", __FUNCTION__, symbol); + return -1; + } + value = elf_uval(elf, sym, st_value); + elf_msg(elf, "%s: symbol \"%s\" at 0x%" PRIx64 "\n", __FUNCTION__, + symbol, value); + return value; +} diff --git a/xen/common/libelf/libelf-private.h b/xen/common/libelf/libelf-private.h new file mode 100644 index 0000000000..dfb47a06d4 --- /dev/null +++ b/xen/common/libelf/libelf-private.h @@ -0,0 +1,51 @@ +#ifdef __XEN__ + +#include <xen/string.h> +#include <xen/lib.h> +#include <public/elfnote.h> +#include <public/libelf.h> + +#define elf_msg(elf, fmt, args ... ) \ + if (elf->verbose) printk(fmt, ## args ) +#define elf_err(elf, fmt, args ... ) \ + printk(fmt, ## args ) + +#define strtoull(str, end, base) simple_strtoull(str, end, base) +#define bswap_16(x) \ + ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)) +#define bswap_32(x) \ + ( (((x) & 0xff000000) >> 24) \ + | (((x) & 0x00ff0000) >> 8) \ + | (((x) & 0x0000ff00) << 8) \ + | (((x) & 0x000000ff) << 24)) +#define bswap_64(x) \ + ( (((x) & 0xff00000000000000ull) >> 56) \ + | (((x) & 0x00ff000000000000ull) >> 40) \ + | (((x) & 0x0000ff0000000000ull) >> 24) \ + | (((x) & 0x000000ff00000000ull) >> 8) \ + | (((x) & 0x00000000ff000000ull) << 8) \ + | (((x) & 0x0000000000ff0000ull) << 24) \ + | (((x) & 0x000000000000ff00ull) << 40) \ + | (((x) & 0x00000000000000ffull) << 56)) + +#else /* !__XEN__ */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <byteswap.h> +#include <xen/elfnote.h> +#include <xen/libelf.h> + +#include "xenctrl.h" +#include "xc_private.h" + +#define elf_msg(elf, fmt, args ... ) \ + if (elf->log && elf->verbose) fprintf(elf->log, fmt , ## args ) +#define elf_err(elf, fmt, args ... ) do { \ + if (elf->log) \ + fprintf(elf->log, fmt , ## args ); \ + xc_set_error(XC_INVALID_KERNEL, fmt , ## args ); \ + } while (0) + +#endif diff --git a/xen/common/libelf/libelf-relocate.c b/xen/common/libelf/libelf-relocate.c new file mode 100644 index 0000000000..7356a94016 --- /dev/null +++ b/xen/common/libelf/libelf-relocate.c @@ -0,0 +1,345 @@ +/* + * ELF relocation code (not used by xen kernel right now). + */ + +#include "libelf-private.h" + +/* ------------------------------------------------------------------------ */ + +static const char *rel_names_i386[] = { + "R_386_NONE", + "R_386_32", + "R_386_PC32", + "R_386_GOT32", + "R_386_PLT32", + "R_386_COPY", + "R_386_GLOB_DAT", + "R_386_JMP_SLOT", + "R_386_RELATIVE", + "R_386_GOTOFF", + "R_386_GOTPC", + "R_386_32PLT", + "R_386_TLS_TPOFF", + "R_386_TLS_IE", + "R_386_TLS_GOTIE", + "R_386_TLS_LE", + "R_386_TLS_GD", + "R_386_TLS_LDM", + "R_386_16", + "R_386_PC16", + "R_386_8", + "R_386_PC8", + "R_386_TLS_GD_32", + "R_386_TLS_GD_PUSH", + "R_386_TLS_GD_CALL", + "R_386_TLS_GD_POP", + "R_386_TLS_LDM_32", + "R_386_TLS_LDM_PUSH", + "R_386_TLS_LDM_CALL", + "R_386_TLS_LDM_POP", + "R_386_TLS_LDO_32", + "R_386_TLS_IE_32", + "R_386_TLS_LE_32", + "R_386_TLS_DTPMOD32", + "R_386_TLS_DTPOFF32", + "R_386_TLS_TPOFF32", +}; + +static int elf_reloc_i386(struct elf_binary *elf, int type, + uint64_t addr, uint64_t value) +{ + void *ptr = elf_get_ptr(elf, addr); + uint32_t *u32; + + switch (type) + { + case 1 /* R_386_32 */ : + u32 = ptr; + *u32 += elf->reloc_offset; + break; + case 2 /* R_386_PC32 */ : + /* nothing */ + break; + default: + return -1; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static const char *rel_names_x86_64[] = { + "R_X86_64_NONE", + "R_X86_64_64", + "R_X86_64_PC32", + "R_X86_64_GOT32", + "R_X86_64_PLT32", + "R_X86_64_COPY", + "R_X86_64_GLOB_DAT", + "R_X86_64_JUMP_SLOT", + "R_X86_64_RELATIVE", + "R_X86_64_GOTPCREL", + "R_X86_64_32", + "R_X86_64_32S", + "R_X86_64_16", + "R_X86_64_PC16", + "R_X86_64_8", + "R_X86_64_PC8", + "R_X86_64_DTPMOD64", + "R_X86_64_DTPOFF64", + "R_X86_64_TPOFF64", + "R_X86_64_TLSGD", + "R_X86_64_TLSLD", + "R_X86_64_DTPOFF32", + "R_X86_64_GOTTPOFF", + "R_X86_64_TPOFF32", +}; + +static int elf_reloc_x86_64(struct elf_binary *elf, int type, + uint64_t addr, uint64_t value) +{ + void *ptr = elf_get_ptr(elf, addr); + uint64_t *u64; + uint32_t *u32; + int32_t *s32; + + switch (type) + { + case 1 /* R_X86_64_64 */ : + u64 = ptr; + value += elf->reloc_offset; + *u64 = value; + break; + case 2 /* R_X86_64_PC32 */ : + u32 = ptr; + *u32 = value - addr; + if (*u32 != (uint32_t) (value - addr)) + { + elf_err(elf, "R_X86_64_PC32 overflow: 0x%" PRIx32 " != 0x%" PRIx32 "\n", + *u32, (uint32_t) (value - addr)); + return -1; + } + break; + case 10 /* R_X86_64_32 */ : + u32 = ptr; + value += elf->reloc_offset; + *u32 = value; + if (*u32 != value) + { + elf_err(elf, "R_X86_64_32 overflow: 0x%" PRIx32 " != 0x%" PRIx64 "\n", + *u32, value); + return -1; + } + break; + case 11 /* R_X86_64_32S */ : + s32 = ptr; + value += elf->reloc_offset; + *s32 = value; + if (*s32 != (int64_t) value) + { + elf_err(elf, "R_X86_64_32S overflow: 0x%" PRIx32 " != 0x%" PRIx64 "\n", + *s32, (int64_t) value); + return -1; + } + break; + default: + return -1; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct relocs { + const char **names; + int count; + int (*func) (struct elf_binary * elf, int type, uint64_t addr, + uint64_t value); +} relocs[] = +/* *INDENT-OFF* */ +{ + [EM_386] = { + .names = rel_names_i386, + .count = sizeof(rel_names_i386) / sizeof(rel_names_i386[0]), + .func = elf_reloc_i386, + }, + [EM_X86_64] = { + .names = rel_names_x86_64, + .count = sizeof(rel_names_x86_64) / sizeof(rel_names_x86_64[0]), + .func = elf_reloc_x86_64, + } +}; +/* *INDENT-ON* */ + +/* ------------------------------------------------------------------------ */ + +static const char *rela_name(int machine, int type) +{ + if (machine > sizeof(relocs) / sizeof(relocs[0])) + return "unknown mach"; + if (!relocs[machine].names) + return "unknown mach"; + if (type > relocs[machine].count) + return "unknown rela"; + return relocs[machine].names[type]; +} + +static int elf_reloc_section(struct elf_binary *elf, + const elf_shdr * rels, + const elf_shdr * sect, const elf_shdr * syms) +{ + const void *ptr, *end; + const elf_shdr *shdr; + const elf_rela *rela; + const elf_rel *rel; + const elf_sym *sym; + uint64_t s_type; + uint64_t r_offset; + uint64_t r_info; + uint64_t r_addend; + int r_type, r_sym; + size_t rsize; + uint64_t shndx, sbase, addr, value; + const char *sname; + int machine; + + machine = elf_uval(elf, elf->ehdr, e_machine); + if (machine >= sizeof(relocs) / sizeof(relocs[0]) || + NULL == relocs[machine].func) + { + elf_err(elf, "%s: can't handle machine %d\n", + __FUNCTION__, machine); + return -1; + } + if (elf_swap(elf)) + { + elf_err(elf, "%s: non-native byte order, relocation not supported\n", + __FUNCTION__); + return -1; + } + + s_type = elf_uval(elf, rels, sh_type); + rsize = (SHT_REL == s_type) ? elf_size(elf, rel) : elf_size(elf, rela); + ptr = elf_section_start(elf, rels); + end = elf_section_end(elf, rels); + + for (; ptr < end; ptr += rsize) + { + switch (s_type) + { + case SHT_REL: + rel = ptr; + r_offset = elf_uval(elf, rel, r_offset); + r_info = elf_uval(elf, rel, r_info); + r_addend = 0; + break; + case SHT_RELA: + rela = ptr; + r_offset = elf_uval(elf, rela, r_offset); + r_info = elf_uval(elf, rela, r_info); + r_addend = elf_uval(elf, rela, r_addend); + break; + default: + /* can't happen */ + return -1; + } + if (elf_64bit(elf)) + { + r_type = ELF64_R_TYPE(r_info); + r_sym = ELF64_R_SYM(r_info); + } + else + { + r_type = ELF32_R_TYPE(r_info); + r_sym = ELF32_R_SYM(r_info); + } + + sym = elf_sym_by_index(elf, r_sym); + shndx = elf_uval(elf, sym, st_shndx); + switch (shndx) + { + case SHN_UNDEF: + sname = "*UNDEF*"; + sbase = 0; + break; + case SHN_COMMON: + elf_err(elf, "%s: invalid section: %" PRId64 "\n", + __FUNCTION__, shndx); + return -1; + case SHN_ABS: + sname = "*ABS*"; + sbase = 0; + break; + default: + shdr = elf_shdr_by_index(elf, shndx); + if (NULL == shdr) + { + elf_err(elf, "%s: invalid section: %" PRId64 "\n", + __FUNCTION__, shndx); + return -1; + } + sname = elf_section_name(elf, shdr); + sbase = elf_uval(elf, shdr, sh_addr); + } + + addr = r_offset; + value = elf_uval(elf, sym, st_value); + value += r_addend; + + if (elf->log && elf->verbose > 1) + { + uint64_t st_name = elf_uval(elf, sym, st_name); + const char *name = st_name ? elf->sym_strtab + st_name : "*NONE*"; + + elf_msg(elf, + "%s: type %s [%d], off 0x%" PRIx64 ", add 0x%" PRIx64 "," + " sym %s [0x%" PRIx64 "], sec %s [0x%" PRIx64 "]" + " -> addr 0x%" PRIx64 " value 0x%" PRIx64 "\n", + __FUNCTION__, rela_name(machine, r_type), r_type, r_offset, + r_addend, name, elf_uval(elf, sym, st_value), sname, sbase, + addr, value); + } + + if (-1 == relocs[machine].func(elf, r_type, addr, value)) + { + elf_err(elf, "%s: unknown/unsupported reloc type %s [%d]\n", + __FUNCTION__, rela_name(machine, r_type), r_type); + return -1; + } + } + return 0; +} + +int elf_reloc(struct elf_binary *elf) +{ + const elf_shdr *rels, *sect, *syms; + uint64_t i, count, type; + + count = elf_shdr_count(elf); + for (i = 0; i < count; i++) + { + rels = elf_shdr_by_index(elf, i); + type = elf_uval(elf, rels, sh_type); + if (type != SHT_REL && type != SHT_RELA) + continue; + + sect = elf_shdr_by_index(elf, elf_uval(elf, rels, sh_info)); + syms = elf_shdr_by_index(elf, elf_uval(elf, rels, sh_link)); + if (NULL == sect || NULL == syms) + continue; + + if (!(elf_uval(elf, sect, sh_flags) & SHF_ALLOC)) + { + elf_msg(elf, "%s: relocations for %s, skipping\n", + __FUNCTION__, elf_section_name(elf, sect)); + continue; + } + + elf_msg(elf, "%s: relocations for %s @ 0x%" PRIx64 "\n", + __FUNCTION__, elf_section_name(elf, sect), + elf_uval(elf, sect, sh_addr)); + if (0 != elf_reloc_section(elf, rels, sect, syms)) + return -1; + } + return 0; +} diff --git a/xen/common/libelf/libelf-tools.c b/xen/common/libelf/libelf-tools.c new file mode 100644 index 0000000000..c6a00c4dc6 --- /dev/null +++ b/xen/common/libelf/libelf-tools.c @@ -0,0 +1,225 @@ +/* + * various helper functions to access elf structures + */ + +#include "libelf-private.h" + +/* ------------------------------------------------------------------------ */ + +uint64_t elf_access_unsigned(struct elf_binary * elf, const void *ptr, + off_t offset, size_t size) +{ + int need_swap = elf_swap(elf); + const uint8_t *u8; + const uint16_t *u16; + const uint32_t *u32; + const uint64_t *u64; + + switch (size) + { + case 1: + u8 = ptr + offset; + return *u8; + case 2: + u16 = ptr + offset; + return need_swap ? bswap_16(*u16) : *u16; + case 4: + u32 = ptr + offset; + return need_swap ? bswap_32(*u32) : *u32; + case 8: + u64 = ptr + offset; + return need_swap ? bswap_64(*u64) : *u64; + default: + return 0; + } +} + +int64_t elf_access_signed(struct elf_binary *elf, const void *ptr, + off_t offset, size_t size) +{ + int need_swap = elf_swap(elf); + const int8_t *s8; + const int16_t *s16; + const int32_t *s32; + const int64_t *s64; + + switch (size) + { + case 1: + s8 = ptr + offset; + return *s8; + case 2: + s16 = ptr + offset; + return need_swap ? bswap_16(*s16) : *s16; + case 4: + s32 = ptr + offset; + return need_swap ? bswap_32(*s32) : *s32; + case 8: + s64 = ptr + offset; + return need_swap ? bswap_64(*s64) : *s64; + default: + return 0; + } +} + +uint64_t elf_round_up(struct elf_binary *elf, uint64_t addr) +{ + int elf_round = (elf_64bit(elf) ? 8 : 4) - 1; + + return (addr + elf_round) & ~elf_round; +} + +/* ------------------------------------------------------------------------ */ + +int elf_shdr_count(struct elf_binary *elf) +{ + return elf_uval(elf, elf->ehdr, e_shnum); +} + +int elf_phdr_count(struct elf_binary *elf) +{ + return elf_uval(elf, elf->ehdr, e_phnum); +} + +const elf_shdr *elf_shdr_by_name(struct elf_binary *elf, const char *name) +{ + uint64_t count = elf_shdr_count(elf); + const elf_shdr *shdr; + const char *sname; + int i; + + for (i = 0; i < count; i++) { + shdr = elf_shdr_by_index(elf, i); + sname = elf_section_name(elf, shdr); + if (sname && 0 == strcmp(sname, name)) + return shdr; + } + return NULL; +} + +const elf_shdr *elf_shdr_by_index(struct elf_binary *elf, int index) +{ + uint64_t count = elf_shdr_count(elf); + const void *ptr = NULL; + + if (index < count) + ptr = elf->image + + elf_uval(elf, elf->ehdr, e_shoff) + + elf_uval(elf, elf->ehdr, e_shentsize) * index; + return ptr; +} + +const elf_phdr *elf_phdr_by_index(struct elf_binary *elf, int index) +{ + uint64_t count = elf_uval(elf, elf->ehdr, e_phnum); + const void *ptr = NULL; + + if (index < count) + ptr = elf->image + + elf_uval(elf, elf->ehdr, e_phoff) + + elf_uval(elf, elf->ehdr, e_phentsize) * index; + return ptr; +} + +const char *elf_section_name(struct elf_binary *elf, const elf_shdr * shdr) +{ + if (NULL == elf->sec_strtab) + return "unknown"; + return elf->sec_strtab + elf_uval(elf, shdr, sh_name); +} + +const void *elf_section_start(struct elf_binary *elf, const elf_shdr * shdr) +{ + return elf->image + elf_uval(elf, shdr, sh_offset); +} + +const void *elf_section_end(struct elf_binary *elf, const elf_shdr * shdr) +{ + return elf->image + + elf_uval(elf, shdr, sh_offset) + elf_uval(elf, shdr, sh_size); +} + +const elf_sym *elf_sym_by_name(struct elf_binary *elf, const char *symbol) +{ + const void *ptr = elf_section_start(elf, elf->sym_tab); + const void *end = elf_section_end(elf, elf->sym_tab); + const elf_sym *sym; + uint64_t info, name; + + for (; ptr < end; ptr += elf_size(elf, sym)) + { + sym = ptr; + info = elf_uval(elf, sym, st_info); + name = elf_uval(elf, sym, st_name); + if (ELF32_ST_BIND(info) != STB_GLOBAL) + continue; + if (strcmp(elf->sym_strtab + name, symbol) != 0) + continue; + return sym; + } + return NULL; +} + +const elf_sym *elf_sym_by_index(struct elf_binary *elf, int index) +{ + const void *ptr = elf_section_start(elf, elf->sym_tab); + const elf_sym *sym; + + sym = ptr + index * elf_size(elf, sym); + return sym; +} + +const char *elf_note_name(struct elf_binary *elf, const elf_note * note) +{ + return (void *)note + elf_size(elf, note); +} + +const void *elf_note_desc(struct elf_binary *elf, const elf_note * note) +{ + int namesz = (elf_uval(elf, note, namesz) + 3) & ~3; + + return (void *)note + elf_size(elf, note) + namesz; +} + +uint64_t elf_note_numeric(struct elf_binary *elf, const elf_note * note) +{ + const void *desc = elf_note_desc(elf, note); + int descsz = elf_uval(elf, note, descsz); + + switch (descsz) + { + case 1: + case 2: + case 4: + case 8: + return elf_access_unsigned(elf, desc, 0, descsz); + default: + return 0; + } +} +const elf_note *elf_note_next(struct elf_binary *elf, const elf_note * note) +{ + int namesz = (elf_uval(elf, note, namesz) + 3) & ~3; + int descsz = (elf_uval(elf, note, descsz) + 3) & ~3; + + return (void *)note + elf_size(elf, note) + namesz + descsz; +} + +/* ------------------------------------------------------------------------ */ + +int elf_is_elfbinary(const void *image) +{ + const Elf32_Ehdr *ehdr = image; + + if (IS_ELF(*ehdr)) + return 1; + return 0; +} + +int elf_phdr_is_loadable(struct elf_binary *elf, const elf_phdr * phdr) +{ + uint64_t p_type = elf_uval(elf, phdr, p_type); + uint64_t p_flags = elf_uval(elf, phdr, p_flags); + + return ((p_type == PT_LOAD) && (p_flags & (PF_W | PF_X)) != 0); +} |