design choices for a loadable module interface

Jeffrey Hutzelman jhutz at
Thu Jun 3 04:38:25 EDT 2010

--On Tuesday, May 25, 2010 05:56:24 PM -0500 Nicolas Williams 
<Nicolas.Williams at> wrote:

> I strongly recommend against exported data symbols in an SPI.

Me too, mostly, though perhaps not for the same reasons.  I might make an 
exception for a simple type, such as an interface version number or a 
string containing the module name, but it hardly seems worth the effort.

>> * exported function symbol(s)
>>   -- separate symbol for each interface function
>>      # Runtime symbol resolution can be slow on some platforms.
> But it's one-time.  The framework should memoize the dlsym() calls.


>>   -- one function that returns a vtable, possibly parameterized by
>>      version identifier
>>      # This may still require additional relocation overhead on some
>>        platforms.
> The function pointers in the vtable should be pointers to either static
> or non-exported functions.

Generally, yes.

>>      # Alternatively, have the function populate a passed-in
>>        caller-allocated vtable, possibly with a sanity check on the
>>        structure size.  This can still cause additional relocations,
>>        unless the function assigns the entries one at a time (e.g., by
>>        doing arithmetic on PLT entries), instead of copying from a
>>        prepopulated (private) struture.
> V-table versioning is needed in either approach.
> Either v-table scheme means having to Write More Code.  Yuck, but I'd
> live.

I mostly dislike the vtable approach, regardless of the specific method of 
finding it or who allocates the storage.  Oddly, I'm fine with a model 
where the library implements register_foo() functions that plugins can call 
to register handlers/objects/whatever, even though those often include an 
ops table in the registration data.

>> Should different modules that implement the same interface each export
>> a different name?  If so, how would the caller discover the correct
>> name to pass to dlsym() or equivalent?
> IMO: yes, they should alluse the same symbol names.

Yes, absolutely, they should all use the same names.  Architectures that do 
otherwise are a major PITA in my opinion.

>> Relatedly, do we want to be able to use the same shared object as both
>> a loadable module and as a builtin?
> For static linking you should reduce the symbol scope to static and then
> resort to a vtable -- an internal dlsym(), effectively.  This might
> require concatenating source files and using a C pre-processor symbol to
> expand to the keyword 'static'.
> Or you might:
>> Using different names per module might simplify linking the module as
>> a builtin, but the loader for builtin modules would still know how to
>> find it.
> use different names (possibly made to appear as the same in the source
> files by using auto-generated C pre-processor macros to rename the
> symbols to the plugin-specific names) and build your own internal
> dlsym() on top of that.

Yeah.  Or do everything via published register_foo() interfaces, and have 
each plugin publish an init function which is called when the plugin is 
loaded, or at startup for built-in plugins.  Of course the init functions 
for builtins might have to have per-plugin names, in which case it'd be 
relatively straightforward to generate a function which calls the init 
functions of all the builtin plugins.

Or they could be static but included in some magic global table of init 
functions to be called, possibly constructed by putting their addresses 
into a separate ELF section, one address per plugin object, with the 
startup code using the base address and length of that section to find the 

Oh dear; have I been staring at Linux too much?  Sorry.

-- Jeff

More information about the krbdev mailing list