libdl: Add symbol searching and loading from archives.

- Load archive symbol tables to support searching of archives
  for symbols.
- Search archive symbols and load the object file that contains
  the symbol.
- Search the global and archives until all remaining unresolved symbols
  are not found. Group the loaded object files in the pending queue.
- Run the object file and loaded dependents as a group before adding to the
  main object list.
- Remove orphaned object files after references are removed.

Updates #3686
This commit is contained in:
Chris Johns
2018-12-17 16:36:48 +11:00
parent b08278e852
commit 89c59be38d
16 changed files with 2182 additions and 444 deletions

View File

@@ -106,7 +106,7 @@ rtems_rtl_obj_free_names (rtems_rtl_obj* obj)
bool
rtems_rtl_obj_free (rtems_rtl_obj* obj)
{
if (obj->users || ((obj->flags & RTEMS_RTL_OBJ_LOCKED) != 0))
if (obj->users > 0 || ((obj->flags & RTEMS_RTL_OBJ_LOCKED) != 0))
{
rtems_rtl_set_error (EINVAL, "cannot free obj still in use");
return false;
@@ -119,18 +119,63 @@ rtems_rtl_obj_free (rtems_rtl_obj* obj)
rtems_rtl_obj_erase_dependents (obj);
rtems_rtl_symbol_obj_erase (obj);
rtems_rtl_obj_free_names (obj);
if (obj->sec_num)
if (obj->sec_num != NULL)
free (obj->sec_num);
if (obj->linkmap)
if (obj->linkmap != NULL)
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, (void*) obj->linkmap);
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, obj);
return true;
}
typedef struct rtems_rtl_obj_unresolved_data
{
bool has_unresolved;
} rtems_rtl_obj_unresolved_data;
static bool
rtems_rtl_obj_unresolved_dependent (rtems_rtl_obj* obj,
rtems_rtl_obj* dependent,
void* data)
{
rtems_rtl_obj_unresolved_data* ud;
ud = (rtems_rtl_obj_unresolved_data*) data;
if ((dependent->flags & RTEMS_RTL_OBJ_DEP_VISITED) == 0)
{
dependent->flags |= RTEMS_RTL_OBJ_DEP_VISITED;
if ((dependent->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0)
ud->has_unresolved = true;
else
{
rtems_rtl_obj_iterate_dependents (dependent,
rtems_rtl_obj_unresolved_dependent,
ud);
}
if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
printf ("rtl: obj: unresolved: dep: %s is %s\n",
dependent->oname, ud->has_unresolved ? "unresolved" : "resolved");
}
return ud->has_unresolved;
}
bool
rtems_rtl_obj_unresolved (rtems_rtl_obj* obj)
{
return (obj->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0 ? true : false;
rtems_rtl_obj_unresolved_data ud = {
.has_unresolved = (obj->flags & RTEMS_RTL_OBJ_UNRESOLVED) != 0
};
if (rtems_rtl_trace (RTEMS_RTL_TRACE_UNRESOLVED))
printf ("rtl: obj: unresolved: dep: %s is %s\n",
obj->oname, ud.has_unresolved ? "unresolved" : "resolved");
if (!ud.has_unresolved)
{
rtems_rtl_obj_update_flags (RTEMS_RTL_OBJ_DEP_VISITED, 0);
obj->flags |= RTEMS_RTL_OBJ_DEP_VISITED;
rtems_rtl_obj_iterate_dependents (obj,
rtems_rtl_obj_unresolved_dependent,
&ud);
rtems_rtl_obj_update_flags (RTEMS_RTL_OBJ_DEP_VISITED, 0);
}
return ud.has_unresolved;
}
bool
@@ -148,6 +193,9 @@ rtems_rtl_parse_name (const char* name,
* Parse the name to determine if the object file is part of an archive or it
* is an object file. If an archive check the name for a '@' to see if the
* archive contains an offset.
*
* Note, if an archive the object file oofset may be know but the
* object file is not. Leave the object name as a NULL.
*/
end = name + strlen (name);
colon = strrchr (name, ':');
@@ -155,7 +203,7 @@ rtems_rtl_parse_name (const char* name,
colon = end;
loname = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, colon - name + 1, true);
if (!oname)
if (!loname)
{
rtems_rtl_set_error (ENOMEM, "no memory for object file name");
return false;
@@ -219,35 +267,6 @@ rtems_rtl_obj_parse_name (rtems_rtl_obj* obj, const char* name)
return rtems_rtl_parse_name (name, &(obj->aname), &(obj->oname), &(obj->ooffset));
}
static bool
rtems_rtl_seek_read (int fd, off_t off, size_t len, uint8_t* buffer)
{
if (lseek (fd, off, SEEK_SET) < 0)
return false;
if (read (fd, buffer, len) != len)
return false;
return true;
}
/**
* Scan the decimal number returning the value found.
*/
static uint64_t
rtems_rtl_scan_decimal (const uint8_t* string, size_t len)
{
uint64_t value = 0;
while (len && (*string != ' '))
{
value *= 10;
value += *string - '0';
++string;
--len;
}
return value;
}
/**
* Section size summer iterator data.
*/
@@ -339,22 +358,6 @@ rtems_rtl_obj_section_handler (uint32_t mask,
return true;
}
bool
rtems_rtl_match_name (rtems_rtl_obj* obj, const char* name)
{
const char* n1 = obj->oname;
while ((*n1 != '\0') && (*n1 != '\n') && (*n1 != '/') &&
(*name != '\0') && (*name != '/') && (*n1 == *name))
{
++n1;
++name;
}
if (((*n1 == '\0') || (*n1 == '\n') || (*n1 == '/')) &&
((*name == '\0') || (*name == '/')))
return true;
return false;
}
bool
rtems_rtl_obj_find_file (rtems_rtl_obj* obj, const char* name)
{
@@ -596,9 +599,15 @@ rtems_rtl_obj_add_dependent (rtems_rtl_obj* obj, rtems_rtl_obj* dependent)
{
rtems_rtl_obj** free_slot;
rtems_chain_node* node;
if (obj == dependent || dependent == rtems_rtl_baseimage ())
return false;
if (rtems_rtl_trace (RTEMS_RTL_TRACE_DEPENDENCY))
printf ("rtl: depend: add: %s -> %s\n", obj->oname, dependent->oname);
free_slot = NULL;
node = rtems_chain_first (&obj->dependents);
while (!rtems_chain_is_tail (&obj->dependents, node))
{
@@ -613,6 +622,7 @@ rtems_rtl_obj_add_dependent (rtems_rtl_obj* obj, rtems_rtl_obj* dependent)
}
node = rtems_chain_next (node);
}
if (free_slot == NULL)
{
if (rtems_rtl_obj_alloc_dependents (obj,
@@ -629,11 +639,47 @@ rtems_rtl_obj_add_dependent (rtems_rtl_obj* obj, rtems_rtl_obj* dependent)
}
}
}
if (free_slot != NULL)
*free_slot = dependent;
return free_slot != NULL;
}
bool
rtems_rtl_obj_remove_dependencies (rtems_rtl_obj* obj)
{
/*
* If there are no references unload the object.
*/
if (obj->refs == 0)
{
/*
* Remove the refences from the object files this file depend on. The
* unload happens once the list of objects to be unloaded has been made and
* the destructors have been called for all those modules.
*/
rtems_chain_node* node = rtems_chain_first (&obj->dependents);
while (!rtems_chain_is_tail (&obj->dependents, node))
{
rtems_rtl_obj_depends* depends = (rtems_rtl_obj_depends*) node;
size_t d;
for (d = 0; d < depends->dependents; ++d)
{
if (depends->depends[d] != NULL)
{
rtems_rtl_obj_dec_reference (depends->depends[d]);
depends->depends[d] = NULL;
}
}
node = rtems_chain_next (node);
}
return true;
}
return false;
}
bool
rtems_rtl_obj_iterate_dependents (rtems_rtl_obj* obj,
rtems_rtl_obj_depends_iterator iterator,
@@ -1065,244 +1111,42 @@ rtems_rtl_obj_run_cdtors (rtems_rtl_obj* obj, uint32_t mask)
}
}
static bool
rtems_rtl_obj_cdtors_to_run (rtems_rtl_obj* obj, uint32_t mask)
{
rtems_chain_node* node = rtems_chain_first (&obj->sections);
while (!rtems_chain_is_tail (&obj->sections, node))
{
rtems_rtl_obj_sect* sect = (rtems_rtl_obj_sect*) node;
if ((sect->flags & mask) == mask)
return true;
node = rtems_chain_next (node);
}
return false;
}
bool
rtems_rtl_obj_ctors_to_run (rtems_rtl_obj* obj)
{
return rtems_rtl_obj_cdtors_to_run (obj, RTEMS_RTL_OBJ_SECT_CTOR);
}
void
rtems_rtl_obj_run_ctors (rtems_rtl_obj* obj)
{
return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_CTOR);
rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_CTOR);
}
bool
rtems_rtl_obj_dtors_to_run (rtems_rtl_obj* obj)
{
return rtems_rtl_obj_cdtors_to_run (obj, RTEMS_RTL_OBJ_SECT_DTOR);
}
void
rtems_rtl_obj_run_dtors (rtems_rtl_obj* obj)
{
return rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_DTOR);
}
/**
* Find a module in an archive returning the offset in the archive in the
* object descriptor.
*/
static bool
rtems_rtl_obj_archive_find (rtems_rtl_obj* obj, int fd)
{
#define RTEMS_RTL_AR_IDENT "!<arch>\n"
#define RTEMS_RTL_AR_IDENT_SIZE (sizeof (RTEMS_RTL_AR_IDENT) - 1)
#define RTEMS_RTL_AR_FHDR_BASE RTEMS_RTL_AR_IDENT_SIZE
#define RTEMS_RTL_AR_FNAME (0)
#define RTEMS_RTL_AR_FNAME_SIZE (16)
#define RTEMS_RTL_AR_SIZE (48)
#define RTEMS_RTL_AR_SIZE_SIZE (10)
#define RTEMS_RTL_AR_MAGIC (58)
#define RTEMS_RTL_AR_MAGIC_SIZE (2)
#define RTEMS_RTL_AR_FHDR_SIZE (60)
size_t fsize = obj->fsize;
off_t extended_file_names;
uint8_t header[RTEMS_RTL_AR_FHDR_SIZE];
bool scanning;
if (read (fd, &header[0], RTEMS_RTL_AR_IDENT_SIZE) != RTEMS_RTL_AR_IDENT_SIZE)
{
rtems_rtl_set_error (errno, "reading archive identifer");
return false;
}
if (memcmp (header, RTEMS_RTL_AR_IDENT, RTEMS_RTL_AR_IDENT_SIZE) != 0)
{
rtems_rtl_set_error (EINVAL, "invalid archive identifer");
return false;
}
/*
* Seek to the current offset in the archive and if we have a valid archive
* file header present check the file name for a match with the oname field
* of the object descriptor. If the archive header is not valid and it is the
* first pass reset the offset and start the search again in case the offset
* provided is not valid any more.
*
* The archive can have a symbol table at the start. Ignore it. A symbol
* table has the file name '/'.
*
* The archive can also have the GNU extended file name table. This
* complicates the processing. If the object's file name starts with '/' the
* remainder of the file name is an offset into the extended file name
* table. To find the extended file name table we need to scan from start of
* the archive for a file name of '//'. Once found we remeber the table's
* start and can direct seek to file name location. In other words the scan
* only happens once.
*
* If the name had the offset encoded we go straight to that location.
*/
if (obj->ooffset != 0)
scanning = false;
else
{
scanning = true;
obj->ooffset = RTEMS_RTL_AR_FHDR_BASE;
}
extended_file_names = 0;
while (obj->ooffset < fsize)
{
/*
* Clean up any existing data.
*/
memset (header, 0, sizeof (header));
if (!rtems_rtl_seek_read (fd, obj->ooffset, RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
{
rtems_rtl_set_error (errno, "seek/read archive file header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
(header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
{
if (scanning)
{
rtems_rtl_set_error (EINVAL, "invalid archive file header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
scanning = true;
obj->ooffset = RTEMS_RTL_AR_FHDR_BASE;
continue;
}
/*
* The archive header is always aligned to an even address.
*/
obj->fsize = (rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;
/*
* Check for the GNU extensions.
*/
if (header[0] == '/')
{
off_t extended_off;
switch (header[1])
{
case ' ':
/*
* Symbols table. Ignore the table.
*/
break;
case '/':
/*
* Extended file names table. Remember.
*/
extended_file_names = obj->ooffset + RTEMS_RTL_AR_FHDR_SIZE;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/*
* Offset into the extended file name table. If we do not have the
* offset to the extended file name table find it.
*/
extended_off =
rtems_rtl_scan_decimal (&header[1], RTEMS_RTL_AR_FNAME_SIZE);
if (extended_file_names == 0)
{
off_t off = obj->ooffset;
while (extended_file_names == 0)
{
off_t esize =
(rtems_rtl_scan_decimal (&header[RTEMS_RTL_AR_SIZE],
RTEMS_RTL_AR_SIZE_SIZE) + 1) & ~1;
off += esize + RTEMS_RTL_AR_FHDR_SIZE;
if (!rtems_rtl_seek_read (fd, off,
RTEMS_RTL_AR_FHDR_SIZE, &header[0]))
{
rtems_rtl_set_error (errno,
"seeking/reading archive ext file name header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if ((header[RTEMS_RTL_AR_MAGIC] != 0x60) ||
(header[RTEMS_RTL_AR_MAGIC + 1] != 0x0a))
{
rtems_rtl_set_error (errno, "invalid archive file header");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if ((header[0] == '/') && (header[1] == '/'))
{
extended_file_names = off + RTEMS_RTL_AR_FHDR_SIZE;
break;
}
}
}
if (extended_file_names)
{
/*
* We know the offset in the archive to the extended file. Read the
* name from the table and compare with the name we are after.
*/
#define RTEMS_RTL_MAX_FILE_SIZE (256)
char name[RTEMS_RTL_MAX_FILE_SIZE];
if (!rtems_rtl_seek_read (fd, extended_file_names + extended_off,
RTEMS_RTL_MAX_FILE_SIZE, (uint8_t*) &name[0]))
{
rtems_rtl_set_error (errno,
"invalid archive ext file seek/read");
obj->ooffset = 0;
obj->fsize = 0;
return false;
}
if (rtems_rtl_match_name (obj, name))
{
obj->ooffset += RTEMS_RTL_AR_FHDR_SIZE;
return true;
}
}
break;
default:
/*
* Ignore the file because we do not know what it it.
*/
break;
}
}
else
{
if (rtems_rtl_match_name (obj, (const char*) &header[RTEMS_RTL_AR_FNAME]))
{
obj->ooffset += RTEMS_RTL_AR_FHDR_SIZE;
return true;
}
}
obj->ooffset += obj->fsize + RTEMS_RTL_AR_FHDR_SIZE;
}
rtems_rtl_set_error (ENOENT, "object file not found");
obj->ooffset = 0;
obj->fsize = 0;
return false;
rtems_rtl_obj_run_cdtors (obj, RTEMS_RTL_OBJ_SECT_DTOR);
}
static bool
@@ -1323,31 +1167,16 @@ rtems_rtl_obj_file_load (rtems_rtl_obj* obj, int fd)
return false;
}
static bool
rtems_rtl_obj_file_unload (rtems_rtl_obj* obj)
static void
rtems_rtl_obj_set_error (int num, const char* text)
{
if (obj->format >= 0 && obj->format < RTEMS_RTL_LOADERS)
{
rtems_chain_node* node;
rtems_rtl_set_error (num, text);
}
if (!loaders[obj->format].unload (obj))
return false;
node = rtems_chain_first (&obj->dependents);
while (!rtems_chain_is_tail (&obj->dependents, node))
{
rtems_rtl_obj_depends* depends = (rtems_rtl_obj_depends*) node;
size_t d;
for (d = 0; d < depends->dependents; ++d)
{
if (depends->depends[d] != NULL)
rtems_rtl_obj_dec_reference (depends->depends[d]);
}
node = rtems_chain_next (node);
}
}
return false;
size_t
rtems_rtl_obj_get_reference (rtems_rtl_obj* obj)
{
return obj->refs;
}
void
@@ -1363,6 +1192,14 @@ rtems_rtl_obj_dec_reference (rtems_rtl_obj* obj)
--obj->refs;
}
bool
rtems_rtl_obj_orphaned (rtems_rtl_obj* obj)
{
return ((obj->flags & RTEMS_RTL_OBJ_LOCKED) == 0 &&
obj->users == 0 &&
rtems_rtl_obj_get_reference (obj) == 0);
}
bool
rtems_rtl_obj_load (rtems_rtl_obj* obj)
{
@@ -1377,7 +1214,7 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj)
fd = open (rtems_rtl_obj_fname (obj), O_RDONLY);
if (fd < 0)
{
rtems_rtl_set_error (ENOMEM, "opening for object file");
rtems_rtl_set_error (errno, "opening for object file");
return false;
}
@@ -1387,7 +1224,14 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj)
*/
if (rtems_rtl_obj_aname_valid (obj))
{
if (!rtems_rtl_obj_archive_find (obj, fd))
off_t enames = 0;
if (!rtems_rtl_obj_archive_find_obj (fd,
obj->fsize,
&obj->oname,
&obj->ooffset,
&obj->fsize,
&enames,
rtems_rtl_obj_set_error))
{
close (fd);
return false;
@@ -1403,7 +1247,10 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj)
return false;
}
if (!_rtld_linkmap_add (obj)) /* For GDB */
/*
* For GDB
*/
if (!_rtld_linkmap_add (obj))
{
close (fd);
return false;
@@ -1417,7 +1264,15 @@ rtems_rtl_obj_load (rtems_rtl_obj* obj)
bool
rtems_rtl_obj_unload (rtems_rtl_obj* obj)
{
_rtld_linkmap_delete(obj);
rtems_rtl_obj_file_unload (obj);
return true;
bool ok = false;
if (obj->format >= 0 && obj->format < RTEMS_RTL_LOADERS)
{
_rtld_linkmap_delete(obj);
ok = loaders[obj->format].unload (obj);
}
else
{
rtems_rtl_set_error (EINVAL, "invalid object loader format");
}
return ok;
}