blob: 5204bc36f99b1099dcf03fa65de8aa2f9a7852eb [file] [log] [blame]
/******************************************************************************
* 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;
}