summaryrefslogtreecommitdiffstats
path: root/qemu/roms/SLOF/lib/libelf/elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/SLOF/lib/libelf/elf.c')
-rw-r--r--qemu/roms/SLOF/lib/libelf/elf.c190
1 files changed, 190 insertions, 0 deletions
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;
+}