0

The following escenario is intriguing me.

(A) source code library.

(B) a shared library which includes (A)

(C) a static library which includes (A)

(D), a top level program linking with (B) and (C).

Previously I read the following answer and understood that if the linkage is static the variables are truly static and exist only once in the whole program, but if the linkage is dynamic the static variables exist as many times as the library is included. What happens to static variables when libraries are statically linked

In example if I include N times an static library with the following code, the __immediately_lambda_call is triggered N times but access to the same gMyClass.

static MyClass& GetSingleton() {
    static std::once_flag flag;
    static MyClass* gMyClass;
    std::call_once(flag, [&]() { gMyClass = new MyClass(); });
    return *gMyClass;
}

static const auto __immediately_lambda_call = []() {
    auto& gMyClass = GetSingleton();
    return true;
}();

But what happens when the same code is included as dynamic library and static library.

Feel free to ignore the next paragraph. I'm more concerned about how memory is shared between static and dynamic library.

The question is because the top level program (D) randomly throws me a double free memory error in a more complex code that I summarize as the following code and I don't understand why, but adding __attribute__((visibility( hidden )) when the dynamic library is compiled seems to fix the issue.

static const int gMyArr[2] = {1,2};
static const int gMyNum = 1;

class API_STATIC_DYNAMIC MyOtherClass
{
public:
    MyOtherClass()
     : mMatrix(new int[20])
    {}
    
    MyOtherClass()
    {
       delete[] mMatrix;
    }
    
    void doSomething()
    {
        int d = gMyArr[0];
    }

    static int gMatrix[10];
    static int gNumber;
    
private:
    int *mMatrix;
}

Edit LIBRARY:

API.h

#if defined(STATIC_LIB)
    #define API_STATIC_DYNAMIC 
#else
    #define API_STATIC_DYNAMIC __attribute__((visibility("hidden"))) // removed "default"
#endif

MYLIB.h

class MyLibApi
{
public:
    void doSomething();
    
private:
    class MyLibImp;
    MyLibImp *mPimpl;
}

MYLIB.cpp

class MyLibImp()
{
public:
    MyOtherClass myOther;
}

void MyLibApi::doSomething()
{
    mPimpl->myOther->doSomething();
}

Edit APP:

int main()
{
   MyLibApi api;
   api.doSomething();
   MyOtherClass other;
   other.doSomething();
   return 0;
}

1 Answers1

0

What happens to static class's variables and methods when libraries are statically and dynamically linked ?

This is operating system (or implementation) specific.

For Linux, read the (quite long) paper How to write shared libraries and also Program Library HowTo and also C++ dlopen minihowto. Read also a good C++ programming book, see also this C++ reference. Read later the C++11 standard n3337 (or the specification of a more recent C++ standard).

To simplify, on Linux:

for statically linked libraries : constructors of static data are run before main by crt0, and destructors of static data are run after main. Some constructors initialize vtables required for virtual methods.

for shared libraries : Constructors of static data are run either at dlopen(3) time, or at ld.so(8) time (so then before main). Destructors are run at dlclose time, or after main by ld.so. Read details of execve(2) and other relevant syscalls(2).

The order of constructor running could be related to the order of linking your libraries at build time. If you link with g++ a.o b.o -lxa -lyb it is likely that static constructors are run in a.o, then in b.o, then in libxa.a, then in libyb.so, but perhaps those from shared libraries could be run first.

You could use the GDB debugger and set a breakpoint in some of your constructors. You might want to compile all your C++ code with g++ -Wall -Wextra -g -O0 -fno-inline for debugging purposes.


the top level program (D) randomly throws me a double free memory

Regarding your double free issue, consider using valgrind and/or the address sanitizer. Read more about invoking GCC.

Of course, enable all warnings and debug info when compiling, so use at learn g++ -Wall -Wextra -g. With GCC 10, try its new static analyzer option. Consider also using Clang static analyzer or Frama-C.

I'm more concerned about how memory is shared between static and dynamic library.

Remember that a given process has a single virtual address space. Use pmap(1) to understand it (or programmatically, with proc(5), from inside your program, /proc/self/maps). Read more about elf(5) and use nm(1), ldd(1), file(1), objdump(1), readelf(1) on your (or other) executables and shared libraries. Use also strace(1), ltrace(1), gdb(1) to understand the runtime behavior of your program.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547