To illustrate better how the functionality works, you can use the sample application located in: C:\Program Files\Micro Focus\Studio Enterprise Edition 6.0\Examples\OPEN-PLI\Dynamic Subroutines.
The main program is A and it is calling a subroutine called B which in turn calls a subroutine C.
To build this application, you need to understand the CALL statements present in each routine and build them from the lowest calling level to the highest. Once the application has been built, you can rebuild individual shared objects without having to relink all shared objects.
To build the example application where A is calling B and B is calling C, you need to execute the commands below in the order they appear:
mfpli -o C.o C.PLI
ld -shared --warn-unresolved-symbols -o C.so C.o -melf_i386 -rpath . $MFPLI_PRODUCT_DIR/lib/libmf.so
mfpli -o B.o B.PLI
ld -shared --warn-unresolved-symbols -o B.so B.o C.so -melf_i386 -rpath . $MFPLI_PRODUCT_DIR/lib/libmf.so
mfpli -o A.o A.PLI
ld -shared --warn-unresolved-symbols -o A.so A.o B.so -melf_i386 -rpath . B.so $MFPLI_PRODUCT_DIR/lib/dllmain.o $MFPLI_PRODUCT_DIR/lib/nojw.o $MFPLI_PRODUCT_DIR/lib/noofm.o $MFPLI_PRODUCT_DIR/lib/libmf.so *.so $HOME/staff/opli/DMFTEST/debug/
As you can see, only the main program A is linked with dllmain.o.
All subroutines are linked with the PLI RTS (libmf.so) and the shared objects for any routines that they call.
In the three ld commands there is --warn-unresolved-symbols which allows you to catch any unexpected missing bits. It is not required but can be useful.
You need the -melf_i386 option only if building on a 64-bit Red Hat or SUSE Linux.