libdl: Add a local symbol table to the object module.

Adding a local symbol lets the relocator find local symbols referenced
in relocation records. The local symbol table is erased once the object
module has been loaded.
This commit is contained in:
Chris Johns
2014-11-04 12:12:25 +11:00
parent 53dd6d6130
commit a2e1e30d98
5 changed files with 202 additions and 96 deletions

View File

@@ -1,5 +1,5 @@
/*
* COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org>
* 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
@@ -73,7 +73,10 @@ rtems_rtl_elf_find_symbol (rtems_rtl_obj_t* obj,
if (ELF_ST_TYPE(sym->st_info) == STT_NOTYPE)
{
rtems_rtl_obj_sym_t* symbol = rtems_rtl_symbol_global_find (symname);
/*
* 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);
@@ -337,9 +340,14 @@ rtems_rtl_elf_symbols (rtems_rtl_obj_t* obj,
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 string_space;
char* string;
int global_string_space;
rtems_rtl_obj_sym_t* gsym;
char* gstring;
int sym;
strtab = rtems_rtl_obj_find_section (obj, ".strtab");
@@ -359,8 +367,10 @@ rtems_rtl_elf_symbols (rtems_rtl_obj_t* obj,
* needed. Also check for duplicate symbols.
*/
globals = 0;
string_space = 0;
globals = 0;
global_string_space = 0;
locals = 0;
local_string_space = 0;
for (sym = 0; sym < (sect->size / sizeof (Elf_Sym)); ++sym)
{
@@ -382,122 +392,182 @@ rtems_rtl_elf_symbols (rtems_rtl_obj_t* obj,
return false;
/*
* Only keep the functions and global or weak symbols.
* 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 ((ELF_ST_TYPE (symbol.st_info) == STT_OBJECT) ||
(ELF_ST_TYPE (symbol.st_info) == STT_FUNC))
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)))
{
if ((ELF_ST_BIND (symbol.st_info) == STB_GLOBAL) ||
(ELF_ST_BIND (symbol.st_info) == STB_WEAK))
rtems_rtl_obj_sect_t* symsect;
symsect = rtems_rtl_obj_find_section_by_index (obj, symbol.st_shndx);
if (symsect)
{
/*
* 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))
if ((ELF_ST_BIND (symbol.st_info) == STB_GLOBAL) ||
(ELF_ST_BIND (symbol.st_info) == STB_WEAK))
{
rtems_rtl_set_error (ENOMEM, "duplicate global symbol: %s", name);
return false;
/*
* 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
else if (ELF_ST_BIND (symbol.st_info) == STB_LOCAL)
{
++globals;
string_space += strlen (name) + 1;
++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)
{
rtems_rtl_obj_sym_t* gsym;
obj->global_size = globals * sizeof (rtems_rtl_obj_sym_t) + string_space;
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;
}
for (sym = 0,
gsym = obj->global_table,
string = (((char*) obj->global_table) +
(globals * sizeof (rtems_rtl_obj_sym_t)));
sym < (sect->size / sizeof (Elf_Sym));
++sym)
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)))
{
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)
{
free (obj->global_table);
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 (((ELF_ST_TYPE (symbol.st_info) == STT_OBJECT) ||
(ELF_ST_TYPE (symbol.st_info) == STT_FUNC)) &&
((ELF_ST_BIND (symbol.st_info) == STB_GLOBAL) ||
(ELF_ST_BIND (symbol.st_info) == STB_WEAK)))
{
rtems_rtl_obj_sect_t* symsect;
symsect = rtems_rtl_obj_find_section_by_index (obj, symbol.st_shndx);
if (!symsect)
{
free (obj->global_table);
obj->global_table = NULL;
obj->global_syms = 0;
obj->global_size = 0;
rtems_rtl_set_error (EINVAL, "sym section not found");
return false;
}
rtems_chain_set_off_chain (&gsym->node);
memcpy (string, name, strlen (name) + 1);
gsym->name = string;
string += strlen (name) + 1;
gsym->value = symbol.st_value + (uint8_t*) symsect->base;
gsym->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, gsym->name,
(int) ELF_ST_BIND (symbol.st_info),
(int) ELF_ST_TYPE (symbol.st_info),
gsym->value, symbol.st_shndx,
(int) symbol.st_size);
++gsym;
}
return false;
}
rtems_rtl_symbol_obj_add (obj);
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;
}
@@ -631,8 +701,9 @@ rtems_rtl_elf_parse_sections (rtems_rtl_obj_t* obj, int fd, Elf_Ehdr* ehdr)
break;
default:
printf ("rtl: unsupported section: %2d: type=%02d flags=%02x\n",
section, (int) shdr.sh_type, (int) shdr.sh_flags);
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;
}
@@ -867,6 +938,8 @@ rtems_rtl_elf_file_load (rtems_rtl_obj_t* obj, int fd)
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;

View File

@@ -145,6 +145,9 @@ struct rtems_rtl_obj_s
size_t fsize; /**< Size of the object file. */
rtems_chain_control sections; /**< The sections of interest in the
* object file. */
rtems_rtl_obj_sym_t* local_table; /**< Local symbol table. */
size_t local_syms; /**< Local symbol count. */
size_t local_size; /**< Local symbol memory usage. */
rtems_rtl_obj_sym_t* global_table; /**< Global symbol table. */
size_t global_syms; /**< Global symbol count. */
size_t global_size; /**< Global symbol memory usage. */

View File

@@ -1,5 +1,5 @@
/*
* COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org>
* 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
@@ -208,9 +208,18 @@ rtems_rtl_symbol_obj_find (rtems_rtl_obj_t* obj, const char* name)
* Check the object file's symbols first. If not found search the
* global symbol table.
*/
for (s = 0, sym = obj->global_table; s < obj->global_syms; ++s, ++sym)
if (strcmp (name, sym->name) == 0)
return sym;
if (obj->local_syms)
{
for (s = 0, sym = obj->local_table; s < obj->local_syms; ++s, ++sym)
if (strcmp (name, sym->name) == 0)
return sym;
}
if (obj->global_syms)
{
for (s = 0, sym = obj->global_table; s < obj->global_syms; ++s, ++sym)
if (strcmp (name, sym->name) == 0)
return sym;
}
return rtems_rtl_symbol_global_find (name);
}
@@ -227,9 +236,22 @@ rtems_rtl_symbol_obj_add (rtems_rtl_obj_t* obj)
rtems_rtl_symbol_global_insert (symbols, sym);
}
void
rtems_rtl_symbol_obj_erase_local (rtems_rtl_obj_t* obj)
{
if (obj->local_table)
{
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_SYMBOL, obj->local_table);
obj->local_table = NULL;
obj->local_size = 0;
obj->local_syms = 0;
}
}
void
rtems_rtl_symbol_obj_erase (rtems_rtl_obj_t* obj)
{
rtems_rtl_symbol_obj_erase_local (obj);
if (obj->global_table)
{
rtems_rtl_obj_sym_t* sym;

View File

@@ -1,5 +1,5 @@
/*
* COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org>
* 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
@@ -114,6 +114,13 @@ rtems_rtl_obj_sym_t* rtems_rtl_symbol_obj_find (rtems_rtl_obj_t* obj,
*/
void rtems_rtl_symbol_obj_add (rtems_rtl_obj_t* obj);
/**
* Erase the object file's local symbols.
*
* @param obj The object file the local symbols are to be erased from.
*/
void rtems_rtl_symbol_obj_erase_local (rtems_rtl_obj_t* obj);
/**
* Erase the object file's symbols.
*

View File

@@ -1,5 +1,5 @@
/*
* COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org>
* 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
@@ -47,6 +47,7 @@ typedef uint32_t rtems_rtl_trace_mask;
#define RTEMS_RTL_TRACE_ALLOCATOR (1UL << 7)
#define RTEMS_RTL_TRACE_UNRESOLVED (1UL << 8)
#define RTEMS_RTL_TRACE_DETAIL (1UL << 9)
#define RTEMS_RTL_TRACE_WARNING (1UL << 10)
/**
* Call to check if this part is bring traced. If RTEMS_RTL_TRACE is defined to