2.14 Building a shared library

This section describes building a tiny shared library implemented in Modula-2 and built with libtool. Consider a project consisting of three definition modules and three implementation modules a.def, a.mod, b.def, b.mod, c.def and c.mod.

DEFINITION MODULE a ;

END a.
IMPLEMENTATION MODULE a ;

FROM libc IMPORT printf ;

BEGIN
   printf ("init: module a\n")
FINALLY
   printf ("finish: module a\n")
END a.

Module b is almost identical, but it imports module a.

DEFINITION MODULE b ;

END b.
IMPLEMENTATION MODULE b ;

IMPORT a ;
FROM libc IMPORT printf ;


BEGIN
   printf ("init: module b\n")
FINALLY
   printf ("finish: module b\n")
END b.

Likewise Module c is almost identical, but it imports from module b.

DEFINITION MODULE c ;

END c.
IMPLEMENTATION MODULE c ;

IMPORT b ;
FROM libc IMPORT printf ;

BEGIN
   printf ("init: module c\n")
FINALLY
   printf ("finish: module c\n")
END c.

The first step is to compile the modules using position independent code. This can be achieved by the following three commands:

libtool --tag=CC --mode=compile gm2 -g -c a.mod -o a.lo
libtool --tag=CC --mode=compile gm2 -g -c b.mod -o b.lo
libtool --tag=CC --mode=compile gm2 -g -c c.mod -o c.lo

The second step is to link these objects into a shared library.

export LIBDIR=$(gm2 -print-file-name=)../../../../lib64

libtool --tag=CC --mode=link gcc -g a.lo b.lo c.lo \
  -L${LIBDIR} -rpath `pwd` -lm2pim -lm2iso -lstdc++ \
  -lm -o libabc.la

At this point the shared library libabc.so will have been created inside the directory .libs.

This library can be called from C++ using the following technique. The define USER_LIB is the name of the library dialect and maybe changed if required. The DEFAULT_RUNTIME_MODULE_OVERRIDE is a predefined string containing the default base core module initialization order.

#include <stdio.h>
#include <m2rts.h>

#define USER_LIB NULL

/* Add the runtime dependency for this file on modules a, b and c.  */

void
dep (void)
{
  m2iso_M2RTS_RequestDependant (__FILE__, USER_LIB, "c", USER_LIB);
  m2iso_M2RTS_RequestDependant (__FILE__, USER_LIB, "b", USER_LIB);
  m2iso_M2RTS_RequestDependant (__FILE__, USER_LIB, "a", USER_LIB);
}

void
init (int, char *[], char *[])
{
  printf ("test.c:init\n");
}

void
fini (int, char *[], char *[])
{
  printf ("test.c:fini\n");
}

void
construct_scaffold (int argc, char *argv[], char *envp[])
{
  m2iso_M2RTS_RegisterModule (__FILE__, USER_LIB,
                              init, fini, dep);
  m2iso_M2RTS_ConstructModules (__FILE__, USER_LIB,
                                DEFAULT_RUNTIME_MODULE_OVERRIDE,
                                argc, argv, envp);
}

void
deconstruct_scaffold (int argc, char *argv[], char *envp[])
{
  m2iso_M2RTS_DeconstructModules (__FILE__, USER_LIB,
                                  argc, argv, envp);
}

int
main (int argc, char *argv[], char *envp[])
{
  printf ("main starts\n");
  construct_scaffold (argc, argv, envp);
  printf ("main application goes here\n");
  deconstruct_scaffold (argc, argv, envp);
  printf ("main tidying up\n");
  return 0;
}

This source file can be compiled and linked using the following command:

export INCLUDE=$(gm2 -print-file-name=)/m2/m2iso
g++ -I${INCLUDE} -g test.c -L. -l:.libs/libabc.so -L${LIBDIR} -lm2iso

and finally run using:

LD_LIBRARY_PATH=.libs:${LIBDIR} ./a.out

main starts
init: module a
init: module b
init: module c
test.c:init
main application goes here
test.c:fini
finish: module c
finish: module b
finish: module a
main tidying up