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?

200_success
  • 1,568
  • 11
  • 20
user1509041
  • 101
  • 1
  • 3
  • 1
    Why should the above sequence crash? – Doc Brown Nov 09 '14 at 08:41
  • Specific to Microsoft, see these KB's: [You may experience an access violation when you access an STL object through a pointer or reference in a different DLL or EXE](https://support.microsoft.com/en-us/kb/172396) and [STL std::string class causes crashes and memory corruption on multi-processor machines](https://support.microsoft.com/en-us/kb/813810). –  Sep 24 '15 at 00:39

2 Answers2

8

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.

Hadi Brais
  • 289
  • 2
  • 4
  • 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. – Konrad Rudolph Nov 09 '14 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 09 '14 at 12:47
  • 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)? – user1509041 Nov 09 '14 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 09 '14 at 17:02
  • I'm personally aware that this does apply to Win32 VC runtime, but does this issue exist on Linux as well? – Kaa Nov 20 '17 at 23:54
  • Does this mean it is okay to statically link to stdc++ in case I have a single executable? Also what's the take on using different operating systems RHEL6 vs RHEL7? – Ayman Salah Apr 22 '19 at 10:44
  • 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 '19 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

mvidelgauz
  • 269
  • 2
  • 4