diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/SLOF/lib/libelf | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/SLOF/lib/libelf')
-rw-r--r-- | qemu/roms/SLOF/lib/libelf/Makefile | 47 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libelf/elf.c | 190 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libelf/elf32.c | 193 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libelf/elf64.c | 473 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libelf/elf_claim.c | 28 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libelf/libelf.code | 43 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libelf/libelf.in | 18 |
7 files changed, 992 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/lib/libelf/Makefile b/qemu/roms/SLOF/lib/libelf/Makefile new file mode 100644 index 000000000..34a8f20b6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/Makefile @@ -0,0 +1,47 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2011 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) +LDFLAGS= -nostdlib + +TARGET = ../libelf.a + +all: $(TARGET) + +SRCS = elf.c elf32.c elf64.c elf_claim.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/qemu/roms/SLOF/lib/libelf/elf.c b/qemu/roms/SLOF/lib/libelf/elf.c new file mode 100644 index 000000000..db2d2abc9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf.c @@ -0,0 +1,190 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * ELF loader + */ + +#include <string.h> +#include <cache.h> +#include <libelf.h> +#include <byteorder.h> + +/** + * elf_check_file tests if the file at file_addr is + * a correct endian, ELF PPC executable + * @param file_addr pointer to the start of the ELF file + * @return the class (1 for 32 bit, 2 for 64 bit) + * -1 if it is not an ELF file + * -2 if it has the wrong endianness + * -3 if it is not an ELF executable + * -4 if it is not for PPC + */ +static int +elf_check_file(unsigned long *file_addr) +{ + struct ehdr *ehdr = (struct ehdr *) file_addr; + uint8_t native_endian; + + /* check if it is an ELF image at all */ + if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46) + return -1; + +#ifdef __BIG_ENDIAN__ + native_endian = ELFDATA2MSB; +#else + native_endian = ELFDATA2LSB; +#endif + + if (native_endian != ehdr->ei_data) { + switch (ehdr->ei_class) { + case 1: + elf_byteswap_header32(file_addr); + break; + case 2: + elf_byteswap_header64(file_addr); + break; + } + } + + /* check if it is an ELF executable ... and also + * allow DYN files, since this is specified by ePAPR */ + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + return -3; + + /* check if it is a PPC ELF executable */ + if (ehdr->e_machine != 0x14 && ehdr->e_machine != 0x15) + return -4; + + return ehdr->ei_class; +} + +/** + * load_elf_file tries to load the ELF file specified in file_addr + * + * it first checks if the file is a PPC ELF executable and then loads + * the segments depending if it is a 64bit or 32 bit ELF file + * + * @param file_addr pointer to the start of the elf file + * @param entry pointer where the ELF loader will store + * the entry point + * @param pre_load handler that is called before copying a segment + * @param post_load handler that is called after copying a segment + * @return 1 for a 32 bit file + * 2 for a 64 bit BE file + * 3 for a 64 bit LE ABIv1 file + * 4 for a 64 bit LE ABIv2 file + * 5 for a 32 bit LE ABIv1 file + * anything else means an error during load + */ +int +elf_load_file(void *file_addr, unsigned long *entry, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + int type = elf_check_file(file_addr); + struct ehdr *ehdr = (struct ehdr *) file_addr; + + switch (type) { + case 1: + *entry = elf_load_segments32(file_addr, 0, pre_load, post_load); + if (ehdr->ei_data != ELFDATA2MSB) { + type = 5; /* LE32 ABIv1 */ + } + break; + case 2: + *entry = elf_load_segments64(file_addr, 0, pre_load, post_load); + if (ehdr->ei_data != ELFDATA2MSB) { + uint32_t flags = elf_get_eflags_64(file_addr); + if ((flags & 0x3) == 2) + type = 4; /* LE64 ABIv2 */ + else + type = 3; /* LE64 ABIv1 */ + } + break; + } + if (*entry == 0) + type = 0; + + return type; +} + + +/** + * load_elf_file_to_addr loads an ELF file to given address. + * This is useful for 64-bit vmlinux images that use the virtual entry + * point address in their headers, and thereby need a special treatment. + * + * @param file_addr pointer to the start of the elf file + * @param entry pointer where the ELF loader will store + * the entry point + * @param pre_load handler that is called before copying a segment + * @param post_load handler that is called after copying a segment + * @return 1 for a 32 bit file + * 2 for a 64 bit file + * anything else means an error during load + */ +int +elf_load_file_to_addr(void *file_addr, void *addr, unsigned long *entry, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + int type; + long offset; + + type = elf_check_file(file_addr); + + switch (type) { + case 1: + /* Parse 32-bit image */ + offset = (long)addr - elf_get_base_addr32(file_addr); + *entry = elf_load_segments32(file_addr, offset, pre_load, + post_load) + offset; + // TODO: elf_relocate32(...) + break; + case 2: + /* Parse 64-bit image */ + offset = (long)addr - elf_get_base_addr64(file_addr); + *entry = elf_load_segments64(file_addr, offset, pre_load, + post_load) + offset; + elf_relocate64(file_addr, offset); + break; + } + + return type; +} + + +/** + * Get the base load address of the ELF image + * @return The base address or -1 for error + */ +long +elf_get_base_addr(void *file_addr) +{ + int type; + + type = elf_check_file(file_addr); + + switch (type) { + case 1: + /* Return 32-bit image base address */ + return elf_get_base_addr32(file_addr); + break; + case 2: + /* Return 64-bit image base address */ + return elf_get_base_addr64(file_addr); + break; + } + + return -1; +} diff --git a/qemu/roms/SLOF/lib/libelf/elf32.c b/qemu/roms/SLOF/lib/libelf/elf32.c new file mode 100644 index 000000000..fea5cf420 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf32.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * 32-bit ELF loader + */ +#include <stdio.h> +#include <string.h> +#include <libelf.h> +#include <byteorder.h> + +struct ehdr32 { + uint32_t ei_ident; + uint8_t ei_class; + uint8_t ei_data; + uint8_t ei_version; + uint8_t ei_pad[9]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint32_t e_entry; + uint32_t e_phoff; + uint32_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +struct phdr32 { + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +}; + + +static struct phdr32* +get_phdr32(void *file_addr) +{ + return (struct phdr32 *) (((unsigned char *)file_addr) + + ((struct ehdr32 *)file_addr)->e_phoff); +} + +static void +load_segment(void *file_addr, struct phdr32 *phdr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + unsigned long src = phdr->p_offset + (unsigned long) file_addr; + unsigned long destaddr; + + destaddr = (unsigned long)phdr->p_paddr; + destaddr = destaddr + offset; + + /* check if we're allowed to copy */ + if (pre_load != NULL) { + if (pre_load((void*)destaddr, phdr->p_memsz) != 0) + return; + } + + /* copy into storage */ + memmove((void *)destaddr, (void *)src, phdr->p_filesz); + + /* clear bss */ + memset((void *)(destaddr + phdr->p_filesz), 0, + phdr->p_memsz - phdr->p_filesz); + + if (phdr->p_memsz && post_load) { + post_load((void*)destaddr, phdr->p_memsz); + } +} + +unsigned int +elf_load_segments32(void *file_addr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + /* Calculate program header address */ + struct phdr32 *phdr = get_phdr32(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == 1) { + if (phdr->p_paddr != phdr->p_vaddr) { + printf("ELF32: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n", + (long)phdr->p_vaddr, (long)phdr->p_paddr); + return 0; + } + + /* copy segment */ + load_segment(file_addr, phdr, offset, pre_load, + post_load); + } + /* step to next header */ + phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + /* Entry point is always a virtual address, so translate it + * to physical before returning it */ + return ehdr->e_entry; +} + +/** + * Return the base address for loading (i.e. the address of the first PT_LOAD + * segment) + * @param file_addr pointer to the ELF file in memory + * @return the base address + */ +long +elf_get_base_addr32(void *file_addr) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + struct phdr32 *phdr = get_phdr32(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == 1) { + return phdr->p_paddr; + } + /* step to next header */ + phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + return 0; +} + +uint32_t elf_get_eflags_32(void *file_addr) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + + return ehdr->e_flags; +} + +void +elf_byteswap_header32(void *file_addr) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + struct phdr32 *phdr; + int i; + + bswap_16p(&ehdr->e_type); + bswap_16p(&ehdr->e_machine); + bswap_32p(&ehdr->e_version); + bswap_32p(&ehdr->e_entry); + bswap_32p(&ehdr->e_phoff); + bswap_32p(&ehdr->e_shoff); + bswap_32p(&ehdr->e_flags); + bswap_16p(&ehdr->e_ehsize); + bswap_16p(&ehdr->e_phentsize); + bswap_16p(&ehdr->e_phnum); + bswap_16p(&ehdr->e_shentsize); + bswap_16p(&ehdr->e_shnum); + bswap_16p(&ehdr->e_shstrndx); + + phdr = get_phdr32(file_addr); + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + bswap_32p(&phdr->p_type); + bswap_32p(&phdr->p_offset); + bswap_32p(&phdr->p_vaddr); + bswap_32p(&phdr->p_paddr); + bswap_32p(&phdr->p_filesz); + bswap_32p(&phdr->p_memsz); + bswap_32p(&phdr->p_flags); + bswap_32p(&phdr->p_align); + + /* step to next header */ + phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } +} diff --git a/qemu/roms/SLOF/lib/libelf/elf64.c b/qemu/roms/SLOF/lib/libelf/elf64.c new file mode 100644 index 000000000..37e9c10a9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf64.c @@ -0,0 +1,473 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * 64-bit ELF loader for PowerPC. + * See the "64-bit PowerPC ELF Application Binary Interface Supplement" and + * the "ELF-64 Object File Format" documentation for details. + */ + +#include <string.h> +#include <stdio.h> +#include <libelf.h> +#include <byteorder.h> + +struct ehdr64 +{ + uint32_t ei_ident; + uint8_t ei_class; + uint8_t ei_data; + uint8_t ei_version; + uint8_t ei_pad[9]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint64_t e_entry; + uint64_t e_phoff; + uint64_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +struct phdr64 +{ + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; +}; + +struct shdr64 +{ + uint32_t sh_name; /* Section name */ + uint32_t sh_type; /* Section type */ + uint64_t sh_flags; /* Section attributes */ + uint64_t sh_addr; /* Virtual address in memory */ + uint64_t sh_offset; /* Offset in file */ + uint64_t sh_size; /* Size of section */ + uint32_t sh_link; /* Link to other section */ + uint32_t sh_info; /* Miscellaneous information */ + uint64_t sh_addralign; /* Address alignment boundary */ + uint64_t sh_entsize; /* Size of entries, if section has table */ +}; + +struct rela /* RelA relocation table entry */ +{ + uint64_t r_offset; /* Address of reference */ + uint64_t r_info; /* Symbol index and type of relocation */ + int64_t r_addend; /* Constant part of expression */ +}; + +struct sym64 +{ + uint32_t st_name; /* Symbol name */ + uint8_t st_info; /* Type and Binding attributes */ + uint8_t st_other; /* Reserved */ + uint16_t st_shndx; /* Section table index */ + uint64_t st_value; /* Symbol value */ + uint64_t st_size; /* Size of object (e.g., common) */ +}; + + +/* For relocations */ +#define ELF_R_SYM(i) ((i)>>32) +#define ELF_R_TYPE(i) ((uint32_t)(i) & 0xFFFFFFFF) +#define ELF_R_INFO(s,t) ((((uint64_t) (s)) << 32) + (t)) + +/* + * Relocation types for PowerPC64. + */ +#define R_PPC64_NONE 0 +#define R_PPC64_ADDR32 1 +#define R_PPC64_ADDR24 2 +#define R_PPC64_ADDR16 3 +#define R_PPC64_ADDR16_LO 4 +#define R_PPC64_ADDR16_HI 5 +#define R_PPC64_ADDR16_HA 6 +#define R_PPC64_ADDR14 7 +#define R_PPC64_ADDR14_BRTAKEN 8 +#define R_PPC64_ADDR14_BRNTAKEN 9 +#define R_PPC64_REL24 10 +#define R_PPC64_REL14 11 +#define R_PPC64_REL14_BRTAKEN 12 +#define R_PPC64_REL14_BRNTAKEN 13 +#define R_PPC64_GOT16 14 +#define R_PPC64_GOT16_LO 15 +#define R_PPC64_GOT16_HI 16 +#define R_PPC64_GOT16_HA 17 +#define R_PPC64_COPY 19 +#define R_PPC64_GLOB_DAT 20 +#define R_PPC64_JMP_SLOT 21 +#define R_PPC64_RELATIVE 22 +#define R_PPC64_UADDR32 24 +#define R_PPC64_UADDR16 25 +#define R_PPC64_REL32 26 +#define R_PPC64_PLT32 27 +#define R_PPC64_PLTREL32 28 +#define R_PPC64_PLT16_LO 29 +#define R_PPC64_PLT16_HI 30 +#define R_PPC64_PLT16_HA 31 +#define R_PPC64_SECTOFF 33 +#define R_PPC64_SECTOFF_LO 34 +#define R_PPC64_SECTOFF_HI 35 +#define R_PPC64_SECTOFF_HA 36 +#define R_PPC64_ADDR30 37 +#define R_PPC64_ADDR64 38 +#define R_PPC64_ADDR16_HIGHER 39 +#define R_PPC64_ADDR16_HIGHERA 40 +#define R_PPC64_ADDR16_HIGHEST 41 +#define R_PPC64_ADDR16_HIGHESTA 42 +#define R_PPC64_UADDR64 43 +#define R_PPC64_REL64 44 +#define R_PPC64_PLT64 45 +#define R_PPC64_PLTREL64 46 +#define R_PPC64_TOC16 47 +#define R_PPC64_TOC16_LO 48 +#define R_PPC64_TOC16_HI 49 +#define R_PPC64_TOC16_HA 50 +#define R_PPC64_TOC 51 +#define R_PPC64_PLTGOT16 52 +#define R_PPC64_PLTGOT16_LO 53 +#define R_PPC64_PLTGOT16_HI 54 +#define R_PPC64_PLTGOT16_HA 55 +#define R_PPC64_ADDR16_DS 56 +#define R_PPC64_ADDR16_LO_DS 57 +#define R_PPC64_GOT16_DS 58 +#define R_PPC64_GOT16_LO_DS 59 +#define R_PPC64_PLT16_LO_DS 60 +#define R_PPC64_SECTOFF_DS 61 +#define R_PPC64_SECTOFF_LO_DS 62 +#define R_PPC64_TOC16_DS 63 +#define R_PPC64_TOC16_LO_DS 64 +#define R_PPC64_PLTGOT16_DS 65 +#define R_PPC64_PLTGOT16_LO_DS 66 +#define R_PPC64_TLS 67 +#define R_PPC64_DTPMOD64 68 +#define R_PPC64_TPREL16 69 +#define R_PPC64_TPREL16_LO 60 +#define R_PPC64_TPREL16_HI 71 +#define R_PPC64_TPREL16_HA 72 +#define R_PPC64_TPREL64 73 +#define R_PPC64_DTPREL16 74 +#define R_PPC64_DTPREL16_LO 75 +#define R_PPC64_DTPREL16_HI 76 +#define R_PPC64_DTPREL16_HA 77 +#define R_PPC64_DTPREL64 78 +#define R_PPC64_GOT_TLSGD16 79 +#define R_PPC64_GOT_TLSGD16_LO 80 +#define R_PPC64_GOT_TLSGD16_HI 81 +#define R_PPC64_GOT_TLSGD16_HA 82 +#define R_PPC64_GOT_TLSLD16 83 +#define R_PPC64_GOT_TLSLD16_LO 84 +#define R_PPC64_GOT_TLSLD16_HI 85 +#define R_PPC64_GOT_TLSLD16_HA 86 +#define R_PPC64_GOT_TPREL16_DS 87 +#define R_PPC64_GOT_TPREL16_LO_ DS 88 +#define R_PPC64_GOT_TPREL16_HI 89 +#define R_PPC64_GOT_TPREL16_HA 90 +#define R_PPC64_GOT_DTPREL16_DS 91 +#define R_PPC64_GOT_DTPREL16_LO_DS 92 +#define R_PPC64_GOT_DTPREL16_HI 93 +#define R_PPC64_GOT_DTPREL16_HA 94 +#define R_PPC64_TPREL16_DS 95 +#define R_PPC64_TPREL16_LO_DS 96 +#define R_PPC64_TPREL16_HIGHER 97 +#define R_PPC64_TPREL16_HIGHERA 98 +#define R_PPC64_TPREL16_HIGHEST 99 +#define R_PPC64_TPREL16_HIGHESTA 100 +#define R_PPC64_DTPREL16_DS 101 +#define R_PPC64_DTPREL16_LO_DS 102 +#define R_PPC64_DTPREL16_HIGHER 103 +#define R_PPC64_DTPREL16_HIGHERA 104 +#define R_PPC64_DTPREL16_HIGHEST 105 +#define R_PPC64_DTPREL16_HIGHESTA 106 + + +static struct phdr64* +get_phdr64(unsigned long *file_addr) +{ + return (struct phdr64 *) (((unsigned char *) file_addr) + + ((struct ehdr64 *)file_addr)->e_phoff); +} + +static void +load_segment64(unsigned long *file_addr, struct phdr64 *phdr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + unsigned long src = phdr->p_offset + (unsigned long) file_addr; + unsigned long destaddr; + + destaddr = phdr->p_paddr + offset; + + /* check if we're allowed to copy */ + if (pre_load != NULL) { + if (pre_load((void*)destaddr, phdr->p_memsz) != 0) + return; + } + + /* copy into storage */ + memmove((void*)destaddr, (void*)src, phdr->p_filesz); + + /* clear bss */ + memset((void*)(destaddr + phdr->p_filesz), 0, + phdr->p_memsz - phdr->p_filesz); + + if (phdr->p_memsz && post_load != NULL) { + post_load((void*)destaddr, phdr->p_memsz); + } +} + +unsigned long +elf_load_segments64(void *file_addr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + /* Calculate program header address */ + struct phdr64 *phdr = get_phdr64(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == PT_LOAD) { + if (phdr->p_paddr != phdr->p_vaddr) { + printf("ELF64: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n", + (long)phdr->p_vaddr, (long)phdr->p_paddr); + return 0; + } + + /* copy segment */ + load_segment64(file_addr, phdr, offset, pre_load, post_load); + } + /* step to next header */ + phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + /* Entry point is always a virtual address, so translate it + * to physical before returning it */ + return ehdr->e_entry; +} + +/** + * Return the base address for loading (i.e. the address of the first PT_LOAD + * segment) + * @param file_addr pointer to the ELF file in memory + * @return the base address + */ +long +elf_get_base_addr64(void *file_addr) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + /* Calculate program header address */ + struct phdr64 *phdr = get_phdr64(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == PT_LOAD) { + /* Return base address */ + return phdr->p_paddr; + } + /* step to next header */ + phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + return 0; +} + + +/** + * Apply one relocation entry. + */ +static void +elf_apply_rela64(void *file_addr, signed long offset, struct rela *relaentry, + struct sym64 *symtabentry) +{ + void *addr; + unsigned long s_a; + unsigned long base_addr; + + base_addr = elf_get_base_addr64(file_addr); + + /* Sanity check */ + if (relaentry->r_offset < base_addr) { + printf("\nELF relocation out of bounds!\n"); + return; + } + + base_addr += offset; + + /* Actual address where the relocation will be applied at. */ + addr = (void*)(relaentry->r_offset + offset); + + /* Symbol value (S) + Addend (A) */ + s_a = symtabentry->st_value + offset + relaentry->r_addend; + + switch (ELF_R_TYPE(relaentry->r_info)) { + case R_PPC64_ADDR32: /* S + A */ + *(uint32_t *)addr = (uint32_t) s_a; + break; + case R_PPC64_ADDR64: /* S + A */ + *(uint64_t *)addr = (uint64_t) s_a; + break; + case R_PPC64_TOC: /* .TOC */ + *(uint64_t *)addr += offset; + break; + case R_PPC64_ADDR16_HIGHEST: /* #highest(S + A) */ + *(uint16_t *)addr = ((s_a >> 48) & 0xffff); + break; + case R_PPC64_ADDR16_HIGHER: /* #higher(S + A) */ + *(uint16_t *)addr = ((s_a >> 32) & 0xffff); + break; + case R_PPC64_ADDR16_HI: /* #hi(S + A) */ + *(uint16_t *)addr = ((s_a >> 16) & 0xffff); + break; + case R_PPC64_ADDR16_LO: /* #lo(S + A) */ + *(uint16_t *)addr = s_a & 0xffff; + break; + case R_PPC64_ADDR16_LO_DS: + *(uint16_t *)addr = (s_a & 0xfffc); + break; + case R_PPC64_ADDR16_HA: /* #ha(S + A) */ + *(uint16_t *)addr = (((s_a >> 16) + ((s_a & 0x8000) ? 1 : 0)) + & 0xffff); + break; + + case R_PPC64_TOC16: /* half16* S + A - .TOC. */ + case R_PPC64_TOC16_LO_DS: + case R_PPC64_TOC16_LO: /* #lo(S + A - .TOC.) */ + case R_PPC64_TOC16_HI: /* #hi(S + A - .TOC.) */ + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_DS: /* (S + A - .TOC) >> 2 */ + case R_PPC64_REL14: + case R_PPC64_REL24: /* (S + A - P) >> 2 */ + case R_PPC64_REL64: /* S + A - P */ + case R_PPC64_GOT16_DS: + case R_PPC64_GOT16_LO_DS: + // printf("\t\tignoring relocation type %i\n", + // ELF_R_TYPE(relaentry->r_info)); + break; + default: + printf("ERROR: Unhandled relocation (A) type %i\n", + ELF_R_TYPE(relaentry->r_info)); + } +} + + +/** + * Step through all relocation entries and apply them one by one. + */ +static void +elf_apply_all_rela64(void *file_addr, signed long offset, struct shdr64 *shdrs, int idx) +{ + struct shdr64 *rela_shdr = &shdrs[idx]; + struct shdr64 *dst_shdr = &shdrs[rela_shdr->sh_info]; + struct shdr64 *sym_shdr = &shdrs[rela_shdr->sh_link]; + struct rela *relaentry; + struct sym64 *symtabentry; + uint32_t symbolidx; + int i; + + /* If the referenced section has not been allocated, then it has + * not been loaded and thus does not need to be relocated. */ + if ((dst_shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC) + return; + + for (i = 0; i < rela_shdr->sh_size; i += rela_shdr->sh_entsize) { + relaentry = (struct rela *)(file_addr + rela_shdr->sh_offset + i); + + symbolidx = ELF_R_SYM(relaentry->r_info); + symtabentry = (struct sym64*)(file_addr + sym_shdr->sh_offset) + symbolidx; + + elf_apply_rela64(file_addr, offset, relaentry, symtabentry); + } +} + + +/** + * Apply ELF relocations + */ +void +elf_relocate64(void *file_addr, signed long offset) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + /* Calculate section header address */ + struct shdr64 *shdrs = (struct shdr64 *) + (((unsigned char *) file_addr) + ehdr->e_shoff); + int i; + + /* loop over all segments */ + for (i = 0; i <= ehdr->e_shnum; i++) { + /* Skip if it is not a relocation segment */ + if (shdrs[i].sh_type == SHT_RELA) { + elf_apply_all_rela64(file_addr, offset, shdrs, i); + } + } +} + +void +elf_byteswap_header64(void *file_addr) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + struct phdr64 *phdr; + int i; + + bswap_16p(&ehdr->e_type); + bswap_16p(&ehdr->e_machine); + bswap_32p(&ehdr->e_version); + bswap_64p(&ehdr->e_entry); + bswap_64p(&ehdr->e_phoff); + bswap_64p(&ehdr->e_shoff); + bswap_32p(&ehdr->e_flags); + bswap_16p(&ehdr->e_ehsize); + bswap_16p(&ehdr->e_phentsize); + bswap_16p(&ehdr->e_phnum); + bswap_16p(&ehdr->e_shentsize); + bswap_16p(&ehdr->e_shnum); + bswap_16p(&ehdr->e_shstrndx); + + phdr = get_phdr64(file_addr); + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + bswap_32p(&phdr->p_type); + bswap_32p(&phdr->p_flags); + bswap_64p(&phdr->p_offset); + bswap_64p(&phdr->p_vaddr); + bswap_64p(&phdr->p_paddr); + bswap_64p(&phdr->p_filesz); + bswap_64p(&phdr->p_memsz); + bswap_64p(&phdr->p_align); + + /* step to next header */ + phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } +} + +uint32_t elf_get_eflags_64(void *file_addr) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + + return ehdr->e_flags; +} diff --git a/qemu/roms/SLOF/lib/libelf/elf_claim.c b/qemu/roms/SLOF/lib/libelf/elf_claim.c new file mode 100644 index 000000000..43540f9b6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf_claim.c @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2009, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <libelf.h> +#include "../../slof/paflof.h" + + +/** + * Call Forth code to try to claim the memory region + */ +int +elf_forth_claim(void *addr, long size) +{ + forth_push((long)addr); + forth_push(size); + forth_eval("elf-claim-segment"); + return forth_pop(); +} diff --git a/qemu/roms/SLOF/lib/libelf/libelf.code b/qemu/roms/SLOF/lib/libelf/libelf.code new file mode 100644 index 000000000..551468bdd --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/libelf.code @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libelf Forth wrapper + */ + +#include <libelf.h> + +// : elf-load-file ( fileaddr -- entry type ) +PRIM(ELF_X2d_LOAD_X2d_FILE) +{ + void *file_addr = TOS.a; + int type; + unsigned long entry; + type = elf_load_file(file_addr, &entry, elf_forth_claim, flush_cache); + TOS.u = entry; + PUSH; TOS.n = type; +} +MIRP + +// : elf-load-file-to-addr ( fileaddr destaddr -- entry type ) +PRIM(ELF_X2d_LOAD_X2d_FILE_X2d_TO_X2d_ADDR) +{ + void *dest_addr = TOS.a; POP; + void *file_addr = TOS.a; + int type; + unsigned long entry; + type = elf_load_file_to_addr(file_addr, dest_addr, &entry, + elf_forth_claim, flush_cache); + TOS.u = entry; + PUSH; TOS.n = type; +} +MIRP diff --git a/qemu/roms/SLOF/lib/libelf/libelf.in b/qemu/roms/SLOF/lib/libelf/libelf.in new file mode 100644 index 000000000..9c5f019ce --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/libelf.in @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libelf bindings for Forth - definitions + */ + +cod(ELF-LOAD-FILE) +cod(ELF-LOAD-FILE-TO-ADDR) |