forked from Imagelibrary/rtems
959 lines
26 KiB
C
959 lines
26 KiB
C
/*
|
|
* 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.com/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_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_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;
|
|
}
|