ldso: Make truly self relocatable

On Linux the linker can now be loaded at arbitrary addresses, this became
necessary for newer kernel versions. The 'linux_arm' target is not supported.

Issue #1728
This commit is contained in:
Sebastian Sumpf 2015-11-26 10:30:23 +01:00 committed by Christian Helmuth
parent e74b53d5dd
commit 41b9f6bd03
13 changed files with 144 additions and 31 deletions

View File

@ -20,12 +20,14 @@
/*
* We cannot just include <semaphore.h> and <pthread.h> here
* because this would impy the nested inclusion of a myriad
* because this would imply the nested inclusion of a myriad
* of Linux types and would pollute the namespace for everyone
* who includes this header file. We want to cleanly separate
* Genode from POSIX.
*/
extern Genode::addr_t _context_area_start;
namespace Genode {
/**
@ -129,10 +131,11 @@ namespace Genode {
* Thread-context area configuration.
*
* Please update platform-specific files after changing these
* values, e.g., 'base-linux/src/platform/context_area.*.ld'.
* functions, e.g., 'base-linux/src/ld/context_area.*.ld'.
*/
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static addr_t context_area_virtual_base() {
return align_addr((addr_t)&_context_area_start, 20); }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }

View File

@ -20,6 +20,9 @@ SECTIONS
{
. = 0x40000000;
_context_area_start = .;
.context_area : { . += 0x10000000; } : context_area
_context_area_end = .;
/*
* Since Linux loads ldso page aligned, we align the context area after
* loading to a 1 MiB boundary, therefore we reserve one MiB more here.
*/
.context_area : { . += 0x10100000; } : context_area
}

View File

@ -16,5 +16,4 @@ SECTIONS
. = 0x40000000;
_context_area_start = .;
.context_area : { . += 0x10000000; }
_context_area_end = .;
}

View File

@ -19,6 +19,12 @@
extern "C" int raw_write_str(const char *str);
/**
* Define context area
*/
Genode::addr_t _context_area_start;
enum { verbose_atexit = false };

View File

@ -9,12 +9,19 @@ SRC_CC = main.cc test.cc exception.cc file.cc dependency.cc debug.cc \
SRC_S = jmp_slot.s
INC_DIR += $(DIR)/include
LD_OPT += -Bsymbolic-functions --version-script=$(DIR)/symbol.map
ENTRY_POINT = _start
ifneq ($(filter linux, $(SPECS)),)
LD_OPT += -T$(call select_from_repositories,src/ld/context_area.nostdlib.ld)
ENTRY_POINT = _start_initial_stack
LD_OPT += -T$(call select_from_repositories,src/ld/context_area.nostdlib.ld) \
ifneq ($(filter x86_32, $(SPECS)),)
LD_OPT += -T$(DIR)/linux-32.ld
endif
else
LD_OPT += -T$(DIR)/linker.ld
ENTRY_POINT = _start
LD_OPT += -T$(DIR)/linker.ld
endif
vpath %.cc $(DIR)

View File

