I’ve been writing in C for over 20 years, and it is humbling when you suddenly realise how little you know of a topic.
It reminds me of when I worked for IBM, and the company wanted a skills register. The questions were along the lines of
Rate your skills in the following areas from 0 (nothing) to 10 (expert).
- z/OS
- DB2
- CICS
- etc
Overall the skills register was found to be not useful, as the rankings were inverted. If someone put themselves down as 10 – it usually meant they knew very little, they knew enough for their day to day work. If someone put themselves down as 2 they may be an expert who realises how much they do not know, or someone who honestly realises they do not know very much.
My humbling discovery was that when I ported some existing C code to run on z/OS, the functions were not visible outside of the C program. There were two reasons for this.
- The functions were defined as static,
- The functions were not exported.
Static functions
static int hidden(int i) { return 0; } int visible(int i) { int x = hidden(1); return 0; }
I think that using “static” in this case is the wrong word. It does not mean static. I think “internal” would be a clearer description, but I do not think that I’ll have any success changing the C language to use “internal”.
The function “hidden” can only be used within the compiled unit. It cannot be referenced from outside of the compiled object. The “visible” function can use the “hidden” function as the code shows.
The function”visible” is potentially visible to external programs. You can load the module and execute the function.
Exported functions
You have to tell the compiler to externalise functions within the compile unit.
For example
#pragma export(COLIN) int COLIN(char * self, int args) { return 8; }
or the compiler option EXPORTALL for example
cc… -Wc,EXPORTALL
What has been exported?
When you bind (linkedit) your program, the binder and report the exported functions. You need the binder parameters XREF and DYNAM=DLL
This gave output like
IMPORT/EXPORT TYPE SYMBOL DLL DDNAME SEQ MEMBER ------------- ------ ---------------- ---------------- -------- --- --------- IMPORT CODE64 __a2e_l CELQV003 CELQS003 01 CELQS003 IMPORT CODE64 malloc CELQV003 CELQS003 01 CELQS003 IMPORT CODE64 CSQB3BAK CSQBLB16 MQ 01 CSQBMQ2X EXPORT DATA64 ascii_tab EXPORT CODE64 printHex EXPORT CODE64 COLIN
This shows the imported symbols, and where they came from, and what was exported.
- ascii_tab is a table of data in 64 bit mode
- printHex is a function in 64 bit mode
- COLIN is a function in 64 bit mode.
How to use it.
You can use handle= dlopen(name,mode) to get the load module into storage, and functionPointer=dlsym(handle,”COLIN”) to locate the external symbol COLIN in the load module.
Why is a static function useful?
With Python external functions (written in C), it uses static functions to hide internals. For example
static PyObject * pymqe_MQCONN( ... ) ... static struct PyMethodDef pymqe_methods[] = { {"MQCONN", (PyCFunction)pymqe_MQCONN,... }, {"MQCONNX", (PyCFunction)pymqe_MQCONNX,... }, .... }
When the external function is imported, the initialisation routine returns the pymqe_methods data to Python.
Python now knows what functions the module provides (MQCONN, MQCONNX), and the C code to be executed when the function is executed.
This means that you cannot load the module, and accidentally try to use the function pymqe_MQCONN; which I thought was good defensive programming.