refactor: module and ABI interface

initial-refactors
Sean McBride 4 years ago
parent 1cfd2e8454
commit 6d2104f268

@ -89,7 +89,7 @@
"worker_thread_execute_epoll_loop.h": "c",
"sandbox_set_as_running.h": "c",
"sandbox_summarize_page_allocations.h": "c",
"wasm_types.h": "c"
"wasm_types.h": "c",
},
"files.exclude": {
"**/.git": true,

@ -0,0 +1,104 @@
#pragma once
#include <assert.h>
#include <dlfcn.h>
/* Wasm initialization functions generated by the compiler */
#define AWSM_ABI_INITIALIZE_GLOBALS "populate_globals"
#define AWSM_ABI_INITIALIZE_MEMORY "populate_memory"
#define AWSM_ABI_INITIALIZE_TABLE "populate_table"
#define AWSM_ABI_INITIALIZE_LIBC "wasmf___init_libc"
#define AWSM_ABI_ENTRYPOINT "wasmf_main"
/* functions in the module to lookup and call per sandbox. */
typedef int32_t (*awsm_abi_entrypoint_fn_t)(int32_t a, int32_t b);
typedef void (*awsm_abi_init_globals_fn_t)(void);
typedef void (*awsm_abi_init_mem_fn_t)(void);
typedef void (*awsm_abi_init_tbl_fn_t)(void);
typedef void (*awsm_abi_init_libc_fn_t)(int32_t, int32_t);
struct awsm_abi {
void * handle;
awsm_abi_init_globals_fn_t initialize_globals;
awsm_abi_init_mem_fn_t initialize_memory;
awsm_abi_init_tbl_fn_t initialize_tables;
awsm_abi_init_libc_fn_t initialize_libc;
awsm_abi_entrypoint_fn_t entrypoint;
};
/* Initializes the ABI object using the *.so file at path */
static inline int
awsm_abi_init(struct awsm_abi *abi, char *path)
{
assert(abi != NULL);
int rc = 0;
abi->handle = dlopen(path, RTLD_LAZY | RTLD_DEEPBIND);
if (abi->handle == NULL) {
fprintf(stderr, "Failed to open %s with error: %s\n", path, dlerror());
goto dl_open_error;
};
/* Resolve the symbols in the dynamic library *.so file */
abi->entrypoint = (awsm_abi_entrypoint_fn_t)dlsym(abi->handle, AWSM_ABI_ENTRYPOINT);
if (abi->entrypoint == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_ENTRYPOINT, path,
dlerror());
goto dl_error;
}
abi->initialize_globals = (awsm_abi_init_globals_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_GLOBALS);
if (abi->initialize_globals == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_INITIALIZE_GLOBALS, path,
dlerror());
goto dl_error;
}
abi->initialize_memory = (awsm_abi_init_mem_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_MEMORY);
if (abi->initialize_memory == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_INITIALIZE_MEMORY, path,
dlerror());
goto dl_error;
};
abi->initialize_tables = (awsm_abi_init_tbl_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_TABLE);
if (abi->initialize_tables == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_INITIALIZE_TABLE, path,
dlerror());
goto dl_error;
};
abi->initialize_libc = (awsm_abi_init_libc_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_LIBC);
if (abi->initialize_libc == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_INITIALIZE_LIBC, path,
dlerror());
goto dl_error;
}
done:
return rc;
dl_error:
dlclose(abi->handle);
dl_open_error:
rc = -1;
goto done;
}
static inline int
awsm_abi_deinit(struct awsm_abi *abi)
{
abi->entrypoint = NULL;
abi->initialize_globals = NULL;
abi->initialize_memory = NULL;
abi->initialize_tables = NULL;
abi->initialize_libc = NULL;
int rc = dlclose(abi->handle);
if (rc != 0) {
fprintf(stderr, "Failed to close *.so file with error: %s\n", dlerror());
return 1;
}
return 0;
}

