title: Do new template instantiations break ABI?
body: <h2>Overview</h2>
  <p>I have a <code>core</code> that dynamically loads different plugins. I explicitly instantiate templates that I export and then use them when link plugins against some version of the <code>core</code>. My expectations were, that when I add new types and template instantiations it would not break the ABI, and existing plugins would not have to be relinked.<br />
  It turned out not to be the case. When I add new instantiations I get symbols that are executed from within a different (than within the <code>core</code>) address.</p>
  <h2>Example</h2>
  <p>The project repo can be found <a href="https://github.com/ElDesalmado/template_abi_test" rel="nofollow noreferrer">here</a>.<br />
  Here is the code example.</p>
  <p>CMakeLists.txt</p>
  <pre><code>cmake_minimum_required(VERSION 3.7.2)
  
  project(template_abi_test)
  
  set(CMAKE_CXX_STANDARD 17)
  
  # old core
  add_executable(core core.cpp)
  
  target_compile_definitions(core
          PRIVATE BUILD_DLL
          PUBLIC DYN_LINK
          )
  
  set_target_properties(core PROPERTIES ENABLE_EXPORTS 1)
  
  # new core
  add_executable(core_new core.cpp)
  
  target_compile_definitions(core_new
          PRIVATE BUILD_DLL NEW_VERSION
          PUBLIC DYN_LINK
          )
  
  set_target_properties(core_new PROPERTIES ENABLE_EXPORTS 1)
  
  # plugin
  add_library(plugin SHARED plugin.cpp)
  target_link_libraries(plugin PRIVATE core)
  </code></pre>
  <p>core.hpp</p>
  <pre><code>#pragma once
  
  #if defined(_WIN32)
  #if defined(DYN_LINK)
  #if defined(CORE_SOURCE)
  #define CORE_DECL __declspec(dllexport)
  #else
  #define CORE_DECL __declspec(dllimport)
  #endif
  #endif
  #endif
  
  #ifndef CORE_DECL
  #define CORE_DECL
  #endif
  
  CORE_DECL int non_template();
  
  template&lt;typename&gt;
  struct foo_template {
      CORE_DECL static int get();
  };
  </code></pre>
  <p>core_impl.ipp // this is a workaround for MinGW64. Otherwise it will not export symbols for in-class implemented functions.</p>
  <pre><code>#pragma once
  
  #define CORE_SOURCE
  
  #include &quot;core.hpp&quot;
  
  template&lt;typename T&gt;
  int foo_template&lt;T&gt;::get()
  {
      static int val = 0;
      return ++val;
  }
  </code></pre>
  <p>export_types.hpp - this file contains exported types for the plugin linked to an &quot;old&quot; version of the core</p>
  <pre><code>#pragma once
  
  #include &quot;core.hpp&quot;
  
  struct old{};
  
  extern template struct foo_template&lt;old&gt;;
  </code></pre>
  <p>plugin.cpp</p>
  <pre><code>
  #include &quot;core.hpp&quot;
  #include &quot;export_types.hpp&quot;
  
  #ifdef _WIN32
  #define DLL_EXPORT __declspec(dllexport)
  #else
  #define DLL_EXPORT
  #endif
  
  extern &quot;C&quot;
  {
  DLL_EXPORT int foo_class_template();
  }
  
  int foo_class_template()
  {
      return foo_template&lt;old&gt;::get();
  }
  </code></pre>
  <p>core.cpp</p>
  <pre><code>#include &quot;core_impl.ipp&quot;
  #include &quot;core.hpp&quot;
  #include &quot;export_types.hpp&quot;
  
  template struct foo_template&lt;old&gt;;
  
  #ifdef NEXT_VERSION
  struct new_0 {
  };
  template struct foo_template&lt;new_0&gt;;
  #endif
  
  #include &lt;filesystem&gt;
  #include &lt;iostream&gt;
  #include &lt;string_view&gt;
  #include &lt;vector&gt;
  #include &lt;Windows.h&gt;
  
  int main(int argc, char **argv)
  {
      // preincrement all the values
      int fooClassTemplate = foo_template&lt;old&gt;::get();
  
      auto plugin = LoadLibraryA(&quot;plugin.dll&quot;);
  
      auto pFooClassTemplate = reinterpret_cast&lt;int(*)()&gt;(GetProcAddress(plugin, &quot;foo_class_template&quot;));
  
      int pluginFooClassTemplate = pFooClassTemplate();
  
      std::cout &lt;&lt; fooClassTemplate + 1 &lt;&lt; &quot; : &quot; &lt;&lt; pluginFooClassTemplate &lt;&lt; &quot;\n&quot;;
  
      return 0;
  }
  </code></pre>
  <p>So, when executing the old version (on Windows, haven't checked on Ubuntu yet) compiled with MSVC, MinGW64 or Clang, the output is as expected:</p>
  <pre><code>2 : 2
  </code></pre>
  <p>But when I run <code>core_new</code> that loads the same plugin, that was linked to an old version of the <code>core</code>, the result is different:</p>
  <pre><code>2 : 1
  </code></pre>
  <p>Indeed, if we inspect the addresses of functions called from within the <code>core</code> and <code>plugin</code>, we will see that they are different, though appear to be located within the <code>core.exe</code></p>
  <pre><code>// code executed within main
  
  (gdb) info sym 0x00007ff67da46be0
  foo_template&lt;old&gt;::get() in section .text of C:\dev\builds\template_abi_test\Debug-MinGW-w64\core_new.exe
  
  (gdb) info sym 0x00007ff67da4a0e0
  foo_template&lt;old&gt;::get()::val in section .data of C:\dev\builds\template_abi_test\Debug-MinGW-w64\core_new.exe
  
  
  // code executed within plugin's functions
  
  (gdb) info sym 0x00007ff6c5cc6be0
  foo_template&lt;old&gt;::get() in section .text of C:\dev\builds\template_abi_test\Debug-MinGW-w64\core.exe
  
  (gdb) info sym 0x00007ff6c5cca0e0
  foo_template&lt;old&gt;::get()::val in section .data of C:\dev\builds\template_abi_test\Debug-MinGW-w64\core.exe
  
  </code></pre>
  <p>To sum up here is a table:</p>
  <div class="s-table-container">
  <table class="s-table">
  <thead>
  <tr>
  <th></th>
  <th>core address</th>
  <th>plugin address</th>
  </tr>
  </thead>
  <tbody>
  <tr>
  <td>foo_template::get</td>
  <td>0x00007ff67da46be0</td>
  <td>0x00007ff6c5cc6be0</td>
  </tr>
  <tr>
  <td>foo_template::get::val</td>
  <td>0x00007ff67da4a0e0</td>
  <td>0x00007ff6c5cca0e0</td>
  </tr>
  </tbody>
  </table>
  </div>
  <p>Since the addresses are located within the executable, I thought that the reason was symbol addresses, hence at first I was comparing symbol tables.</p>
  <p><strong>Why does it happen? Can it be avoided?</strong></p>
  <hr />
  <p><strong>This is the original text. I thought that difference in symbol addresses was the reason, but as people in the comments pointed out, symbol addresses don't matter. So it was a false direction.</strong></p>
  <p>Consider the following example code for a dynamic library:</p>
  <pre><code>template &lt;typename&gt;
  struct foo_template
  {
      static inline int get()
      {
          static int val = 0;
          return ++val;
      }
  };
  
  struct foo
  {
      template &lt;typename&gt;
      static inline int get()
      {
          static int val = 0;
          return ++val;
      }
  };
  
  template &lt;typename&gt;
  inline int get()
  {
      static int val = 0;
      return ++val;
  }
  
  struct old{};
  
  template struct foo_template&lt;old&gt;;
  template int foo::get&lt;old&gt;();
  template int get&lt;old&gt;();
  
  #ifdef NEXT_VERSION
  
  struct new_0{};
  template struct foo_template&lt;new_0&gt;;
  template int foo::get&lt;new_0&gt;();
  template int get&lt;new_0&gt;();
  
  struct new_1{};
  template struct foo_template&lt;new_1&gt;;
  template int foo::get&lt;new_1&gt;();
  template int get&lt;new_1&gt;();
  
  struct new_2{};
  template struct foo_template&lt;new_2&gt;;
  template int foo::get&lt;new_2&gt;();
  template int get&lt;new_2&gt;();
  
  struct new_3{};
  template struct foo_template&lt;new_3&gt;;
  template int foo::get&lt;new_3&gt;();
  template int get&lt;new_3&gt;();
  
  #endif
  </code></pre>
  <p>In new versions when adding new template instantiations the ABI gets broken for class template and template member functions, while the existing code is intact.<br />
  New instantiations are added in a strict order - only after existing instantiations.</p>
  <p><a href="https://www.diffchecker.com/WuMBDoez" rel="nofollow noreferrer">This</a> is a comparison of output via <code>objdump -t</code> between &quot;old&quot; and &quot;next&quot; versions.
  As it can be seen, only function template symbol <code>int get</code> hasn't changed:</p>
  <pre><code>// Old
  AUX scnlen 0x1b nreloc 3 nlnno 0 checksum 0x0 assoc 0 comdat 2  
  [ 90](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 0) 0x00000000000012d0 _Z3getI3oldEiv    
  [ 91](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000000070 .data$_ZZ3getI3oldEivE3val
  </code></pre>
  <pre><code>// New
  AUX scnlen 0x1b nreloc 3 nlnno 0 checksum 0x0 assoc 0 comdat 2
  [ 90](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 0) 0x00000000000012d0 _Z3getI3oldEiv
  [ 91](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000000070 .data$_ZZ3getI3oldEivE3val
  </code></pre>
  <p>But class template and template static member functions changed their addresses:</p>
  <pre><code>// Old
      File    
  [ 77](sec  1)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x00000000000012f0 .text$_ZN12foo_templateI3oldE3getEv   
  AUX scnlen 0x1b nreloc 3 nlnno 0 checksum 0x0 assoc 0 comdat 2  
  [ 79](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 1) 0x00000000000012f0 _ZN12foo_templateI3oldE3getEv 
  AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0  
  [ 81](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000000080 .data$_ZZN12foo_templateI3oldE3getEvE3val 
  AUX scnlen 0x4 nreloc 0 nlnno 0 checksum 0x0 assoc 0 comdat 3   
  [ 83](sec  1)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000001310 .text$_ZN3foo3getI3oldEEiv    
  AUX scnlen 0x1b nreloc 3 nlnno 0 checksum 0x0 assoc 0 comdat 2  
  [ 85](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 0) 0x0000000000001310 _ZN3foo3getI3oldEEiv  
  [ 86](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000000090 .data$_ZZN3foo3getI3oldEEivE3val
  </code></pre>
  <pre><code>// New
  File 
  [ 77](sec  1)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000001370 .text$_ZN12foo_templateI3oldE3getEv
  AUX scnlen 0x1b nreloc 3 nlnno 0 checksum 0x0 assoc 0 comdat 2
  [ 79](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 1) 0x0000000000001370 _ZN12foo_templateI3oldE3getEv
  AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
  [ 81](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x00000000000000c0 .data$_ZZN12foo_templateI3oldE3getEvE3val
  AUX scnlen 0x4 nreloc 0 nlnno 0 checksum 0x0 assoc 0 comdat 3
  [ 83](sec  1)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000001410 .text$_ZN3foo3getI3oldEEiv
  AUX scnlen 0x1b nreloc 3 nlnno 0 checksum 0x0 assoc 0 comdat 2
  [ 85](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 0) 0x0000000000001410 _ZN3foo3getI3oldEEiv
  [ 86](sec  2)(fl 0x00)(ty   0)(scl   3) (nx 1) 0x0000000000000110 .data$_ZZN3foo3getI3oldEEivE3val
  </code></pre>