blob: 3f6311823aa4aededae83c47976e015ccf13572c [file] [log] [blame]
/*
* YASM module loader
*
* Copyright (C) 2002 Peter Johnson
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER 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 AUTHOR OR OTHER 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 <util.h>
/*@unused@*/ RCSID("$Id$");
#include <libyasm/compat-queue.h>
#include <libyasm.h>
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
#include "ltdl.h"
#include "yasm-module.h"
extern const lt_dlsymlist lt_preloaded_symbols[];
typedef struct module {
SLIST_ENTRY(module) link;
module_type type; /* module type */
/*@only@*/ char *type_str;
char *keyword; /* module keyword */
lt_dlhandle handle; /* dlopen handle */
} module;
static SLIST_HEAD(modulehead, module) modules = SLIST_HEAD_INITIALIZER(modules);
static const char *module_type_str[] = {
"arch",
"dbgfmt",
"objfmt",
"listfmt",
"optimizer",
"parser",
"preproc"
};
static /*@dependent@*/ /*@null@*/ module *
load_module(module_type type, const char *keyword)
{
module *m;
char *name;
lt_dlhandle handle;
size_t typelen;
/* See if the module has already been loaded. */
SLIST_FOREACH(m, &modules, link) {
if (m->type == type && yasm__strcasecmp(m->keyword, keyword) == 0)
return m;
}
/* Look for dynamic module. First build full module name from keyword. */
typelen = strlen(module_type_str[type]);
name = yasm_xmalloc(typelen+strlen(keyword)+2);
strcpy(name, module_type_str[type]);
strcat(name, "_");
strcat(name, keyword);
handle = lt_dlopenext(name);
if (!handle) {
yasm_xfree(name);
return NULL;
}
m = yasm_xmalloc(sizeof(module));
m->type = type;
m->type_str = name;
name[typelen] = '\0';
m->keyword = &name[typelen+1];
m->handle = handle;
SLIST_INSERT_HEAD(&modules, m, link);
return m;
}
void
unload_modules(void)
{
module *m;
while (!SLIST_EMPTY(&modules)) {
m = SLIST_FIRST(&modules);
SLIST_REMOVE_HEAD(&modules, link);
yasm_xfree(m->type_str);
lt_dlclose(m->handle);
yasm_xfree(m);
}
}
void *
get_module_data(module_type type, const char *keyword, const char *symbol)
{
char *name;
/*@dependent@*/ module *m;
void *data;
/* Load module */
m = load_module(type, keyword);
if (!m)
return NULL;
name = yasm_xmalloc(strlen(keyword)+strlen(symbol)+11);
strcpy(name, "yasm_");
strcat(name, keyword);
strcat(name, "_LTX_");
strcat(name, symbol);
/* Find and return data pointer: NULL if it doesn't exist */
data = lt_dlsym(m->handle, name);
yasm_xfree(name);
return data;
}
typedef struct list_module_info {
SLIST_ENTRY(list_module_info) link;
char keyword[20];
char name[60];
} list_module_info;
typedef struct list_module_data {
module_type type;
list_module_info *matches;
size_t matches_num, matches_alloc;
} list_module_data;
static int
list_module_load(const char *filename, lt_ptr data)
{
list_module_data *lmdata = data;
const char *base;
const char *module_keyword = NULL, *module_name = NULL;
char *name;
yasm_arch_module *arch_module;
yasm_dbgfmt_module *dbgfmt_module;
yasm_objfmt_module *objfmt_module;
yasm_listfmt_module *listfmt_module;
yasm_optimizer_module *optimizer_module;
yasm_parser_module *parser_module;
yasm_preproc_module *preproc_module;
lt_dlhandle handle;
/* Strip off path components, if any */
base = basename(filename);
if (!base)
return 0;
/* All modules have '_' in them; early check */
if (!strchr(base, '_'))
return 0;
/* Check to see if module is of the type we're looking for.
* Even though this check is also implicitly performed below, there's a
* massive speedup in avoiding the dlopen() call.
*/
if (strncmp(base, module_type_str[lmdata->type],
strlen(module_type_str[lmdata->type])) != 0)
return 0;
/* Load it */
handle = lt_dlopenext(filename);
if (!handle)
return 0;
/* Build base symbol name */
name = yasm_xmalloc(strlen(base)+5+
strlen(module_type_str[lmdata->type])+1);
strcpy(name, base);
strcat(name, "_LTX_");
strcat(name, module_type_str[lmdata->type]);
/* Prefix with yasm in the right place and get name/keyword */
switch (lmdata->type) {
case MODULE_ARCH:
strncpy(name, "yasm", 4);
arch_module = lt_dlsym(handle, name);
if (arch_module) {
module_keyword = arch_module->keyword;
module_name = arch_module->name;
}
break;
case MODULE_DBGFMT:
strncpy(name+2, "yasm", 4);
dbgfmt_module = lt_dlsym(handle, name+2);
if (dbgfmt_module) {
module_keyword = dbgfmt_module->keyword;
module_name = dbgfmt_module->name;
}
break;
case MODULE_OBJFMT:
strncpy(name+2, "yasm", 4);
objfmt_module = lt_dlsym(handle, name+2);
if (objfmt_module) {
module_keyword = objfmt_module->keyword;
module_name = objfmt_module->name;
}
break;
case MODULE_LISTFMT:
strncpy(name+3, "yasm", 4);
listfmt_module = lt_dlsym(handle, name+3);
if (listfmt_module) {
module_keyword = listfmt_module->keyword;
module_name = listfmt_module->name;
}
break;
case MODULE_OPTIMIZER:
strncpy(name+5, "yasm", 4);
optimizer_module = lt_dlsym(handle, name+5);
if (optimizer_module) {
module_keyword = optimizer_module->keyword;
module_name = optimizer_module->name;
}
break;
case MODULE_PARSER:
strncpy(name+2, "yasm", 4);
parser_module = lt_dlsym(handle, name+2);
if (parser_module) {
module_keyword = parser_module->keyword;
module_name = parser_module->name;
}
break;
case MODULE_PREPROC:
strncpy(name+3, "yasm", 4);
preproc_module = lt_dlsym(handle, name+3);
if (preproc_module) {
module_keyword = preproc_module->keyword;
module_name = preproc_module->name;
}
break;
}
if (module_keyword && module_name) {
list_module_info *lminfo;
/* Find empty location in array */
if (lmdata->matches_num >= lmdata->matches_alloc) {
lmdata->matches_alloc *= 2;
lmdata->matches =
yasm_xrealloc(lmdata->matches,
sizeof(list_module_info)*lmdata->matches_alloc);
}
lminfo = &lmdata->matches[lmdata->matches_num++];
/* Build lminfo structure */
strncpy(lminfo->keyword, module_keyword, sizeof(lminfo->keyword) - 1);
lminfo->keyword[sizeof(lminfo->keyword) - 1] = '\0';
strncpy(lminfo->name, module_name, sizeof(lminfo->name) - 1);
lminfo->name[sizeof(lminfo->name) - 1] = '\0';
}
/* Clean up */
yasm_xfree(name);
lt_dlclose(handle);
return 0;
}
static int
list_module_compare(const void *n1, const void *n2)
{
const list_module_info *i1 = n1, *i2 = n2;
return strcmp(i1->keyword, i2->keyword);
}
void
list_modules(module_type type,
void (*printfunc) (const char *name, const char *keyword))
{
size_t i;
const lt_dlsymlist *preloaded;
char name[100];
char *dot;
list_module_data lmdata;
char *prev_keyword = NULL;
lmdata.type = type;
lmdata.matches_num = 0;
lmdata.matches_alloc = 10;
lmdata.matches =
yasm_xmalloc(sizeof(list_module_info)*lmdata.matches_alloc);
/* Search preloaded symbols */
preloaded = lt_preloaded_symbols;
while (preloaded->name) {
/* Strip out any library extension */
strncpy(name, preloaded->name, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
dot = strrchr(name, '.');
if (dot)
*dot = '\0';
/* Search it */
list_module_load(name, &lmdata);
preloaded++;
}
/* Search external module path */
lt_dlforeachfile(NULL, list_module_load, &lmdata);
lt_dlforeachfile(".", list_module_load, &lmdata);
/* Sort, print, and cleanup */
yasm__mergesort(lmdata.matches, lmdata.matches_num,
sizeof(list_module_info), list_module_compare);
for (i=0; i<lmdata.matches_num; i++) {
/* Don't print duplicates */
if (!prev_keyword || strcmp(prev_keyword, lmdata.matches[i].keyword))
printfunc(lmdata.matches[i].name, lmdata.matches[i].keyword);
prev_keyword = lmdata.matches[i].keyword;
}
yasm_xfree(lmdata.matches);
}