244 lines
6.2 KiB
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);
|