Skip to content

Commit

Permalink
Add LoadKernelExtension, IsKernelExtensionAvailable
Browse files Browse the repository at this point in the history
  • Loading branch information
fingolfin committed May 26, 2021
1 parent 5a1fe5f commit 7f2573f
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 61 deletions.
47 changes: 30 additions & 17 deletions doc/ref/gappkg.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,20 @@ $ gap4/bin/i386-ibm-linux-gcc2/gac -d test.c
]]></Log>
<P/>
This will produce a file <F>test.so</F>, which then can be loaded into &GAP;
with <Ref Func="LoadDynamicModule"/>.
with <Ref Func="LoadKernelExtension"/>. If the kernel module is required
for the package to work, then its <F>PackageInfo.g</F> should define
a <C>AvailabilityTest</C> which calls <Ref Func="IsKernelExtensionAvailable"/>,
see <Ref Subsect="Test for the Existence of GAP Package Binaries"/> for details.
<P/>
Note that before GAP 4.12, <Ref Func="LoadDynamicModule"/> was used for this.
It is still available and in fact <Ref Func="LoadKernelExtension"/> call it;
but the latter provides a higher level abstraction and is more convenient to use.

</Subsection>

<ManSection>
<Func Name="LoadDynamicModule" Arg='filename'/>

<Description>
To load a compiled file, the command <Ref Func="LoadDynamicModule"/> is used.
This command loads <A>filename</A> as module.
<P/>
<Log><![CDATA[
gap> LoadDynamicModule("./test.so");
]]></Log>
<P/>
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.
</Description>
</ManSection>
<#Include Label="IsKernelExtensionAvailable">
<#Include Label="LoadKernelExtension">
<#Include Label="LoadDynamicModule">

<Subsection Label="The PackageInfo.g File">
<Heading>The PackageInfo.g File</Heading>
Expand Down Expand Up @@ -1280,6 +1274,25 @@ some compilers or default compiler options.
See&nbsp;<Ref Sect="Testing for the System Architecture"/>
for information on how to test for the architecture.
<P/>
Package using a kernel module (see <Ref Subsect="Kernel modules"/>),
one may use a test like this:
<Listing><![CDATA[
...
AvailabilityTest := function()
# see if example.so exists and is a loadable kernel extension
if IsKernelExtensionAvailable("example") then
LogPackageLoadingMessage( PACKAGE_WARNING,
[ "The kernel extension `example' is unavailable,",
"perhaps it needs to be recompiled?",
"See the installation instructions;",
"type: ?Installing the Example package" ] );
return false;
fi;
return true;
end,
...
]]></Listing>
<P/>
<Index Key="LogPackageLoadingMessage"><C>LogPackageLoadingMessage</C></Index>
Last but not least: do not print anything in the <C>AvailabilityTest</C>
function of the package via <C>Print</C> or <C>Info</C>. Instead one should
Expand Down
125 changes: 125 additions & 0 deletions lib/files.gd
Original file line number Diff line number Diff line change
Expand Up @@ -566,12 +566,24 @@ end );
##
#F LoadDynamicModule( <filename> ) . . . . . . try to load a dynamic module
##
## <#GAPDoc Label="LoadDynamicModule">
## <ManSection>
## <Func Name="LoadDynamicModule" Arg='filename'/>
##
## <Description>
## To load a compiled file, the command <Ref Func="LoadDynamicModule"/> is
## used. This command loads <A>filename</A> as module.
## <P/>
## <Log><![CDATA[
## gap> LoadDynamicModule("./test.so");
## ]]></Log>
## <P/>
## 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.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BIND_GLOBAL( "LoadDynamicModule", function( filename )

Expand Down Expand Up @@ -605,6 +617,119 @@ BIND_GLOBAL( "LoadStaticModule", function( filename )
end );


