Devolus
Devolus

Reputation: 22084

Global names between main and shared library not visible

I compiled a shared library with gcc and linked it to my main. The main class should initialize a logger class, which should be visible inside the shared library, but it looks as if the shared library has it's own instance of it.

The include file looks like this:

extern Log gLog;

In main it is declared.

Log gLog(new StreamWriter());

When I try to link it, I get linker errors undefined symbol _gLog in the shared library. I thought that it might be because it is a class instance, so I changed it to a pointer, but I get the same. To make it worse, I figured I could create a small dummy module where I create the same global variable in the shared library and then call a function to initialize it. But for this function I also get a linker error because it is not visible in main.

In the shared library:

Log *gLogger;

int initLibrary(Log *pLogger)
{
    gLogger = pLogger;
}

And in main:

Log gLog(new StreamWriter());
int initLibrary(Log *pLogger);
main()
{
    initLibrary(&gLog);
}

Again I get an undefined symbol in the linker, this time for my initLibrary function.

For now I solve the problem by creating a dummy class, which works. However, I would like to know how to properly define symbols across shared library boundaries, as my understanding seems to be wrong about it.

When using google I found some threads here Using a global variable in a shared library and Global variables, shared libraries and -fPIC effect as examples (there are several others as well with this problem). However I tried to recompile everything with -fpic, also the main module and it still doesn't work. The -rdynamic option is unknown so I don't know where this comes from.

I can use classes from the shared library and vice versa, so this affects only global symbols. So what am I doing wrong that the main code and the shared library can not see symbols from each other?

Upvotes: 1

Views: 1143

Answers (3)

Devolus
Devolus

Reputation: 22084

I finally found the problem why this didn't work for global symbols(no problem with classes).

I'm compiling under cygwin, and apparently the shared object is compiled as a DLL. I tried to look into the library and notized that it is an EXE format and not ELF. So I tried to use the Microsoft DLL syntax for exporting symbols and suddenly it worked. Adding a __declspec(dllexport) to the symbol did the trick.

I expected that I would have to use __declspec(dllimport) in the main project for importing the symbols, but this doesn't work. Not sure if I missunderstood this parameter or if the cygwin version of gcc does some magic work.

So when you compile a shared library under cygwin it must look like this if you want to export symbols.

Shared library foo.h:

__declspec(dllexport) int foo(int a);

Shared library foo.cpp:

int foo(int a)
{
 ....
}

In the executable foo.h:

int foo(int a);

In the executable main.cpp:

main()
{
     foo(1);
}

The shared library must be compiled with the -fpic switch and linked with -shared.

Upvotes: 0

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506925

You need to declare you variables with default visibility to make it visible to other shared libraries or the main program. It appears that you are compiling with -fvisibility=hidden, so that a symbol in the library does not resolve to a definition in the main program or other libraries and vice versa. With the visibility attribute of GCC, you can reverse this effect.

In a nutshell

  • scope visibility of a declaration across entities of a single object file (global, local, ..)
  • linkage visibility of a declaration across entities of multiple object files (external, internal)
  • visibility visibility of a declaration across entities of different shared libraries (default, hidden).

Another possibility is that you are mixing C and C++ code and messing up with the language linkage.

Upvotes: 0

Claudio
Claudio

Reputation: 10947

The right approach is to create the instance of the Logger inside the shared library, using either a global variable (better if encapsulated in a namespace) or a Singleton class. Then let your main program use it.

Upvotes: 1

Related Questions