[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

View File

@@ -2,17 +2,74 @@
/// \file init.h
/// Process initialization utility functions
#include <stdint.h>
#include <j6/types.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
#define add_header(name) \
static constexpr j6_arg_type type_id = j6_arg_type_ ## name; \
j6_arg_header header;
#else
#define add_header(name) \
j6_arg_header header;
#endif
enum j6_arg_type {
j6_arg_type_none,
j6_arg_type_sysv_init,
j6_arg_type_loader,
j6_arg_type_driver,
j6_arg_type_handles,
};
struct j6_arg_header
{
uint32_t size;
uint16_t type;
};
struct j6_arg_loader
{
add_header(loader);
uintptr_t loader_base;
uintptr_t image_base;
uintptr_t phdr;
size_t phdr_size;
size_t phdr_count;
uintptr_t entrypoint;
};
struct j6_arg_driver
{
add_header(driver);
uint64_t device;
uint8_t data [0];
};
struct j6_arg_handle_entry
{
uint64_t proto;
j6_handle_t handle;
};
struct j6_arg_handles
{
add_header(handles);
size_t nhandles;
j6_arg_handle_entry handles[0];
};
struct j6_init_args
{
uint64_t args[2];
};
/// Find the first handle of the given type held by this process
j6_handle_t j6_find_first_handle(j6_object_type obj_type);
@@ -22,3 +79,5 @@ int driver_main(unsigned, const char **, const char **, const j6_init_args *);
#ifdef __cplusplus
} // extern "C"
#endif
#undef add_header

View File

@@ -0,0 +1,11 @@
#pragma once
/// \file j6/protocols/vfs.h
/// Definitions for the virtual file system protocol
#include <j6/protocols.h>
enum j6_proto_vfs_tag
{
j6_proto_vfs_load = j6_proto_base_first_proto_id,
j6_proto_vfs_file,
};

View File

@@ -0,0 +1,22 @@
#include <j6/protocols/vfs.h>
#include <j6/types.h>
namespace j6::proto::vfs {
class client
{
public:
/// Constructor.
/// \arg vfs_mb Handle to the VFS service's mailbox
client(j6_handle_t vfs_mb);
/// Load a file into a VMA
/// \arg path Path of the file to load
/// \arg vma [out] Handle to the loaded VMA, or invalid if not found
j6_status_t load_file(char *path, j6_handle_t &vma);
private:
j6_handle_t m_service;
};
} // namespace j6::proto::vfs

View File

@@ -12,6 +12,7 @@ j6 = module("j6",
"mutex.cpp",
"protocol_ids.cpp",
"protocols/service_locator.cpp",
"protocols/vfs.cpp",
"syscalls.s.cog",
"sysconf.cpp.cog",
"syslog.cpp",

View File

@@ -0,0 +1,57 @@
#include <j6/errors.h>
#include <j6/protocols/vfs.hh>
#include <j6/syscalls.h>
#include <j6/syslog.hh>
#ifndef __j6kernel
namespace j6::proto::vfs {
client::client(j6_handle_t vfs_mb) :
m_service {vfs_mb}
{
}
inline size_t simple_strlen(const char *s) { size_t n = 0; while (s && *s) n++; return n; }
j6_status_t
client::load_file(char *path, j6_handle_t &vma)
{
if (!path)
return j6_err_invalid_arg;
uint64_t tag = j6_proto_vfs_load;
size_t handle_count = 1;
vma = j6_handle_invalid;
// Always need to send a big enough buffer for a status code
size_t path_len = simple_strlen(path);
size_t data_len = path_len;
char *data = path;
j6_status_t alternate = 0;
if (path_len < sizeof(alternate)) {
data = reinterpret_cast<char*>(alternate);
for (unsigned i = 0; i < path_len; ++i)
data[i] = path[i];
data_len = sizeof(alternate);
}
j6_status_t s = j6_mailbox_call(m_service, &tag,
data, &data_len, path_len,
&vma, &handle_count);
if (s != j6_status_ok)
return s;
if (tag == j6_proto_vfs_file)
return j6_status_ok; // handle is already in `vma`
else if (tag == j6_proto_base_status)
return *reinterpret_cast<j6_status_t*>(data); // contains a status
return j6_err_unexpected;
}
} // namespace j6::proto::vfs
#endif // __j6kernel