Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LoadKernelExtension, IsKernelExtensionAvailable #4231

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -205,32 +205,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 @@ -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);
Expand Down Expand Up @@ -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 "
Expand Down Expand Up @@ -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),
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);