HelenOS sources
This source file includes following definitions.
- ldr_get_taskid
- ldr_set_cwd
- ldr_set_program
- ldr_set_args
- ldr_add_inbox
- ldr_load
- ldr_run
- ldr_connection
- main
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <ipc/services.h>
#include <ipc/loader.h>
#include <ns.h>
#include <loader/pcb.h>
#include <entry_point.h>
#include <errno.h>
#include <async.h>
#include <str.h>
#include <as.h>
#include <elf/elf.h>
#include <elf/elf_load.h>
#include <vfs/vfs.h>
#include <vfs/inbox.h>
#include <libc.h>
#ifdef CONFIG_RTLD
#include <rtld/rtld.h>
#endif
#define DPRINTF(...) ((void) 0)
static char *progname = NULL;
static int program_fd = -1;
static pcb_t pcb;
static char *cwd = NULL;
static int argc = 0;
static char **argv = NULL;
static char *arg_buf = NULL;
static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
static int inbox_entries = 0;
static elf_info_t prog_info;
static bool connected = false;
static void ldr_get_taskid(ipc_call_t *req)
{
ipc_call_t call;
task_id_t task_id;
size_t len;
task_id = task_get_id();
if (!async_data_read_receive(&call, &len)) {
async_answer_0(&call, EINVAL);
async_answer_0(req, EINVAL);
return;
}
if (len > sizeof(task_id))
len = sizeof(task_id);
DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
async_data_read_finalize(&call, &task_id, len);
async_answer_0(req, EOK);
}
static void ldr_set_cwd(ipc_call_t *req)
{
char *buf;
errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
if (rc == EOK) {
if (cwd != NULL)
free(cwd);
cwd = buf;
}
DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
async_answer_0(req, rc);
}
static void ldr_set_program(ipc_call_t *req)
{
ipc_call_t call;
size_t namesize;
if (!async_data_write_receive(&call, &namesize)) {
async_answer_0(req, EINVAL);
return;
}
char *name = malloc(namesize);
errno_t rc = async_data_write_finalize(&call, name, namesize);
if (rc != EOK) {
async_answer_0(req, EINVAL);
return;
}
int file;
rc = vfs_receive_handle(true, &file);
if (rc != EOK) {
async_answer_0(req, EINVAL);
return;
}
DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
progname = name;
program_fd = file;
async_answer_0(req, EOK);
}
static void ldr_set_args(ipc_call_t *req)
{
char *buf;
size_t buf_size;
errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
if (rc == EOK) {
char *cur = buf;
int count = 0;
while (cur < buf + buf_size) {
size_t arg_size = str_size(cur);
cur += arg_size + 1;
count++;
}
char **_argv = (char **) malloc((count + 1) * sizeof(char *));
if (_argv == NULL) {
free(buf);
async_answer_0(req, ENOMEM);
return;
}
cur = buf;
count = 0;
while (cur < buf + buf_size) {
_argv[count] = cur;
size_t arg_size = str_size(cur);
cur += arg_size + 1;
count++;
}
_argv[count] = NULL;
if (arg_buf != NULL)
free(arg_buf);
if (argv != NULL)
free(argv);
for (int i = 0; i < count; i++)
DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
argc = count;
arg_buf = buf;
argv = _argv;
}
async_answer_0(req, rc);
}
static void ldr_add_inbox(ipc_call_t *req)
{
if (inbox_entries == INBOX_MAX_ENTRIES) {
async_answer_0(req, ERANGE);
return;
}
ipc_call_t call;
size_t namesize;
if (!async_data_write_receive(&call, &namesize)) {
async_answer_0(req, EINVAL);
return;
}
char *name = malloc(namesize);
errno_t rc = async_data_write_finalize(&call, name, namesize);
if (rc != EOK) {
async_answer_0(req, EINVAL);
return;
}
int file;
rc = vfs_receive_handle(true, &file);
if (rc != EOK) {
async_answer_0(req, EINVAL);
return;
}
DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
if (str_cmp(name, "root") == 0)
vfs_root_set(file);
inbox[inbox_entries].name = name;
inbox[inbox_entries].file = file;
inbox_entries++;
async_answer_0(req, EOK);
}
static int ldr_load(ipc_call_t *req)
{
DPRINTF("LOADER_LOAD()\n");
errno_t rc = elf_load(program_fd, &prog_info);
if (rc != EOK) {
DPRINTF("Failed to load executable for '%s'.\n", progname);
async_answer_0(req, EINVAL);
return 1;
}
DPRINTF("Loaded.\n");
#ifdef CONFIG_RTLD
if (prog_info.env) {
pcb.tcb = rtld_tls_make(prog_info.env);
} else {
pcb.tcb = tls_make(prog_info.finfo.base);
}
#else
pcb.tcb = tls_make(prog_info.finfo.base);
#endif
if (!pcb.tcb) {
DPRINTF("Failed to make TLS for '%s'.\n", progname);
async_answer_0(req, ENOMEM);
return 1;
}
elf_set_pcb(&prog_info, &pcb);
DPRINTF("PCB set.\n");
pcb.cwd = cwd;
pcb.argc = argc;
pcb.argv = argv;
pcb.inbox = inbox;
pcb.inbox_entries = inbox_entries;
DPRINTF("Answering.\n");
async_answer_0(req, EOK);
return 0;
}
static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
{
DPRINTF("Set task name\n");
task_set_name(progname);
DPRINTF("Reply OK\n");
async_answer_0(req, EOK);
async_get_call(req);
assert(!ipc_get_imethod(req));
async_answer_0(req, EOK);
DPRINTF("Jump to entry point at %p\n", pcb.entry);
__libc_fini();
__tcb_reset();
entry_point_jmp(prog_info.finfo.entry, &pcb);
}
static void ldr_connection(ipc_call_t *icall, void *arg)
{
if (connected) {
async_answer_0(icall, ELIMIT);
return;
}
connected = true;
async_accept_0(icall);
(void) icall;
while (true) {
errno_t retval;
ipc_call_t call;
async_get_call(&call);
if (!ipc_get_imethod(&call)) {
async_answer_0(&call, EOK);
exit(0);
}
switch (ipc_get_imethod(&call)) {
case LOADER_GET_TASKID:
ldr_get_taskid(&call);
continue;
case LOADER_SET_CWD:
ldr_set_cwd(&call);
continue;
case LOADER_SET_PROGRAM:
ldr_set_program(&call);
continue;
case LOADER_SET_ARGS:
ldr_set_args(&call);
continue;
case LOADER_ADD_INBOX:
ldr_add_inbox(&call);
continue;
case LOADER_LOAD:
ldr_load(&call);
continue;
case LOADER_RUN:
ldr_run(&call);
default:
retval = EINVAL;
break;
}
async_answer_0(&call, retval);
}
}
int main(int argc, char *argv[])
{
task_id_t id = task_get_id();
errno_t rc = ns_intro(id);
if (rc != EOK)
return rc;
rc = service_register(SERVICE_LOADER, INTERFACE_LOADER,
ldr_connection, NULL);
if (rc != EOK)
return rc;
async_manager();
return 0;
}
HelenOS homepage, sources at GitHub