@ -7,17 +7,11 @@
#include "admissions_control.h"
#include "admissions_info.h"
#include "awsm_abi.h"
#include "http.h"
#include "panic.h"
#include "types.h"
/* Wasm initialization functions generated by the compiler */
#define MODULE_INITIALIZE_GLOBALS "populate_globals"
#define MODULE_INITIALIZE_MEMORY "populate_memory"
#define MODULE_INITIALIZE_TABLE "populate_table"
#define MODULE_INITIALIZE_LIBC "wasmf___init_libc"
#define MODULE_MAIN "wasmf_main"
#define MODULE_DEFAULT_REQUEST_RESPONSE_SIZE (PAGE_SIZE)
#define MODULE_MAX_NAME_LENGTH 32
@ -58,14 +52,8 @@ struct module {
struct sockaddr_in socket_address;
int socket_descriptor;
/* Dynamic Library Handle and Symbols */
void *dynamic_library_handle; /* Handle to the *.so of the serverless function */
/* Functions to initialize aspects of sandbox */
mod_glb_fn_t initialize_globals;
mod_mem_fn_t initialize_memory;
mod_tbl_fn_t initialize_tables;
mod_libc_fn_t initialize_libc;
mod_main_fn_t main; /* Entry Function */
/* Handle and ABI Symbols for *.so file */
struct awsm_abi abi;
_Atomic uint32_t reference_count; /* ref count how many instances exist here. */
struct indirect_table_entry indirect_table[INDIRECT_TABLE_SIZE];
@ -94,8 +82,7 @@ module_acquire(struct module *module)
static inline void
module_initialize_globals(struct module *module)
{
/* called in a sandbox. */
module->initialize_globals();
module->abi.initialize_globals();
}
/**
@ -105,8 +92,7 @@ module_initialize_globals(struct module *module)
static inline void
module_initialize_table(struct module *module)
{
/* called at module creation time (once only per module). */
module->initialize_tables();
module->abi.initialize_tables();
}
/**
@ -118,8 +104,7 @@ module_initialize_table(struct module *module)
static inline void
module_initialize_libc(struct module *module, int32_t env, int32_t arguments)
{
/* called in a sandbox. */
module->initialize_libc(env, arguments);
module->abi.initialize_libc(env, arguments);
}
/**
@ -129,24 +114,7 @@ module_initialize_libc(struct module *module, int32_t env, int32_t arguments)
static inline void
module_initialize_memory(struct module *module)
{
// called in a sandbox.
module->initialize_memory();
}
/**
* Validate module, defined as having a non-NULL dynamical library handle and entry function pointer
* @param module - module to validate
*/
static inline void
module_validate(struct module *module)
{
if (!module) {
panic("module %p | module is unexpectedly NULL\n", module);
} else if (!module->dynamic_library_handle) {
panic("module %p | module->dynamic_library_handle is unexpectedly NULL\n", module);
} else if (!module->main) {
panic("module %p | module->main is unexpectedly NULL\n", module);
}
module->abi.initialize_memory();
}
/**
@ -157,9 +125,9 @@ module_validate(struct module *module)
* @return return code of module's main function
*/
static inline int32_t
module_main(struct module *module, int32_t argc, int32_t argv)
module_entrypoint(struct module *module, int32_t argc, int32_t argv)
{
return module->main(argc, argv);
return module->abi.entrypoint(argc, argv);
}
/**

@ -5,9 +5,6 @@
#include "wasm_types.h"
#define PAGE_SIZE (unsigned long)(1 << 12)
/* For this family of macros, do NOT pass zero as the pow2 */
#define round_to_pow2(x, pow2) (((unsigned long)(x)) & (~((pow2)-1)))
#define round_up_to_pow2(x, pow2) (round_to_pow2(((unsigned long)(x)) + (pow2)-1, (pow2)))
@ -19,16 +16,9 @@
#define IMPORT __attribute__((visibility("default")))
#define INLINE __attribute__((always_inline))
#define PAGE_ALIGNED __attribute__((aligned(PAGE_SIZE)))
#define PAGE_SIZE (unsigned long)(1 << 12)
#define WEAK __attribute__((weak))
/* These are per module symbols and I'd need to dlsym for each module. instead just use global constants, see above
macros. The code generator compiles in the starting number of wasm pages, and the maximum number of pages If we try
and allocate more than max_pages, we should fault */
/* The code generator also compiles in stubs that populate the linear memory and function table */
void populate_memory(void);
void populate_table(void);
/* memory also provides the table access functions */
#define INDIRECT_TABLE_SIZE (1 << 10)
@ -44,10 +34,3 @@ struct sandbox_context_cache {
};
extern __thread struct sandbox_context_cache local_sandbox_context_cache;
/* functions in the module to lookup and call per sandbox. */
typedef int32_t (*mod_main_fn_t)(int32_t a, int32_t b);
typedef void (*mod_glb_fn_t)(void);
typedef void (*mod_mem_fn_t)(void);
typedef void (*mod_tbl_fn_t)(void);
typedef void (*mod_libc_fn_t)(int32_t, int32_t);

