/*
* COPYRIGHT (c) 2012-2019 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <inttypes.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 <rtems/rtl/rtl-trace.h>
#include "rtl-trampoline.h"
#include "rtl-unwind.h"
#include <rtems/rtl/rtl-unresolved.h>
/**
* The offsets in the reloc words.
*/
#define REL_R_OFFSET (0)
#define REL_R_INFO (1)
#define REL_R_ADDEND (2)
/**
* The ELF format signature.
*/
static rtems_rtl_loader_format elf_sig =
{
.label = "ELF",
.flags = RTEMS_RTL_FMT_ELF
};
static const char*
rtems_rtl_elf_sym_type_label (Elf_Byte st_info)
{
const char* label;
switch (ELF_ST_TYPE (st_info))
{
case STT_NOTYPE:
label = "STT_NOTYPE";
break;
case STT_OBJECT:
label = "STT_OBJECT";
break;
case STT_FUNC:
label = "STT_FUNC";
break;
case STT_SECTION:
label = "STT_SECTION";
break;
case STT_FILE:
label = "STT_FILE";
break;
case STT_COMMON:
label = "STT_COMMON";
break;
case STT_TLS:
label = "STT_TLS";
break;
default:
label = "unknown";
break;
}
return label;
}
static const char*
rtems_rtl_elf_sym_bind_label (Elf_Byte st_info)
{
const char* label;
switch (ELF_ST_BIND (st_info))
{
case STB_LOCAL:
label = "STB_LOCAL";
break;
case STB_GLOBAL:
label = "STB_GLOBAL";
break;
case STB_WEAK:
label = "STB_WEAK";
break;
default:
label = "unknown";
break;
}
return label;
}
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;
}
static const char*
rtems_rtl_elf_separated_section (const char* name)
{
struct {
const char* label;
size_t len;
} prefix[] = {
#define SEPARATED_PREFIX(_p) { _p, sizeof (_p) - 1 }
SEPARATED_PREFIX (".text."),
SEPARATED_PREFIX (".rel.text."),
SEPARATED_PREFIX (".data."),
SEPARATED_PREFIX (".rel.data."),
SEPARATED_PREFIX (".rodata."),
SEPARATED_PREFIX (".rel.rodata.")
};
const size_t prefixes = sizeof (prefix) / sizeof (prefix[0]);
size_t p;
for (p = 0; p < prefixes; ++p)
{
if (strncmp (name, prefix[p].label, prefix[p].len) == 0)
return name + prefix[p].len;
}
return NULL;
}
static bool
rtems_rtl_elf_find_symbol (rtems_rtl_obj* obj,
const Elf_Sym* sym,
const char* symname,
rtems_rtl_obj_sym** symbol,
Elf_Word* value)
{
rtems_rtl_obj_sect* sect;
/*
* If the symbol type is STT_NOTYPE the symbol references a global
* symbol. The gobal symbol table is searched to find it and that value
* returned. If the symbol is local to the object module the section for the
* symbol is located and it's base added to the symbol's value giving an
* absolute location.
*/
if (ELF_ST_TYPE(sym->st_info) == STT_NOTYPE || sym->st_shndx == SHN_COMMON)
{
/*
* Search the object file then the global table for the symbol.
*/
*symbol = rtems_rtl_symbol_obj_find (obj, symname);
if (!*symbol)
return false;
*value = (Elf_Addr) (*symbol)->value;
return true;
}
*symbol = NULL;
sect = rtems_rtl_obj_find_section_by_index (obj, sym->st_shndx);
if (!sect)
return false;
*value = sym->st_value + (Elf_Addr) sect->base;
return true;
}
/**
* Relocation worker routine.
*/
typedef bool (*rtems_rtl_elf_reloc_handler)(rtems_rtl_obj* obj,
bool is_rela,
void* relbuf,
rtems_rtl_obj_sect* targetsect,
rtems_rtl_obj_sym* symbol,
Elf_Sym* sym,
const char* symname,
Elf_Word symvalue,
bool resolved,
void* data);
/**
* Relocation parser data.
*/
typedef struct
{
size_t dependents; /**< The number of dependent object files. */
size_t unresolved; /**< The number of unresolved symbols. */
} rtems_rtl_elf_reloc_data;
static bool
rtems_rtl_elf_reloc_parser (rtems_rtl_obj* obj,
bool is_rela,
void* relbuf,
rtems_rtl_obj_sect* targetsect,
rtems_rtl_obj_sym* symbol,
Elf_Sym* sym,
const char* symname,
Elf_Word symvalue,
bool resolved,
void* data)
{
rtems_rtl_elf_reloc_data* rd = (rtems_rtl_elf_reloc_data*) data;
rtems_rtl_word rel_words[3];
rtems_rtl_elf_rel_status rs;
/*
* Check the reloc record to see if a trampoline is needed.
*/
if (is_rela)
{
const Elf_Rela* rela = (const Elf_Rela*) relbuf;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rela tramp: sym: %c:%s(%d)=%08jx type:%d off:%08jx addend:%d\n",
ELF_ST_BIND (sym->st_info) == STB_GLOBAL ||
ELF_ST_BIND (sym->st_info) == STB_WEAK ? 'G' : 'L',
symname, (int) ELF_R_SYM (rela->r_info),
(uintmax_t) symvalue, (int) ELF_R_TYPE (rela->r_info),
(uintmax_t) rela->r_offset, (int) rela->r_addend);
rs = rtems_rtl_elf_relocate_rela_tramp (obj, rela, targetsect,
symname, sym->st_info, symvalue);
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
{
const Elf_Rel* rel = (const Elf_Rel*) relbuf;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rel tramp: sym: %c:%s(%d)=%08jx type:%d off:%08jx\n",
ELF_ST_BIND (sym->st_info) == STB_GLOBAL ||
ELF_ST_BIND (sym->st_info) == STB_WEAK ? 'G' : 'L',
symname, (int) ELF_R_SYM (rel->r_info),
(uintmax_t) symvalue, (int) ELF_R_TYPE (rel->r_info),
(uintmax_t) rel->r_offset);
rs = rtems_rtl_elf_relocate_rel_tramp (obj, rel, targetsect,
symname, sym->st_info, symvalue);
rel_words[REL_R_OFFSET] = rel->r_offset;
rel_words[REL_R_INFO] = rel->r_info;
rel_words[REL_R_ADDEND] = 0;
}
if (rs == rtems_rtl_elf_rel_failure)
return false;
if (rs == rtems_rtl_elf_rel_tramp_cache || rs == rtems_rtl_elf_rel_tramp_add)
{
uint32_t flags = (is_rela ? 1 : 0) | (resolved ? 0 : 1 << 1) | (sym->st_info << 8);
if (!rtems_rtl_trampoline_add (obj, flags,
targetsect->section, symvalue, rel_words))
return false;
}
/*
* Handle any dependencies if there is a valid symbol.
*/
if (symname != NULL)
{
/*
* Find the symbol's object file. It cannot be NULL so ignore that result
* if returned, it means something is corrupted. We are in an iterator.
*/
rtems_rtl_obj* sobj = rtems_rtl_find_obj_with_symbol (symbol);
if (sobj != NULL)
{
/*
* A dependency is not the base kernel image or itself. Tag the object as
* having been visited so we count it only once.
*/
if (sobj != rtems_rtl_baseimage () && obj != sobj &&
(sobj->flags & RTEMS_RTL_OBJ_RELOC_TAG) == 0)
{
sobj->flags |= RTEMS_RTL_OBJ_RELOC_TAG;
++rd->dependents;
}
}
}
return true;
}
static bool
rtems_rtl_elf_reloc_relocator (rtems_rtl_obj* obj,
bool is_rela,
void* relbuf,
rtems_rtl_obj_sect* targetsect,
rtems_rtl_obj_sym* symbol,
Elf_Sym* sym,
const char* symname,
Elf_Word symvalue,
bool resolved,
void* data)
{
const Elf_Rela* rela = (const Elf_Rela*) relbuf;
const Elf_Rel* rel = (const Elf_Rel*) relbuf;
if (!resolved)
{
uint16_t flags = 0;
rtems_rtl_word rel_words[3];
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;
}
else
{
rtems_rtl_obj* sobj;
rtems_rtl_elf_rel_status rs;
if (is_rela)
{
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rela: sym:%s(%d)=%08jx type:%d off:%08jx addend:%d\n",
symname, (int) ELF_R_SYM (rela->r_info),
(uintmax_t) symvalue, (int) ELF_R_TYPE (rela->r_info),
(uintmax_t) rela->r_offset, (int) rela->r_addend);
rs = rtems_rtl_elf_relocate_rela (obj, rela, targetsect,
symname, sym->st_info, symvalue);
if (rs != rtems_rtl_elf_rel_no_error)
return false;
}
else
{
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rel: sym:%s(%d)=%08jx type:%d off:%08jx\n",
symname, (int) ELF_R_SYM (rel->r_info),
(uintmax_t) symvalue, (int) ELF_R_TYPE (rel->r_info),
(uintmax_t) rel->r_offset);
rs = rtems_rtl_elf_relocate_rel (obj, rel, targetsect,
symname, sym->st_info, symvalue);
if (rs != rtems_rtl_elf_rel_no_error)
return false;
}
sobj = rtems_rtl_find_obj_with_symbol (symbol);
if (rtems_rtl_trace (RTEMS_RTL_TRACE_DEPENDENCY))
printf ("rtl: depend: %s -> %s:%s\n",
obj->oname,
sobj == NULL ? "not-found" : sobj->oname,
symname);
if (sobj != NULL)
{
if (rtems_rtl_obj_add_dependent (obj, sobj))
rtems_rtl_obj_inc_reference (sobj);
}
}
return true;
}
static bool
rtems_rtl_elf_relocate_worker (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* sect,
rtems_rtl_elf_reloc_handler handler,
void* data)
{
rtems_rtl_obj_cache* symbols;
rtems_rtl_obj_cache* strings;
rtems_rtl_obj_cache* relocs;
rtems_rtl_obj_sect* targetsect;
rtems_rtl_obj_sect* symsect;
rtems_rtl_obj_sect* 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;
/*
* The section muct has been loaded. It could be a separate section in an
* archive and not loaded.
*/
if ((targetsect->flags & RTEMS_RTL_OBJ_SECT_LOAD) == 0)
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;
rtems_rtl_obj_sym* symbol = NULL;
Elf_Sym sym;
const char* symname = NULL;
off_t off;
Elf_Word rel_type;
Elf_Word symvalue = 0;
bool resolved;
off = obj->ooffset + sect->offset + (reloc * reloc_size);
if (!rtems_rtl_obj_cache_read_byval (relocs, fd, off,
&relbuf[0], reloc_size))
return false;
/*
* Read the symbol details.
*/
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 or a common symbol.
*/
if (ELF_ST_TYPE (sym.st_info) == STT_OBJECT ||
ELF_ST_TYPE (sym.st_info) == STT_COMMON ||
ELF_ST_TYPE (sym.st_info) == STT_FUNC ||
ELF_ST_TYPE (sym.st_info) == STT_NOTYPE ||
ELF_ST_TYPE (sym.st_info) == STT_TLS ||
sym.st_shndx == SHN_COMMON)
{
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 external. The load of an
* object after this one may provide the unresolved externals.
*/
if (is_rela)
rel_type = ELF_R_TYPE(rela->r_info);
else
rel_type = ELF_R_TYPE(rel->r_info);
resolved = true;
if (rtems_rtl_elf_rel_resolve_sym (rel_type))
resolved = rtems_rtl_elf_find_symbol (obj,
&sym, symname,
&symbol, &symvalue);
if (!handler (obj,
is_rela, relbuf, targetsect,
symbol, &sym, symname, symvalue, resolved,
data))
return false;
}
/*
* Set the unresolved externals status if there are unresolved externals.
*/
if (obj->unresolved)
obj->flags |= RTEMS_RTL_OBJ_UNRESOLVED;
return true;
}
static bool
rtems_rtl_elf_relocs_parser (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* sect,
void* data)
{
bool r = rtems_rtl_elf_relocate_worker (obj, fd, sect,
rtems_rtl_elf_reloc_parser, data);
return r;
}
static bool
rtems_rtl_elf_relocs_locator (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* sect,
void* data)
{
return rtems_rtl_elf_relocate_worker (obj, fd, sect,
rtems_rtl_elf_reloc_relocator, data);
}
bool
rtems_rtl_obj_relocate_unresolved (rtems_rtl_unresolv_reloc* reloc,
rtems_rtl_obj_sym* sym)
{
rtems_rtl_obj_sect* sect;
bool is_rela;
Elf_Word symvalue;
rtems_rtl_obj* sobj;
rtems_rtl_elf_rel_status rs;
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:%d type:%d off:%08jx addend:%d\n",
(int) ELF_R_SYM (rela.r_info), (int) ELF_R_TYPE (rela.r_info),
(uintmax_t) rela.r_offset, (int) rela.r_addend);
rs = rtems_rtl_elf_relocate_rela (reloc->obj, &rela, sect,
sym->name, sym->data, symvalue);
if (rs != rtems_rtl_elf_rel_no_error)
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:%d type:%d off:%08jx\n",
(int) ELF_R_SYM (rel.r_info), (int) ELF_R_TYPE (rel.r_info),
(uintmax_t) rel.r_offset);
rs = rtems_rtl_elf_relocate_rel (reloc->obj, &rel, sect,
sym->name, sym->data, symvalue);
if (rs != rtems_rtl_elf_rel_no_error)
return false;
}
if (reloc->obj->unresolved > 0)
{
--reloc->obj->unresolved;
if (reloc->obj->unresolved == 0)
reloc->obj->flags &= ~RTEMS_RTL_OBJ_UNRESOLVED;
}
sobj = rtems_rtl_find_obj_with_symbol (sym);
if (rtems_rtl_trace (RTEMS_RTL_TRACE_DEPENDENCY))
printf ("rtl: depend: %s -> %s:%s\n",
reloc->obj->oname,
sobj == NULL ? "not-found" : sobj->oname,
sym->name);
if (sobj != NULL)
{
if (rtems_rtl_obj_add_dependent (reloc->obj, sobj))
rtems_rtl_obj_inc_reference (sobj);
}
return true;
}
/**
* Common symbol iterator data.
*/
typedef struct
{
size_t size; /**< The size of the common section */
uint32_t alignment; /**< The alignment of the common section. */
} rtems_rtl_elf_common_data;
static bool
rtems_rtl_elf_common (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* sect,
void* data)
{
rtems_rtl_elf_common_data* common = (rtems_rtl_elf_common_data*) data;
rtems_rtl_obj_cache* symbols;
int sym;
rtems_rtl_obj_caches (&symbols, NULL, NULL);
if (!symbols)
return false;
/*
* Find the number size of the common section by finding all symbols that
* reference the SHN_COMMON section.
*/
for (sym = 0; sym < (sect->size / sizeof (Elf_Sym)); ++sym)
{
Elf_Sym symbol;
off_t off;
off = obj->ooffset + sect->offset + (sym * sizeof (symbol));
if (!rtems_rtl_obj_cache_read_byval (symbols, fd, off,
&symbol, sizeof (symbol)))
return false;
if ((symbol.st_shndx == SHN_COMMON) &&
((ELF_ST_TYPE (symbol.st_info) == STT_OBJECT) ||
(ELF_ST_TYPE (symbol.st_info) == STT_COMMON)))
{
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: com:elf:%-2d bind:%-2d type:%-2d size:%d value:%d name:%d\n",
sym, (int) ELF_ST_BIND (symbol.st_info),
(int) ELF_ST_TYPE (symbol.st_info),
(int) symbol.st_size, (int) symbol.st_value,
(int) symbol.st_name);
/*
* If the size is zero this is the first entry, it defines the common
* section's aligment. The symbol's value is the alignment.
*/
if (common->size == 0)
common->alignment = symbol.st_value;
common->size +=
rtems_rtl_obj_align (common->size, symbol.st_value) + symbol.st_size;
}
}
return true;
}
/**
* Struct to handle trampoline reloc recs in the unresolved table.
*/
typedef struct rtems_rtl_tramp_data
{
bool failure;
rtems_rtl_obj* obj;
size_t count;
size_t total;
} rtems_rtl_tramp_data;
static bool
rtems_rtl_elf_tramp_resolve_reloc (rtems_rtl_unresolv_rec* rec,
void* data)
{
rtems_rtl_tramp_data* td = (rtems_rtl_tramp_data*) data;
if (rec->type == rtems_rtl_trampoline_reloc)
{
const rtems_rtl_tramp_reloc* tramp = &rec->rec.tramp;
++td->total;
if (tramp->obj == td->obj)
{
const rtems_rtl_obj_sect* targetsect;
Elf_Byte st_info;
Elf_Word symvalue;
rtems_rtl_elf_rel_status rs;
bool* failure = (bool*) data;
const bool is_rela = (tramp->flags & 1) == 1;
const bool unresolved = (tramp->flags & (1 << 1)) != 0;
++td->count;
targetsect = rtems_rtl_obj_find_section_by_index (tramp->obj, tramp->sect);
st_info = tramp->flags >> 8;
symvalue = tramp->symvalue;
if (is_rela)
{
Elf_Rela rela = {
.r_offset = tramp->rel[REL_R_OFFSET],
.r_info = tramp->rel[REL_R_INFO],
.r_addend = tramp->rel[REL_R_ADDEND]
};
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rela tramp: check: %c(%d)=%08jx type:%d off:%08jx addend:%d\n",
ELF_ST_BIND (st_info) == STB_GLOBAL ||
ELF_ST_BIND (st_info) == STB_WEAK ? 'G' : 'L',
(int) ELF_R_SYM (rela.r_info),
(uintmax_t) symvalue, (int) ELF_R_TYPE (rela.r_info),
(uintmax_t) rela.r_offset, (int) rela.r_addend);
rs = rtems_rtl_elf_relocate_rela_tramp (tramp->obj, &rela, targetsect,
NULL, st_info, symvalue);
}
else
{
Elf_Rel rel = {
.r_offset = tramp->rel[REL_R_OFFSET],
.r_info = tramp->rel[REL_R_INFO],
};
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: rel tramp: check: %c(%d)=%08jx type:%d off:%08jx\n",
ELF_ST_BIND (st_info) == STB_GLOBAL ||
ELF_ST_BIND (st_info) == STB_WEAK ? 'G' : 'L',
(int) ELF_R_SYM (rel.r_info),
(uintmax_t) symvalue, (int) ELF_R_TYPE (rel.r_info),
(uintmax_t) rel.r_offset);
rs = rtems_rtl_elf_relocate_rel_tramp (tramp->obj, &rel, targetsect,
NULL, st_info, symvalue);
}
if (unresolved || rs == rtems_rtl_elf_rel_tramp_add)
tramp->obj->tramps_size += tramp->obj->tramp_size;
if (rs == rtems_rtl_elf_rel_failure)
{
*failure = true;
return true;
}
}
}
return false;
}
static bool
rtems_rtl_elf_alloc_trampoline (rtems_rtl_obj* obj, size_t unresolved)
{
rtems_rtl_tramp_data td = { 0 };
td.obj = obj;
/*
* See which relocs are out of range and need a trampoline.
*/
rtems_rtl_unresolved_iterate (rtems_rtl_elf_tramp_resolve_reloc, &td);
if (td.failure)
return false;
rtems_rtl_trampoline_remove (obj);
obj->tramp_relocs = obj->tramp_size == 0 ? 0 : obj->tramps_size / obj->tramp_size;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: tramp:elf: tramps: %zu count:%zu total:%zu\n",
obj->tramp_relocs, td.count, td.total);
/*
* Add on enough space to handle the unresolved externals that need to be
* resolved at some point in time. They could all require fixups and
* trampolines.
*/
obj->tramps_size += obj->tramp_size * unresolved;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
printf ("rtl: tramp:elf: slots: %zu (%zu)\n",
obj->tramp_size == 0 ? 0 : obj->tramps_size / obj->tramp_size,
obj->tramps_size);
return rtems_rtl_obj_alloc_trampoline (obj);
}
static bool
rtems_rtl_elf_dependents (rtems_rtl_obj* obj, rtems_rtl_elf_reloc_data* reloc)
{
/*
* If there are dependencies and no unresolved externals allocate and size
* the dependency table to the number of dependent object files. If there are
* unresolved externals the number of dependencies is unknown at this point
* in time so use dynamic allocation to allocate the block size number of
* entries when the entries are added.
*/
if (reloc->dependents > 0 && reloc->unresolved == 0)
{
if (!rtems_rtl_obj_alloc_dependents (obj, reloc->dependents))
return false;
}
return true;
}
static bool
rtems_rtl_elf_symbols_load (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* sect,
void* data)
{
rtems_rtl_obj_cache* symbols;
rtems_rtl_obj_cache* strings;
rtems_rtl_obj_sect* strtab;
int locals;
int local_string_space;
rtems_rtl_obj_sym* lsym;
char* lstring;
int globals;
int global_string_space;
rtems_rtl_obj_sym* gsym;
char* gstring;
size_t common_offset;
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 (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: sym:elf:%-4d name:%-4d: %-20s: bind:%-2d:%-12s " \
"type:%-2d:%-10s sect:%-5d size:%-5d value:%d\n",
sym, (int) symbol.st_name, name,
(int) ELF_ST_BIND (symbol.st_info),
rtems_rtl_elf_sym_bind_label (symbol.st_info),
(int) ELF_ST_TYPE (symbol.st_info),
rtems_rtl_elf_sym_type_label (symbol.st_info),
symbol.st_shndx,
(int) symbol.st_size,
(int) symbol.st_value);
/*
* If a duplicate forget it.
*/
if (rtems_rtl_symbol_global_find (name))
continue;
if ((symbol.st_shndx != 0) &&
((ELF_ST_TYPE (symbol.st_info) == STT_OBJECT) ||
(ELF_ST_TYPE (symbol.st_info) == STT_COMMON) ||
(ELF_ST_TYPE (symbol.st_info) == STT_FUNC) ||
(ELF_ST_TYPE (symbol.st_info) == STT_NOTYPE)))
{
/*
* There needs to be a valid section for the symbol.
*/
rtems_rtl_obj_sect* symsect;
symsect = rtems_rtl_obj_find_section_by_index (obj, symbol.st_shndx);
if (symsect != NULL)
{
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 check if the object file being loaded is
* from an archive. If the base image is built with text sections a
* symbol with it's section will be linked into the base image and not
* another symbol. If not an archive rause 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))
{
if (!rtems_rtl_obj_aname_valid (obj))
{
rtems_rtl_set_error (ENOMEM, "duplicate global symbol: %s", name);
return false;
}
}
else
{
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: sym:elf:%-4d name:%-4d: %-20s: global\n",
sym, (int) symbol.st_name, name);
++globals;
global_string_space += strlen (name) + 1;
}
}
else if (ELF_ST_BIND (symbol.st_info) == STB_LOCAL)
{
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: sym:elf:%-4d name:%-4d: %-20s: local\n",
sym, (int) symbol.st_name, name);
++locals;
local_string_space += strlen (name) + 1;
}
}
}
}
if (locals)
{
obj->local_size = locals * sizeof (rtems_rtl_obj_sym) + 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) + 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)));
gsym = obj->global_table;
gstring =
(((char*) obj->global_table) + (globals * sizeof (rtems_rtl_obj_sym)));
common_offset = 0;
for (sym = 0; sym < (sect->size / sizeof (Elf_Sym)); ++sym)
{
Elf_Sym symbol;
off_t off;
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 (obj->local_syms)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->local_table);
obj->local_table = NULL;
obj->local_size = 0;
obj->local_syms = 0;
}
if (obj->global_syms)
{
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;
}
if ((symbol.st_shndx != 0) &&
((ELF_ST_TYPE (symbol.st_info) == STT_OBJECT) ||
(ELF_ST_TYPE (symbol.st_info) == STT_COMMON) ||
(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* symsect;
/*
* There needs to be a valid section for the symbol.
*/
symsect = rtems_rtl_obj_find_section_by_index (obj, symbol.st_shndx);
if (symsect != NULL)
{
rtems_rtl_obj_sym* osym;
char* string;
Elf_Word value;
const char* name;
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 a duplicate forget it.
*/
if (rtems_rtl_symbol_global_find (name))
continue;
if ((ELF_ST_BIND (symbol.st_info) == STB_GLOBAL) ||
(ELF_ST_BIND (symbol.st_info) == STB_WEAK))
{
size_t slen = strlen (name) + 1;
if ((gstring + slen) > (char*) obj->global_table + obj->global_size)
string = NULL;
else
{
osym = gsym;
string = gstring;
gstring += slen;
++gsym;
}
}
else
{
size_t slen = strlen (name) + 1;
if ((lstring + slen) > (char*) obj->local_table + obj->local_size)
string = NULL;
else
{
osym = lsym;
string = lstring;
lstring += slen;
++lsym;
}
}
/*
* See if the loading has overflowed the allocated tables.
*/
if (string == NULL)
{
if (obj->local_syms)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->local_table);
obj->local_table = NULL;
obj->local_size = 0;
obj->local_syms = 0;
}
if (obj->global_syms)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->global_table);
obj->global_table = NULL;
obj->global_syms = 0;
obj->global_size = 0;
}
rtems_rtl_set_error (ENOMEM, "syms overlow, parsing/loading size mismatch");
return false;
}
/*
* Allocate any common symbols in the common section.
*/
if (symbol.st_shndx == SHN_COMMON)
{
size_t value_off = rtems_rtl_obj_align (common_offset,
symbol.st_value);
common_offset = value_off + symbol.st_size;
value = value_off;
}
else
{
value = symbol.st_value;
}
rtems_chain_set_off_chain (&osym->node);
memcpy (string, name, strlen (name) + 1);
osym->name = string;
osym->value = (uint8_t*) value;
osym->data = symbol.st_shndx;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: sym:add:%-4d name:%-4d: %-20s: bind:%-2d " \
"type:%-2d val:%-8p sect:%-3d 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);
}
}
}
return true;
}
static bool
rtems_rtl_elf_symbols_locate (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* sect,
void* data)
{
int sym;
for (sym = 0; sym < obj->local_syms; ++sym)
{
rtems_rtl_obj_sym* osym = &obj->local_table[sym];
rtems_rtl_obj_sect* symsect;
symsect = rtems_rtl_obj_find_section_by_index (obj, osym->data);
if (symsect)
{
osym->value += (intptr_t) symsect->base;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: sym:locate:local :%-4d name: %-20s val:%-8p sect:%-3d (%s, %p)\n",
sym, osym->name, osym->value, osym->data,
symsect->name, symsect->base);
}
}
for (sym = 0; sym < obj->global_syms; ++sym)
{
rtems_rtl_obj_sym* osym = &obj->global_table[sym];
rtems_rtl_obj_sect* symsect;
symsect = rtems_rtl_obj_find_section_by_index (obj, osym->data);
if (symsect)
{
osym->value += (intptr_t) symsect->base;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_SYMBOL))
printf ("rtl: sym:locate:global:%-4d name: %-20s val:%-8p sect:%-3d (%s, %p)\n",
sym, osym->name, osym->value, osym->data,
symsect->name, symsect->base);
}
}
if (obj->global_size)
rtems_rtl_symbol_obj_add (obj);
return true;
}
static bool
rtems_rtl_elf_arch_alloc (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* sect,
void* data)
{
if (rtems_rtl_obj_sect_is_arch_alloc (sect))
return rtems_rtl_elf_arch_section_alloc (obj, sect);
return true;
}
static bool
rtems_rtl_elf_arch_free (rtems_rtl_obj* obj)
{
int index = -1;
while (true)
{
rtems_rtl_obj_sect* sect;
sect = rtems_rtl_obj_find_section_by_mask (obj,
index,
RTEMS_RTL_OBJ_SECT_ARCH_ALLOC);
if (sect == NULL)
break;
if (!rtems_rtl_elf_arch_section_free (obj, sect))
return false;
index = sect->section;
}
return true;
}
static bool
rtems_rtl_elf_loader (rtems_rtl_obj* obj,
int fd,
rtems_rtl_obj_sect* 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* obj, int fd, Elf_Ehdr* ehdr)
{
rtems_rtl_obj_cache* sects;
rtems_rtl_obj_cache* 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)
{
char* name;
size_t len;
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;
len = RTEMS_RTL_ELF_STRING_MAX;
if (!rtems_rtl_obj_cache_read (strings, fd,
sectstroff + shdr.sh_name,
(void**) &name, &len))
return false;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
printf ("rtl: section: %2d: name=%s type=%d flags=%08x link=%d info=%d\n",
section, name, (int) shdr.sh_type, (unsigned int) shdr.sh_flags,
(int) shdr.sh_link, (int) shdr.sh_info);
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 | RTEMS_RTL_OBJ_SECT_LOAD;
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 | RTEMS_RTL_OBJ_SECT_LOAD;
break;
case SHT_SYMTAB:
flags = RTEMS_RTL_OBJ_SECT_SYM;
break;
case SHT_STRTAB:
flags = RTEMS_RTL_OBJ_SECT_STR;
break;
case SHT_INIT_ARRAY:
/*
* Constructors are text and need to be loaded.
*/
flags = (RTEMS_RTL_OBJ_SECT_CTOR |
RTEMS_RTL_OBJ_SECT_TEXT |
RTEMS_RTL_OBJ_SECT_LOAD);
break;
case SHT_FINI_ARRAY:
/*
* Destructors are text and need to be loaded.
*/
flags = (RTEMS_RTL_OBJ_SECT_DTOR |
RTEMS_RTL_OBJ_SECT_TEXT |
RTEMS_RTL_OBJ_SECT_LOAD);
break;
default:
/*
* See if there are architecture specific flags?
*/
flags = rtems_rtl_elf_section_flags (obj, &shdr);
if (flags == 0)
{
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)
{
/*
* If the object file is part of a library check the section's name. If it
* starts with '.text.*' see if the last part is a global symbol. If a
* global symbol exists we have to assume the symbol in the archive is a
* duplicate can can be ignored.
*/
if (rtems_rtl_obj_aname_valid (obj))
{
const char* symname = rtems_rtl_elf_separated_section (name);
if (symname != NULL && rtems_rtl_symbol_global_find (symname))
flags &= ~RTEMS_RTL_OBJ_SECT_LOAD;
}
/*
* If link ordering this section must appear in the same order in memory
* as the linked-to section relative to the sections it loads with.
*/
if ((shdr.sh_flags & SHF_LINK_ORDER) != 0)
flags |= RTEMS_RTL_OBJ_SECT_LINK;
/*
* Some architexctures have a named PROGBIT section for INIT/FINI.
*/
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_elf_unwind_parse (obj, name, flags))
{
flags &= ~(RTEMS_RTL_OBJ_SECT_TEXT | RTEMS_RTL_OBJ_SECT_CONST);
flags |= RTEMS_RTL_OBJ_SECT_EH;
}
/*
* Architecture specific parsing. Modified or extends the flags.
*/
flags = rtems_rtl_elf_arch_parse_section (obj, section, name, &shdr, flags);
if (flags == 0)
{
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);
rtems_rtl_set_error (ENOMEM, "invalid architecture section: %s", name);
return false;
}
/*
* Add the section.
*/
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;
}
static bool
rtems_rtl_elf_add_common (rtems_rtl_obj* obj, size_t size, uint32_t alignment)
{
if (size > 0)
{
if (!rtems_rtl_obj_add_section (obj, SHN_COMMON, ".common.rtems.rtl",
size, 0, alignment, 0, 0,
RTEMS_RTL_OBJ_SECT_BSS | RTEMS_RTL_OBJ_SECT_ZERO))
return false;
}
return true;
}
bool
rtems_rtl_elf_file_check (rtems_rtl_obj* obj, int fd)
{
rtems_rtl_obj_cache* 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;
}
static bool
rtems_rtl_elf_load_linkmap (rtems_rtl_obj* obj)
{
rtems_chain_control* sections = NULL;
rtems_chain_node* node = NULL;
int sec_num = 0;
section_detail* sd;
int i = 0;
size_t m;
/*
* The section masks to add to the linkmap.
*/
const uint32_t sect_mask[] = {
RTEMS_RTL_OBJ_SECT_TEXT | RTEMS_RTL_OBJ_SECT_LOAD,
RTEMS_RTL_OBJ_SECT_CONST | RTEMS_RTL_OBJ_SECT_LOAD,
RTEMS_RTL_OBJ_SECT_DATA | RTEMS_RTL_OBJ_SECT_LOAD,
RTEMS_RTL_OBJ_SECT_BSS
};
const size_t sect_masks = sizeof (sect_mask) / sizeof (sect_mask[0]);
/*
* Caculate the size of sections' name.
*/
for (m = 0; m < sect_masks; ++m)
{
sections = &obj->sections;
node = rtems_chain_first (sections);
while (!rtems_chain_is_tail (sections, node))
{
rtems_rtl_obj_sect* sect = (rtems_rtl_obj_sect*) node;
const uint32_t mask = sect_mask[m];
if ((sect->size != 0) && ((sect->flags & mask) == mask))
{
++sec_num;
}
node = rtems_chain_next (node);
}
}
obj->obj_num = 1;
obj->linkmap = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT,
sizeof(struct link_map) +
sec_num * sizeof (section_detail), true);
if (!obj->linkmap)
{
rtems_rtl_set_error (ENOMEM, "no memory for obj linkmap");
return false;
}
obj->linkmap->name = obj->oname;
obj->linkmap->sec_num = sec_num;
obj->linkmap->sec_detail = (section_detail*) (obj->linkmap + 1);
obj->linkmap->rpathlen = 0;
obj->linkmap->rpath = NULL;
obj->linkmap->l_next = NULL;
obj->linkmap->l_prev = NULL;
obj->linkmap->sec_addr[rap_text] = obj->text_base;
obj->linkmap->sec_addr[rap_const] = obj->const_base;
obj->linkmap->sec_addr[rap_data] = obj->data_base;
obj->linkmap->sec_addr[rap_bss] = obj->bss_base;
sd = obj->linkmap->sec_detail;
for (m = 0; m < sect_masks; ++m)
{
sections = &obj->sections;
node = rtems_chain_first (sections);
while (!rtems_chain_is_tail (sections, node))
{
rtems_rtl_obj_sect* sect = (rtems_rtl_obj_sect*) node;
const uint32_t mask = sect_mask[m];
if ((sect->size != 0) && ((sect->flags & mask) == mask))
{
sd[i].name = sect->name;
sd[i].size = sect->size;
if ((mask & RTEMS_RTL_OBJ_SECT_TEXT) != 0)
{
sd[i].rap_id = rap_text;
sd[i].offset = sect->base - obj->text_base;
}
if ((mask & RTEMS_RTL_OBJ_SECT_CONST) != 0)
{
sd[i].rap_id = rap_const;
sd[i].offset = sect->base - obj->const_base;
}
if ((mask & RTEMS_RTL_OBJ_SECT_DATA) != 0)
{
sd[i].rap_id = rap_data;
sd[i].offset = sect->base - obj->data_base;
}
if ((mask & RTEMS_RTL_OBJ_SECT_BSS) != 0)
{
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* obj, int fd)
{
rtems_rtl_obj_cache* header;
Elf_Ehdr ehdr;
rtems_rtl_elf_reloc_data relocs = { 0 };
rtems_rtl_elf_common_data common = { 0 };
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;
}
/*
* Set the format's architecture's maximum tramp size.
*/
obj->tramp_size = rtems_rtl_elf_relocate_tramp_max_size ();
/*
* 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;
/*
* Set the entry point if there is one.
*/
obj->entry = (void*)(uintptr_t) ehdr.e_entry;
/*
* Load the symbol table.
*
* 1. See if there are any common variables and if there are add a
* common section.
* 2. Add up the common.
* 3. The load the symbols.
*/
if (!rtems_rtl_obj_load_symbols (obj, fd, rtems_rtl_elf_common, &common))
return false;
if (!rtems_rtl_elf_add_common (obj, common.size, common.alignment))
return false;
if (!rtems_rtl_obj_load_symbols (obj, fd, rtems_rtl_elf_symbols_load, &ehdr))
return false;
/*
* Parse the relocation records. It lets us know how many dependents
* and fixup trampolines there are.
*/
if (!rtems_rtl_obj_relocate (obj, fd, rtems_rtl_elf_relocs_parser, &relocs))
return false;
/*
* Lock the allocator so the section memory and the trampoline memory are as
* clock as possible.
*/
rtems_rtl_alloc_lock ();
/*
* Allocate the sections.
*/
if (!rtems_rtl_obj_alloc_sections (obj, fd, rtems_rtl_elf_arch_alloc, &ehdr))
return false;
if (!rtems_rtl_obj_load_symbols (obj, fd, rtems_rtl_elf_symbols_locate, &ehdr))
return false;
if (!rtems_rtl_elf_dependents (obj, &relocs))
return false;
if (!rtems_rtl_elf_alloc_trampoline (obj, relocs.unresolved))
return false;
/*
* Unlock the allocator.
*/
rtems_rtl_alloc_unlock ();
/*
* Load the sections and symbols and then relocation to the base address.
*/
if (!rtems_rtl_obj_load_sections (obj, fd, rtems_rtl_elf_loader, &ehdr))
return false;
/*
* Fix up the relocations.
*/
if (!rtems_rtl_obj_relocate (obj, fd, rtems_rtl_elf_relocs_locator, &ehdr))
return false;
rtems_rtl_symbol_obj_erase_local (obj);
if (!rtems_rtl_elf_load_linkmap (obj))
{
return false;
}
if (!rtems_rtl_elf_unwind_register (obj))
{
return false;
}
return true;
}
bool
rtems_rtl_elf_file_unload (rtems_rtl_obj* obj)
{
rtems_rtl_elf_arch_free (obj);
rtems_rtl_elf_unwind_deregister (obj);
return true;
}
rtems_rtl_loader_format*
rtems_rtl_elf_file_sig (void)
{
return &elf_sig;
}