#############################################################################
##
#F IsKernelExtensionAvailable( <pkgname> [, <modname> ] )
##
## <#GAPDoc Label="IsKernelExtensionAvailable">
## <ManSection>
## <Func Name="IsKernelExtensionAvailable" Arg='pkgname[, modname]'/>
##
## <Description>
## For use by packages: Search for a loadable kernel module inside package
## <A>pkgname</A> with name <A>modname</A> and return <K>true</K> if found,
## otherwise <K>false</K>.
## If <A>modname</A> is omitted, then <A>pkgname</A> is used instead. Note
## that package names are case insensitive, but <A>modname</A> is not.
## <P/>
## This function first appeared in GAP 4.12. It is typically called in the
## <C>AvailabilityTest</C> function of a package
## (see <Ref Subsect="Test for the Existence of GAP Package Binaries"/>).
## <Log><![CDATA[
## gap> IsKernelExtensionAvailable("myPackageWithKernelExtension");
## true
## ]]></Log>
## </Description>
## </ManSection>
## <#/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( <pkgname> [, <modname> ] )" );
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( <pkgname> [, <modname> ] )
##
## <#GAPDoc Label="LoadKernelExtension">
## <ManSection>
## <Func Name="LoadKernelExtension" Arg='pkgname[, modname]'/>
##
## <Description>
## For use by packages: Search for a loadable kernel module inside package
## <A>pkgname</A> with name <A>modname</A>, and load it if found.
## If <A>modname</A> is omitted, then <A>pkgname</A> is used instead. Note
## that package names are case insensitive, but <A>modname</A> is not.
## <P/>
## This function first appeared in GAP 4.12. It is typically called in the
## <F>init.g</F> file of a package.
## <P/>
## Previously, packages with a kernel module typically used code like this:
## <Listing><![CDATA[
## path := Filename(DirectoriesPackagePrograms("SomePackage"), "SomePackage.so");
## if path <> fail then
## LoadDynamicModule(path);
## fi;
## ]]></Listing>
## That can now be replaced by the following, which also produces more
## helpful error messages for the user:
## <Listing><![CDATA[
## LoadKernelExtension("SomePackage");
## ]]></Listing>
## 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:
## <Log><![CDATA[
## LoadKernelExtension("SomePackage", "kext"); # this will look for kext.so
## ]]></Log>
## </Description>
## </ManSection>
## <#/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( <pkgname> [, <modname> ] )" );
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( <filename> ) . . . . . . . . . . . . . . . . . edit and read file
Expand Down
89 changes: 59 additions & 30 deletions src/modules.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,32 +203,66 @@ StructInitInfo * LookupStaticModule(const char * name)
** This function attempts to load a compiled module <name>.
** If successful, it returns 0, and sets <func> to a pointer to the init
** function of the module. In case of an error, <func> 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( <self>, <name> ) . 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
}


/****************************************************************************
**
Expand All @@ -248,14 +282,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);
Expand Down Expand Up @@ -855,17 +887,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 "
Expand Down Expand Up @@ -1051,6 +1079,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),
Expand Down
15 changes: 1 addition & 14 deletions tst/testinstall/kernel/gap.tst
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,6 @@ Error, GAP_CRC: <filename> must be a string (not the value 'fail')
gap> GAP_CRC("foobar");
0

#
gap> LOAD_DYN(fail);
Error, LOAD_DYN: <filename> must be a string (not the value 'fail')

#
gap> LOAD_STAT(fail);
Error, LOAD_STAT: <filename> 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\
Expand Down Expand Up @@ -186,7 +173,7 @@ gap> Sleep(0);
gap> Sleep(1);

#
gap> MicroSleep(fail);
gap> MicroSleep(fail);
Error, MicroSleep: <msecs> must be a small integer (not the value 'fail')
gap> MicroSleep(0);
gap> MicroSleep(1);
Expand Down
24 changes: 24 additions & 0 deletions tst/testinstall/kernel/modules.tst
Original file line number Diff line number Diff line change
@@ -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: <filename> must be a string (not the value 'fail')

#
gap> LOAD_DYN(fail);
Error, LOAD_DYN: <filename> must be a string (not the value 'fail')

#
gap> LOAD_STAT(fail);
Error, LOAD_STAT: <filename> must be a string (not the value 'fail')
gap> LOAD_STAT("foobar");
false

#
gap> LoadedModules();;

#
gap> STOP_TEST("kernel/modules.tst", 1);

0 comments on commit 7f2573f

Please sign in to comment.