From 28cd96c7bf901068ee632ecd51283011660b1352 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 26 May 2021 15:54:58 +0200 Subject: [PATCH] Add LoadKernelExtension, IsKernelExtensionAvailable --- doc/ref/gappkg.xml | 47 +++++++---- lib/files.gd | 125 +++++++++++++++++++++++++++++ src/modules.c | 89 +++++++++++++------- tst/testinstall/kernel/gap.tst | 15 +--- tst/testinstall/kernel/modules.tst | 24 ++++++ 5 files changed, 239 insertions(+), 61 deletions(-) create mode 100644 tst/testinstall/kernel/modules.tst diff --git a/doc/ref/gappkg.xml b/doc/ref/gappkg.xml index 83e10c79d8..d2b4895a24 100644 --- a/doc/ref/gappkg.xml +++ b/doc/ref/gappkg.xml @@ -143,26 +143,20 @@ $ gap4/bin/i386-ibm-linux-gcc2/gac -d test.c ]]>

This will produce a file test.so, which then can be loaded into ⪆ -with . +with . If the kernel module is required +for the package to work, then its PackageInfo.g should define +a AvailabilityTest which calls , +see for details. +

+Note that before GAP 4.12, was used for this. +It is still available and in fact call it; +but the latter provides a higher level abstraction and is more convenient to use. - - - - -To load a compiled file, the command is used. -This command loads filename as module. -

- LoadDynamicModule("./test.so"); -]]> -

-On some operating systems, once you have loaded a dynamic module with -a certain filename, loading another with the same filename will have -no effect, even if the file on disk has changed. - - +<#Include Label="IsKernelExtensionAvailable"> +<#Include Label="LoadKernelExtension"> +<#Include Label="LoadDynamicModule"> The PackageInfo.g File @@ -1280,6 +1274,25 @@ some compilers or default compiler options. See  for information on how to test for the architecture.

+Package using a kernel module (see ), +one may use a test like this: +

+

LogPackageLoadingMessage Last but not least: do not print anything in the AvailabilityTest function of the package via Print or Info. Instead one should diff --git a/lib/files.gd b/lib/files.gd index d6a54bb48f..0d56fbc2ba 100644 --- a/lib/files.gd +++ b/lib/files.gd @@ -566,12 +566,24 @@ end ); ## #F LoadDynamicModule( ) . . . . . . try to load a dynamic module ## +## <#GAPDoc Label="LoadDynamicModule"> ## ## ## ## +## To load a compiled file, the command is +## used. This command loads filename as module. +##

+## LoadDynamicModule("./test.so"); +## ]]> +##

+## On some operating systems, once you have loaded a dynamic module with a +## certain filename, loading another with the same filename will have no +## effect, even if the file on disk has changed. ## ## +## <#/GAPDoc> ## BIND_GLOBAL( "LoadDynamicModule", function( filename ) @@ -605,6 +617,119 @@ BIND_GLOBAL( "LoadStaticModule", function( filename ) end ); +############################################################################# +## +#F IsKernelExtensionAvailable( [, ] ) +## +## <#GAPDoc Label="IsKernelExtensionAvailable"> +## +## +## +## +## For use by packages: Search for a loadable kernel module inside package +## pkgname with name modname and return true if found, +## otherwise false. +## If modname is omitted, then pkgname is used instead. Note +## that package names are case insensitive, but modname is not. +##

+## This function first appeared in GAP 4.12. It is typically called in the +## AvailabilityTest function of a package +## (see ). +## IsKernelExtensionAvailable("myPackageWithKernelExtension"); +## true +## ]]> +## +## +## <#/GAPDoc> +## +BIND_GLOBAL( "IsKernelExtensionAvailable", function( pkgname, modname... ) + local fname; + + if Length(modname) = 0 then + modname := pkgname; + elif Length(modname) = 1 then + modname := modname[1]; + else + Error( "usage: IsKernelExtensionAvailable( [, ] )" ); + fi; + + if modname in SHOW_STAT() then + return true; + fi; + fname := Filename(DirectoriesPackagePrograms(pkgname), Concatenation(modname, ".so")); + if fname <> fail then + return IS_LOADABLE_DYN(fname); + fi; + return false; +end ); + + +############################################################################# +## +#F LoadKernelExtension( [, ] ) +## +## <#GAPDoc Label="LoadKernelExtension"> +## +## +## +## +## For use by packages: Search for a loadable kernel module inside package +## pkgname with name modname, and load it if found. +## If modname is omitted, then pkgname is used instead. Note +## that package names are case insensitive, but modname is not. +##