@ -85,7 +85,7 @@ current_sandbox_start(void)
/* Executing the function */
int32_t argument_count = 0;
current_sandbox_enable_preemption(sandbox);
sandbox->return_value = module_main(current_module, argument_count, sandbox->arguments_offset);
sandbox->return_value = module_entrypoint(current_module, argument_count, sandbox->arguments_offset);
current_sandbox_disable_preemption(sandbox);
sandbox->timestamp_of.completion = __getcycles();

@ -87,7 +87,7 @@ err:
/**
* Sets the HTTP Request and Response Headers and Content type on a module
* Sets the HTTP Response Content type on a module
* @param module
* @param response_content_type
*/
@ -116,14 +116,12 @@ void
module_free(struct module *module)
{
if (module == NULL) return;
if (module->dynamic_library_handle == NULL) return;
/* Do not free if we still have oustanding references */
if (module->reference_count) return;
close(module->socket_descriptor);
dlclose(module->dynamic_library_handle);
awsm_abi_deinit(&module->abi);
free(module);
}
@ -149,61 +147,16 @@ module_new(char *name, char *path, uint32_t stack_size, uint32_t max_memory, uin
{
int rc = 0;
errno = 0;
struct module *module = (struct module *)malloc(sizeof(struct module));
struct module *module = (struct module *)calloc(1, sizeof(struct module));
if (!module) {
fprintf(stderr, "Failed to allocate module: %s\n", strerror(errno));
goto err;
};
memset(module, 0, sizeof(struct module));
atomic_init(&module->reference_count, 0);
/* Load the dynamic library *.so file with lazy function call binding and deep binding
* RTLD_DEEPBIND is incompatible with certain clang sanitizers, so it might need to be temporarily disabled at
* times. See https://github.com/google/sanitizers/issues/611
*/
module->dynamic_library_handle = dlopen(path, RTLD_LAZY | RTLD_DEEPBIND);
if (module->dynamic_library_handle == NULL) {
fprintf(stderr, "Failed to open %s with error: %s\n", path, dlerror());
goto dl_open_error;
};
/* Resolve the symbols in the dynamic library *.so file */
module->main = (mod_main_fn_t)dlsym(module->dynamic_library_handle, MODULE_MAIN);
if (module->main == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", MODULE_MAIN, path, dlerror());
goto dl_error;
}
module->initialize_globals = (mod_glb_fn_t)dlsym(module->dynamic_library_handle, MODULE_INITIALIZE_GLOBALS);
if (module->initialize_globals == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", MODULE_INITIALIZE_GLOBALS, path,
dlerror());
goto dl_error;
}
module->initialize_memory = (mod_mem_fn_t)dlsym(module->dynamic_library_handle, MODULE_INITIALIZE_MEMORY);
if (module->initialize_memory == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", MODULE_INITIALIZE_MEMORY, path,
dlerror());
goto dl_error;
};
module->initialize_tables = (mod_tbl_fn_t)dlsym(module->dynamic_library_handle, MODULE_INITIALIZE_TABLE);
if (module->initialize_tables == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", MODULE_INITIALIZE_TABLE, path,
dlerror());
goto dl_error;
};
module->initialize_libc = (mod_libc_fn_t)dlsym(module->dynamic_library_handle, MODULE_INITIALIZE_LIBC);
if (module->initialize_libc == NULL) {
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", MODULE_INITIALIZE_LIBC, path,
dlerror());
goto dl_error;
}
rc = awsm_abi_init(&module->abi, path);
if (rc != 0) goto awsm_abi_init_err;
/* Set fields in the module struct */
strncpy(module->name, name, MODULE_MAX_NAME_LENGTH);
@ -266,9 +219,7 @@ done:
return module;
err_listen:
dl_error:
dlclose(module->dynamic_library_handle);
dl_open_error:
awsm_abi_init_err:
free(module);
err:
module = NULL;
@ -286,17 +237,22 @@ module_new_from_json(char *file_name)
assert(file_name != NULL);
int return_code = -1;
/* Use stat to get file attributes and make sure file is there and OK */
/* Use stat to get file attributes and make sure file is present and not empty */
struct stat stat_buffer;
memset(&stat_buffer, 0, sizeof(struct stat));
errno = 0;
if (stat(file_name, &stat_buffer) < 0) {
fprintf(stderr, "Attempt to stat %s failed: %s\n", file_name, strerror(errno));
goto err;
}
if (stat_buffer.st_size == 0) {
fprintf(stderr, "File %s is unexpectedly empty\n", file_name);
goto err;
}
if (!S_ISREG(stat_buffer.st_mode)) {
fprintf(stderr, "File %s is not a regular file\n", file_name);
goto err;
}
/* Open the file */
errno = 0;
FILE *module_file = fopen(file_name, "r");
if (!module_file) {
fprintf(stderr, "Attempt to open %s failed: %s\n", file_name, strerror(errno));
@ -304,17 +260,13 @@ module_new_from_json(char *file_name)
}
/* Initialize a Buffer */
assert(stat_buffer.st_size != 0);
errno = 0;
char *file_buffer = malloc(stat_buffer.st_size);
char *file_buffer = calloc(1, stat_buffer.st_size);
if (file_buffer == NULL) {
fprintf(stderr, "Attempt to allocate file buffer failed: %s\n", strerror(errno));
goto stat_buffer_alloc_err;
}
memset(file_buffer, 0, stat_buffer.st_size);
/* Read the file into the buffer and check that the buffer size equals the file size */
errno = 0;
int total_chars_read = fread(file_buffer, sizeof(char), stat_buffer.st_size, module_file);
#ifdef LOG_MODULE_LOADING
debuglog("size read: %d content: %s\n", total_chars_read, file_buffer);
@ -326,7 +278,6 @@ module_new_from_json(char *file_name)
assert(total_chars_read > 0);
/* Close the file */
errno = 0;
if (fclose(module_file) == EOF) {
fprintf(stderr, "Attempt to close buffer containing %s failed: %s\n", file_name, strerror(errno));
goto fclose_err;
@ -364,8 +315,6 @@ module_new_from_json(char *file_name)
char module_name[MODULE_MAX_NAME_LENGTH] = { 0 };
char module_path[MODULE_MAX_PATH_LENGTH] = { 0 };
errno = 0;
int32_t request_size = 0;
int32_t response_size = 0;
uint32_t port = 0;

@ -115,7 +115,6 @@ sandbox_allocate(struct sandbox_request *sandbox_request)
{
/* Validate Arguments */
assert(sandbox_request != NULL);
module_validate(sandbox_request->module);
struct sandbox *sandbox;
char * error_message = "";

Loading…
Cancel
Save