title: Why would dlopen reuse the address of a previously loaded symbol?
body: <p>I just debugged a strange issue where I have two libraries let's call it libA.so and libB.so</p>
  
  <p>Application <s>dlopens libA.so</s>(EDIT: it isn't: it's linked by the -l option) which is a thin library which then loads libB.so which is the actual implementation.</p>
  
  <p>The dlopen are called using the RTLD_NOW option, no other options are passed.</p>
  
  <p>And both libraries use the same logger module where the logger's state is stored in a global variable, since both use the same logger and linked to them statically the global variable in both of them is of the same name.</p>
  
  <p>When libB is loaded the two global variables are sitting at the same address and conflicting. So the dynamic linker reused the address of the variable to use the same variable in libB.</p>
  
  <p>If it matters this variable is defined deep within a .cpp file, I'm not sure if linking between C and C++ is different.</p>
  
  <p>Reading the <a href="https://linux.die.net/man/3/dlopen" rel="nofollow noreferrer">dlopen's documentation</a> it says:</p>
  
  <blockquote>
    <p><strong>RTLD_GLOBAL</strong></p>
    
    <p>The symbols defined by this library will be made available for symbol resolution of subsequently loaded libraries.</p>
    
    <p><strong>RTLD_LOCAL</strong></p>
    
    <p>This is the converse of RTLD_GLOBAL, and the default if neither flag is specified. Symbols defined in this library are not made available to resolve references in subsequently loaded libraries.</p>
  </blockquote>
  
  <p>So RTLD_LOCAL is supposed to be the default, that is libA's symbols shouldn't be used when resolving libB's symbols. But it's still happening. Why?</p>
  
  <p>As a workaround I added visibility("hidden") option to this global to avoid exporting. And raised a ticket to make all symbols hidden by default, so collisions like this shouldn't happen in the future, but I'm still wondering why this happens when it shouldn't.</p>
  
  <p>EDIT2: </p>
  
  <p>Source example:</p>
  
  <p>commonvar.h:</p>
  
  <pre><code>#pragma once
  
  #include &lt;iostream&gt;
  
  struct A
  {
      A()
      {
          std::cout &lt;&lt; "A inited. Address: " &lt;&lt; this &lt;&lt; "\n";
      }
      virtual ~A() {}
  };
  
  extern A object;
  
  struct POD
  {
      int x, y, z;
  };
  
  extern POD pod;
  </code></pre>
  
  <p>commonvar.cpp:</p>
  
  <pre><code>#include &lt;string&gt;
  #include "commonvar.h"
  
  A object;
  
  POD pod = {1, 2, 3};
  </code></pre>
  
  <p>a.h:</p>
  
  <pre><code>#pragma once
  
  extern "C" void foo();
  </code></pre>
  
  <p>a.cpp:</p>
  
  <pre><code>#include &lt;iostream&gt;
  #include "commonvar.h"
  
  using FnFoo = void (*)();
  
  extern "C" void foo()
  {
      std::cout &lt;&lt; "A called.\n";
      std::cout &lt;&lt; "A: Address of foo  is: " &lt;&lt; &amp;object &lt;&lt; "\n";
      std::cout &lt;&lt; "A: Address of pod  is: " &lt;&lt; &amp;pod &lt;&lt; "\n";
      std::cout &lt;&lt; "A: {" &lt;&lt; pod.x &lt;&lt; ", " &lt;&lt; pod.y &lt;&lt; ", " &lt;&lt; pod.z &lt;&lt; "}\n";
  
      pod.x = 42;
  }
  </code></pre>
  
  <p>b.cpp:</p>
  
  <pre><code>#include &lt;iostream&gt;
  #include &lt;string&gt;
  #include "commonvar.h"
  
  extern "C" void foo()
  {
      std::cout &lt;&lt; "B called.\n";
      std::cout &lt;&lt; "B: Address of foo  is: " &lt;&lt; &amp;object &lt;&lt; "\n";
      std::cout &lt;&lt; "B: Address of pod  is: " &lt;&lt; &amp;pod &lt;&lt; "\n";
      std::cout &lt;&lt; "B: {" &lt;&lt; pod.x &lt;&lt; ", " &lt;&lt; pod.y &lt;&lt; ", " &lt;&lt; pod.z &lt;&lt; "}\n";
  }
  </code></pre>
  
  <p>main.cpp:</p>
  
  <pre><code>#include &lt;dlfcn.h&gt;
  #include &lt;iostream&gt;
  #include &lt;cassert&gt;
  
  #include "a.h"
  
  using FnFoo = void (*)();
  
  int main()
  {
      std::cout &lt;&lt; "Start of program.\n";
      foo();
  
      std::cout &lt;&lt; "Loading B\n";
      void *b = dlopen("libb.so", RTLD_NOW);
      assert(b);
      FnFoo fnB;
      fnB = FnFoo(dlsym(b, "foo"));
      assert(fnB);
  
      fnB();
  }
  </code></pre>
  
  <p>Build script:</p>
  
  <pre><code>#!/bin/bash
  
  g++ -fPIC -c commonvar.cpp
  ar rcs common.a commonvar.o
  g++ -fPIC -shared a.cpp common.a -o liba.so
  g++ -fPIC -shared b.cpp common.a -o libb.so
  g++ main.cpp liba.so -ldl -o main
  </code></pre>
  
  <p>Dynamic symbols of main:</p>
  
  <pre><code>                U __assert_fail
  0000000000202010 B __bss_start
                   U __cxa_atexit
                   w __cxa_finalize
                   U dlopen
                   U dlsym
  0000000000202010 D _edata
  0000000000202138 B _end
  0000000000000bc4 T _fini
                   U foo
                   w __gmon_start__
  0000000000000860 T _init
                   w _ITM_deregisterTMCloneTable
                   w _ITM_registerTMCloneTable
                   U __libc_start_main
                   U _ZNSt8ios_base4InitC1Ev
                   U _ZNSt8ios_base4InitD1Ev
  0000000000202020 B _ZSt4cout
                   U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  </code></pre>
  
  <p>Dynamic symbols of liba.so:</p>
  
  <pre><code>0000000000202064 B __bss_start
                   U __cxa_atexit
                   w __cxa_finalize
  0000000000202064 D _edata
  0000000000202080 B _end
  0000000000000e6c T _fini
  0000000000000bba T foo
                   w __gmon_start__
  0000000000000a30 T _init
                   w _ITM_deregisterTMCloneTable
                   w _ITM_registerTMCloneTable
  0000000000202070 B object
  0000000000202058 D pod
                   U _ZdlPvm
  0000000000000dca W _ZN1AC1Ev
  0000000000000dca W _ZN1AC2Ev
  0000000000000e40 W _ZN1AD0Ev
  0000000000000e22 W _ZN1AD1Ev
  0000000000000e22 W _ZN1AD2Ev
                   U _ZNSolsEi
                   U _ZNSolsEPKv
                   U _ZNSt8ios_base4InitC1Ev
                   U _ZNSt8ios_base4InitD1Ev
                   U _ZSt4cout
                   U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  0000000000201dd0 V _ZTI1A
  0000000000000ed5 V _ZTS1A
  0000000000201db0 V _ZTV1A
                   U _ZTVN10__cxxabiv117__class_type_infoE
  </code></pre>
  
  <p>Dynamic symbols of libb.so:</p>
  
  <pre><code>$ nm -D libb.so
  0000000000202064 B __bss_start
                   U __cxa_atexit
                   w __cxa_finalize
  0000000000202064 D _edata
  0000000000202080 B _end
  0000000000000e60 T _fini
  0000000000000bba T foo
                   w __gmon_start__
  0000000000000a30 T _init
                   w _ITM_deregisterTMCloneTable
                   w _ITM_registerTMCloneTable
  0000000000202070 B object
  0000000000202058 D pod
                   U _ZdlPvm
  0000000000000dbe W _ZN1AC1Ev
  0000000000000dbe W _ZN1AC2Ev
  0000000000000e34 W _ZN1AD0Ev
  0000000000000e16 W _ZN1AD1Ev
  0000000000000e16 W _ZN1AD2Ev
                   U _ZNSolsEi
                   U _ZNSolsEPKv
                   U _ZNSt8ios_base4InitC1Ev
                   U _ZNSt8ios_base4InitD1Ev
                   U _ZSt4cout
                   U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  0000000000201dd0 V _ZTI1A
  0000000000000ec9 V _ZTS1A
  0000000000201db0 V _ZTV1A
                   U _ZTVN10__cxxabiv117__class_type_infoE
  </code></pre>
  
  <p>Output:</p>
  
  <pre><code>A inited. Address: 0x7efd6cf97070
  Start of program.
  A called.
  A: Address of foo  is: 0x7efd6cf97070
  A: Address of pod  is: 0x7efd6cf97058
  A: {1, 2, 3}
  Loading B
  A inited. Address: 0x7efd6cf97070
  B called.
  B: Address of foo  is: 0x7efd6cf97070
  B: Address of pod  is: 0x7efd6cf97058
  B: {42, 2, 3}
  </code></pre>
  
  <p>As it can be seen the addresses of the variables collide but the function's address doesn't.</p>
  
  <p>Moreover the C++ initialization are peculiar: aggregates the <code>pod</code> variable are initialized only once you can see that call to foo() modifies it, but when B is loaded it won't reinitialize it, but does call the constructor for the full object when libb.so is loaded.</p>