mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[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:
@@ -5,6 +5,6 @@ ccflags: [
|
|||||||
|
|
||||||
ldflags: [
|
ldflags: [
|
||||||
"-pie",
|
"-pie",
|
||||||
"--dynamic-linker", "/tools/ld.so",
|
"--dynamic-linker", "/jsix/tools/ld.so",
|
||||||
"-lc++", "-lc++abi", "-lunwind",
|
"-lc++", "-lc++abi", "-lunwind",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -15,11 +15,8 @@ ccflags: [
|
|||||||
"-U__linux__",
|
"-U__linux__",
|
||||||
|
|
||||||
"--sysroot='${source_root}/sysroot'",
|
"--sysroot='${source_root}/sysroot'",
|
||||||
|
|
||||||
"-fpic"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
cxxflags: [
|
cxxflags: [
|
||||||
"-fno-exceptions",
|
"-fno-exceptions",
|
||||||
"-fno-rtti",
|
"-fno-rtti",
|
||||||
@@ -32,5 +29,5 @@ ldflags: [
|
|||||||
"-L", "${source_root}/sysroot/lib",
|
"-L", "${source_root}/sysroot/lib",
|
||||||
"-z", "separate-code",
|
"-z", "separate-code",
|
||||||
"--no-dependent-libraries",
|
"--no-dependent-libraries",
|
||||||
|
"-Bstatic",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -12,3 +12,5 @@ services:
|
|||||||
drivers:
|
drivers:
|
||||||
- drv.uart
|
- drv.uart
|
||||||
- drv.uefi_fb
|
- drv.uefi_fb
|
||||||
|
tools:
|
||||||
|
- ld.so
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <util/counted.h>
|
#include <util/counted.h>
|
||||||
#include <util/enum_bitfields.h>
|
|
||||||
|
|
||||||
namespace bootproto {
|
namespace bootproto {
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,74 @@
|
|||||||
/// \file init.h
|
/// \file init.h
|
||||||
/// Process initialization utility functions
|
/// Process initialization utility functions
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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
|
struct j6_init_args
|
||||||
{
|
{
|
||||||
uint64_t args[2];
|
uint64_t args[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// Find the first handle of the given type held by this process
|
/// Find the first handle of the given type held by this process
|
||||||
j6_handle_t j6_find_first_handle(j6_object_type obj_type);
|
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
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#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",
|
"mutex.cpp",
|
||||||
"protocol_ids.cpp",
|
"protocol_ids.cpp",
|
||||||
"protocols/service_locator.cpp",
|
"protocols/service_locator.cpp",
|
||||||
|
"protocols/vfs.cpp",
|
||||||
"syscalls.s.cog",
|
"syscalls.s.cog",
|
||||||
"sysconf.cpp.cog",
|
"sysconf.cpp.cog",
|
||||||
"syslog.cpp",
|
"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;
|
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 bitset64 = bitset<64>;
|
||||||
using bitset32 = bitset<32>;
|
using bitset32 = bitset<32>;
|
||||||
|
using bitset16 = bitset<16>;
|
||||||
|
using bitset8 = bitset<8>;
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|||||||
15
src/user/ld.so/ld.so.module
Normal file
15
src/user/ld.so/ld.so.module
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# vim: ft=python
|
||||||
|
|
||||||
|
ldso = module("ld.so",
|
||||||
|
kind = "shared",
|
||||||
|
output = "ld.so",
|
||||||
|
targets = [ "user" ],
|
||||||
|
deps = [ "libc", "util", "elf" ],
|
||||||
|
description = "Dynamic Linker",
|
||||||
|
sources = [
|
||||||
|
"main.cpp",
|
||||||
|
"relocate.cpp",
|
||||||
|
"start.s",
|
||||||
|
])
|
||||||
|
|
||||||
|
ldso.variables["ldflags"] = ["${ldflags}", "--entry=_ldso_start"]
|
||||||
70
src/user/ld.so/main.cpp
Normal file
70
src/user/ld.so/main.cpp
Normal 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;
|
||||||
|
}
|
||||||
155
src/user/ld.so/relocate.cpp
Normal file
155
src/user/ld.so/relocate.cpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <j6/syslog.hh>
|
||||||
|
#include <util/counted.h>
|
||||||
|
#include "relocate.h"
|
||||||
|
|
||||||
|
enum class dyn_type : uint64_t {
|
||||||
|
null, needed, pltrelsz, pltgot, hash, strtab, symtab, rela, relasz, relaent,
|
||||||
|
strsz, syment, init, fini, soname, rpath, symbolic, rel, relsz, relent, pltrel,
|
||||||
|
debug, textrel, jmprel, bind_now, init_array, fini_array, init_arraysz, fini_arraysz,
|
||||||
|
gnu_hash = 0x6ffffef5, relacount = 0x6ffffff9,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dyn_entry {
|
||||||
|
dyn_type tag;
|
||||||
|
uintptr_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dyn_values {
|
||||||
|
uintptr_t pltrelsz;
|
||||||
|
uintptr_t pltgot;
|
||||||
|
uintptr_t hash;
|
||||||
|
uintptr_t pltrel;
|
||||||
|
uintptr_t strtab;
|
||||||
|
uintptr_t symtab;
|
||||||
|
uintptr_t rela;
|
||||||
|
uintptr_t relasz;
|
||||||
|
uintptr_t relaent;
|
||||||
|
uintptr_t strsz;
|
||||||
|
uintptr_t syment;
|
||||||
|
uintptr_t jmprel;
|
||||||
|
uintptr_t relacount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define read_dyn_value(name) \
|
||||||
|
case dyn_type::name: \
|
||||||
|
values.name = dyn.value; \
|
||||||
|
break;
|
||||||
|
|
||||||
|
void
|
||||||
|
read_dynamic_values(uintptr_t base, uintptr_t dyn_section, dyn_values &values)
|
||||||
|
{
|
||||||
|
const dyn_entry *dyns = reinterpret_cast<const dyn_entry*>(dyn_section + base);
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
while (true) {
|
||||||
|
const dyn_entry &dyn = dyns[i++];
|
||||||
|
switch (dyn.tag) {
|
||||||
|
case dyn_type::null:
|
||||||
|
return;
|
||||||
|
|
||||||
|
read_dyn_value(pltrelsz);
|
||||||
|
read_dyn_value(pltgot);
|
||||||
|
read_dyn_value(pltrel);
|
||||||
|
read_dyn_value(strtab);
|
||||||
|
read_dyn_value(symtab);
|
||||||
|
read_dyn_value(rela);
|
||||||
|
read_dyn_value(relasz);
|
||||||
|
read_dyn_value(relaent);
|
||||||
|
read_dyn_value(strsz);
|
||||||
|
read_dyn_value(syment);
|
||||||
|
read_dyn_value(jmprel);
|
||||||
|
read_dyn_value(relacount);
|
||||||
|
|
||||||
|
case dyn_type::gnu_hash:
|
||||||
|
values.hash = dyn.value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef read_dyn_value(name)
|
||||||
|
|
||||||
|
class string_table :
|
||||||
|
private util::counted<const char>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
string_table(const char *start, size_t size) :
|
||||||
|
util::counted<const char> {start, size} {}
|
||||||
|
|
||||||
|
const char *lookup(size_t offset) {
|
||||||
|
if (offset > count) return nullptr;
|
||||||
|
return pointer + offset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct symbol
|
||||||
|
{
|
||||||
|
uint32_t name;
|
||||||
|
uint8_t type : 4;
|
||||||
|
uint8_t binding : 4;
|
||||||
|
uint8_t _reserved0;
|
||||||
|
uint16_t section;
|
||||||
|
uintptr_t address;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gnu_hash_table
|
||||||
|
{
|
||||||
|
uint32_t bucket_count;
|
||||||
|
uint32_t start_symbol;
|
||||||
|
uint32_t bloom_count;
|
||||||
|
uint32_t bloom_shift;
|
||||||
|
uint64_t bloom [0];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class reloc : uint32_t {
|
||||||
|
relative = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rela
|
||||||
|
{
|
||||||
|
uintptr_t address;
|
||||||
|
reloc type;
|
||||||
|
uint32_t symbol;
|
||||||
|
ptrdiff_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> T off(uintptr_t p, uintptr_t base) { return reinterpret_cast<T>(p ? p + base : 0); }
|
||||||
|
|
||||||
|
void
|
||||||
|
relocate_image(uintptr_t base, uintptr_t dyn_section)
|
||||||
|
{
|
||||||
|
dyn_values values = {0};
|
||||||
|
read_dynamic_values(base, dyn_section, values);
|
||||||
|
|
||||||
|
string_table strtab {
|
||||||
|
reinterpret_cast<const char *>(values.strtab + base),
|
||||||
|
values.strsz,
|
||||||
|
};
|
||||||
|
|
||||||
|
symbol *symtab = off<symbol *>(values.symtab, base);
|
||||||
|
gnu_hash_table *hashtab = off<gnu_hash_table *>(values.hash, base);
|
||||||
|
|
||||||
|
uintptr_t *jmprel = off<uintptr_t *>(values.jmprel, base);
|
||||||
|
uintptr_t *plt = off<uintptr_t *>(values.pltgot, base);
|
||||||
|
|
||||||
|
size_t nrela = values.relacount;
|
||||||
|
for (size_t i = 0; i < values.relacount; ++i) {
|
||||||
|
rela *rel = off<rela*>(values.rela + i * values.relaent, base);
|
||||||
|
switch (rel->type)
|
||||||
|
{
|
||||||
|
case reloc::relative:
|
||||||
|
*reinterpret_cast<uint64_t*>(rel->address + base) = base + rel->offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
j6::syslog("Unknown relocation type %d", rel->type);
|
||||||
|
exit(126);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/user/ld.so/relocate.h
Normal file
7
src/user/ld.so/relocate.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file relocate.h
|
||||||
|
/// Image relocation services
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void relocate_image(uintptr_t base, uintptr_t dyn_section);
|
||||||
33
src/user/ld.so/start.s
Normal file
33
src/user/ld.so/start.s
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
extern ldso_init
|
||||||
|
extern _GLOBAL_OFFSET_TABLE_
|
||||||
|
|
||||||
|
global _ldso_start:function hidden (_ldso_start.end - _ldso_start)
|
||||||
|
_ldso_start:
|
||||||
|
mov rbp, rsp
|
||||||
|
|
||||||
|
; Save off anything that might be a function arg
|
||||||
|
push rdi
|
||||||
|
push rsi
|
||||||
|
push rdx
|
||||||
|
push rcx
|
||||||
|
push r8
|
||||||
|
push r9
|
||||||
|
|
||||||
|
; Call ldso_init with the loader-provided stack data and
|
||||||
|
; also the address of the GOT, since clang refuses to take
|
||||||
|
; the address of it, only dereference it.
|
||||||
|
mov rdi, rbp
|
||||||
|
lea rsi, [rel _GLOBAL_OFFSET_TABLE_]
|
||||||
|
call ldso_init
|
||||||
|
; The real program's entrypoint is now in rax
|
||||||
|
|
||||||
|
; Put the function call params back
|
||||||
|
pop r9
|
||||||
|
pop r8
|
||||||
|
pop rcx
|
||||||
|
pop rdx
|
||||||
|
pop rsi
|
||||||
|
pop rdi
|
||||||
|
|
||||||
|
jmp rax
|
||||||
|
.end:
|
||||||
@@ -7,6 +7,7 @@ init = module("srv.init",
|
|||||||
ld_script = "init.ld",
|
ld_script = "init.ld",
|
||||||
sources = [
|
sources = [
|
||||||
"acpi.cpp",
|
"acpi.cpp",
|
||||||
|
"initfs.cpp",
|
||||||
"j6romfs.cpp",
|
"j6romfs.cpp",
|
||||||
"loader.cpp",
|
"loader.cpp",
|
||||||
"main.cpp",
|
"main.cpp",
|
||||||
|
|||||||
94
src/user/srv.init/initfs.cpp
Normal file
94
src/user/srv.init/initfs.cpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <j6/flags.h>
|
||||||
|
#include <j6/errors.h>
|
||||||
|
#include <j6/protocols/vfs.h>
|
||||||
|
#include <j6/syscalls.h>
|
||||||
|
#include <j6/syslog.hh>
|
||||||
|
|
||||||
|
#include "j6romfs.h"
|
||||||
|
#include "initfs.h"
|
||||||
|
|
||||||
|
static uint64_t initfs_running = 1;
|
||||||
|
static constexpr size_t buffer_size = 2048;
|
||||||
|
static constexpr uintptr_t load_addr = 0xf00000000;
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
handle_load_request(j6romfs::fs &fs, const char *path, j6_handle_t &vma)
|
||||||
|
{
|
||||||
|
const j6romfs::inode *in = fs.lookup_inode(path);
|
||||||
|
if (!in) {
|
||||||
|
vma = j6_handle_invalid;
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_vma_create_map(&vma, in->size, load_addr, j6_vm_flag_write);
|
||||||
|
util::buffer dest = util::buffer::from(load_addr, in->size);
|
||||||
|
fs.load_inode_data(in, dest);
|
||||||
|
j6_vma_unmap(vma, 0);
|
||||||
|
|
||||||
|
return j6_err_nyi;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sanitize(char *s, size_t len)
|
||||||
|
{
|
||||||
|
if (len >= buffer_size) len--;
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
if (!s || !*s) return;
|
||||||
|
s[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
initfs_start(j6romfs::fs &fs, j6_handle_t mb)
|
||||||
|
{
|
||||||
|
uint64_t tag = 0;
|
||||||
|
|
||||||
|
char *buffer = new char [buffer_size];
|
||||||
|
size_t out_len = buffer_size;
|
||||||
|
|
||||||
|
uint64_t reply_tag = 0;
|
||||||
|
|
||||||
|
size_t handles_count = 1;
|
||||||
|
j6_handle_t give_handle = j6_handle_invalid;
|
||||||
|
uint64_t proto_id;
|
||||||
|
|
||||||
|
j6_status_t s = j6_mailbox_respond(mb, &tag,
|
||||||
|
buffer, &out_len, 0,
|
||||||
|
&give_handle, &handles_count,
|
||||||
|
&reply_tag, j6_flag_block);
|
||||||
|
|
||||||
|
while (initfs_running) {
|
||||||
|
if (s != j6_status_ok) {
|
||||||
|
j6::syslog("initfs: error in j6_mailbox_respond: %x", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t data_out = 0;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case j6_proto_vfs_load:
|
||||||
|
sanitize(buffer, out_len);
|
||||||
|
s = handle_load_request(fs, buffer, give_handle);
|
||||||
|
if (s != j6_status_ok) {
|
||||||
|
tag = j6_proto_base_status;
|
||||||
|
*reinterpret_cast<j6_status_t*>(buffer) = s;
|
||||||
|
data_out = sizeof(j6_status_t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tag = j6_proto_vfs_file;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
tag = j6_proto_base_status;
|
||||||
|
*reinterpret_cast<j6_status_t*>(buffer) = j6_err_invalid_arg;
|
||||||
|
data_out = sizeof(j6_status_t);
|
||||||
|
give_handle = j6_handle_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_len = buffer_size;
|
||||||
|
s = j6_mailbox_respond(mb, &tag,
|
||||||
|
buffer, &out_len, data_out,
|
||||||
|
&give_handle, &handles_count,
|
||||||
|
&reply_tag, j6_flag_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/user/srv.init/initfs.h
Normal file
10
src/user/srv.init/initfs.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file initfs.h
|
||||||
|
/// Temporary VFS service until we can load the real VFS service
|
||||||
|
|
||||||
|
#include <j6/types.h>
|
||||||
|
|
||||||
|
namespace j6romfs { class fs; }
|
||||||
|
|
||||||
|
void initfs_start(j6romfs::fs &fs, j6_handle_t mb);
|
||||||
|
void initfs_stop();
|
||||||
@@ -6,17 +6,22 @@
|
|||||||
#include <elf/headers.h>
|
#include <elf/headers.h>
|
||||||
#include <j6/errors.h>
|
#include <j6/errors.h>
|
||||||
#include <j6/flags.h>
|
#include <j6/flags.h>
|
||||||
|
#include <j6/init.h>
|
||||||
|
#include <j6/protocols.h>
|
||||||
#include <j6/syscalls.h>
|
#include <j6/syscalls.h>
|
||||||
#include <j6/syslog.hh>
|
#include <j6/syslog.hh>
|
||||||
#include <util/enum_bitfields.h>
|
|
||||||
|
|
||||||
|
#include "j6romfs.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
|
||||||
using bootproto::module;
|
using bootproto::module;
|
||||||
|
|
||||||
constexpr uintptr_t load_addr = 0xf8000000;
|
static uintptr_t load_addr = 0xf'000'0000;
|
||||||
constexpr size_t stack_size = 0x10000;
|
static constexpr size_t stack_size = 0x10000;
|
||||||
constexpr uintptr_t stack_top = 0x80000000000;
|
static constexpr uintptr_t stack_top = 0x80000000000;
|
||||||
|
static constexpr size_t MiB = 0x10'0000ull;
|
||||||
|
|
||||||
|
inline uintptr_t align_up(uintptr_t a) { return ((a-1) & ~(MiB-1)) + MiB; }
|
||||||
|
|
||||||
j6_handle_t
|
j6_handle_t
|
||||||
map_phys(j6_handle_t sys, uintptr_t phys, size_t len, j6_vm_flags flags)
|
map_phys(j6_handle_t sys, uintptr_t phys, size_t len, j6_vm_flags flags)
|
||||||
@@ -33,125 +38,248 @@ map_phys(j6_handle_t sys, uintptr_t phys, size_t len, j6_vm_flags flags)
|
|||||||
return vma;
|
return vma;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
load_program(
|
stack_push_sentinel(uint8_t *&stack)
|
||||||
const char *name,
|
|
||||||
util::const_buffer data,
|
|
||||||
j6_handle_t sys, j6_handle_t slp,
|
|
||||||
const module *arg)
|
|
||||||
{
|
{
|
||||||
uintptr_t base_address = reinterpret_cast<uintptr_t>(data.pointer);
|
static constexpr size_t size = sizeof(j6_arg_header);
|
||||||
|
|
||||||
elf::file progelf {data};
|
stack -= size;
|
||||||
bool dyn = progelf.type() == elf::filetype::shared;
|
memset(stack, 0, size);
|
||||||
uintptr_t image_base = dyn ? 0xa'0000'0000 : 0;
|
j6_arg_header *header = reinterpret_cast<j6_arg_header*>(stack);
|
||||||
|
header->type = j6_arg_type_none;
|
||||||
|
header->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!progelf.valid(dyn ? elf::filetype::shared : elf::filetype::executable)) {
|
template <typename T> T *
|
||||||
j6::syslog(" ** error loading program '%s': ELF is invalid", name);
|
stack_push(uint8_t *&stack, size_t extra)
|
||||||
return false;
|
{
|
||||||
}
|
size_t len = sizeof(T) + extra;
|
||||||
|
size_t size = (len + 7) & ~7ull;
|
||||||
|
stack -= size;
|
||||||
|
memset(stack, 0, sizeof(T));
|
||||||
|
T * arg = reinterpret_cast<T*>(stack);
|
||||||
|
arg->header.type = T::type_id;
|
||||||
|
arg->header.size = len;
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
j6_handle_t proc = j6_handle_invalid;
|
uintptr_t
|
||||||
j6_status_t res = j6_process_create(&proc);
|
load_program_into(j6_handle_t proc, elf::file &file, uintptr_t image_base, const char *path)
|
||||||
if (res != j6_status_ok) {
|
{
|
||||||
j6::syslog(" ** error loading program '%s': creating process: %lx", name, res);
|
uintptr_t eop = 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = j6_process_give_handle(proc, sys);
|
for (auto &seg : file.segments()) {
|
||||||
if (res != j6_status_ok) {
|
|
||||||
j6::syslog(" ** error loading program '%s': giving system handle: %lx", name, res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = j6_process_give_handle(proc, slp);
|
|
||||||
if (res != j6_status_ok) {
|
|
||||||
j6::syslog(" ** error loading program '%s': giving SLP handle: %lx", name, res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &seg : progelf.segments()) {
|
|
||||||
if (seg.type != elf::segment_type::load)
|
if (seg.type != elf::segment_type::load)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
uintptr_t addr = load_addr;
|
||||||
|
load_addr = align_up(load_addr + seg.mem_size);
|
||||||
|
|
||||||
// TODO: way to remap VMA as read-only if there's no write flag on
|
// TODO: way to remap VMA as read-only if there's no write flag on
|
||||||
// the segment
|
// the segment
|
||||||
unsigned long flags = j6_vm_flag_write;
|
unsigned long flags = j6_vm_flag_write;
|
||||||
if (seg.flags && elf::segment_flags::exec)
|
if (seg.flags && elf::segment_flags::exec)
|
||||||
flags |= j6_vm_flag_exec;
|
flags |= j6_vm_flag_exec;
|
||||||
|
|
||||||
uintptr_t start = base_address + seg.offset;
|
uintptr_t start = file.base() + seg.offset;
|
||||||
size_t prologue = seg.vaddr & 0xfff;
|
size_t prologue = seg.vaddr & 0xfff;
|
||||||
size_t epilogue = seg.mem_size - seg.file_size;
|
size_t epilogue = seg.mem_size - seg.file_size;
|
||||||
|
|
||||||
j6_handle_t sub_vma = j6_handle_invalid;
|
j6_handle_t sub_vma = j6_handle_invalid;
|
||||||
res = j6_vma_create_map(&sub_vma, seg.mem_size+prologue, load_addr, flags);
|
j6_status_t res = j6_vma_create_map(&sub_vma, seg.mem_size+prologue, addr, flags);
|
||||||
if (res != j6_status_ok) {
|
if (res != j6_status_ok) {
|
||||||
j6::syslog(" ** error loading program '%s': creating sub vma: %lx", name, res);
|
j6::syslog(" ** error loading ELF '%s': creating sub vma: %lx", path, res);
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *src = reinterpret_cast<uint8_t *>(start);
|
uint8_t *src = reinterpret_cast<uint8_t *>(start);
|
||||||
uint8_t *dest = reinterpret_cast<uint8_t *>(load_addr);
|
uint8_t *dest = reinterpret_cast<uint8_t *>(addr);
|
||||||
memset(dest, 0, prologue);
|
memset(dest, 0, prologue);
|
||||||
memcpy(dest+prologue, src, seg.file_size);
|
memcpy(dest+prologue, src, seg.file_size);
|
||||||
memset(dest+prologue+seg.file_size, 0, epilogue);
|
memset(dest+prologue+seg.file_size, 0, epilogue);
|
||||||
|
|
||||||
res = j6_vma_map(sub_vma, proc, (image_base + seg.vaddr) & ~0xfffull);
|
uintptr_t eos = image_base + seg.vaddr + seg.mem_size + prologue;
|
||||||
|
if (eos > eop)
|
||||||
|
eop = eos;
|
||||||
|
|
||||||
|
uintptr_t start_addr = (image_base + seg.vaddr);
|
||||||
|
j6::syslog("Mapping segment from %s at %012lx - %012lx", path, start_addr, start_addr+seg.mem_size);
|
||||||
|
res = j6_vma_map(sub_vma, proc, start_addr & ~0xfffull);
|
||||||
if (res != j6_status_ok) {
|
if (res != j6_status_ok) {
|
||||||
j6::syslog(" ** error loading program '%s': mapping sub vma to child: %lx", name, res);
|
j6::syslog(" ** error loading ELF '%s': mapping sub vma to child: %lx", path, res);
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = j6_vma_unmap(sub_vma, 0);
|
res = j6_vma_unmap(sub_vma, 0);
|
||||||
if (res != j6_status_ok) {
|
if (res != j6_status_ok) {
|
||||||
j6::syslog(" ** error loading program '%s': unmapping sub vma: %lx", name, res);
|
j6::syslog(" ** error loading ELF '%s': unmapping sub vma: %lx", path, res);
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
j6_handle_t stack_vma = j6_handle_invalid;
|
return eop;
|
||||||
res = j6_vma_create_map(&stack_vma, stack_size, load_addr, j6_vm_flag_write);
|
}
|
||||||
if (res != j6_status_ok) {
|
|
||||||
j6::syslog(" ** error loading program '%s': creating stack vma: %lx", name, res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t arg0 = 0;
|
static bool
|
||||||
|
give_handle(j6_handle_t proc, j6_handle_t h, const char *name)
|
||||||
uint64_t *stack = reinterpret_cast<uint64_t*>(load_addr + stack_size);
|
{
|
||||||
memset(stack - 512, 0, 512 * sizeof(uint64_t)); // Zero top page
|
if (h != j6_handle_invalid) {
|
||||||
|
j6_status_t res = j6_process_give_handle(proc, h);
|
||||||
size_t stack_consumed = 0;
|
if (res != j6_status_ok) {
|
||||||
|
j6::syslog(" ** error loading program: giving %s handle: %lx", name, res);
|
||||||
if (arg) {
|
return false;
|
||||||
size_t arg_size = arg->bytes - sizeof(module);
|
}
|
||||||
const uint8_t *arg_data = arg->data<uint8_t>();
|
|
||||||
uint8_t *arg_dest = reinterpret_cast<uint8_t*>(stack) - arg_size;
|
|
||||||
memcpy(arg_dest, arg_data, arg_size);
|
|
||||||
stack_consumed += arg_size;
|
|
||||||
arg0 = stack_top - stack_consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = j6_vma_map(stack_vma, proc, stack_top-stack_size);
|
|
||||||
if (res != j6_status_ok) {
|
|
||||||
j6::syslog(" ** error loading program '%s': mapping stack vma: %lx", name, res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = j6_vma_unmap(stack_vma, 0);
|
|
||||||
if (res != j6_status_ok) {
|
|
||||||
j6::syslog(" ** error loading program '%s': unmapping stack vma: %lx", name, res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_handle_t thread = j6_handle_invalid;
|
|
||||||
res = j6_thread_create(&thread, proc, stack_top - stack_consumed, image_base + progelf.entrypoint(), image_base, arg1);
|
|
||||||
if (res != j6_status_ok) {
|
|
||||||
j6::syslog(" ** error loading program '%s': creating thread: %lx", name, res);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static j6_handle_t
|
||||||
|
create_process(j6_handle_t sys, j6_handle_t slp, j6_handle_t vfs)
|
||||||
|
{
|
||||||
|
j6_handle_t proc = j6_handle_invalid;
|
||||||
|
j6_status_t res = j6_process_create(&proc);
|
||||||
|
if (res != j6_status_ok) {
|
||||||
|
j6::syslog(" ** error loading program: creating process: %lx", res);
|
||||||
|
return j6_handle_invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!give_handle(proc, sys, "system")) return j6_handle_invalid;
|
||||||
|
if (!give_handle(proc, slp, "SLP")) return j6_handle_invalid;
|
||||||
|
if (!give_handle(proc, vfs, "VFS")) return j6_handle_invalid;
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static util::buffer
|
||||||
|
load_file(const j6romfs::fs &fs, const char *path)
|
||||||
|
{
|
||||||
|
const j6romfs::inode *in = fs.lookup_inode(path);
|
||||||
|
if (!in || in->type != j6romfs::inode_type::file)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
j6::syslog(" Loading file: %s", path);
|
||||||
|
|
||||||
|
uint8_t *data = new uint8_t [in->size];
|
||||||
|
util::buffer program {data, in->size};
|
||||||
|
|
||||||
|
fs.load_inode_data(in, program);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
load_program(
|
||||||
|
const char *path, const j6romfs::fs &fs,
|
||||||
|
j6_handle_t sys, j6_handle_t slp, j6_handle_t vfs,
|
||||||
|
const module *arg)
|
||||||
|
{
|
||||||
|
j6::syslog("Loading program '%s' into new process", path);
|
||||||
|
util::buffer program_data = load_file(fs, path);
|
||||||
|
if (!program_data.pointer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
elf::file program_elf {program_data};
|
||||||
|
|
||||||
|
bool dyn = program_elf.type() == elf::filetype::shared;
|
||||||
|
uintptr_t program_image_base = dyn ? 0xa00'0000 : 0;
|
||||||
|
|
||||||
|
if (!program_elf.valid(dyn ? elf::filetype::shared : elf::filetype::executable)) {
|
||||||
|
j6::syslog(" ** error loading program '%s': ELF is invalid", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_handle_t proc = create_process(sys, slp, vfs);
|
||||||
|
uintptr_t eop = load_program_into(proc, program_elf, program_image_base, path);
|
||||||
|
if (!eop)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uintptr_t stack_addr = load_addr;
|
||||||
|
load_addr = align_up(load_addr + stack_size);
|
||||||
|
j6_handle_t stack_vma = j6_handle_invalid;
|
||||||
|
j6_status_t res = j6_vma_create_map(&stack_vma, stack_size, stack_addr, j6_vm_flag_write);
|
||||||
|
if (res != j6_status_ok) {
|
||||||
|
j6::syslog(" ** error loading program '%s': creating stack vma: %lx", path, res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *stack_orig = reinterpret_cast<uint8_t*>(stack_addr + stack_size);
|
||||||
|
uint8_t *stack = stack_orig;
|
||||||
|
memset(stack - 4096, 0, 4096); // Zero top page
|
||||||
|
stack_push_sentinel(stack);
|
||||||
|
|
||||||
|
if (arg) {
|
||||||
|
size_t data_size = arg->bytes - sizeof(module);
|
||||||
|
j6_arg_driver *driver_arg = stack_push<j6_arg_driver>(stack, data_size);
|
||||||
|
driver_arg->device = arg->type_id;
|
||||||
|
|
||||||
|
const uint8_t *arg_data = arg->data<uint8_t>();
|
||||||
|
memcpy(driver_arg->data, arg_data, data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t entrypoint = program_elf.entrypoint() + program_image_base;
|
||||||
|
|
||||||
|
if (dyn) {
|
||||||
|
j6_arg_loader *loader_arg = stack_push<j6_arg_loader>(stack, 0);
|
||||||
|
const elf::file_header *h = program_elf.header();
|
||||||
|
loader_arg->image_base = program_image_base;
|
||||||
|
loader_arg->phdr = h->ph_offset;
|
||||||
|
loader_arg->phdr_size = h->ph_entsize;
|
||||||
|
loader_arg->phdr_count = h->ph_num;
|
||||||
|
loader_arg->entrypoint = program_elf.entrypoint();
|
||||||
|
|
||||||
|
j6_arg_handles *handles_arg = stack_push<j6_arg_handles>(stack, 2 * sizeof(j6_arg_handle_entry));
|
||||||
|
handles_arg->nhandles = 1;
|
||||||
|
handles_arg->handles[0].handle = slp;
|
||||||
|
handles_arg->handles[0].proto = j6::proto::sl::id;
|
||||||
|
handles_arg->handles[1].handle = vfs;
|
||||||
|
handles_arg->handles[1].proto = j6::proto::vfs::id;
|
||||||
|
|
||||||
|
uintptr_t ldso_image_base = (eop & ~(MiB-1)) + MiB;
|
||||||
|
|
||||||
|
for (auto seg : program_elf.segments()) {
|
||||||
|
if (seg.type == elf::segment_type::interpreter) {
|
||||||
|
const char *ldso_path = reinterpret_cast<const char*>(program_elf.base() + seg.offset);
|
||||||
|
util::buffer ldso_data = load_file(fs, ldso_path);
|
||||||
|
if (!ldso_data.pointer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
elf::file ldso_elf {ldso_data};
|
||||||
|
if (!ldso_elf.valid(elf::filetype::shared)) {
|
||||||
|
j6::syslog(" ** error loading dynamic linker for program '%s': ELF is invalid", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_program_into(proc, ldso_elf, ldso_image_base, ldso_path);
|
||||||
|
loader_arg->loader_base = ldso_image_base;
|
||||||
|
entrypoint = ldso_elf.entrypoint() + ldso_image_base;
|
||||||
|
delete [] reinterpret_cast<uint8_t*>(ldso_data.pointer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = j6_vma_map(stack_vma, proc, stack_top-stack_size);
|
||||||
|
if (res != j6_status_ok) {
|
||||||
|
j6::syslog(" ** error loading program '%s': mapping stack vma: %lx", path, res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = j6_vma_unmap(stack_vma, 0);
|
||||||
|
if (res != j6_status_ok) {
|
||||||
|
j6::syslog(" ** error loading program '%s': unmapping stack vma: %lx", path, res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_handle_t thread = j6_handle_invalid;
|
||||||
|
ptrdiff_t stack_consumed = stack_orig - stack;
|
||||||
|
res = j6_thread_create(&thread, proc, stack_top - stack_consumed, entrypoint, program_image_base, 0);
|
||||||
|
if (res != j6_status_ok) {
|
||||||
|
j6::syslog(" ** error loading program '%s': creating thread: %lx", path, res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: smart pointer this, it's a memory leak if we return early
|
||||||
|
delete [] reinterpret_cast<uint8_t*>(program_data.pointer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ namespace bootproto {
|
|||||||
struct module;
|
struct module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace j6romfs {
|
||||||
|
class fs;
|
||||||
|
}
|
||||||
|
|
||||||
bool load_program(
|
bool load_program(
|
||||||
const char *name,
|
const char *path, const j6romfs::fs &fs,
|
||||||
util::const_buffer data,
|
j6_handle_t sys, j6_handle_t slp, j6_handle_t vfs,
|
||||||
j6_handle_t sys, j6_handle_t slp,
|
|
||||||
const bootproto::module *arg = nullptr);
|
const bootproto::module *arg = nullptr);
|
||||||
|
|
||||||
j6_handle_t map_phys(j6_handle_t sys, uintptr_t phys, size_t len, j6_vm_flags flags = j6_vm_flag_none);
|
j6_handle_t map_phys(j6_handle_t sys, uintptr_t phys, size_t len, j6_vm_flags flags = j6_vm_flag_none);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <j6/init.h>
|
#include <j6/init.h>
|
||||||
#include <j6/syscalls.h>
|
#include <j6/syscalls.h>
|
||||||
#include <j6/syslog.hh>
|
#include <j6/syslog.hh>
|
||||||
|
#include <j6/thread.hh>
|
||||||
#include <j6/types.h>
|
#include <j6/types.h>
|
||||||
|
|
||||||
#include <bootproto/acpi.h>
|
#include <bootproto/acpi.h>
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
#include <bootproto/devices/framebuffer.h>
|
#include <bootproto/devices/framebuffer.h>
|
||||||
|
|
||||||
#include "acpi.h"
|
#include "acpi.h"
|
||||||
|
#include "initfs.h"
|
||||||
#include "j6romfs.h"
|
#include "j6romfs.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
#include "modules.h"
|
#include "modules.h"
|
||||||
@@ -22,30 +24,7 @@
|
|||||||
using bootproto::module;
|
using bootproto::module;
|
||||||
using bootproto::module_type;
|
using bootproto::module_type;
|
||||||
|
|
||||||
void
|
constexpr uintptr_t stack_top = 0xf80000000;
|
||||||
load_driver(
|
|
||||||
j6romfs::fs &initrd,
|
|
||||||
const j6romfs::inode *dir,
|
|
||||||
const char *name,
|
|
||||||
j6_handle_t sys,
|
|
||||||
j6_handle_t slp,
|
|
||||||
const module *arg = nullptr)
|
|
||||||
{
|
|
||||||
const j6romfs::inode *in = initrd.lookup_inode_in_dir(dir, name);
|
|
||||||
|
|
||||||
if (in->type != j6romfs::inode_type::file)
|
|
||||||
return;
|
|
||||||
|
|
||||||
j6::syslog("Loading driver: %s", name);
|
|
||||||
|
|
||||||
uint8_t *data = new uint8_t [in->size];
|
|
||||||
util::buffer program {data, in->size};
|
|
||||||
|
|
||||||
initrd.load_inode_data(in, program);
|
|
||||||
load_program(name, program, sys, slp, arg);
|
|
||||||
|
|
||||||
delete [] data;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
driver_main(unsigned argc, const char **argv, const char **env, const j6_init_args *initp)
|
driver_main(unsigned argc, const char **argv, const char **env, const j6_init_args *initp)
|
||||||
@@ -58,6 +37,9 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
|||||||
j6_handle_t sys = j6_handle_invalid;
|
j6_handle_t sys = j6_handle_invalid;
|
||||||
j6_handle_t sys_child = j6_handle_invalid;
|
j6_handle_t sys_child = j6_handle_invalid;
|
||||||
|
|
||||||
|
j6_handle_t vfs_mb = j6_handle_invalid;
|
||||||
|
j6_handle_t vfs_mb_child = j6_handle_invalid;
|
||||||
|
|
||||||
j6_log("srv.init starting");
|
j6_log("srv.init starting");
|
||||||
|
|
||||||
sys = j6_find_first_handle(j6_object_type_system);
|
sys = j6_find_first_handle(j6_object_type_system);
|
||||||
@@ -82,6 +64,16 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
|||||||
if (s != j6_status_ok)
|
if (s != j6_status_ok)
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
|
s = j6_mailbox_create(&vfs_mb);
|
||||||
|
if (s != j6_status_ok)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
s = j6_handle_clone(vfs_mb, &vfs_mb_child,
|
||||||
|
j6_cap_mailbox_send |
|
||||||
|
j6_cap_object_clone);
|
||||||
|
if (s != j6_status_ok)
|
||||||
|
return s;
|
||||||
|
|
||||||
uintptr_t modules_addr = initp->args[0];
|
uintptr_t modules_addr = initp->args[0];
|
||||||
|
|
||||||
std::vector<const module*> mods;
|
std::vector<const module*> mods;
|
||||||
@@ -130,19 +122,17 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
|||||||
// have driver_source objects..
|
// have driver_source objects..
|
||||||
j6romfs::fs initrd {initrd_buf};
|
j6romfs::fs initrd {initrd_buf};
|
||||||
|
|
||||||
|
j6::thread vfs_thread {[=, &initrd](){ initfs_start(initrd, vfs_mb); }, stack_top};
|
||||||
|
j6_status_t result = vfs_thread.start();
|
||||||
|
|
||||||
load_acpi(sys, acpi_module);
|
load_acpi(sys, acpi_module);
|
||||||
|
|
||||||
const j6romfs::inode *driver_dir = initrd.lookup_inode("/jsix/drivers");
|
load_program("/jsix/drivers/drv.uart.elf", initrd, sys_child, slp_mb_child, vfs_mb_child);
|
||||||
if (!driver_dir) {
|
|
||||||
j6_log("Could not load drivers directory");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
load_driver(initrd, driver_dir, "drv.uart.elf", sys_child, slp_mb_child);
|
|
||||||
for (const module *m : devices) {
|
for (const module *m : devices) {
|
||||||
switch (m->type_id) {
|
switch (m->type_id) {
|
||||||
case bootproto::devices::type_id_uefi_fb:
|
case bootproto::devices::type_id_uefi_fb:
|
||||||
load_driver(initrd, driver_dir, "drv.uefi_fb.elf", sys_child, slp_mb_child, m);
|
load_program("/jsix/drivers/drv.uefi_fb.elf", initrd, sys_child, slp_mb_child, vfs_mb_child, m);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -155,16 +145,10 @@ driver_main(unsigned argc, const char **argv, const char **env, const j6_init_ar
|
|||||||
if (in->type != j6romfs::inode_type::file)
|
if (in->type != j6romfs::inode_type::file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
j6::syslog("Loading service: %s", name);
|
char path [128];
|
||||||
|
sprintf(path, "/jsix/services/%s", name);
|
||||||
uint8_t *data = new uint8_t [in->size];
|
load_program(path, initrd, sys_child, slp_mb_child, vfs_mb_child);
|
||||||
util::buffer program {data, in->size};
|
});
|
||||||
|
|
||||||
initrd.load_inode_data(in, program);
|
|
||||||
load_program(name, program, sys_child, slp_mb_child);
|
|
||||||
|
|
||||||
delete [] data;
|
|
||||||
});
|
|
||||||
|
|
||||||
service_locator_start(slp_mb);
|
service_locator_start(slp_mb);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user