/****************************************************************************** * 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 #include #include #include /** * 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; }