satip-axe/kernel/lib/libelf/libelf-main.c

244 lines
6.2 KiB
C

/*
* File : libelf-main.c
* Synopsis : Utility library for handling ELF files
* Author : Carl Shaw <carl.shaw@st.com>
* Author : Giuseppe Condorelli <giuseppe.condorelli@st.com>
*
* Copyright (c) 2008 STMicroelectronics Limited.
*
*/
#include <linux/libelf.h>
#include <linux/string.h>
#include <linux/module.h>
#define ELF_CHECK_FLAG(x) ({x ? x : ~SHF_NULL; })
#define ELF_CHECK_TYPE(x) ({x ? x : ~SHT_NULL; })
/* Check elf file identity */
unsigned int ELF_checkIdent(Elf32_Ehdr *hdr)
{
return memcmp(hdr->e_ident, ELFMAG, SELFMAG);
}
EXPORT_SYMBOL(ELF_checkIdent);
static inline int ELF_valid_offset(struct ELFinfo *elfinfo, Elf32_Off off,
Elf32_Off struct_size)
{
return off + struct_size <= elfinfo->size;
}
/* Initialise in-memory ELF file */
struct ELFinfo *ELF_initFromMem(uint8_t *elffile,
uint32_t size, int mmapped)
{
Elf32_Shdr *sec;
struct ELFinfo *elfinfo;
int i;
elfinfo = (struct ELFinfo *)kmalloc(sizeof(struct ELFinfo), GFP_KERNEL);
if (elfinfo == NULL)
return NULL;
elfinfo->mmapped = mmapped;
elfinfo->size = size;
elfinfo->base = (uint8_t *)elffile;
elfinfo->header = (Elf32_Ehdr *)elffile;
/* Check that image is really an ELF file */
if (size < sizeof(Elf32_Ehdr))
goto fail;
if (ELF_checkIdent((Elf32_Ehdr *)elffile))
goto fail;
/* Make sure it is 32 bit, little endian and current version */
if (elffile[EI_CLASS] != ELFCLASS32 ||
elffile[EI_DATA] != ELFDATA2LSB ||
elffile[EI_VERSION] != EV_CURRENT)
goto fail;
/* Make sure we are a data file, relocatable or executable file */
if ((elfinfo->header)->e_type > 3) {
printk(KERN_ERR"Invalid ELF object type\n");
goto fail;
}
/* Check the structure sizes are what we expect */
if ((elfinfo->header->e_ehsize != sizeof(Elf32_Ehdr)) ||
(elfinfo->header->e_phentsize != sizeof(Elf32_Phdr)) ||
(elfinfo->header->e_shentsize != sizeof(Elf32_Shdr)))
goto fail;
/* Get number of sections */
if ((elfinfo->header)->e_shnum != 0) {
/* Normal numbering */
elfinfo->numsections = (elfinfo->header)->e_shnum;
} else {
/* Extended numbering */
elfinfo->numsections = (elfinfo->secbase)->sh_size;
}
/* Get number of program headers */
if ((elfinfo->header)->e_phnum != 0xffff) { /* PN_XNUM */
/* Normal numbering */
elfinfo->numpheaders = (elfinfo->header)->e_phnum;
} else {
/* Extended numbering */
elfinfo->numpheaders = (elfinfo->secbase)->sh_info;
}
/* Validate header offsets and sizes */
if (!ELF_valid_offset(elfinfo, elfinfo->header->e_shoff,
sizeof(Elf32_Shdr) * elfinfo->numsections) ||
!ELF_valid_offset(elfinfo, elfinfo->header->e_phoff,
sizeof(Elf32_Phdr) * elfinfo->numpheaders))
goto fail;
/* Cache commonly-used addresses and values */
elfinfo->secbase = (Elf32_Shdr *)(elfinfo->base +
(elfinfo->header)->e_shoff);
elfinfo->progbase = (Elf32_Phdr *)(elfinfo->base +
(elfinfo->header)->e_phoff);
/* Validate section headers */
for (i = 0; i < elfinfo->numsections; i++) {
Elf32_Shdr *shdr;
shdr = &elfinfo->secbase[i];
if (!ELF_valid_offset(elfinfo, shdr->sh_offset,
shdr->sh_size))
goto fail;
}
/* Validate program headers */
for (i = 0; i < elfinfo->numpheaders; i++) {
Elf32_Phdr *phdr;
phdr = &elfinfo->progbase[i];
if (!ELF_valid_offset(elfinfo, phdr->p_offset,
phdr->p_filesz))
goto fail;
if (phdr->p_filesz > phdr->p_memsz)
goto fail;
}
/* Get address of string table */
if ((elfinfo->header)->e_shstrndx != SHN_HIRESERVE) {
/* Normal numbering */
elfinfo->strsecindex = (elfinfo->header)->e_shstrndx;
} else {
/* Extended numbering */
elfinfo->strsecindex = (elfinfo->secbase)->sh_link;
}
if (elfinfo->strsecindex >= elfinfo->numsections)
goto fail;
sec = &elfinfo->secbase[elfinfo->strsecindex];
elfinfo->strtab = (char *)(elfinfo->base + sec->sh_offset);
elfinfo->strtabsize = sec->sh_size;
return elfinfo;
fail:
kfree(elfinfo);
return NULL;
}
EXPORT_SYMBOL(ELF_initFromMem);
/* Free up memory-based resources */
uint32_t ELF_free(struct ELFinfo *elfinfo)
{
kfree((void *)elfinfo);
return 0;
}
EXPORT_SYMBOL(ELF_free);
Elf32_Shdr *ELF_getSectionByIndex(const struct ELFinfo *elfinfo, uint32_t index)
{
return (Elf32_Shdr *)((uint8_t *)(elfinfo->secbase) +
((elfinfo->header)->e_shentsize * index));
}
EXPORT_SYMBOL(ELF_getSectionByIndex);
/*
* Search for section starting from its name. Also shflag and shtype are given
* to restrict search for those sections matching them.
* No flags check will be performed if SHF_NULL and SHT_NULL will be given.
*/
Elf32_Shdr *ELF_getSectionByNameCheck(const struct ELFinfo *elfinfo,
const char *secname,
uint32_t *index, int shflag, int shtype)
{
uint32_t i;
char *str;
Elf32_Shdr *sec;
if (index)
*index = 0;
for (i = 0; i < elfinfo->numsections; i++) {
if ((elfinfo->secbase[i].sh_flags & ELF_CHECK_FLAG(shflag)) &&
(elfinfo->secbase[i].sh_type & ELF_CHECK_TYPE(shtype))) {
sec = ELF_getSectionByIndex(elfinfo, i);
str = elfinfo->strtab + sec->sh_name;
if (strcmp(secname, str) == 0) {
if (index)
*index = i;
return sec;
}
}
}
return NULL;
}
EXPORT_SYMBOL(ELF_getSectionByNameCheck);
unsigned long ELF_findBaseAddrCheck(Elf32_Ehdr *hdr, Elf32_Shdr *sechdrs,
unsigned long *base, int shflag, int shtype)
{
unsigned int i;
int prev_index = 0;
for (i = 1; i < hdr->e_shnum; i++)
if ((sechdrs[i].sh_flags & ELF_CHECK_FLAG(shflag))
&& (sechdrs[i].sh_type & ELF_CHECK_TYPE(shtype))) {
if (sechdrs[i].sh_addr < *base)
*base = sechdrs[i].sh_addr;
prev_index = i;
}
return prev_index;
}
EXPORT_SYMBOL(ELF_findBaseAddrCheck);
/*
* Check if the given section is present in the elf file. This function
* also returns the index where section was found, if it was.
*/
int ELF_searchSectionType(const struct ELFinfo *elfinfo, const char *name,
int *index)
{
uint32_t i, n;
Elf32_Shdr *sec;
struct typess elftypes[] = {ELF_TYPES};
for (i = 0; i < elfinfo->numsections; i++) {
sec = ELF_getSectionByIndex(elfinfo, i);
for (n = 0; elftypes[n].name != NULL; n++)
if ((strcmp(elftypes[n].name, name) == 0) &&
(elftypes[n].val == sec->sh_type)) {
if (index != NULL)
*index = i;
return 0;
}
}
return 1;
}
EXPORT_SYMBOL(ELF_searchSectionType);