8

Here is the scenario:

  1. libA.so and libB.so both statically link to the same STL.
  2. libA.so has a public API that returns a std::string.
  3. libB.so calls this function and receives a copy of the string.
  4. When libB.so's copy of the string goes out of scope the string's destructor is called.
  5. The application seg faults attempting to free the copied string.

I've read elsewhere that statically linking like this is bad but I'd like to better understand why it's bad. Can anyone explain why the above sequence would crash?

2

2 Answers 2

9

Let's think about this very carefully. libA.so is statically linked with the STL. However, the STL does not exist in isolation, it requires the C runtime (CRT). They both reside in the libstdc++.a static library. This means that libA.so and libB.so have separate CRT data structures. In particular, the heap used by libA.so is different from the heap used by libB.so. Allocating a string from libA's runtime heap and attempting to free from libB's runtime will simply not work because libB's runtime has no records of allocating the string. The only way to correctly destruct the string is by calling the destructor within libA.so.

One might ask: but libB.so receives a copy of the string, right? Yes that's right but who has allocated this copy? It has been allocated using the copy constructor within the context of the libA's runtime.

That said, you can still use the string from libB.so. You just cannot destruct it from there.

You can also let libB receive a pointer to the string and then creating a copy of it within the context of the libB's runtime. That copy can be destructed by libB.

And that's why static linking is sometimes bad.

7
  • 1
    I’m unclear what “string” refers to here. If the talk is of a std::string then the problem simply does not exist: either libB.so receives a copy of the string, with memory managed in its own memory, or it will receive a reference/pointer to the string in libA.so, and won’t attempt to remove the string from its own memory. Nov 9, 2014 at 11:04
  • 1
    @KonradRudolph Among others, (N)RVO optimalisations create situations where the returned string is constructed by libA.so while destructed by libB.so.
    – Sjoerd
    Nov 9, 2014 at 12:47
  • 1
    This answer is consistent with what I've seen in practice so I guess the part I'm not fully understanding is how the heap works for each library. Initially I thought the heap was a singular global thing in the process but the way you're describing it makes it sound like there are multiple heaps. Is this just a matter of how the allocators (in libc?) are implemented or is there something else that controls this (like the loader)? Nov 9, 2014 at 16:02
  • Whether there is a global heap or multiple heaps in a process depends on the specific CRT implementation being used. I have seen both cases. You seem to be having multiple heaps. The loader has nothing to do with heaps.
    – Hadi Brais
    Nov 9, 2014 at 17:02
  • 1
    @AymanSalah Yes that should work fine. The OS version only matters when you're using OS-specific APIs rather than language standard APIs.
    – Hadi Brais
    Apr 22, 2019 at 12:03
3

The STL is so-called "state-full" (as opposite to being "state-less") meaning that it has some static stuff inside. When you link STL statically to both libA.so and libB.so you get two instances of STL library in memory at run-time (with two copies of static stuff). Each of those two copies manages allocated resources independently and resource allocated in one instance of library cannot be freed in another

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.