My “simple” problem was to have one application, using functions from another application, and allow me to upgrade both, and continue working. I want to write some code for openssl to call my program. I’m changing my code frequently, and I do not want to have to recompile and rebind openssl every time I change my program. (The recompile takes about 3 hours on my baby machine).
It took me a couple of days to get working; I learned a lot.
The simple solution which does not really work
I have a suite of programs called mycode, and I want to use stuff from another package which I’ll call otherstuff.
I can simply bind mycode, and otherstuff together to resolve all of the functions entry points. It works, but if I upgrade mycode, or otherstuff, I will not pick up the latest versions, and the code which is in the final load module may be incompatible with the newer versions.
Use fetch!
In mycode I can use the C function fetch, to load a load module, and give me the entry point. I then call the functions using the entry point.
This works – after a fashion. The key word is load module. A load module exists in a PDS or a PDSE. A Program Object can be in Unix or in a PDS, so the fetch solution does not work with file files in Unix.
The code worked if the module was in a load module – which I didn’t want.
I could not use fetch to load a program object from Unix.
Putting the code in a load module
I used the binder to create a load module.
xlc dummy.o-o "//'COLIN.LOAD(dummy)'" ...
To get my program to run I had to use
EXPORT STEPLIB=COLIN.LOAD
./myprog
The fetch worked, and my program was called.
Use Dynamic Link Library
The Unix eqivilent to a load module is the DLL.
When you create a DLL, two parts are created.
- The .x file which is used for C to find how to call a function (stub code)
- The .so file which contains the executable code.
The .x file
The .x file contains data like
IMPORT DATA,'cpfopen','ascii_tab'
IMPORT CODE,'cpfopen','cpfopen1'
IMPORT CODE,'cpfopen','printHex'
Which says
- there is an entry point cpfopen1 in program object cpfopen
- there is an external data object called ascii_tab in program object cpfopen
- there is an entry point printHex in program object cpfopen
This .x file contains information for C to create stub code to load the actual code.
My calling program has
#pragma linkage(cpfopen1 ,OS)
...
int myopen ;
myopen = cpfopen1("ABC","DEF");
...
This needs to be complied with option
-Wc,DLL
If you do not use the DLL option it ignores the IMPORT statement, and reports cpfopen1 and printHex are not found.
I bound it using
xlc -o fopen myfile.o cpfopen1.x V
The executed code.
You need to compile your code and specify which function names you want to make visible to other programs(export).
You can use
- the compiler option EXPORTALL or
- #pragma export(function1) #pragma export(function2) within your code.
At bind time
l1="-Wl,DLL "
xlc cpfopen1.o -o cpfopen1 -V $l1 1>a
This creates
- the executable with the name in the -o parameter (cpfopen1)
- and cpfopen1.x using the name of the object in the -o parameter
With
l1="-Wl,DLL "
xlc cpfopen1.o -o cpfopen1.so -V $l1 1>a
the file cpfopen1.x has
IMPORT CODE,'cpfopen1.so','cpfopen1'
Doing it this way, and letting the C and the binder resolve the entry point is the easy way of doing it.
Advanced DLL
You can load a DLL yourself
void * hDLL;
int (*fptr)(const char * filename, const char *mode);
hDLL = dlopen("cpfopen1", RTLD_LOCAL | RTLD_LAZY );
if (!hDLL) {
fprintf(stderr, "%s\n", dlerror());
exit(99);
}
dlerror(); /* Clear any existing error */
The find the entry point using dlsym. This uses the handle of the DLL object loaded above, and you pass the entry point name. (It might not exist).
Define a pointer to the function
int (*fptr)(const char * filename, const char *mode);
This defines a pointer to a function fptr, which returns an int, and has two char * parameters.
fptr = (int (*)(const char *, const char *)) dlsym(hDLL, "cpfopen1");
if ( fptr != 0)
{
myopen = (*fptr)("ABC","DEF");
printf("result %i\n",myopen);
}
It took me some time to get the definitions of the function parameters correct.
The called function is
#pragma linkage(cpfopen1, fetchable)
int cpfopen1(char * filename, char *mode)
{
printf(">>>>>>>>>>>>>>>In cpfopen1 %s %s\n",filename,mode);
return 7 ;
}
It was much easier to include the .x file at bind time, and let C run time sort it out.
Clean up
You should use dlclose(handle) to close the object and free its resources