diff --git a/repos/base/src/lib/ldso/include/file.h b/repos/base/src/lib/ldso/include/file.h index fce09531f..1c9ce53ca 100644 --- a/repos/base/src/lib/ldso/include/file.h +++ b/repos/base/src/lib/ldso/include/file.h @@ -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), diff --git a/repos/base/src/lib/ldso/include/region_map.h b/repos/base/src/lib/ldso/include/region_map.h index 5982aded9..cbee34c63 100644 --- a/repos/base/src/lib/ldso/include/region_map.h +++ b/repos/base/src/lib/ldso/include/region_map.h @@ -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); }