libdl: Add object file dependencies to track references

Tracking references lets us manage when an object file can be
unloaded. If an object file has references to it, it cannot be
unloaded.

Modules that depend on each other cannot be unloaded.

Updates #3605
This commit is contained in:
Chris Johns
2018-11-20 14:56:11 +11:00
parent 8e7c72a7f5
commit 03139d5b1c
27 changed files with 1853 additions and 142 deletions

View File

@@ -83,6 +83,7 @@ rtems_rtl_obj_alloc (void)
* Initialise the chains.
*/
rtems_chain_initialize_empty (&obj->sections);
rtems_chain_initialize_empty (&obj->dependents);
/*
* No valid format.
*/
@@ -114,6 +115,8 @@ rtems_rtl_obj_free (rtems_rtl_obj* obj)
rtems_chain_extract (&obj->link);
rtems_rtl_alloc_module_del (&obj->text_base, &obj->const_base, &obj->eh_base,
&obj->data_base, &obj->bss_base);
rtems_rtl_obj_erase_sections (obj);
rtems_rtl_obj_erase_dependents (obj);
rtems_rtl_symbol_obj_erase (obj);
rtems_rtl_obj_free_names (obj);
if (obj->sec_num)
@@ -550,6 +553,110 @@ rtems_rtl_obj_find_section_by_mask (const rtems_rtl_obj* obj,
return match.sect;
}
bool
rtems_rtl_obj_alloc_dependents (rtems_rtl_obj* obj, size_t dependents)
{
rtems_rtl_obj_depends* depends;
size_t size;
size = sizeof (rtems_rtl_obj_depends) + sizeof (rtems_rtl_obj*) * dependents;
depends = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT,
size,
true);
if (depends == NULL)
{
rtems_rtl_set_error (ENOMEM, "no memory for the dependency");
}
else
{
depends->dependents = dependents;
rtems_chain_append (&obj->dependents, &depends->node);
}
return depends != NULL;
}
void
rtems_rtl_obj_erase_dependents (rtems_rtl_obj* obj)
{
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;
rtems_chain_node* next_node = rtems_chain_next (node);
rtems_chain_extract (node);
rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, depends);
node = next_node;
}
}
bool
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;
free_slot = NULL;
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 (free_slot == NULL && depends->depends[d] == NULL)
free_slot = &(depends->depends[d]);
if (depends->depends[d] == dependent)
return false;
}
node = rtems_chain_next (node);
}
if (free_slot == NULL)
{
if (rtems_rtl_obj_alloc_dependents (obj,
RTEMS_RTL_DEPENDENCY_BLOCK_SIZE))
{
rtems_rtl_obj_depends* depends;
node = rtems_chain_last (&obj->dependents);
depends = (rtems_rtl_obj_depends*) node;
free_slot = &(depends->depends[0]);
if (*free_slot != NULL)
{
rtems_rtl_set_error (EINVAL, "new dependency node not empty");
free_slot = NULL;
}
}
}
if (free_slot != NULL)
*free_slot = dependent;
return free_slot != NULL;
}
bool
rtems_rtl_obj_iterate_dependents (rtems_rtl_obj* obj,
rtems_rtl_obj_depends_iterator iterator,
void* data)
{
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])
{
if (iterator (obj, depends->depends[d], data))
return true;
}
}
node = rtems_chain_next (node);
}
return false;
}
size_t
rtems_rtl_obj_text_size (const rtems_rtl_obj* obj)
{
@@ -1220,10 +1327,42 @@ static bool
rtems_rtl_obj_file_unload (rtems_rtl_obj* obj)
{
if (obj->format >= 0 && obj->format < RTEMS_RTL_LOADERS)
return loaders[obj->format].unload (obj);
{
rtems_chain_node* node;
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;
}
void
rtems_rtl_obj_inc_reference (rtems_rtl_obj* obj)
{
++obj->refs;
}
void
rtems_rtl_obj_dec_reference (rtems_rtl_obj* obj)
{
if (obj->refs)
--obj->refs;
}
bool
rtems_rtl_obj_load (rtems_rtl_obj* obj)
{