ldso: heuristics for libs needed during execve

The allocation of regions within the linker area is normally left to the
best-fit 'Allocator_avl', which happens to populate the linker area
starting with the binary followed by all loaded libraried with no gaps
in between.

When replacing the binary during execve, however, we need to ensure that
the new binary does not conflict with any library that stays resident
during execve. This patch tweaks the linker's region allocation scheme
such that these libraries are placed at the end of the linker area.

Issue #3481
This commit is contained in:
Norman Feske 2019-08-21 16:08:20 +02:00 committed by Christian Helmuth
parent fa48054959
commit 66d5359d75
2 changed files with 52 additions and 19 deletions

View File

@ -117,7 +117,32 @@ struct Linker::Elf_file : File
return rom_connection->dataspace();
}
Elf_file(Env &env, Allocator &md_alloc, char const *name, bool load)
void _allocate_region_within_linker_area(Name const &name)
{
bool const binary = (start != 0);
if (binary) {
Region_map::r()->alloc_region_at(size, start);
reloc_base = 0;
return;
}
/*
* Assign libraries that must stay resident during 'execve' to
* the end of the linker area to ensure that the newly loaded
* binary has enough room within the linker area.
*/
bool const resident = (name == "libc.lib.so")
|| (name == "libm.lib.so")
|| (name == "posix.lib.so")
|| (strcmp(name.string(), "vfs", 3) == 0);
reloc_base = resident ? Region_map::r()->alloc_region_at_end(size)
: Region_map::r()->alloc_region(size);
start = 0;
}
Elf_file(Env &env, Allocator &md_alloc, Name const &name, bool load)
:
env(env), rom_cap(_rom_dataspace(name)), loaded(load)
{
@ -133,8 +158,10 @@ struct Linker::Elf_file : File
if (load && !Region_map::r().constructed())
Region_map::r().construct(env, md_alloc, start);
if (load)
if (load) {
_allocate_region_within_linker_area(name);
load_segments();
}
}
virtual ~Elf_file()
@ -239,10 +266,6 @@ struct Linker::Elf_file : File
/* search for PT_LOAD */
loadable_segments(p);
/* allocate region */
reloc_base = Region_map::r()->alloc_region(size, start);
reloc_base = (start == reloc_base) ? 0 : reloc_base;
if (verbose_loading)
log("LD: reloc_base: ", Hex(reloc_base),
" start: ", Hex(start),

View File

@ -47,6 +47,7 @@ class Linker::Region_map
Region_map_client _rm { _env.pd().linker_area() };
Allocator_avl _range; /* VM range allocator */
addr_t const _base; /* base address of dataspace */
addr_t _end = _base + Pd_session::LINKER_AREA_SIZE;
protected:
@ -65,23 +66,32 @@ class Linker::Region_map
static Constructible_region_map &r();
/**
* Reserve VM region of 'size' at 'vaddr'. Allocate any free region if
* 'vaddr' is zero
* Allocate region anywhere within the region map
*/
addr_t alloc_region(size_t size, addr_t vaddr = 0)
addr_t alloc_region(size_t size)
{
addr_t addr = vaddr;
if (addr && (_range.alloc_addr(size, addr).error()))
addr_t result = 0;
if (_range.alloc_aligned(size, (void **)&result,
get_page_size_log2()).error())
throw Region_conflict();
else if (!addr &&
_range.alloc_aligned(size, (void **)&addr,
get_page_size_log2()).error())
{
throw Region_conflict();
}
return addr;
return result;
}
/**
* Allocate region at specified 'vaddr'
*/
void alloc_region_at(size_t size, addr_t vaddr)
{
if (_range.alloc_addr(size, vaddr).error())
throw Region_conflict();
}
addr_t alloc_region_at_end(size_t size)
{
_end -= align_addr(size, get_page_size_log2());
alloc_region_at(size, _end);
return _end;
}
void free_region(addr_t vaddr) { _range.free((void *)vaddr); }