Goto sanos source index
//
// moddb.c
//
// Module loader
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os.h>
#include <os/pe.h>
#include <string.h>
#include <stdarg.h>
#include <inifile.h>
#include <moddb.h>
#ifdef KERNEL
#include <os/krnl.h>
#endif
int vsprintf(char *buf, const char *fmt, va_list args);
static void logmsg(struct moddb *db, const char *fmt, ...)
{
va_list args;
char buffer[1024];
if (db->log)
{
va_start(args, fmt);
vsprintf(buffer, fmt, args);
va_end(args);
db->log(buffer);
}
}
static char *get_basename(char *filename, char *buffer, int size)
{
char *basename = filename;
char *bufend = buffer + size - 1;
char *p = filename;
while (*p && buffer < bufend)
{
*buffer = *p;
if (*buffer == PS1 || *buffer == PS2) basename = buffer + 1;
buffer++;
p++;
}
*buffer = 0;
return basename;
}
static void insert_before(struct module *m, struct module *n)
{
n->next = m;
n->prev = m->prev;
m->prev->next = n;
m->prev = n;
}
static void insert_after(struct module *m, struct module *n)
{
n->next = m->next;
n->prev = m;
m->next->prev = n;
m->next = n;
}
static void remove(struct module *m)
{
m->next->prev = m->prev;
m->prev->next = m->next;
}
struct image_header *get_image_header(hmodule_t hmod)
{
struct dos_header *doshdr = (struct dos_header *) hmod;
if (doshdr == NULL) return NULL;
return (struct image_header *) RVA(hmod, doshdr->e_lfanew);
}
static void *get_image_directory(hmodule_t hmod, int dir)
{
unsigned long addr = get_image_header(hmod)->optional.data_directory[dir].virtual_address;
return (void *) addr ? RVA(hmod, addr) : 0;
}
struct module *get_module_for_handle(struct moddb *db, hmodule_t hmod)
{
struct module *m;
// Try to find module in module list
m = db->modules;
while (1)
{
if (hmod == m->hmod) return m;
m = m->next;
if (m == db->modules) break;
}
return NULL;
}
static char *find_in_modpaths(struct moddb *db, char *name, char *path)
{
int i;
char *p;
char *s;
char *basename;
char *dot;
char *pathend = path + MAXPATH - 1;
int len;
struct modalias *ma;
for (i = 0; i < db->nmodpaths; i++)
{
// Build path name
len = strlen(db->modpaths[i]);
if (len > MAXPATH - 2) continue;
p = path;
memcpy(p, db->modpaths[i], len);
p += len;
*p++ = PS1;
basename = p;
dot = NULL;
s = name;
while (*s && p < pathend)
{
if (*s == '.')
{
dot = s;
*p++ = '.';
}
else if (*s >= 'A' && *s <= 'Z')
*p++ = *s + ('a' - 'A');
else
*p++ = *s;
s++;
}
if (!dot && p + 4 < pathend)
{
memcpy(p, ".dll", 4);
p += 4;
}
*p = 0;
// Check for alias
ma = db->aliases;
while (ma)
{
if (strcmp(basename, ma->name) == 0 && basename + strlen(ma->name) < pathend)
{
strcpy(basename, ma->alias);
break;
}
ma = ma->next;
}
// Return path if file exists
if (stat(path, NULL) >= 0) return basename;
}
return NULL;
}
static char *get_module_name(struct moddb *db, char *name, char *path)
{
char *dot;
char *basename;
char *s;
// Check for file path in module name
s = name;
while (*s != 0 && *s != PS1 && *s != PS2) s++;
if (*s)
{
// Get full path name for module
basename = get_basename(name, path, MAXPATH);
// Convert base name to lower case
dot = NULL;
s = basename;
while (*s)
{
if (*s == '.')
dot = s;
else if (*s >= 'A' && *s <= 'Z')
*s = *s + ('a' - 'A');
s++;
}
// Add .dll to name if no extension
if (!dot)
{
memcpy(s, ".dll", 4);
s += 4;
}
*s = 0;
}
else
{
// Search for file in library paths
basename = find_in_modpaths(db, name, path);
}
return basename;
}
static struct module *get_module(struct moddb *db, char *name)
{
char buffer[MAXPATH];
char *basename;
char *dot;
char *p;
char *s;
struct module *m;
struct modalias *ma;
// Get canonical module name
basename = name;
while (*name)
{
if (*name == PS1 || *name == PS2) basename = name + 1;
name++;
}
p = buffer;
s = basename;
dot = NULL;
while (*s)
{
if (p - buffer == MAXPATH - 1) break;
if (*s == '.')
{
dot = s;
*p++ = '.';
}
else if (*s >= 'A' && *s <= 'Z')
*p++ = *s + ('a' - 'A');
else
*p++ = *s;
s++;
}
if (!dot && p - buffer < MAXPATH - 5)
{
memcpy(p, ".dll", 4);
p += 4;
}
*p = 0;
// Check for alias
ma = db->aliases;
while (ma)
{
if (strcmp(buffer, ma->name) == 0) return get_module(db, ma->alias);
ma = ma->next;
}
// Try to find module in module list
m = db->modules;
while (1)
{
if (strcmp(buffer, m->name) == 0) return m;
m = m->next;
if (m == db->modules) break;
}
return NULL;
}
static void *get_proc_by_name(hmodule_t hmod, int hint, char *procname)
{
struct image_export_directory *exp;
unsigned int *names;
unsigned int i;
exp = (struct image_export_directory *) get_image_directory(hmod, IMAGE_DIRECTORY_ENTRY_EXPORT);
if (!exp) return NULL;
names = (unsigned int *) RVA(hmod, exp->address_of_names);
if (hint >= 0 && hint < (int) exp->number_of_names && strcmp(procname, RVA(hmod, names[hint])) == 0)
{
unsigned short idx;
idx = *((unsigned short *) RVA(hmod, exp->address_of_name_ordinals) + hint);
return RVA(hmod, *((unsigned long *) RVA(hmod, exp->address_of_functions) + idx));
}
for (i = 0; i < exp->number_of_names; i++)
{
char *name = RVA(hmod, *names);
if (strcmp(name, procname) == 0)
{
unsigned short idx;
idx = *((unsigned short *) RVA(hmod, exp->address_of_name_ordinals) + i);
return RVA(hmod, *((unsigned long *) RVA(hmod, exp->address_of_functions) + idx));
}
names++;
}
return NULL;
}
static void *get_proc_by_ordinal(hmodule_t hmod, unsigned int ordinal)
{
struct image_export_directory *exp;
exp = (struct image_export_directory *) get_image_directory(hmod, IMAGE_DIRECTORY_ENTRY_EXPORT);
if (!exp) return NULL;
if (ordinal < exp->base || ordinal >= exp->number_of_functions + exp->base) panic("invalid ordinal");
return RVA(hmod, *((unsigned long *) RVA(hmod, exp->address_of_functions) + (ordinal - exp->base)));
}
static struct module *resolve_imports(struct module *mod)
{
struct image_import_descriptor *imp;
struct module *modlist = mod;
char path[MAXPATH];
// Find import directory in image
imp = (struct image_import_descriptor *) get_image_directory(mod->hmod, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (!imp) return mod;
// Load each dependent module
while (imp->characteristics != 0)
{
char *name = RVA(mod->hmod, imp->name);
struct module *newmod = get_module(mod->db, name);
if (newmod == NULL)
{
char *imgbase;
name = get_module_name(mod->db, name, path);
if (!name)
{
logmsg(mod->db, "module %s not found", RVA(mod->hmod, imp->name));
return NULL;
}
imgbase = mod->db->load_image(path);
if (imgbase == NULL)
{
logmsg(mod->db, "unable to load module %s", path);
return NULL;
}
newmod = (struct module *) malloc(sizeof(struct module));
newmod->hmod = imgbase;
newmod->db = mod->db;
newmod->name = strdup(name);
newmod->path = strdup(path);
newmod->refcnt = 0;
newmod->flags = MODULE_LOADED;
insert_before(mod, newmod);
newmod = resolve_imports(newmod);
if (newmod == NULL) return NULL;
if (modlist == mod) modlist = newmod;
}
imp++;
}
return modlist;
}
static int bind_imports(struct module *mod)
{
struct image_import_descriptor *imp;
int errs = 0;
// Find import directory in image
imp = (struct image_import_descriptor *) get_image_directory(mod->hmod, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (!imp) return 0;
if (imp->forwarder_chain != 0 && imp->forwarder_chain != 0xFFFFFFFF)
{
logmsg(mod->db, "import forwarder chains not supported (%s)", mod->name);
return -ENOSYS;
}
// Update Import Address Table (IAT)
while (imp->characteristics != 0)
{
unsigned long *thunks;
unsigned long *origthunks;
struct image_import_by_name *ibn;
char *name;
struct module *expmod;
name = RVA(mod->hmod, imp->name);
expmod = get_module(mod->db, name);
if (!expmod)
{
logmsg(mod->db, "module %s no longer loaded", name);
return -ENOEXEC;
}
thunks = (unsigned long *) RVA(mod->hmod, imp->first_thunk);
origthunks = (unsigned long *) RVA(mod->hmod, imp->original_first_thunk);
while (*thunks)
{
if (*origthunks & IMAGE_ORDINAL_FLAG)
{
// Import by ordinal
unsigned long ordinal = *origthunks & ~IMAGE_ORDINAL_FLAG;
*thunks = (unsigned long) get_proc_by_ordinal(expmod->hmod, ordinal);
if (*thunks == 0)
{
logmsg(mod->db, "unable to resolve %s:#%d in %s", expmod->name, ordinal, mod->name);
errs++;
}
}
else
{
// Import by name (and hint)
ibn = (struct image_import_by_name *) RVA(mod->hmod, *origthunks);
*thunks = (unsigned long) get_proc_by_name(expmod->hmod, ibn->hint, ibn->name);
if (*thunks == 0)
{
logmsg(mod->db, "unable to resolve %s:%s in %s", expmod->name, ibn->name, mod->name);
errs++;
}
}
thunks++;
origthunks++;
}
imp++;
}
if (errs) return -ENOEXEC;
mod->flags |= MODULE_BOUND;
return 0;
}
static int relocate_module(struct module *mod)
{
unsigned long offset;
char *pagestart;
unsigned short *fixup;
int i;
struct image_base_relocation *reloc;
int nrelocs;
offset = (unsigned long) mod->hmod - get_image_header(mod->hmod)->optional.image_base;
if (offset == 0) return 0;
if (get_image_header(mod->hmod)->header.characteristics & IMAGE_FILE_RELOCS_STRIPPED)
{
logmsg(mod->db, "relocation info missing for %s", mod->name);
return -ENOEXEC;
}
reloc = (struct image_base_relocation *) get_image_directory(mod->hmod, IMAGE_DIRECTORY_ENTRY_BASERELOC);
if (!reloc) return 0;
while (reloc->virtual_address != 0 || reloc->size_of_block != 0)
{
pagestart = RVA(mod->hmod, reloc->virtual_address);
nrelocs = (reloc->size_of_block - sizeof(struct image_base_relocation)) / 2;
fixup = (unsigned short *) (reloc + 1);
for (i = 0; i < nrelocs; i++, fixup++)
{
unsigned short type = *fixup >> 12;
unsigned short pos = *fixup & 0xfff;
if (type == IMAGE_REL_BASED_HIGHLOW)
*(unsigned long *) (pagestart + pos) += offset;
else if (type != IMAGE_REL_BASED_ABSOLUTE)
{
logmsg(mod->db, "unsupported relocation type %d in %s", type, mod->name);
return -ENOEXEC;
}
}
reloc = (struct image_base_relocation *) fixup;
}
mod->flags |= MODULE_RELOCATED;
return 0;
}
static int protect_module(struct module *mod)
{
struct image_header *imghdr = get_image_header(mod->hmod);
int i;
if (!mod->db->protect_region) return 0;
// Set page protect for each section
for (i = 0; i < imghdr->header.number_of_sections; i++)
{
int protect;
unsigned long scn = imghdr->sections[i].characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE);
if (scn == (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ))
protect = PAGE_EXECUTE_READ;
else if (scn == (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE))
protect = PAGE_READWRITE;
else if (scn == IMAGE_SCN_MEM_READ)
protect = PAGE_READONLY;
else
protect = PAGE_EXECUTE_READWRITE;
mod->db->protect_region(RVA(mod->hmod, imghdr->sections[i].virtual_address), imghdr->sections[i].size_of_raw_data, protect);
}
mod->flags |= MODULE_PROTECTED;
return 0;
}
static void update_refcount(struct module *mod)
{
struct image_import_descriptor *imp;
// If imports already ref counted just skip
if (mod->flags & MODULE_IMPORTS_REFED) return;
// Find import directory in image
imp = (struct image_import_descriptor *) get_image_directory(mod->hmod, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (!imp) return;
// Get or load each dependent module
while (imp->characteristics != 0)
{
char *name = RVA(mod->hmod, imp->name);
struct module *depmod = get_module(mod->db, name);
if (depmod != NULL) depmod->refcnt++;
imp++;
}
mod->flags |= MODULE_IMPORTS_REFED;
}
static int remove_module(struct module *mod)
{
struct image_header *imghdr;
struct image_import_descriptor *imp;
imghdr = get_image_header(mod->hmod);
if (mod->flags & MODULE_INITIALIZED)
{
// Notify DLL
if (imghdr->header.characteristics & IMAGE_FILE_DLL)
{
((int (__stdcall *)(hmodule_t, int, void *)) get_entrypoint(mod->hmod))(mod->hmod, DLL_PROCESS_DETACH, NULL);
}
mod->flags &= ~MODULE_INITIALIZED;
}
if (mod->flags & MODULE_IMPORTS_REFED)
{
// Decrement reference count on all dependent modules
imp = (struct image_import_descriptor *) get_image_directory(mod->hmod, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (imp)
{
while (imp->characteristics != 0)
{
char *name = RVA(mod->hmod, imp->name);
struct module *depmod = get_module(mod->db, name);
if (depmod && --depmod->refcnt == 0) remove_module(depmod);
imp++;
}
}
mod->flags &= ~MODULE_IMPORTS_REFED;
}
// Release memory for module
if (mod->flags & MODULE_LOADED)
{
mod->db->unload_image(mod->hmod, imghdr->optional.size_of_image);
mod->flags &= ~MODULE_LOADED;
}
// Notify
if (mod->db->notify_unload) mod->db->notify_unload(mod->hmod);
// Remove module from module list
remove(mod);
free(mod->name);
free(mod->path);
free(mod);
return 0;
}
static void free_unused_modules(struct moddb *db)
{
struct module *mod;
mod = db->modules;
while (1)
{
if (mod->refcnt == 0)
{
remove_module(mod);
mod = db->modules;
}
else
{
mod = mod->next;
if (mod == db->modules) break;
}
}
}
void *get_proc_address(hmodule_t hmod, char *procname)
{
return get_proc_by_name(hmod, -1, procname);
}
hmodule_t get_module_handle(struct moddb *db, char *name)
{
struct module *mod;
if (!name || strlen(name) > MAXPATH - 1) return NULL;
mod = get_module(db, name);
if (mod == NULL) return NULL;
return mod->hmod;
}
int get_module_filename(struct moddb *db, hmodule_t hmod, char *buffer, int size)
{
struct module *mod;
mod = get_module_for_handle(db, hmod);
if (mod == NULL) return -EINVAL;
strncpy(buffer, mod->path, size);
return strlen(mod->path);
}
void *get_entrypoint(hmodule_t hmod)
{
return RVA(hmod, get_image_header(hmod)->optional.address_of_entry_point);
}
hmodule_t load_module(struct moddb *db, char *name, int flags)
{
char buffer[MAXPATH];
char *basename;
char *imgbase;
struct module *mod;
struct module *modlist;
struct module *m;
int rc;
// Return existing handle if module already loaded
mod = get_module(db, name);
if (mod != NULL)
{
mod->refcnt++;
return mod->hmod;
}
// Get canonical name
if (!name || strlen(name) > MAXPATH - 1) return NULL;
basename = get_module_name(db, name, buffer);
if (!basename) return NULL;
// Load image into memory
imgbase = db->load_image(buffer);
if (imgbase == NULL) return NULL;
// Create new module
mod = (struct module *) malloc(sizeof(struct module));
mod->hmod = imgbase;
mod->db = db;
mod->name = strdup(basename);
mod->path = strdup(buffer);
mod->refcnt = 0;
mod->flags = MODULE_LOADED;
insert_before(db->modules, mod);
// Resolve module dependencies
modlist = resolve_imports(mod);
if (modlist == NULL)
{
free_unused_modules(db);
return NULL;
}
// Relocate, bind imports and protect new modules
m = modlist;
while (1)
{
rc = relocate_module(m);
if (rc < 0)
{
free_unused_modules(db);
return NULL;
}
rc = bind_imports(m);
if (rc < 0)
{
free_unused_modules(db);
return NULL;
}
rc = protect_module(m);
if (rc < 0)
{
free_unused_modules(db);
return NULL;
}
if (m == mod) break;
m = m->next;
}
// Initialize and notify new modules
m = modlist;
while (1)
{
if (get_image_header(m->hmod)->header.characteristics & IMAGE_FILE_DLL)
{
if ((flags & MODLOAD_NOINIT) == 0 || m != mod)
{
int ok;
//logmsg(db, "initializing module %s", m->name);
ok = ((int (__stdcall *)(hmodule_t, int, void *)) get_entrypoint(m->hmod))(m->hmod, DLL_PROCESS_ATTACH, NULL);
//logmsg(db, "module %s initialized%s", m->name, ok ? "" : ", init failed");
if (!ok)
{
free_unused_modules(db);
return NULL;
}
m->flags |= MODULE_INITIALIZED;
}
}
// Notify
if (db->notify_load) db->notify_load(m->hmod, &m->name);
if (m == mod) break;
m = m->next;
}
// Update ref counts on depend module
mod->refcnt++;
m = modlist;
while (1)
{
update_refcount(m);
if (m == mod) break;
m = m->next;
}
return mod->hmod;
}
int unload_module(struct moddb *db, hmodule_t hmod)
{
struct module *mod;
// Find module
mod = get_module_for_handle(db, hmod);
if (mod == NULL) return -EINVAL;
// Decrement reference count, return if not zero
if (--mod->refcnt > 0) return 0;
// Remove module
return remove_module(mod);
}
static struct image_resource_directory_entry *find_resource(char *resbase, struct image_resource_directory *dir, char *id)
{
struct image_resource_directory_entry *direntry;
struct image_resource_directory_string *entname;
int i;
direntry = (struct image_resource_directory_entry *) (dir + 1);
if ((unsigned long) id < 0x10000)
{
// Lookup by ID, first skip named entries
direntry += dir->number_of_named_entries;
for (i = 0; i < dir->number_of_id_entries; i++)
{
if (direntry->id == (unsigned long) id) return direntry;
direntry++;
}
}
else
{
// Lookup by name
for (i = 0; i < dir->number_of_named_entries; i++)
{
unsigned short *p1;
unsigned char *p2;
int left;
unsigned short ch1;
unsigned short ch2;
entname = (struct image_resource_directory_string *) RVA(resbase, direntry->name_offset);
p1 = (unsigned short *) entname->name_string;
p2 = (unsigned char *) id;
left = entname->length;
while (left > 0 && *p2 != 0)
{
if (((ch1 = *p1++) >= 'a') && (ch1 <= 'z')) ch1 += 'A' - 'a';
if (((ch2 = *p2++) >= 'a') && (ch2 <= 'z')) ch2 += 'A' - 'a';
if (ch1 != ch2) break;
left--;
}
if (left == 0 && *p2 == 0) return direntry;
direntry++;
}
}
return NULL;
}
int get_resource_data(hmodule_t hmod, char *id1, char *id2, char *id3, void **data)
{
char *resbase;
struct image_resource_directory *dir;
struct image_resource_directory_entry *direntry;
struct image_resource_data_entry *dataentry;
// Find resource root directory
resbase = get_image_directory(hmod, IMAGE_DIRECTORY_ENTRY_RESOURCE);
if (!resbase) return -ENOENT;
dir = (struct image_resource_directory *) resbase;
// Find first level entry
direntry = find_resource(resbase, dir, id1);
if (!direntry) return -ENOENT;
if (!direntry->data_is_directory) return -EINVAL;
dir = (struct image_resource_directory *) RVA(resbase, direntry->offset_to_directory);
// Find second level entry
direntry = find_resource(resbase, dir, id2);
if (!direntry) return -ENOENT;
if (!direntry->data_is_directory) return -EINVAL;
dir = (struct image_resource_directory *) RVA(resbase, direntry->offset_to_directory);
// Find third level entry
direntry = find_resource(resbase, dir, id3);
if (!direntry) return -ENOENT;
if (direntry->data_is_directory) return -EINVAL;
dataentry = (struct image_resource_data_entry *) RVA(resbase, direntry->offset_to_data);
*data = RVA(hmod, dataentry->offset_to_data);
return dataentry->size;
}
int init_module_database(struct moddb *db, char *name, hmodule_t hmod, char *libpath, struct section *aliassect, int flags)
{
char buffer[MAXPATH];
struct property *prop;
// Set flags
db->flags = flags;
// Set library paths
if (libpath)
{
int n;
char *p;
char *q;
char *path;
p = libpath;
n = 1;
while (*p)
{
if (*p == ';') n++;
p++;
}
db->nmodpaths = n;
db->modpaths = (char **) malloc(n * sizeof(char *));
p = libpath;
n = 0;
while (*p)
{
q = p;
while (*q && *q != ';') q++;
db->modpaths[n++] = path = (char *) malloc(q - p + 1);
memcpy(path, p, q - p);
path[q - p] = 0;
if (*q)
p = q + 1;
else
p = q;
}
}
else
{
db->modpaths = NULL;
db->nmodpaths = 0;
}
// Setup module aliases
db->aliases = NULL;
if (aliassect)
{
prop = aliassect->properties;
while (prop)
{
struct modalias *ma;
ma = (struct modalias *) malloc(sizeof(struct modalias));
if (!ma) return -ENOMEM;
ma->name = strdup(prop->name);
ma->alias = strdup(prop->value);
ma->next = db->aliases;
db->aliases = ma;
prop = prop->next;
}
}
// Setup module database with initial module
if (!find_in_modpaths(db, name, buffer)) panic("initial module missing");
db->modules = (struct module *) malloc(sizeof(struct module));
if (!db->modules) return -ENOMEM;
db->modules->hmod = hmod;
db->modules->db = db;
db->modules->name = strdup(name);
db->modules->path = strdup(buffer);
db->modules->next = db->modules;
db->modules->prev = db->modules;
db->modules->refcnt = 1;
db->modules->flags = MODULE_LOADED | MODULE_IMPORTS_REFED | MODULE_RESOLVED | MODULE_RELOCATED | MODULE_BOUND | MODULE_PROTECTED | MODULE_INITIALIZED;
// Protect initial module
if (db->protect_region) protect_module(db->modules);
return 0;
}