+## This function first appeared in GAP 4.12. It is typically called in the +## init.g file of a package. +##

+## Previously, packages with a kernel module typically used code like this: +##

fail then +## LoadDynamicModule(path); +## fi; +## ]]> +## That can now be replaced by the following, which also produces more +## helpful error messages for the user: +## +## For packages where the name of the kernel extension is not identical to +## that of the package, you can either rename the kernel extension to have a +## matching name (recommended if you only have a single kernel extension in +## your package, which is how we recommend to set up things anyway), or else +## use the two argument version: +## +## +## +## <#/GAPDoc> +## +BIND_GLOBAL( "LoadKernelExtension", function( pkgname, modname... ) + local fname; + + if Length(modname) = 0 then + modname := pkgname; + elif Length(modname) = 1 then + modname := modname[1]; + else + Error( "usage: LoadKernelExtension( [, ] )" ); + fi; + + if modname in SHOW_STAT() then + LoadStaticModule(modname); + return true; + fi; + fname := Filename(DirectoriesPackagePrograms(pkgname), Concatenation(modname, ".so")); + if fname <> fail then + LoadDynamicModule(fname); + return true; + fi; + return false; +end ); + + ############################################################################# ## #F Edit( ) . . . . . . . . . . . . . . . . . edit and read file diff --git a/src/modules.c b/src/modules.c index b0b6f0c7e6..16d4bb6503 100644 --- a/src/modules.c +++ b/src/modules.c @@ -205,32 +205,66 @@ StructInitInfo * LookupStaticModule(const char * name) ** This function attempts to load a compiled module . ** If successful, it returns 0, and sets to a pointer to the init ** function of the module. In case of an error, is set to 0, and the -** return value indicates which error occurred. +** return value is a pointer to a string with more information. */ #ifdef HAVE_DLOPEN -static Int SyLoadModule(const Char * name, InitInfoFunc * func) +static const char * SyLoadModule(const Char * name, InitInfoFunc * func) { - void * init; - void * handle; - - *func = 0; - - handle = dlopen( name, RTLD_LAZY | RTLD_LOCAL); - if ( handle == 0 ) { - Pr("#W dlopen() error: %s\n", (long) dlerror(), 0); - return 1; + void * handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL); + if (handle == 0) { + *func = 0; + return dlerror(); } - init = dlsym( handle, "Init__Dynamic" ); - if ( init == 0 ) - return 3; + *func = (InitInfoFunc)dlsym(handle, "Init__Dynamic"); + if (*func == 0) + return "symbol 'Init__Dynamic' not found"; - *func = (InitInfoFunc) init; return 0; } #endif +/**************************************************************************** +** +*F FuncIS_LOADABLE_DYN( , ) . test if a dyn. module is loadable +*/ +static Obj FuncIS_LOADABLE_DYN(Obj self, Obj filename) +{ + RequireStringRep(SELF_NAME, filename); + +#if !defined(HAVE_DLOPEN) + return False; +#else + + InitInfoFunc init; + + // try to load the module + SyLoadModule(CONST_CSTR_STRING(filename), &init); + if (init == 0) + return False; + + // get the description structure + StructInitInfo * info = (*init)(); + if (info == 0) + return False; + + // info->type should not be larger than kernel version + if (info->type / 10 > GAP_KERNEL_API_VERSION) + return False; + + // info->type should not have an older major version + if (info->type / 10000 < GAP_KERNEL_MAJOR_VERSION) + return False; + + // info->type % 10 should be 0, 1 or 2, for the 3 types of module + if (info->type % 10 > 2) + return False; + + return True; +#endif +} + /**************************************************************************** ** @@ -250,14 +284,12 @@ static Obj FuncLOAD_DYN(Obj self, Obj filename) InitInfoFunc init; - /* try to read the module */ - Int res = SyLoadModule(CONST_CSTR_STRING(filename), &init); - if (res == 1) - ErrorQuit("module '%g' not found", (Int)filename, 0); - else if (res == 3) - ErrorQuit("symbol 'Init_Dynamic' not found", 0, 0); + // try to read the module + const char * res = SyLoadModule(CONST_CSTR_STRING(filename), &init); + if (res) + ErrorQuit("failed to load dynamic module %g, %s", (Int)filename, (Int)res); - /* get the description structure */ + // get the description structure StructInitInfo * info = (*init)(); if (info == 0) ErrorQuit("call to init function failed", 0, 0); @@ -857,17 +889,13 @@ void LoadModules(void) InitInfoFunc init; #ifdef HAVE_DLOPEN - int res = SyLoadModule(buf, &init); - if (res != 0) { - Panic("Failed to load needed dynamic module %s, error " - "code %d\n", - buf, res); + const char * res = SyLoadModule(buf, &init); + if (init == 0) { + Panic("failed to load dynamic module %s, %s\n", buf, res); } info = (*init)(); if (info == 0) { - Panic("Failed to init needed dynamic module %s, error " - "code %d\n", - buf, res); + Panic("failed to init dynamic module %s\n", buf); } #else Panic("workspace require dynamic module %s, but dynamic " @@ -1053,6 +1081,7 @@ void ModulesPostRestore(void) */ static StructGVarFunc GVarFuncs[] = { GVAR_FUNC_1ARGS(GAP_CRC, filename), + GVAR_FUNC_1ARGS(IS_LOADABLE_DYN, filename), GVAR_FUNC_1ARGS(LOAD_DYN, filename), GVAR_FUNC_1ARGS(LOAD_STAT, filename), GVAR_FUNC_0ARGS(SHOW_STAT), diff --git a/tst/testinstall/kernel/gap.tst b/tst/testinstall/kernel/gap.tst index 672bdaaa9f..db7d2f5eeb 100644 --- a/tst/testinstall/kernel/gap.tst +++ b/tst/testinstall/kernel/gap.tst @@ -130,19 +130,6 @@ Error, GAP_CRC: must be a string (not the value 'fail') gap> GAP_CRC("foobar"); 0 -# -gap> LOAD_DYN(fail); -Error, LOAD_DYN: must be a string (not the value 'fail') - -# -gap> LOAD_STAT(fail); -Error, LOAD_STAT: must be a string (not the value 'fail') -gap> LOAD_STAT("foobar"); -false - -# -gap> LoadedModules();; - # gap> GASMAN(); Error, usage: GASMAN( "display"|"displayshort"|"clear"|"collect"|"message"|"pa\ @@ -186,7 +173,7 @@ gap> Sleep(0); gap> Sleep(1); # -gap> MicroSleep(fail); +gap> MicroSleep(fail); Error, MicroSleep: must be a small integer (not the value 'fail') gap> MicroSleep(0); gap> MicroSleep(1); diff --git a/tst/testinstall/kernel/modules.tst b/tst/testinstall/kernel/modules.tst new file mode 100644 index 0000000000..e74c6a03b5 --- /dev/null +++ b/tst/testinstall/kernel/modules.tst @@ -0,0 +1,24 @@ +# +# Tests for functions defined in src/modules.c +# +gap> START_TEST("kernel/modules.tst"); + +# +gap> IS_LOADABLE_DYN(fail); +Error, IS_LOADABLE_DYN: must be a string (not the value 'fail') + +# +gap> LOAD_DYN(fail); +Error, LOAD_DYN: must be a string (not the value 'fail') + +# +gap> LOAD_STAT(fail); +Error, LOAD_STAT: must be a string (not the value 'fail') +gap> LOAD_STAT("foobar"); +false + +# +gap> LoadedModules();; + +# +gap> STOP_TEST("kernel/modules.tst", 1);