Using an external function

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

Leave a comment