📄 mal_linker.mx
字号:
@' The contents of this file are subject to the MonetDB Public License@' Version 1.1 (the "License"); you may not use this file except in@' compliance with the License. You may obtain a copy of the License at@' http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html@'@' Software distributed under the License is distributed on an "AS IS"@' basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the@' License for the specific language governing rights and limitations@' under the License.@'@' The Original Code is the MonetDB Database System.@'@' The Initial Developer of the Original Code is CWI.@' Portions created by CWI are Copyright (C) 1997-2007 CWI.@' All Rights Reserved.@a M. Kersten@v 0.0@-@+ Module LoadingThe server is bootstrapped by processing a MAL script withmodule definitions or extensions.For each module file encountered, the object library lib_<modulename>.so is searched for in @dots{}/lib(64)/MonetDB.The corresponding signature are defined in @dots{}/lib(64)/<modulename>.mal.@-The default bootstrap script is called @dots{}/lib(64)/MonetDB/mal_init.maland it is designated in the configuration file as the mal_init property.The rationale for this set-up is that database administrators canextend/overload the bootstrap procedure without affecting the distributedsoftware package. It merely requires a different direction for the mal_init property.@-The scheme also isolates the functionality embedded in modules frominadvertise use on non-compliant databases.[access control issue, how to limit what a user can do on a database?]@-Unlike previous versions of MonetDB, modules can not be unloaded. Dynamic libraries are always global and, therefore, itis best to load them as part of the server initialization phase.@-The MAL program should be compiled with -rdynamic and -ldl.This enables loading the routines and finding out the addressof a particular routine@-@{For the time being we assume that all commands are statically linked.@h#ifndef _MAL_LINKER_H#include "mal_module.h"#ifdef HAVE_DLFCN_H#include <dlfcn.h>#else #define RTLD_LAZY 1#define RTLD_NOW 2#define RTLD_GLOBAL 4#define RTLD_NOW_REPORT_ERROR 8#endif/* #define DEBUG_MAL_LINKER */#define MONET64 1mal_export MALfcn getAddress(str filename, str modnme, str fcnname,int silent);mal_export char *MSP_locate_script(const char *mod_name);mal_export char *MSP_locate_file(const char *mod_name);mal_export str loadLibrary(str modulename);mal_export void unloadLibraries(void);mal_export void initLibraries(void);mal_export int isPreloaded(str nme);mal_export int isLoaded(str modulename);#endif@}@-The mapping from MAL module.function() identifier to an address isresolved in the function getAddress. Since all modules libraries are loadedcompletely with GLOBAL visibility, it suffices to provide the internal function name.In case an attempt to link to an address fails, a final attempt is made to locate the *.o file inthe current directory.@{Note, however, that the libraries are reference counted. Although wedo;t close them until end of session it seems prudent to maintainthe consistency of this counter.@c#include "mal_config.h"#include "mal_linker.h"#include "mal_function.h" /* for throwException() */#include "mal_import.h" /* for slash_2_dir_sep() */static int noDlopen;#define MAXMODULES 256typedef struct{ str filename; str fullname; void **handle;} FileRecord;static FileRecord *filesLoaded;static int maxfiles = 0;static int lastfile = 0;MALfcngetLocalObjectFile(str filename){ int mode = RTLD_NOW | RTLD_GLOBAL; void *handle = NULL; char *fullname; fullname = MSP_locate_file(filename); if (fullname == NULL) {#ifdef DEBUG_MAL_LINKER stream_printf(GDKout,"Name not resolved %s\n",filename);#endif return 0; }#ifdef DEBUG_MAL_LINKER printf("attempt to load function %s from %s\n",filename, fullname);#endif handle = dlopen(fullname, mode);#ifdef DEBUG_MAL_LINKER stream_printf(GDKout, "localObjectLoaded ? %s\n",(handle!=0?"yes":"no"));#endif if (handle) return (MALfcn) dlsym(handle, filename); stream_printf(GDKout, "could not access library %s\n", dlerror()); return 0;}MALfcngetAddress(str filename, str modnme, str fcnname, int silent){ void *dl = 0; MALfcn adr; int idx;#ifdef DEBUG_MAL_LINKER2 stream_printf(GDKout, "address of %s.%s from '%s' ?\n", (modnme?modnme:"<unknown>"), fcnname, (filename?filename:"<stdin>"));#else (void) modnme;#endif@-Search for occurrence of the function in the library identified by the filename.@c for(idx =0; idx < lastfile && filesLoaded[idx].filename; idx++) if( strcmp(filename, filesLoaded[idx].filename)==0) { adr = (MALfcn) dlsym(filesLoaded[idx].handle, fcnname); if( adr != NULL) return adr; /* found it */ }@-Search for occurrence of the function in any library already loaded.This deals with the case that files are linked together to reducethe loading time, while the signatures of the functions are stillobtained from the source-file MAL script.@c for(idx =0; idx < lastfile && filesLoaded[idx].handle; idx++){ adr = (MALfcn) dlsym(filesLoaded[idx].handle, fcnname); if( adr != NULL) return adr; /* found it */ }@-Try the program libraries at large or run through allloaded files and try to resolve the functionname again.@c dl = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); if( dl != NULL){ adr = (MALfcn) dlsym(dl, fcnname); if( adr != NULL) return adr; /* found it */ } if( !silent) showException(MAL,"MAL.getAddress", "address of '%s.%s' not found", (modnme?modnme:"<unknown>"), fcnname); return NULL;}@}@+ Module file loadingThe default location to search for the module is in monet_mod_pathunless an absolute path is given.Loading further relies on the Linux policy to search for the modulelocation in the following order: 1) the colon-separated list ofdirectories in the user's LD_LIBRARY, 2) the libraries specifiedin /etc/ld.so.cache, and 3) /usr/lib followed by /libIf the module contains a routine _init, then that code is executedbefore the loader returns. Likewise the routine _fini is called justbefore the module is unloaded.A module loading conflict emerges if a function is redefined.A duplicate load is simply ignored by keeping track of modulesalready loaded.@{@cstatic voidinitM5Loader(){ FileRecord *newFilesLoaded; int i, newsize = maxfiles + MAXMODULES; /* assume a safe call environment */ if (filesLoaded == NULL) { mal_set_lock(mal_contextLock, "loadFiles"); if (filesLoaded == NULL) { maxfiles = MAXMODULES; filesLoaded = (FileRecord *) GDKzalloc(maxfiles * sizeof(FileRecord)); lastfile = 0; } mal_unset_lock(mal_contextLock, "loadFiles"); } else if (lastfile + 2 == maxfiles) { /* guarantee at least one free slot */ mal_set_lock(mal_contextLock, "loadFiles"); newFilesLoaded = (FileRecord *)GDKzalloc(newsize * sizeof(FileRecord)); memcpy((char *) newFilesLoaded, (char *) filesLoaded, maxfiles * sizeof(FileRecord)); GDKfree(filesLoaded); filesLoaded = newFilesLoaded; for (i = maxfiles; i < newsize; i++) { filesLoaded[i].filename = NULL; filesLoaded[i].fullname = NULL; filesLoaded[i].handle = NULL; } maxfiles = newsize; mal_unset_lock(mal_contextLock, "loadFiles"); }}intisLoaded(str modulename){ int idx; for (idx = 0; filesLoaded[idx].filename && idx < lastfile; idx++) if (strcmp(filesLoaded[idx].filename, modulename) == 0) { return 1; } return 0;}#ifndef MAXPATHLEN#define MAXPATHLEN 1024#endifstrloadLibrary(str filename){ int mode = RTLD_NOW | RTLD_GLOBAL; char nme[MAXPATHLEN]; void *handle = NULL; str fullname, s; int idx; const char *errmsg; initM5Loader(); for (idx = 0; filesLoaded[idx].filename && idx < lastfile; idx++) if (strcmp(filesLoaded[idx].filename, filename) == 0) {#ifdef DEBUG_MAL_LINKER stream_printf(GDKout, "already loaded:%s:%x\n", filename, (int)filesLoaded[idx].handle); return (str) throwException("loaderException", "Module loaded twice", filename);#endif return MAL_SUCCEED; }@-Use the search path provided in the configuration file@c s = strrchr(filename, DIR_SEP); snprintf(nme, MAXPATHLEN, "_%s", s ? s + 1 : filename); fullname = MSP_locate_file(nme); if (fullname == NULL) {#ifdef DEBUG_MAL_LINKER2 stream_printf(GDKout, "file not found %s\n", filename);#endif return MAL_SUCCEED; }/* AIX requires RTLD_MEMBER to load a module that is a member of an archive. */#ifdef RTLD_MEMBER mode |= RTLD_MEMBER;#endif handle = dlopen(fullname, mode); if (handle == NULL) { errmsg = dlerror(); if (!errmsg) errmsg = "(no error from dlerror())";#ifdef DEBUG_MAL_LINKER2 { char errbuf[1024]; snprintf(errbuf, 1024, "%s: loader error %s", filename, errmsg); stream_printf(GDKout, "load[%d]:%s %s:%s\n", lastfile, filename, fullname, errbuf); }#endif GDKfree(fullname); throw(LOADER, "loadLibrary", "%s: loader error %s", filename, errmsg); }#ifdef DEBUG_MAL_LINKER2 stream_printf(GDKout, "load[%d]:%s %s:succeeded\n", lastfile, filename, fullname);#endif mal_set_lock(mal_contextLock, "loadModule"); if (lastfile == maxfiles) { showException(MAL,"loadModule", "internal error, too many modules loaded"); } else { filesLoaded[lastfile].filename = GDKstrdup(filename); filesLoaded[lastfile].fullname = fullname; filesLoaded[lastfile].handle = handle; lastfile ++; } mal_unset_lock(mal_contextLock, "loadModule"); return MAL_SUCCEED;}@-For analysis of memory leaks we should cleanup the libraries beforewe exit the server. This does not involve the libraries themselves,because they may still be in use.@cvoidunloadLibraries(){ int i; mal_set_lock(mal_contextLock, "unloadModule"); for (i = 0; i < lastfile; i++) if (filesLoaded[i].fullname) { /* dlclose(filesLoaded[i].handle);*/ GDKfree(filesLoaded[i].filename); GDKfree(filesLoaded[i].fullname); } lastfile = 0; GDKfree(filesLoaded); filesLoaded=0; mal_unset_lock(mal_contextLock, "unloadModule");}@}@- To speedup restart and to simplify debugging, the Monet server canbe statically linked with some (or all) of the modules libraries. A complicating factor is then to avoid users to initiate another loadof the module file, because it would lead to a dlopen error.The partial way out of this dilema is to administer somewherethe statically bound modules, or to enforce that each modulecomes with a known routine for which we can search.In the current version we use the former approach.@{The routine below turns off dynamic loading while parsing thecommand signature files.@cstatic str preloaded[] = { "kernel/bat", 0};intisPreloaded(str nme){ int i;#ifdef DEBUG_MAL_LINKER stream_printf(GDKout, "load:%s:preloaded?\n", nme);#endif for (i = 0; preloaded[i]; i++) if (strcmp(preloaded[i], nme) == 0) return 1; return 0;}voidinitLibraries(){ int i; noDlopen = TRUE; if(noDlopen == FALSE) for(i=0;preloaded[i];i++) {#ifdef DEBUG_MAL_LINKER stream_printf(GDKout,"loading %s\n",preloaded[i]);#endif loadLibrary(preloaded[i]); }}@+ Handling of Module Library Search PathThe plausible locations of the modules can be designated byan environment variable. @cstatic char *locate_file(const char *basename, const char *ext){ char *mod_path = GDKgetenv("monet_mod_path"); char *fullname; size_t fullnamelen; size_t filelen = strlen(basename) + strlen(ext); if (mod_path == NULL) return NULL; while (*mod_path == PATH_SEP) mod_path++; if (*mod_path == 0) return NULL; fullnamelen = 512; fullname = GDKmalloc(fullnamelen); while (*mod_path) { size_t i; char *p; int fd; if ((p = strchr(mod_path, PATH_SEP)) != NULL) { i = p - mod_path; } else { i = strlen(mod_path); } while (i + filelen + 2 > fullnamelen) { fullnamelen += 512; fullname = GDKrealloc(fullname, fullnamelen); } /* we are now sure the directory name, file base name, extension, and separator fit into fullname, so we don't need to do any extra checks */ strncpy(fullname, mod_path, i); fullname[i] = DIR_SEP; strcpy(fullname + i + 1, basename); strcat(fullname + i + 1, ext); if ((fd = open(fullname, O_RDONLY)) >= 0) { close(fd); return GDKrealloc(fullname, strlen(fullname) + 1); } if ((mod_path = p) == NULL) break; while (*mod_path == PATH_SEP) mod_path++; } /* not found */ GDKfree(fullname); return NULL;}#define MAL_EXT ".mal"char *MSP_locate_script(const char *filename){ return locate_file(filename, MAL_EXT);}char *MSP_locate_file(const char *filename){ char *lib_name = GDKmalloc(strlen(filename) + strlen(SO_PREFIX) + 1); char *fullname; strcpy(lib_name, SO_PREFIX); strcpy(lib_name + strlen(SO_PREFIX), filename); fullname = locate_file(lib_name, SO_EXT);#ifdef _AIX fullname = GDKrealloc(fullname, strlen(fullname) + strlen(lib_name) + 5); strcat(fullname, "("); strcat(fullname, lib_name); strcat(fullname, ".0)");#endif GDKfree(lib_name); return fullname;}@}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -