Going further on my research about creating a Windows 64bit library, I wondered how I could bring it into a Linux environment. I knew some modifications would be required (since there's no "windows" library on Linux, for instance, and there's no need for the DllMain function to exist) but I wanted to know what exactly should change.

First of all, going a little bit deeper on the libraries' subject, there's something that is worth mentioning. As Wikipedia states, "a library is a collection of implementations of behavior, written in terms of a language, that has a well-defined interface by which the behavior is invoked". There're two types of libraries: static and dynamic ones. Static libraries (*.lib in Windows/*.a in Linux) are "merged"/copied into a target application and becomes part of it. Dynamic libraries (*.dll in Windows/*.so in Linux) don't. They can be dynamically loaded/unloaded, shared and versioned (there is an interesting discussion about pros 'n cons here). A program that makes use a dynamic library can achieve that by being linked to one or more libraries at compile time (and all the linking work is done by a dynamic linker) or can dynamically load/unload this library at run-time (and everything has to be done "manually", as we did to use our DLL in Windows).

Coming back to our original code, following the suggestion found here, we can adjust our code by segregating OS' specific codes, identifying the compiler being used in compile time.

/* dll.h */
#ifndef __DLL_H__
#define __DLL_H__

#if defined(_MSC_VER) // Microsoft 
   #define DLL_EXPORT __declspec(dllexport)
   #define DLL_IMPORT __declspec(dllimport)
#elif defined(_GCC)   // GCC
   #define DLL_EXPORT __attribute__((visibility("default")))
   #define DLL_IMPORT
#else                 // do nothing and hope for the best?
   #define DLL_EXPORT
   #define DLL_IMPORT
   #pragma warning Unknown dynamic link import/export semantics.
#endif

char * DLL_EXPORT hello(char * user); // returns a string "Hello USER!";
void DLL_EXPORT helloworld(); //prints out a "Hello World"

#endif // __DLL_H__

 

/* dllmain.c */
#include "dll.h"
#include <stdio.h>

#if defined(_MSC_VER)

   #include <windows.h>
   BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
   {
      switch(fdwReason)
      {
         case DLL_PROCESS_ATTACH:
         {
            break;
         }
         case DLL_PROCESS_DETACH:
         {
            break;
         }
         case DLL_THREAD_ATTACH:
         {
            break;
         }
         case DLL_THREAD_DETACH:
         {
            break;
         }
      }
 
      /* Return TRUE on success, FALSE on failure */
      return TRUE;
   }

#endif

char * DLL_EXPORT hello(char * user) // // returns a string "Hello USER!";
{
   char * greeting = malloc( strlen(user) + 7); // +Hello!
   sprintf(greeting, "Hello %s!", user);
   return greeting;
}

void DLL_EXPORT helloworld()
{
   printf("Hello World\n");
}

To create a shared library on Linux, we must follow a two step procedure: create a Object File with PIC (Position Independent Code) and then use it to create a Shared Object library (.so).

gcc -c -fPIC dllmain.c -o dllmain.o
gcc -shared -o libhelloworld.so dllmain.o

There's a convention for naming shared object libraries: libNAMEOFYOURLIBRARY.so. It can also carry the library's version number at the end, like libhelloworld.so.1.0.

Okay, our libhelloworld.so is now created. Let's focus about how to use it: by dynamically loading/unloading the library at run time (the way we did in Windows here) or using the dynamic linker to point our library calls towards its shared instance on memory. In the later, whenever we compile our source code, we must link it with all shared object libraries used within our code. Let's see an example of both:

Dynamically load/unload at run-time:

(Source code adapted from the one available at http://linux.die.net/man/3/dlopen)

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv)
{
   void * handle;
   char * (*hello)(char*);
   void * (*helloworld)();
   char * error;

   handle = dlopen("libhelloworld.so", RTLD_LAZY);
   if (!handle)
   {
      fprintf(stderr, "%s\n", dlerror());
      exit(EXIT_FAILURE);
   }

   dlerror(); /* Clear any existing error */

   *(void **) (&hello) = dlsym(handle, "hello");
   *(void **) (&helloworld) = dlsym(handle, "helloworld");

   if ((error = dlerror()) != NULL)
   {
      fprintf(stderr, "%s\n", error);
      exit(EXIT_FAILURE);
   }

   printf("%s\n", (*hello)("carr3r"));
   helloworld();

   dlclose(handle);
   exit(EXIT_SUCCESS);
}

To compile it, pass the parameters -rdynamic and -ldl (dynamic loader library) to GCC:

gcc -rdynamic -o main main.c -ldl

Run it: ./main

Using Shared Object library:

#include <stdio.h> 
#include "dll.h" 
 
int main(int argc, char* argv[])
{
   printf("%s\n", hello("carr3r"));
   helloworld();
   exit(EXIT_SUCCESS); 
}

When compiling it, we must link it to the shared library "helloworld" (libhelloworld.so) that we're using (and eventually all others libraries), so the compiler can record the library routines the program needs to call. Whenever this program is loaded, the dynamic linker is responsible for managing and addressing all procedures references to the their shared-object libraries in memory, respectively.

gcc -o main main.c lhelloworld -L./

We can use the ldd (List Dynamic Dependencies) application to see a list of shared libraries required by a program to run. In our case:

[carrer@localhost MyLib]$ ldd main
linux-gate.so.1 => (0xb77d1000)
libhelloworld.so => not found
libc.so.6 => /lib/libc.so.6 (0xb7600000)
/lib/ld-linux.so.2 (0xb77d2000)

Curiously, our library couldn't be found, even though it is in the same directory we're working on. If we are stubborn and try to run it, it will throw the error "./main: error while loading shared libraries: libhelloworld.so: cannot open shared object file: No such file or directory". That's because shared libraries are made to be shared and the system will lookup for these resources in a few standard locations (such as /lib and /usr/lib). So, if you're using a non-standard location to store you library, then you can:

  • include the library's path to the LD_LIBRARY_PATH environment variable;
    • export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:OUR_LIBRARY_PATH;
  • use ldconfig program or edit /etc/ld.so.conf to add your library's path into the search directories list;
  • move the library into a standard folder, like /usr/lib;

 

Interesting threads:

Creating and using shared libraries in Linux
Creating Shared Libraries in Linux - Part 2
Static, Shared Dynamic and Loadable Linux Libraries
http://www.tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

Source codes