Of course I could use dlopen(3) in my code, but then it seems I’d have
to use dlsym for maybe hundreds of functions of the .so. On windoze I
can use /DELAYLOAD and the exe starts without bothering about the dll,
until there’s a reference, and I just have to handle an
ERROR_MOD_NOT_FOUND exception on attempts to call the main entry
functions of the dll.
Thanks,
Per
Thereby leading to disappointment when they _do_ attempt to use
those services after accumulating several hours of state that
is lost because the program did not verify the presence of
all resources at the beginning.
> Of course I could use dlopen(3) in my code, but then it seems I’d have
> to use dlsym for maybe hundreds of functions of the .so.
No doubt you have heard of a loop that updates a transfer vector?
> On windoze I
> can use /DELAYLOAD and the exe starts without bothering about the dll,
> until there’s a reference, and I just have to handle an
> ERROR_MOD_NOT_FOUND exception on attempts to call the main entry
> functions of the dll.
You can simulate the same thing with ELF .so by using a transfer vector
whose every entry begins by pointing to a "dlopen the .so" function.
--
> Of course I could use dlopen(3) in my code, but then it seems I’d have
> to use dlsym for maybe hundreds of functions of the .so.
Umm, so what? Computers are great at performing tedious, repetitive
tasks millions of times. Hundreds should be no problem at all.
DS
> > Of course I could use dlopen(3) in my code, but then it seems I’d have
> > to use dlsym for maybe hundreds of functions of the .so.
>
> No doubt you have heard of a loop that updates a transfer vector?
My ignorance is almost complete on ELF.
>
> > On windoze I
> > can use /DELAYLOAD and the exe starts without bothering about the dll,
> > until there’s a reference, and I just have to handle an
> > ERROR_MOD_NOT_FOUND exception on attempts to call the main entry
> > functions of the dll.
>
> You can simulate the same thing with ELF .so by using a transfer vector
> whose every entry begins by pointing to a "dlopen the .so" function.
>
Here is what I understand: Dlsym returns a function pointer, which can
be used to call the function, and I can of course collect the pointers
in an array. A big disadvantage is that the compiler, as far as I
know, won't check any parameters of the calls to the .so. I don't know
anything about the proposed ELF solution.
Best regards,
Per
> Here is what I understand: Dlsym returns a function pointer, which can
> be used to call the function, and I can of course collect the pointers
> in an array. A big disadvantage is that the compiler, as far as I
> know, won't check any parameters of the calls to the .so. I don't know
> anything about the proposed ELF solution.
The compiler will still check the parameters, so long as you ask it
to.
For example, assume that the library contains a function called
'some_library_func'. Assume that your code will only call this
function if the library exists and the function can be resolved in it
(you need some kind of 'loader' function that fills in all the
pointers and makes sure the library is there). Your wrapper looks like
this:
int some_library_func(int foo, void *bar)
{
return my_table->some_library_func(foo, bar);
}
This is one of several ways to do it that still preserves the types.
Another way:
int some_library_func(int foo, void *bar)
{
static void *(slf)(int, void *)=NULL;
if(slf==NULL)
{
if(library_ptr==NULL) abort(); // not supposed to happen
slf=((void) (*) (int, void *)) dlsym(library_ptr,
"some_library_func");
if(slf==NULL) abort(); // not supposed to happen
}
return (foo, bar);
}
Note: All examples untested.
DS
Thanks, David!
No solution without writing wrapper functions? I'm shuddering for
declaring a function pointer to the following example of function in
the library in question:
sword OCIEnvNlsCreate (OCIEnv **envp, ub4 mode, dvoid *ctxp,
dvoid *(*malocfp)(dvoid *ctxp, size_t size),
dvoid *(*ralocfp)(dvoid *ctxp, dvoid *memptr, size_t
newsize),
void (*mfreefp)(dvoid *ctxp, dvoid *memptr),
size_t xtramem_sz, dvoid **usrmempp,
ub2 charset, ub2 ncharset);
/Per
> Thanks, David!
> No solution without writing wrapper functions? I'm shuddering for
> declaring a function pointer to the following example of function in
> the library in question:
> sword OCIEnvNlsCreate (OCIEnv **envp, ub4 mode, dvoid *ctxp,
> dvoid *(*malocfp)(dvoid *ctxp, size_t size),
> dvoid *(*ralocfp)(dvoid *ctxp, dvoid *memptr, size_t
> newsize),
> void (*mfreefp)(dvoid *ctxp, dvoid *memptr),
> size_t xtramem_sz, dvoid **usrmempp,
> ub2 charset, ub2 ncharset);
I know there are ways to automate/simplify this. I can't seem to find
any of them right now. Maybe someone reading this thread will chime in
with a suggestion?
DS
C actually supports function types, not only function pointer types.
This means a pointer to the function above can be declared (with the
help of typedef) as below:
typedef sword create_routine(OCIENV **, ub4, dvoid *,
dvoid *(*)(dvoid *, size_t),
dvoid *(*)(dvoid *, dvoid *, size_t),
void (*)(dvoid *, dvoid *),
size_t, void **, ub2, ub2);
create_routine *create_it;
A further simplification would be to additionally declare the types of
the passed function pointers, eg
typedef dvoid *maloc_routine(dvoid *, size_t);
typedef dvoid *raloc_routine(dvoid *, dvoid *, size_t);
typedef void free_routine(dvoid *, dvoid *);
typedef sword create_routine(OCIEnv **, ub4, dvoid *,
maloc_routine *,
raloc_routine *,
free_routine *,
size_t, dvoid **, ub2, ub2);
create_routine *create_it;
(idea stolen from one of the Steven's books)
That's a case were a typedef may make things a lot easier to read:
typedef sword ( * strange_func )( OCIEnv **, ub4, dvoid *,
dvoid * ( * )( dvoid *, size_t),
dvoid * ( * )( dvoid *, dvoid *, size_t ),
void ( *)( dvoid *, dvoid * ),
size_t, dvoid **, ub2, ub2 );
and afterwards just use 'strange_func' for defining pointers to
it, casting etc. And if you give up on Hungarian notation things
may become even more readable;-)
Regards, Jens
--
\ Jens Thoms Toerring ___ j...@toerring.de
\__________________________ http://toerring.de
The typedef solves the function pointer declaration! But it seems I
have to do something like the following for each and every API
function (sorry about the Hungarian notation):
typedef sword typeOCIEnvCreate(OCIEnv **, ub4, dvoid *,
dvoid *(*)(dvoid *, size_t),
dvoid *(*)(dvoid *, dvoid *, size_t),
void (*)(dvoid *, dvoid *),
size_t, void **);
typeOCIEnvCreate * fOCIEnvCreate;
sword OCIEnvCreate (OCIEnv **envp, ub4 mode, dvoid *ctxp,
dvoid *(*malocfp)(dvoid *ctxp, size_t size),
dvoid *(*ralocfp)(dvoid *ctxp, dvoid *memptr, size_t
newsize),
void (*mfreefp)(dvoid *ctxp, dvoid *memptr),
size_t xtramem_sz, dvoid **usrmempp)
{
return fOCIEnvCreate(envp, mode, ctxp, malocfp, ralocfp, mfreefp,
xtramem_sz, usrmempp);
}
It seems error-prone and tedious, or a bit of work to generate the
code. A favourite quote by Terrence Parr is ringing in my ears
( something like "Why spend 5 days writing by hand what you can spend
5 years of your life automating").
Isn't there any other solution? On Windows it's so simple with
delayload; no wrappers, just handle an exception on the main API
functions.
/Per
> On 23 Juni, 16:53, j...@toerring.de (Jens Thoms Toerring) wrote:
[snip - function wrapper stuff]
> It seems error-prone and tedious, or a bit of work to generate the
> code. A favourite quote by Terrence Parr is ringing in my ears
> ( something like "Why spend 5 days writing by hand what you can spend
> 5 years of your life automating").
>
> Isn't there any other solution? On Windows it's so simple with
> delayload; no wrappers, just handle an exception on the main API
> functions.
Create another stub version of the shared library that has definitions
for all functions without any real body. Something like this:
/* stub.c */
#include <stdio.h>
#include <stdlib.h>
#define F(funcname) \
void funcname(void) { \
fprintf(stderr, #funcname "called!\n"); \
abort(); }
F(foo)
F(bar)
F(baz)
F(OCIEnvNlsCreate)
...
You could probably build this stub.c automatically with a script using
the output from nm run against the *real* .so file. Then you include
the resulting (stub) .so file with your base product -- it satisfies the
loader but will abort if any of the symbols in the stub file are called.
In the event the customer buys the real "commercial product" .so, simply
replace the fake one with the real one.
This won't work exactly the same with a C++ library since the symbol
names get mangled by the compiler to (in effect) include the argument
types. But you can probably still do it by using nm to create the
stub.c file with already-mangled names.
GH
Perfry
You don't have to do the prototypes -- obviously, the prototypes are
correct in the library function header that you are using.
So, define each of the functions as:
#define OCIEnvCreate (*pOCIEnvCreate)
... more function pointer declarations
in an include file. Now, when the include file is processed, each of the
calls becomes "pointer to function" automatically. No source change is
then needed.
In a separate file:
#define DEFINE(f) int (*p##f)() = NULL
DEFINE(OCIEnvCreate);
... more pointer definitions ...
#define TABLE_ENTRY(f) #f, &f
struct {
char *name;
void *p
} table = {
TABLE_ENTRY(OCIEnvCreate),
... more table entries for dlsym parsing ...
0,0
};
In this separate file, DO NOT including the prototypes. Scan the table,
filling in function pointer pointers (after suitable casting) at load
time.
Warning: not tested in the slightest -- just adding some syntactic sugar
here.
You can also use "Foreign Function Interface" -- libffi, as well.