diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/SLOF/lib/libelf/elf.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/SLOF/lib/libelf/elf.c')
-rw-r--r-- | roms/SLOF/lib/libelf/elf.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/roms/SLOF/lib/libelf/elf.c b/roms/SLOF/lib/libelf/elf.c new file mode 100644 index 000000000..f6a052ef3 --- /dev/null +++ b/roms/SLOF/lib/libelf/elf.c @@ -0,0 +1,224 @@ +/****************************************************************************** + * 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; + struct ehdr *ehdr = (struct ehdr *) file_addr; + + 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); + 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; + } + + 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; +} + +/** + * Get the file size of the ELF image that has been loaded into a + * buffer larger than the size of the file + * @return The size of the ELF image or < 0 for error + */ +long elf_get_file_size(const void *buffer, const unsigned long buffer_size) +{ + const struct ehdr *ehdr = (const struct ehdr *)buffer; + + if (buffer_size < sizeof(struct ehdr)) + return -1; + + /* check if it is an ELF image at all */ + if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46) + return -1; + + switch (ehdr->ei_class) { + case 1: + return elf_get_file_size32(buffer, buffer_size); + case 2: + return elf_get_file_size64(buffer, buffer_size); + } + + return -1; +} |