/*
* COPYRIGHT (c) 2012-2014 Chris Johns <chrisj@rtems.org>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
/**
* @file
*
* @ingroup rtems_rtld
*
* @brief RTEMS Run-Time Link Editor
*
* This is the RTL implementation.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <rtems/rtl/rtl.h>
#include "rtl-elf.h"
#include "rtl-error.h"
#include "rtl-trace.h"
#include "rtl-unresolved.h"
/**
* The offsets in the unresolved array.
*/
#define REL_R_OFFSET (0)
#define REL_R_INFO (1)
#define REL_R_ADDEND (2)
/**
* The ELF format signature.
*/
static rtems_rtl_loader_format_t elf_sig =
{
.label = "ELF",
.flags = RTEMS_RTL_FMT_ELF
};
static bool
rtems_rtl_elf_machine_check (Elf_Ehdr* ehdr)
{
/*
* This code is determined by the NetBSD machine headers.
*/
switch (ehdr->e_machine)
{
ELFDEFNNAME (MACHDEP_ID_CASES)
default:
return false;
}
return true;
}
bool
rtems_rtl_elf_find_symbol (rtems_rtl_obj_t* obj,
const Elf_Sym* sym,
const char* symname,
Elf_Word* value)
{
rtems_rtl_obj_sect_t* sect;
if (ELF_ST_TYPE(sym->st_info) == STT_NOTYPE)
{
/*
* Search the object file then the global table for the symbol.
*/
rtems_rtl_obj_sym_t* symbol = rtems_rtl_symbol_obj_find (obj, symname);
if (!symbol)
{
rtems_rtl_set_error (EINVAL, "global symbol not found: %s", symname);
return false;
}
*value = (Elf_Word) symbol->value;
return true;
}
sect = rtems_rtl_obj_find_section_by_index (obj, sym->st_shndx);
if (!sect)
{
rtems_rtl_set_error (EINVAL, "reloc symbol's section not found");
return false;
}
*value = sym->st_value + (Elf_Word) sect->base;
return true;
}
static bool
rtems_rtl_elf_relocator (rtems_rtl_obj_t* obj,
int fd,
rtems_rtl_obj_sect_t* sect,
void* data)
{
rtems_rtl_obj_cache_t* symbols;
rtems_rtl_obj_cache_t* strings;
rtems_rtl_obj_cache_t* relocs;
rtems_rtl_obj_sect_t* targetsect;
rtems_rtl_obj_sect_t* symsect;
rtems_rtl_obj_sect_t* strtab;
bool is_rela;
size_t reloc_size;
int reloc;
/*
* First check if the section the relocations are for exists. If it does not
* exist ignore these relocations. They are most probably debug sections.
*/
targetsect = rtems_rtl_obj_find_section_by_index (obj, sect->info);
if (!targetsect)
return true;
rtems_rtl_obj_caches (&symbols, &strings, &relocs);
if (!symbols || !strings || !relocs)
return false;
symsect = rtems_rtl_obj_find_section (obj, ".symtab");
if (!symsect)
{
rtems_rtl_set_error (EINVAL, "no .symtab section");
return false;
}
strtab = rtems_rtl_obj_find_section (obj, ".strtab");
if (!strtab)
{
rtems_rtl_set_error (EINVAL, "no .strtab section");
return false;
}
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: relocation: %s, syms:%s\n", sect->name, symsect->name);
/*
* Handle the different relocation record types.
*/
is_rela = ((sect->flags & RTEMS_RTL_OBJ_SECT_RELA) ==
RTEMS_RTL_OBJ_SECT_RELA) ? true : false;
reloc_size = is_rela ? sizeof (Elf_Rela) : sizeof (Elf_Rel);
for (reloc = 0; reloc < (sect->size / reloc_size); ++reloc)
{
uint8_t relbuf[reloc_size];
const Elf_Rela* rela = (const Elf_Rela*) relbuf;
const Elf_Rel* rel = (const Elf_Rel*) relbuf;
Elf_Sym sym;
const char* symname = NULL;
off_t off;
Elf_Word type;
Elf_Word symvalue = 0;
bool relocate;
off = obj->ooffset + sect->offset + (reloc * reloc_size);
if (!rtems_rtl_obj_cache_read_byval (relocs, fd, off,
&relbuf[0], reloc_size))
return false;
if (is_rela)
off = (obj->ooffset + symsect->offset +
(ELF_R_SYM (rela->r_info) * sizeof (sym)));
else
off = (obj->ooffset + symsect->offset +
(ELF_R_SYM (rel->r_info) * sizeof (sym)));
if (!rtems_rtl_obj_cache_read_byval (symbols, fd, off,
&sym, sizeof (sym)))
return false;
/*
* Only need the name of the symbol if global.
*/
if (ELF_ST_TYPE (sym.st_info) == STT_NOTYPE)
{
size_t len;
off = obj->ooffset + strtab->offset + sym.st_name;
len = RTEMS_RTL_ELF_STRING_MAX;
if (!rtems_rtl_obj_cache_read (strings, fd, off,
(void**) &symname, &len))
return false;
}
/*
* See if the record references an external symbol. If it does find the
* symbol value. If the symbol cannot be found flag the object file as
* having unresolved externals and store the externals. The load of an
* object after this one may provide the unresolved externals.
*/
if (is_rela)
type = ELF_R_TYPE(rela->r_info);
else
type = ELF_R_TYPE(rel->r_info);
relocate = true;
if (rtems_rtl_elf_rel_resolve_sym (type))
{
if (!rtems_rtl_elf_find_symbol (obj, &sym, symname, &symvalue))
{
uint16_t flags = 0;
rtems_rtl_word_t rel_words[3];
relocate = false;
if (is_rela)
{
flags = 1;
rel_words[REL_R_OFFSET] = rela->r_offset;
rel_words[REL_R_INFO] = rela->r_info;
rel_words[REL_R_ADDEND] = rela->r_addend;
}
else
{
rel_words[REL_R_OFFSET] = rel->r_offset;
rel_words[REL_R_INFO] = rel->r_info;
rel_words[REL_R_ADDEND] = 0;
}
if (!rtems_rtl_unresolved_add (obj,
flags,
symname,
targetsect->section,
rel_words))
return false;
++obj->unresolved;
}
}
if (relocate)
{
if (is_rela)
{
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rela: sym:%s(%-2d)=%08lx type:%-2d off:%08lx addend:%d\n",
symname, (int) ELF_R_SYM (rela->r_info), symvalue,
(int) ELF_R_TYPE (rela->r_info), rela->r_offset, (int) rela->r_addend);
if (!rtems_rtl_elf_relocate_rela (obj, rela, targetsect,
symname, sym.st_info, symvalue))
return false;
}
else
{
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rel: sym:%s(%-2d)=%08lx type:%-2d off:%08lx\n",
symname, (int) ELF_R_SYM (rel->r_info), symvalue,
(int) ELF_R_TYPE (rel->r_info), rel->r_offset);
if (!rtems_rtl_elf_relocate_rel (obj, rel, targetsect,
symname, sym.st_info, symvalue))
return false;
}
}
}
/*
* Set the unresolved externals status if there are unresolved externals.
*/
if (obj->unresolved)
obj->flags |= RTEMS_RTL_OBJ_UNRESOLVED;
return true;
}
bool
rtems_rtl_obj_relocate_unresolved (rtems_rtl_unresolv_reloc_t* reloc,
rtems_rtl_obj_sym_t* sym)
{
rtems_rtl_obj_sect_t* sect;
bool is_rela;
Elf_Word symvalue;
is_rela =reloc->flags & 1;
sect = rtems_rtl_obj_find_section_by_index (reloc->obj, reloc->sect);
if (!sect)
{
rtems_rtl_set_error (ENOEXEC, "unresolved sect not found");
return false;
}
symvalue = (Elf_Word) (intptr_t) sym->value;
if (is_rela)
{
Elf_Rela rela;
rela.r_offset = reloc->rel[REL_R_OFFSET];
rela.r_info = reloc->rel[REL_R_INFO];
rela.r_addend = reloc->rel[REL_R_ADDEND];
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rela: sym:%-2d type:%-2d off:%08lx addend:%d\n",
(int) ELF_R_SYM (rela.r_info), (int) ELF_R_TYPE (rela.r_info),
rela.r_offset, (int) rela.r_addend);
if (!rtems_rtl_elf_relocate_rela (reloc->obj, &rela, sect,
sym->name, sym->data, symvalue))
return false;
}
else
{
Elf_Rel rel;
rel.r_offset = reloc->rel[REL_R_OFFSET];
rel.r_info = reloc->rel[REL_R_INFO];
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rel: sym:%-2d type:%-2d off:%08lx\n",
(int) ELF_R_SYM (rel.r_info), (int) ELF_R_TYPE (rel.r_info),
rel.r_offset);
if (!rtems_rtl_elf_relocate_rel (reloc->obj, &rel, sect,
sym->name, sym->data, symvalue))
return false;
}
if (reloc->obj->unresolved)
{
--reloc->obj->unresolved;
if (!reloc->obj->unresolved)
reloc->obj->flags &= ~RTEMS_RTL_OBJ_UNRESOLVED;
}
return true;
}
static bool
rtems_rtl_elf_symbols (rtems_rtl_obj_t* obj,
int fd,
rtems_rtl_obj_sect_t* sect,
void* data)
{
rtems_rtl_obj_cache_t* symbols;
rtems_rtl_obj_cache_t* strings;
rtems_rtl_obj_sect_t* strtab;
int locals;
int local_string_space;
rtems_rtl_obj_sym_t* lsym;
char* lstring;
int globals;
int global_string_space;
rtems_rtl_obj_sym_t* gsym;
char* gstring;
int sym;
strtab = rtems_rtl_obj_find_section (obj, ".strtab");
if (!strtab)
{
rtems_rtl_set_error (EINVAL, "no .strtab section");
return false;
}
rtems_rtl_obj_caches (&symbols, &strings, NULL);
if (!symbols || !strings)
return false;
/*
* Find the number of globals and the amount of string space
* needed. Also check for duplicate symbols.
*/
globals = 0;
global_string_space = 0;
locals = 0;
local_string_space = 0;
for (sym = 0; sym < (sect->size / sizeof (Elf_Sym)); ++sym)
{
Elf_Sym symbol;
off_t off;
const char* name;
size_t len;
off = obj->ooffset + sect->offset + (sym * sizeof (symbol));
if (!rtems_rtl_obj_cache_read_byval (symbols, fd, off,
&symbol, sizeof (symbol)))
return false;
off = obj->ooffset + strtab->offset + symbol.st_name;
len = RTEMS_RTL_ELF_STRING_MAX;
if (!rtems_rtl_obj_cache_read (strings, fd, off, (void**) &name, &len))
return false;
/*
* Only keep the functions and global or weak symbols so place them in a
* separate table to local symbols. Local symbols are not needed after the
* object file has been loaded. Undefined symbols are NOTYPE so for locals
* we need to make sure there is a valid seciton.
*/
if ((symbol.st_shndx != 0) &&
((ELF_ST_TYPE (symbol.st_info) == STT_OBJECT) ||
(ELF_ST_TYPE (symbol.st_info) == STT_FUNC) ||
(ELF_ST_TYPE (symbol.st_info) == STT_NOTYPE)))
{
rtems_rtl_obj_sect_t* symsect;
symsect = rtems_rtl_obj_find_section_by_index (obj, symbol.st_shndx);
if (symsect)
{
if ((ELF_ST_BIND (symbol.st_info) == STB_GLOBAL) ||
(ELF_ST_BIND (symbol.st_info) == STB_WEAK))
{
/*
* If there is a globally exported symbol already present and this
* symbol is not weak raise an error. If the symbol is weak and
* present globally ignore this symbol and use the global one and if
* it is not present take this symbol global or weak. We accept the
* first weak symbol we find and make it globally exported.
*/
if (rtems_rtl_symbol_global_find (name) &&
(ELF_ST_BIND (symbol.st_info) != STB_WEAK))
{
rtems_rtl_set_error (ENOMEM, "duplicate global symbol: %s", name);
return false;
}
else
{
++globals;
global_string_space += strlen (name) + 1;
}
}
else if (ELF_ST_BIND (symbol.st_info) == STB_LOCAL)
{
++locals;
local_string_space += strlen (name) + 1;
}
}
}
}
if (locals)
{
obj->local_size = locals * sizeof (rtems_rtl_obj_sym_t) + local_string_space;
obj->local_table = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL,
obj->local_size, true);
if (!obj->local_table)
{
obj->local_size = 0;
rtems_rtl_set_error (ENOMEM, "no memory for obj local syms");
return false;
}
obj->local_syms = locals;
}
if (globals)
{
obj->global_size = globals * sizeof (rtems_rtl_obj_sym_t) + global_string_space;
obj->global_table = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_SYMBOL,
obj->global_size, true);
if (!obj->global_table)
{
if (locals)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->local_table);
obj->local_size = 0;
obj->local_syms = 0;
}
obj->global_size = 0;
rtems_rtl_set_error (ENOMEM, "no memory for obj global syms");
return false;
}
obj->global_syms = globals;
}
lsym = obj->local_table;
lstring =
(((char*) obj->local_table) + (locals * sizeof (rtems_rtl_obj_sym_t)));
gsym = obj->global_table;
gstring =
(((char*) obj->global_table) + (globals * sizeof (rtems_rtl_obj_sym_t)));
for (sym = 0; sym < (sect->size / sizeof (Elf_Sym)); ++sym)
{
Elf_Sym symbol;
off_t off;
const char* name;
size_t len;
off = obj->ooffset + sect->offset + (sym * sizeof (symbol));
if (!rtems_rtl_obj_cache_read_byval (symbols, fd, off,
&symbol, sizeof (symbol)))
{
if (locals)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->local_table);
obj->local_table = NULL;
obj->local_size = 0;
obj->local_syms = 0;
}
if (globals)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->global_table);
obj->global_table = NULL;
obj->global_syms = 0;
obj->global_size = 0;
}
return false;
}
off = obj->ooffset + strtab->offset + symbol.st_name;
len = RTEMS_RTL_ELF_STRING_MAX;
if (!rtems_rtl_obj_cache_read (strings, fd, off, (void**) &name, &len))
return false;
if ((symbol.st_shndx != 0) &&
((ELF_ST_TYPE (symbol.st_info) == STT_OBJECT) ||
(ELF_ST_TYPE (symbol.st_info) == STT_FUNC) ||
(ELF_ST_TYPE (symbol.st_info) == STT_NOTYPE)) &&
((ELF_ST_BIND (symbol.st_info) == STB_GLOBAL) ||
(ELF_ST_BIND (symbol.st_info) == STB_WEAK) ||
(ELF_ST_BIND (symbol.st_info) == STB_LOCAL)))
{
rtems_rtl_obj_sect_t* symsect;
rtems_rtl_obj_sym_t* osym;
char* string;
symsect = rtems_rtl_obj_find_section_by_index (obj, symbol.st_shndx);
if (symsect)
{
if ((ELF_ST_BIND (symbol.st_info) == STB_GLOBAL) ||
(ELF_ST_BIND (symbol.st_info) == STB_WEAK))
{
osym = gsym;
string = gstring;
gstring += strlen (name) + 1;
++gsym;
}
else
{
osym = lsym;
string = lstring;
lstring += strlen (name) + 1;
++lsym;
}
rtems_chain_set_off_chain (&osym->node);
memcpy (string, name, strlen (name) + 1);
osym->name = string;
osym->value = symbol.st_value + (uint8_t*) symsect->base;
osym->data = symbol.st_info;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: sym:add:%-2d name:%-2d:%-20s bind:%-2d " \
"type:%-2d val:%8p sect:%d size:%d\n",
sym, (int) symbol.st_name, osym->name,
(int) ELF_ST_BIND (symbol.st_info),
(int) ELF_ST_TYPE (symbol.st_info),
osym->value, symbol.st_shndx,
(int) symbol.st_size);
}
}
}
if (globals)
rtems_rtl_symbol_obj_add (obj);
return true;
}
static bool
rtems_rtl_elf_loader (rtems_rtl_obj_t* obj,
int fd,
rtems_rtl_obj_sect_t* sect,
void* data)
{
uint8_t* base_offset;
size_t len;
if (lseek (fd, obj->ooffset + sect->offset, SEEK_SET) < 0)
{
rtems_rtl_set_error (errno, "section load seek failed");
return false;
}
base_offset = sect->base;
len = sect->size;
while (len)
{
ssize_t r = read (fd, base_offset, len);
if (r <= 0)
{
rtems_rtl_set_error (errno, "section load read failed");
return false;
}
base_offset += r;
len -= r;
}
return true;
}
static bool
rtems_rtl_elf_parse_sections (rtems_rtl_obj_t* obj, int fd, Elf_Ehdr* ehdr)
{
rtems_rtl_obj_cache_t* sects;
rtems_rtl_obj_cache_t* strings;
int section;
off_t sectstroff;
off_t off;
Elf_Shdr shdr;
rtems_rtl_obj_caches (§s, &strings, NULL);
if (!sects || !strings)
return false;
/*
* Get the offset to the section string table.
*/
off = obj->ooffset + ehdr->e_shoff + (ehdr->e_shstrndx * ehdr->e_shentsize);
if (!rtems_rtl_obj_cache_read_byval (sects, fd, off, &shdr, sizeof (shdr)))
return false;
if (shdr.sh_type != SHT_STRTAB)
{
rtems_rtl_set_error (EINVAL, "bad .sectstr section type");
return false;
}
sectstroff = obj->ooffset + shdr.sh_offset;
for (section = 0; section < ehdr->e_shnum; ++section)
{
uint32_t flags;
/*
* Make sure section is at least 32bits to avoid 16-bit overflow errors.
*/
off = obj->ooffset + ehdr->e_shoff + (((uint32_t) section) * ehdr->e_shentsize);
if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
printf ("rtl: section header: %2d: offset=%d\n", section, (int) off);
if (!rtems_rtl_obj_cache_read_byval (sects, fd, off, &shdr, sizeof (shdr)))
return false;
flags = 0;
switch (shdr.sh_type)
{
case SHT_NULL:
/*
* Ignore.
*/
break;
case SHT_PROGBITS:
/*
* There are 2 program bits sections. One is the program text and the
* other is the program data. The program text is flagged
* alloc/executable and the program data is flagged alloc/writable.
*/
if ((shdr.sh_flags & SHF_ALLOC) == SHF_ALLOC)
{
if ((shdr.sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR)
flags = RTEMS_RTL_OBJ_SECT_TEXT | RTEMS_RTL_OBJ_SECT_LOAD;
else if ((shdr.sh_flags & SHF_WRITE) == SHF_WRITE)
flags = RTEMS_RTL_OBJ_SECT_DATA | RTEMS_RTL_OBJ_SECT_LOAD;
else
flags = RTEMS_RTL_OBJ_SECT_CONST | RTEMS_RTL_OBJ_SECT_LOAD;
}
break;
case SHT_NOBITS:
/*
* There is 1 NOBIT section which is the .bss section. There is nothing
* but a definition as the .bss is just a clear region of memory.
*/
if ((shdr.sh_flags & (SHF_ALLOC | SHF_WRITE)) == (SHF_ALLOC | SHF_WRITE))
flags = RTEMS_RTL_OBJ_SECT_BSS | RTEMS_RTL_OBJ_SECT_ZERO;
break;
case SHT_RELA:
flags = RTEMS_RTL_OBJ_SECT_RELA;
break;
case SHT_REL:
/*
* The sh_link holds the section index for the symbol table. The sh_info
* holds the section index the relocations apply to.
*/
flags = RTEMS_RTL_OBJ_SECT_REL;
break;
case SHT_SYMTAB:
flags = RTEMS_RTL_OBJ_SECT_SYM;
break;
case SHT_STRTAB:
flags = RTEMS_RTL_OBJ_SECT_STR;
break;
default:
if (rtems_rtl_trace (RTEMS_RTL_TRACE_WARNING))
printf ("rtl: unsupported section: %2d: type=%02d flags=%02x\n",
section, (int) shdr.sh_type, (int) shdr.sh_flags);
break;
}
if (flags != 0)
{
char* name;
size_t len;
len = RTEMS_RTL_ELF_STRING_MAX;
if (!rtems_rtl_obj_cache_read (strings, fd,
sectstroff + shdr.sh_name,
(void**) &name, &len))
return false;
if (strcmp (".ctors", name) == 0)
flags |= RTEMS_RTL_OBJ_SECT_CTOR;
if (strcmp (".dtors", name) == 0)
flags |= RTEMS_RTL_OBJ_SECT_DTOR;
if (!rtems_rtl_obj_add_section (obj, section, name,
shdr.sh_size, shdr.sh_offset,
shdr.sh_addralign, shdr.sh_link,
shdr.sh_info, flags))
return false;
}
}
return true;
}
bool
rtems_rtl_elf_file_check (rtems_rtl_obj_t* obj, int fd)
{
rtems_rtl_obj_cache_t* header;
Elf_Ehdr ehdr;
rtems_rtl_obj_caches (&header, NULL, NULL);
if (!rtems_rtl_obj_cache_read_byval (header, fd, obj->ooffset,
&ehdr, sizeof (ehdr)))
return false;
/*
* Check we have a valid ELF file.
*/
if ((memcmp (ELFMAG, ehdr.e_ident, SELFMAG) != 0)
|| ehdr.e_ident[EI_CLASS] != ELFCLASS)
{
return false;
}
if ((ehdr.e_ident[EI_VERSION] != EV_CURRENT)
|| (ehdr.e_version != EV_CURRENT)
|| (ehdr.e_ident[EI_DATA] != ELFDEFNNAME (MACHDEP_ENDIANNESS)))
{
return false;
}
return true;
}
bool rtems_rtl_elf_load_details (rtems_rtl_obj_t* obj)
{
rtems_chain_control* sections = NULL;
rtems_chain_node* node = NULL;
size_t mask = 0;
struct link_map* l = NULL;
int sec_num = 0;
int i = 0;
/* caculate the size of sections' name. */
for (mask = RTEMS_RTL_OBJ_SECT_TEXT;
mask <= RTEMS_RTL_OBJ_SECT_BSS;
mask <<= 1)
{
sections = &obj->sections;
node = rtems_chain_first (sections);
while (!rtems_chain_is_tail (sections, node))
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
if ((sect->size != 0) && ((sect->flags & mask) != 0))
{
++sec_num;
}
node = rtems_chain_next (node);
}
}
obj->obj_num = 1;
obj->detail = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT,
sizeof(struct link_map) +
sec_num * sizeof (section_detail), true);
if (!obj->detail)
{
rtems_rtl_set_error (ENOMEM, "no memory for obj global syms");
return false;
}
l = (struct link_map*) obj->detail;
l->name = obj->oname;
l->sec_num = sec_num;
l->sec_detail = (section_detail*) (l + 1);
l->rpathlen = 0;
l->rpath = NULL;
l->l_next = NULL;
l->l_prev = NULL;
l->sec_addr[rap_text] = obj->text_base;
l->sec_addr[rap_const] = obj->const_base;
l->sec_addr[rap_data] = obj->data_base;
l->sec_addr[rap_bss] = obj->bss_base;
section_detail* sd = l->sec_detail;
sections = &obj->sections;
node = rtems_chain_first (sections);
for (mask = RTEMS_RTL_OBJ_SECT_TEXT;
mask <= RTEMS_RTL_OBJ_SECT_BSS;
mask <<= 1)
{
sections = &obj->sections;
node = rtems_chain_first (sections);
while (!rtems_chain_is_tail (sections, node))
{
rtems_rtl_obj_sect_t* sect = (rtems_rtl_obj_sect_t*) node;
if ((sect->size != 0) && ((sect->flags & mask) != 0))
{
sd[i].name = sect->name;
sd[i].size = sect->size;
if (mask == RTEMS_RTL_OBJ_SECT_TEXT)
{
sd[i].rap_id = rap_text;
sd[i].offset = sect->base - obj->text_base;
}
if (mask == RTEMS_RTL_OBJ_SECT_CONST)
{
sd[i].rap_id = rap_const;
sd[i].offset = sect->base - obj->const_base;
}
if (mask == RTEMS_RTL_OBJ_SECT_DATA)
{
sd[i].rap_id = rap_data;
sd[i].offset = sect->base - obj->data_base;
}
if (mask == RTEMS_RTL_OBJ_SECT_BSS)
{
sd[i].rap_id = rap_bss;
sd[i].offset = sect->base - obj->bss_base;
}
++i;
}
node = rtems_chain_next (node);
}
}
return true;
}
bool
rtems_rtl_elf_file_load (rtems_rtl_obj_t* obj, int fd)
{
rtems_rtl_obj_cache_t* header;
Elf_Ehdr ehdr;
rtems_rtl_obj_caches (&header, NULL, NULL);
if (!rtems_rtl_obj_cache_read_byval (header, fd, obj->ooffset,
&ehdr, sizeof (ehdr)))
return false;
/*
* Check we have a valid ELF file.
*/
if ((memcmp (ELFMAG, ehdr.e_ident, SELFMAG) != 0)
|| ehdr.e_ident[EI_CLASS] != ELFCLASS)
{
rtems_rtl_set_error (EINVAL, "invalid ELF file format");
return false;
}
if ((ehdr.e_ident[EI_VERSION] != EV_CURRENT)
|| (ehdr.e_version != EV_CURRENT)
|| (ehdr.e_ident[EI_DATA] != ELFDEFNNAME (MACHDEP_ENDIANNESS)))
{
rtems_rtl_set_error (EINVAL, "unsupported ELF file version");
return false;
}
if (!rtems_rtl_elf_machine_check (&ehdr))
{
rtems_rtl_set_error (EINVAL, "unsupported machine type");
return false;
}
if (ehdr.e_type == ET_DYN)
{
rtems_rtl_set_error (EINVAL, "unsupported ELF file type");
return false;
}
if (ehdr.e_phentsize != 0)
{
rtems_rtl_set_error (EINVAL, "ELF file contains program headers");
return false;
}
if (ehdr.e_shentsize != sizeof (Elf_Shdr))
{
rtems_rtl_set_error (EINVAL, "invalid ELF section header size");
return false;
}
/*
* Parse the section information first so we have the memory map of the object
* file and the memory allocated. Any further allocations we make to complete
* the load will not fragment the memory.
*/
if (!rtems_rtl_elf_parse_sections (obj, fd, &ehdr))
return false;
obj->entry = (void*)(uintptr_t) ehdr.e_entry;
if (!rtems_rtl_obj_load_sections (obj, fd, rtems_rtl_elf_loader, &ehdr))
return false;
if (!rtems_rtl_obj_load_symbols (obj, fd, rtems_rtl_elf_symbols, &ehdr))
return false;
if (!rtems_rtl_obj_relocate (obj, fd, rtems_rtl_elf_relocator, &ehdr))
return false;
rtems_rtl_obj_synchronize_cache (obj);
rtems_rtl_symbol_obj_erase_local (obj);
if (!rtems_rtl_elf_load_details (obj))
{
return false;
}
return true;
}
rtems_rtl_loader_format_t*
rtems_rtl_elf_file_sig (void)
{
return &elf_sig;
}