[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:
@@ -6,7 +6,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <util/counted.h>
|
||||
#include <util/enum_bitfields.h>
|
||||
|
||||
namespace bootproto {
|
||||
|
||||
|
||||
@@ -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
|
||||
11
src/libraries/j6/include/j6/protocols/vfs.h
Normal file
11
src/libraries/j6/include/j6/protocols/vfs.h
Normal 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,
|
||||
};
|
||||
22
src/libraries/j6/include/j6/protocols/vfs.hh
Normal file
22
src/libraries/j6/include/j6/protocols/vfs.hh
Normal 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
|
||||
@@ -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",
|
||||
|
||||
57
src/libraries/j6/protocols/vfs.cpp
Normal file
57
src/libraries/j6/protocols/vfs.cpp
Normal 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
|
||||
@@ -179,7 +179,126 @@ private:
|
||||
uint32_t m_bits;
|
||||
};
|
||||
|
||||
/// A statically-sized templated bitset
|
||||
template <>
|
||||
class bitset<16>
|
||||
{
|
||||
template <typename T>
|
||||
static constexpr uint16_t bit_or(T b) { return 1u << uint16_t(b); }
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
static constexpr uint16_t bit_or(T b, Args... bs) { return (1u << uint16_t(b)) | bit_or(bs...); }
|
||||
|
||||
public:
|
||||
bitset(uint16_t v = 0) : m_bits {v} {}
|
||||
|
||||
bitset(const bitset<16> &o) : m_bits {o.m_bits} {}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
|
||||
|
||||
template <typename T>
|
||||
inline bitset & operator=(T v) { m_bits = static_cast<uint16_t>(v); return *this; }
|
||||
|
||||
inline constexpr operator uint16_t () const { return m_bits; }
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline constexpr bool get(T i) const {
|
||||
return m_bits & bit(i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bitset & set(T i) {
|
||||
m_bits |= bit(i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bitset & clear(T i) {
|
||||
m_bits &= ~bit(i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bool operator[](T i) const { return get(i); }
|
||||
|
||||
inline bool empty() const { return m_bits == 0; }
|
||||
|
||||
inline constexpr uint16_t value() const { return m_bits; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
inline uint16_t bit(T i) const { return (1u << static_cast<uint16_t>(i)); }
|
||||
|
||||
uint16_t m_bits;
|
||||
};
|
||||
|
||||
|
||||
/// A statically-sized templated bitset
|
||||
template <>
|
||||
class bitset<8>
|
||||
{
|
||||
template <typename T>
|
||||
static constexpr uint8_t bit_or(T b) { return 1u << uint8_t(b); }
|
||||
|
||||
template <typename T, typename ...Args>
|
||||
static constexpr uint8_t bit_or(T b, Args... bs) { return (1u << uint8_t(b)) | bit_or(bs...); }
|
||||
|
||||
public:
|
||||
bitset(uint8_t v = 0) : m_bits {v} {}
|
||||
|
||||
bitset(const bitset<8> &o) : m_bits {o.m_bits} {}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
|
||||
|
||||
template <typename T>
|
||||
inline bitset & operator=(T v) { m_bits = static_cast<uint8_t>(v); return *this; }
|
||||
|
||||
inline constexpr operator uint8_t () const { return m_bits; }
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline constexpr bool get(T i) const {
|
||||
return m_bits & bit(i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bitset & set(T i) {
|
||||
m_bits |= bit(i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bitset & clear(T i) {
|
||||
m_bits &= ~bit(i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
__attribute__ ((force_inline))
|
||||
inline bool operator[](T i) const { return get(i); }
|
||||
|
||||
inline bool empty() const { return m_bits == 0; }
|
||||
|
||||
inline constexpr uint8_t value() const { return m_bits; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
inline uint8_t bit(T i) const { return (1u << static_cast<uint8_t>(i)); }
|
||||
|
||||
uint8_t m_bits;
|
||||
};
|
||||
|
||||
using bitset64 = bitset<64>;
|
||||
using bitset32 = bitset<32>;
|
||||
using bitset16 = bitset<16>;
|
||||
using bitset8 = bitset<8>;
|
||||
|
||||
} // namespace util
|
||||
|
||||
Reference in New Issue
Block a user