[ld.so] Add a minimal dynamic linker

This commit includes a number of changes to enable loading of PIE
executables:

- The loader in srv.init checks for a `PT_INTERP` segment in the program
  its loading, and if it exists, loads the specified interpreter and
  passes control to it instead of the program itself.
- Added ld.so the dynamic linker executable and set it as the
  interpreter for all user-target programs.
- Program initial stack changed again to now contain a number of
  possible tagged structures, including a new one for ld.so's arguments,
  and for passing handles tagged with protocol ids.
- Added a stub for a new VFS protocol. Unused so far, but srv.init will
  need to serve VFS requests from ld.so once I transition libraries to
  shared libs for user-target programs. (Right now all executables are
  PIE but statically linked, so they only need internal relocations.)
- Added 16 and 8 bit variants of `util::bitset`. This ended up not being
  used, but could be useful.
This commit is contained in:
Justin C. Miller
2023-08-12 22:55:37 -07:00
parent 3cfd0cf86b
commit c4bb60299e
21 changed files with 903 additions and 136 deletions

70
src/user/ld.so/main.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include <stdint.h>
#include <stdlib.h>
#include <elf/headers.h>
#include <j6/init.h>
#include <j6/syslog.hh>
#include <util/pointers.h>
#include "relocate.h"
uintptr_t
locate_dyn_section(uintptr_t base, uintptr_t phdr, size_t phdr_size, size_t phdr_count)
{
if (!phdr || !phdr_count)
return 0;
const elf::segment_header *ph_base =
reinterpret_cast<const elf::segment_header*>(phdr + base);
for (size_t i = 0; i < phdr_count; ++i) {
const elf::segment_header *ph =
util::offset_pointer(ph_base, i*phdr_size);
if (ph->type == elf::segment_type::dynamic)
return ph->vaddr;
}
return 0;
}
extern "C" uintptr_t
ldso_init(j6_arg_header *stack_args, uintptr_t const *got)
{
j6_arg_loader *arg_loader = nullptr;
j6_arg_handles *arg_handles = nullptr;
j6_arg_header *arg = stack_args;
while (arg && arg->type != j6_arg_type_none) {
switch (arg->type)
{
case j6_arg_type_loader:
arg_loader = reinterpret_cast<j6_arg_loader*>(arg);
break;
case j6_arg_type_handles:
arg_handles = reinterpret_cast<j6_arg_handles*>(arg);
break;
default:
break;
}
arg = util::offset_pointer(arg, arg->size);
}
if (!arg_loader) {
j6::syslog("ld.so: error: did not get j6_arg_loader");
exit(127);
}
relocate_image(arg_loader->loader_base, got[0]);
uintptr_t dyn_section = locate_dyn_section(
arg_loader->image_base,
arg_loader->phdr,
arg_loader->phdr_size,
arg_loader->phdr_count);
relocate_image(arg_loader->image_base, dyn_section);
return arg_loader->entrypoint + arg_loader->image_base;
}