blob: 3f5f374162909771010a02884f7858dd4a63d0e9 [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
*****************************************************************************/
/*
* 32-bit ELF loader
*/
#include <string.h>
#include <libelf.h>
#include <byteorder.h>
struct ehdr32 {
uint32_t ei_ident;
uint8_t ei_class;
uint8_t ei_data;
uint8_t ei_version;
uint8_t ei_pad[9];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry;
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct phdr32 {
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
static struct phdr32*
get_phdr32(void *file_addr)
{
return (struct phdr32 *) (((unsigned char *)file_addr)
+ ((struct ehdr32 *)file_addr)->e_phoff);
}
static void
load_segment(void *file_addr, struct phdr32 *phdr, signed long offset,
int (*pre_load)(void*, long),
void (*post_load)(void*, long))
{
unsigned long src = phdr->p_offset + (unsigned long) file_addr;
unsigned long destaddr;
destaddr = (unsigned long)phdr->p_paddr;
destaddr = destaddr + offset;
/* check if we're allowed to copy */
if (pre_load != NULL) {
if (pre_load((void*)destaddr, phdr->p_memsz) != 0)
return;
}
/* copy into storage */
memmove((void *)destaddr, (void *)src, phdr->p_filesz);
/* clear bss */
memset((void *)(destaddr + phdr->p_filesz), 0,
phdr->p_memsz - phdr->p_filesz);
if (phdr->p_memsz && post_load) {
post_load((void*)destaddr, phdr->p_memsz);
}
}
unsigned int
elf_load_segments32(void *file_addr, signed long offset,
int (*pre_load)(void*, long),
void (*post_load)(void*, long))
{
struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
/* Calculate program header address */
struct phdr32 *phdr = get_phdr32(file_addr);
int i;
signed int virt2phys = 0; /* Offset between virtual and physical */
/* loop e_phnum times */
for (i = 0; i <= ehdr->e_phnum; i++) {
/* PT_LOAD ? */
if (phdr->p_type == 1) {
if (!virt2phys) {
virt2phys = phdr->p_paddr - phdr->p_vaddr;
}
/* copy segment */
load_segment(file_addr, phdr, offset, pre_load,
post_load);
}
/* step to next header */
phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
}
/* Entry point is always a virtual address, so translate it
* to physical before returning it */
return ehdr->e_entry + virt2phys;
}
/**
* Return the base address for loading (i.e. the address of the first PT_LOAD
* segment)
* @param file_addr pointer to the ELF file in memory
* @return the base address
*/
long
elf_get_base_addr32(void *file_addr)
{
struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
struct phdr32 *phdr = get_phdr32(file_addr);
int i;
/* loop e_phnum times */
for (i = 0; i <= ehdr->e_phnum; i++) {
/* PT_LOAD ? */
if (phdr->p_type == 1) {
return phdr->p_paddr;
}
/* step to next header */
phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
}
return 0;
}
void
elf_byteswap_header32(void *file_addr)
{
struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
struct phdr32 *phdr;
int i;
bswap_16p(&ehdr->e_type);
bswap_16p(&ehdr->e_machine);
bswap_32p(&ehdr->e_version);
bswap_32p(&ehdr->e_entry);
bswap_32p(&ehdr->e_phoff);
bswap_32p(&ehdr->e_shoff);
bswap_32p(&ehdr->e_flags);
bswap_16p(&ehdr->e_ehsize);
bswap_16p(&ehdr->e_phentsize);
bswap_16p(&ehdr->e_phnum);
bswap_16p(&ehdr->e_shentsize);
bswap_16p(&ehdr->e_shnum);
bswap_16p(&ehdr->e_shstrndx);
phdr = get_phdr32(file_addr);
/* loop e_phnum times */
for (i = 0; i <= ehdr->e_phnum; i++) {
bswap_32p(&phdr->p_type);
bswap_32p(&phdr->p_offset);
bswap_32p(&phdr->p_vaddr);
bswap_32p(&phdr->p_paddr);
bswap_32p(&phdr->p_filesz);
bswap_32p(&phdr->p_memsz);
bswap_32p(&phdr->p_flags);
bswap_32p(&phdr->p_align);
/* step to next header */
phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
}
}