224 lines
6 KiB
C
224 lines
6 KiB
C
/******************************************************************************
|
|
* 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 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;
|
|
}
|