@ -42,7 +42,6 @@ void Thread_base::Context::stack_size(size_t const size)
/* check if the stack enhancement fits the context region */
enum {
CONTEXT_SIZE = Native_config::context_virtual_size(),
CONTEXT_AREA_BASE = Native_config::context_area_virtual_base(),
UTCB_SIZE = sizeof(Native_utcb),
PAGE_SIZE_LOG2 = 12,
PAGE_SIZE = (1UL << PAGE_SIZE_LOG2),
@ -52,7 +51,8 @@ void Thread_base::Context::stack_size(size_t const size)
if (stack_base - ds_size < context_base) { throw Stack_too_large(); }
/* allocate and attach backing store for the stack enhancement */
addr_t const ds_addr = stack_base - ds_size - CONTEXT_AREA_BASE;
addr_t const ds_addr = stack_base - ds_size -
Native_config::context_area_virtual_base();
try {
Ram_session * const ram = env_context_area_ram_session();
Ram_dataspace_capability const ds_cap = ram->alloc(ds_size);

View File

@ -21,13 +21,6 @@ namespace Linker {
struct Dynamic;
}
/**
* Offset of dynamic section of this ELF. This is filled out during linkage by
* static linker.
*/
extern Genode::addr_t _DYNAMIC;
/**
* ELF hash table and hash function
*/
@ -108,7 +101,7 @@ struct Linker::Dynamic
Dynamic(Dependency const *dep)
:
dep(dep), obj(dep->obj), dynamic((Elf::Dyn *)(obj->reloc_base() + &_DYNAMIC))
dep(dep), obj(dep->obj), dynamic((Elf::Dyn *)dynamic_address())
{
init();
}

View File

@ -34,6 +34,11 @@ constexpr bool verbose_exception = false;
constexpr bool verbose_shared = false;
constexpr bool verbose_loading = false;
extern const unsigned long _GLOBAL_OFFSET_TABLE_[] __attribute__((visibility("hidden")));
extern unsigned long _DYNAMIC[] __attribute__((visibility("hidden")));
extern Elf::Addr etext;
/**
* Forward declartions and helpers
*/
@ -126,6 +131,32 @@ namespace Linker {
*/
constexpr char const *binary_name() { return "binary"; }
constexpr char const *linker_name() { return "ld.lib.so"; }
/**
* Address of .dynamic section in GOT
*/
static inline unsigned long dynamic_address_got()
{
return _GLOBAL_OFFSET_TABLE_[0];
}
/**
* Address of .dynamic section from symbol
*/
static inline unsigned long dynamic_address()
{
return (unsigned long)&_DYNAMIC;
}
/**
* Return the run-time load address of the shared object.
*/
static inline unsigned long relocation_address(void)
{
return dynamic_address() < dynamic_address_got() ?
dynamic_address_got() - dynamic_address() :
dynamic_address() - dynamic_address_got();
}
}
@ -141,12 +172,13 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
char _name[MAX_PATH];
File const *_file = nullptr;
Elf::Addr _reloc_base = 0;
public:
Object() { }
Object(Elf::Addr reloc_base) : _reloc_base(reloc_base) { }
Object(char const *path, File const *file)
: _file(file)
: _file(file), _reloc_base(file->reloc_base)
{
Genode::strncpy(_name, Linker::file(path), MAX_PATH);
}
@ -157,8 +189,8 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
destroy(Genode::env()->heap(), const_cast<File *>(_file));
}
Elf::Addr reloc_base() const { return _file ? _file->reloc_base : 0; }
char const *name() const { return _name; }
Elf::Addr reloc_base() const { return _reloc_base; }
char const *name() const { return _name; }
File const *file() { return _file; }
Elf::Size const size() const { return _file ? _file->size : 0; }
@ -168,7 +200,7 @@ class Linker::Object : public Genode::Fifo<Object>::Element,
virtual void relocate() = 0;
virtual void load() = 0;
virtual void load() = 0;
virtual bool unload() { return false;}
/**

View File

@ -0,0 +1,20 @@
/**
* \brief Linker script for Linux
* \author Sebastian Sumpf
* \date 2015-12-08
*
* On Linux 32 bit, we remove the ".text.crt0" section because it contains a
* text relocation and is not used as startup code for the dynamic linker.
*/
/*
* Copyright (C) 2009-2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
SECTIONS
{
/DISCARD/ : { *(.text.crt0) }
}

View File

@ -64,7 +64,8 @@ struct Linker::Elf_object : Object, Genode::Fifo<Elf_object>::Element
unsigned flags = 0;
bool relocated = false;
Elf_object(Dependency const *dep) : dyn(dep)
Elf_object(Dependency const *dep, Elf::Addr reloc_base)
: Object(reloc_base), dyn(dep)
{ }
Elf_object(char const *path, Dependency const *dep, unsigned flags = 0)
@ -248,7 +249,8 @@ struct Linker::Elf_object : Object, Genode::Fifo<Elf_object>::Element
*/
struct Linker::Ld : Dependency, Elf_object
{
Ld() : Dependency(this, nullptr), Elf_object(this)
Ld()
: Dependency(this, nullptr), Elf_object(this, relocation_address())
{
Genode::strncpy(_name, linker_name(), Object::MAX_PATH);
}
@ -525,7 +527,7 @@ extern "C" void init_rtld()
linker_stack.ref_count++;
/*
* Create actual linker object with different vtable type and set PLT to new
* Create actual linker object with different vtable type and set PLT to new
* DAG.
*/
Ld::linker()->dynamic()->plt_setup();
@ -569,8 +571,12 @@ int main()
/* print loaded object information */
try {
if (Genode::config()->xml_node().attribute("ld_verbose").has_value("yes"))
if (Genode::config()->xml_node().attribute("ld_verbose").has_value("yes")) {
PINF(" %lx .. %lx: context area", Genode::Native_config::context_area_virtual_base(),
Genode::Native_config::context_area_virtual_base() +
Genode::Native_config::context_area_virtual_size() - 1);
dump_loaded();
}
} catch (...) { }
Link_map::dump();

View File

@ -22,6 +22,8 @@
/* program entry-point */
.global _start
_start:
.global _start_initial_stack
_start_initial_stack:
/* make initial value of some registers available to higher-level code */
ldr r4, =__initial_sp

View File

@ -17,6 +17,26 @@
** .text (program code) **
**************************/
.section ".text.crt0.ld"
/* ld.lib.so entry point for Linux */
.global _start_initial_stack
_start_initial_stack:
/* set GOT */
call 1f
1:
pop %ebx
addl $_GLOBAL_OFFSET_TABLE_ + 1, %ebx
/* init_rtld relocates the linker */
call init_rtld
/* the address of __initial_sp is now correct */
movl %esp, __initial_sp@GOTOFF(%ebx)
jmp 2f
.section ".text.crt0"
/* program entry-point */
@ -44,11 +64,16 @@
/* if this is the dynamic linker, init_rtld relocates the linker */
call init_rtld
jmp 2f
.section ".text.crt0.common"
2:
/* create proper environment for the main thread */
call init_main_thread
/* apply environment that was created by init_main_thread */
movl init_main_thread_result, %esp
movl init_main_thread_result@GOTOFF(%ebx), %esp
/* clear the base pointer in order that stack backtraces will work */
xor %ebp, %ebp

View File

@ -19,6 +19,22 @@
.section ".text.crt0"
/* ld.lib.so entry point for Linux */
.global _start_initial_stack
_start_initial_stack:
/* initialize GLOBAL OFFSET TABLE */
leaq _GLOBAL_OFFSET_TABLE_(%rip), %r15
/* init_rtld relocates the linker */
call init_rtld
/* the address of __initial_sp is now correct */
movq __initial_sp@GOTPCREL(%rip), %rax
movq %rsp, (%rax)
jmp 1f
/* program entry-point */
.global _start
_start:
@ -41,9 +57,10 @@
leaq _stack_high@GOTPCREL(%rip),%rax
movq (%rax), %rsp
/* if this is the dynamic linker, init_rtld relocates the linker */
/* init_rtld relocates the linker */
call init_rtld
1:
/* create proper environment for the main thread */
call init_main_thread