    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Working with GNU Export Maps</title>
        <link>https://members.accu.org/index.php/articles/1372</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>




<div class="xar-mod-head"><span class="xar-mod-title">Programming Topics + Overload Journal #79 - Jun 2007</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c65/">Programming</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c78/">Overload</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c224/">79</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/c65-224/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/articles/c65+224/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Working with GNU Export Maps</h1>
<p><strong>Author:</strong>&nbsp;</p>
<p>
<strong>Date:</strong> 04 June 2007 11:56:00 +01:00 or Mon, 04 June 2007 11:56:00 +01:00</p>
<p><strong>Summary:</strong>&nbsp;Taking control over the symbols exported from shared libraries built with the GNU toolchain.</p>
<p><strong>Body:</strong>&nbsp;<p>
Recently I've been preparing version 2 of an internal shared library
for my company and I needed to make sure that both the old version of
the library and the new one could be loaded into a process at the same
time, without causing problems. I knew from previous work that the
answer was probably GNU export maps, but my memory was distinctly rusty
on the details. Having been to the discussion on writing for the ACCU
at the conference, it occurred to me that other people might also find
some notes on the topic useful. </p>
<h2> Exporting C++ from a shared library </h2>
<p> When exporting symbols from a shared library, the GNU ELF shared
library linker behaves in a significantly different way to the
Microsoft Windows linker. On Windows, nothing is exported from a DLL
unless the programmer explicitly requests it. The GNU ELF linker, on
the other hand, exports everything by default. </p>
<p> The GNU ELF default undoubtedly makes initial C++ application
development simpler; hands up everyone who has at some point struggled
to export a class from a DLL, because it uses an STL container...
There's a cost to that initial simplicity though. A C++ shared library
will typically contain a large number of symbols. When an application
is linked against that library, the compiler and linker generate a
reference for each of those symbols. When the library is loaded at run
time, each of those references has to be bound to the corresponding
symbol in the shared library. </p>
<p> Let's take a look at a trivial example (Listing 1).<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">// spaceship.h<br>#include &lt;string&gt;<br>#include &lt;vector&gt;<br><br>namespace scifi<br>{<br>class Spaceship<br>{<br>public:<br>  Spaceship( std::string const&amp; name );<br>  ~Spaceship();<br>  void stabliseIonFluxers();<br>  void initiateHyperwarp();<br>private:<br>  Spaceship( Spaceship const&amp; );<br>  Spaceship&amp; operator=( Spaceship const&amp; );<br>private:<br>  typedef unsigned int            FluxLevel;<br>  typedef std::vector&lt;FluxLevel&gt;  FluxLevels;<br>private:<br>  void doSomethingInternal();<br>  FluxLevel checkFluxLevel( size_t ionFluxerIdx );<br>private:<br>  std::string m_name;<br>  FluxLevels  m_fluxLevels;<br>};<br>}<br><br>// spaceship.cpp omitted for brevity    <br><br>// testflight.cpp<br>#include &quot;spaceship.h&quot;<br>int main( int, char** )<br>{<br>  scifi::Spaceship*  ship = new scifi::Spaceship(<br>      &quot;Beagle&quot; );<br>  ship-&gt;stabiliseIonFluxers();<br>  ship-&gt;initiateHyperwarp();<br>  delete ship;<br>  return 0;<br>}<br></pre>
</td></tr><tr><td class="title">
Listing 1
</td></tr></table>
<p>
If we build spaceship.cpp into a shared library and testflight.cpp into
an executable linked against that library, we can examine what happens
at runtime, using the <tt class="code">LD_DEBUG</tt> environment
variable. </p>
<pre class="programlisting">&gt; g++ -shared -fPIC spaceship.cpp -o <br>libspaceship.so.1 -Wl,-soname=libspaceship.so.1<br>&gt; ln -s libspaceship.so.1 libspaceship.so<br>&gt; g++ testflight.cpp -L. -lspaceship -o testflight<br>&gt; export LD_DEBUG=symbols<br>&gt; export LD_LIBRARY_PATH=.<br>&gt; ./testflight   </pre>
<p> This produces a lot of output. Digging through it, we see some
things that we are expecting to be resolved to our library, like those
shown in Figure 1.<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">5975: symbol=_ZN9SpaceshipC1ERKSs;  lookup in file=./testflight<br>5975: symbol=_ZN9SpaceshipC1ERKSs;  lookup in file=./libspaceship.so.1<br>5975: symbol=_ZN9Spaceship19stabiliseIonFluxersEv;  lookup in file=./testflight<br>5975: symbol=_ZN9Spaceship19stabiliseIonFluxersEv;  lookup in file=./libspaceship.so.1<br></pre>
</td></tr><tr><td class="title">
Figure 1
</td></tr></table>
<p> But we also see a lot more that we might not have expected, like
those in Figure 2.<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">5975: symbol=_ZNSt6vectorIjSaIjEEC1IiEET_S3_RKS0_;  lookup in file=./testflight<br>5975: symbol=_ZNSt6vectorIjSaIjEEC1IiEET_S3_RKS0_;  lookup in file=./libspaceship.so.1<br>5975: symbol=_ZNSt18_Vector_alloc_baseIjSaIjELb1EE11_M_allocateEj;  lookup in file=./testflight<br>5975: symbol=_ZNSt18_Vector_alloc_baseIjSaIjELb1EE11_M_allocateEj;  lookup in file=./libspaceship.so.1<br></pre>
</td></tr><tr><td class="title">
Figure 2
</td></tr></table>
<p> In total, there are twenty one symbols that get resolved to our
library. That's quite a lot of fix-ups for such a small amount of code.
It's worse than it immediately looks, as well, because each lookup is
done by doing a string compare against each possible function in each
library, until a match is found, so the number of symbols exported from
our library affects not just how many symbols have to be fixed up by
the executable, but how many string matches have to be done for each
fix-up. Imagine how that scales up for a real C++ library. </p>
<p> Notice how even though we didn't deliberately export them from our
library, there are quite a lot of symbols from STL instantiations being
looked up there; in fact they swamp the symbols we actually intended to
export. </p>
<p> We can use <tt class="code">nm</tt> to look at what's being
exported from our library: </p>
<pre class="programlisting">&gt; nm -g -D -C --defined-only libspaceship.so.1    </pre>
<p> As with the output generated by <tt class="code">LD_DEBUG</tt>, we
see some symbols that relate directly to our class, as shown in Figure
3, and we also see many other symbols relating to the STL classes we
used in the implementation, like those shown in Figure 4.<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">0000132a T scifi::Spaceship::checkFluxLevel(unsigned int)<br>0000131e T scifi::Spaceship::initiateHyperwarp()<br>00001324 T scifi::Spaceship::doSomethingInternal()<br>00001318 T scifi::Spaceship::stabiliseIonFluxers()<br>00001134 T scifi::Spaceship::Spaceship(std::string const&amp;)<br>00001270 T scifi::Spaceship::~Spaceship()<br></pre>
</td></tr><tr><td class="title">
Figure 3
</td></tr></table>
<br>
<table class="sidebartable"><tr><td>
<pre class="programlisting">00001330 W std::allocator&lt;unsigned int&gt;::allocator()<br>00001336 W std::allocator&lt;unsigned int&gt;::~allocator()<br>0000154a W std::_Vector_base&lt;unsigned int, std::allocator&lt;unsigned int&gt; <br>&gt;::_Vector_base(std::allocator&lt;unsigned int&gt; const&amp;)<br>0000147c W std::_Vector_base&lt;unsigned int, std::allocator&lt;unsigned int&gt; <br>&gt;::~_Vector_base() 000014e6 W std::__simple_alloc&lt;unsigned int, <br>std::__default_alloc_template&lt;true, 0&gt; &gt;::deallocate(unsigned int*, <br>unsigned int)<br></pre>
</td></tr><tr><td class="title">
Figure 4
</td></tr></table>
<p> In amongst the noise of the weakly defined STL template
instantiations being exported from our library, notice how our private
member functions are also exported. </p>
<p> Remember that we did not build with debug information. Also don't
be fooled into thinking that it's because the header file listed them;
remember that there's nothing special about header files in C++; the
compiler and linker don't even know they exist, so even if we use
idioms like Cheshire Cat or abstract base classes, all that
implementation detail will still be exported and available for perusal
by anyone who cares to run freely available tools like <tt class="code">nm</tt>
over the shared library. </p>
<p> For some projects, this could represent an unacceptable IP leakage.
</p>
<p> If those problems don't concern you, there is another issue you
might like to consider. </p>
<p> Let's imagine that we have successfully deployed version 1 of our <tt
 class="code">spaceship</tt> library. It's being used in a few places
and is perhaps referenced by a few other shared libraries. Consider
what happens if we now want to do a version 2, which isn't compatible.
Obviously, we'll build it into libspaceship.so.2, with the <tt
 class="code">SONAME</tt> set appropriately, so we're versioned and
everything is OK, right? </p>
<p> Not quite. When the dynamic linker is resolving symbols, it simply
searches the list of modules, in order. There is no information in the
symbol to say which library it ought to be loaded from. So let's
imagine that one part of our application is linked against
libspaceship.so.2, but another part hasn't been updated yet and still
links against libspaceship.so.1. If libspaceship.so.1 gets loaded
first, then whenever the <tt class="code">Spaceship</tt> constructor
is searched for, the one in libspaceship.so.1 will always be found. If
we then try to use a facility that we added to libspaceship.so.2,
disaster will ensue. </p>
<p> Fortunately, there is a mechanism which can solve both problems.
What we need to do is take control over which symbols are exported from
our library. The GNU tool-chain offers a few ways of doing this. One
involves decorating the code with <tt class="code">__attribute__
((visibility(&quot;xxx&quot;))</tt> tags; another, introduced with GCC 4.0, uses <tt
 class="code">#pragma GCC visibility</tt>, but I'm going to focus on
GNU Export Maps, sometimes called Version Scripts. This is partly
because I don't like adding large amounts of tool-chain specific
decoration to my code and partly because, at present, as far as I know,
only export maps can help with versioning symbols. </p>
<p> An export map is simply a text file listing which symbols should be
exported and which should not. A really simple example to export one
'C' function called <tt class="code">foo</tt> from a shared library
would look like this: </p>
<pre class="programlisting">{<br>  global:<br>    foo;<br>  local:<br>    *;<br>};<br>    </pre>
<p> Unfortunately, the situation for C++ is, inevitably, slightly more
complex... you've guessed, of course: name mangling! Export maps are
used by the linker, by which time the compiler has mangled the names.
The good news is that the GNU linker understands the GNU compiler's C++
name mangling, we just have to tell it that the symbols are C++. </p>
<p> So for our spaceship, we might write: </p>
<pre class="programlisting">{<br>  global:<br>    extern &quot;C++&quot; {<br>      *scifi::Spaceship;<br>      scifi::Spaceship::Spaceship*;<br>      scifi::Spaceship::?Spaceship*;<br>      scifi::Spaceship::stabliseIonFluxers*;<br>      scifi::Spaceship::initiateHyperwarp*<br>    };<br>  local<br>    *;<br>};    </pre>
<p> A few points need explaining here. </p>
<p> The first entry exports the typeinfo for the class. In this
example, it's not strictly necessary, but I have included it to show
how. See the sidebar entitled 'Exporting TypeInfo' for an explanation
of why you might need to do so.<br>
</p>
<table class="sidebartable"><tr><td class="title">
Exporting Typeinfo
</td></tr><tr><td class="sidebar">
<p>It is important to export <tt class="code">typeinfo</tt> for your classes if you
want any language features that rely on <tt class="code">typeinfo</tt>, like
<tt class="code">dynamic_cast</tt> or exceptions, to work across the shared library
interface. This is because when the GCC runtime compares two <tt class="code">typeinfo</tt>s,
it does so by pointer. If a shared library does <tt class="code">typeinfo</tt> for a class it
defines, then the not export the executable will contain its own copy
generated from the relevant header file. Thus when when an instance of
a class is created inside the library, it will have the library's copy
of the <tt class="code">typeinfo</tt> associated with it, but when a client executable
<tt class="code">dynamic_cast</tt> or a <tt class="code">catch(TheType)</tt>, it will be performs a looking for the
executable's copy of the <tt class="code">typeinfo</tt> and the two will not match, leading
to unpleasant surprises and extended debugging sessions...</p>
</td></tr></table>
<p> Tilde (~) is not a valid character in an export map, so in the
export line for the destructor, we replace it with a single character
wildcard. </p>
<p> Next, notice how within the extern C++ block, every entry ends with
a semi-colon except the last. This is not a typo! The syntax is defined
that way. </p>
<p> Lastly, the wildcards on the end of the function names are because
the full function name includes its signature and we don't want to have
to write it out here. </p>
<p> Let's build our example using the example export map and see what
happens. This is done by passing an extra option to the linker: </p>
<pre class="programlisting">&gt; g++ -shared spaceship.cpp -o libspaceship.so.1 <br>-Wl,-soname=libspaceship.so.1 -Wl,<br>--version-script=spaceship.expmap<br>&gt; g++ testflight.cpp -L. -lspaceship -o testflight    </pre>
<p> First the output of <tt class="code">nm</tt>, to show that we are
now only exporting what we actually want to (see Figure 5).<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">&gt; nm -g -D -C --defined-only libspaceship.so.1<br>00000b4e T scifi::Spaceship::initiateHyperwarp()<br>00000b48 T scifi::Spaceship::stabiliseIonFluxers()<br>00000a02 T scifi::Spaceship::Spaceship(std::string const&amp;)<br>00000964 T scifi::Spaceship::Spaceship(std::string const&amp;)<br>00000af4 T scifi::Spaceship::~Spaceship()<br>00000aa0 T scifi::Spaceship::~Spaceship()<br></pre>
</td></tr><tr><td class="title">
Figure 5
</td></tr></table>
<p> All of the implementation details of our class are now safely
hidden away as they should be and only our public interface is visible
outside the library. </p>
<p> Obviously, the first thing we must do is run the test harness to
check that we haven't broken anything by restricting the exports. It
runs without any problems, so we can use <tt class="code">LD_DEBUG</tt>
again to see what difference it has made to the runtime behaviour.
Filtering the output to show only those symbols that were resolved to
the spaceship library, this time we get Figure 6.<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">13421: symbol=_ZN5scifi9SpaceshipC1ERKSs;  lookup in file=./testflight<br>13421: symbol=_ZN5scifi9SpaceshipC1ERKSs;  lookup in file=./libspaceship.so.1<br>13421: symbol=_ZN5scifi9Spaceship19stabiliseIonFluxersEv;  lookup in file=./testflight<br>13421: symbol=_ZN5scifi9Spaceship19stabiliseIonFluxersEv;  lookup in file=./libspaceship.so.1      13421: symbol=_ZN5scifi9Spaceship17initiateHyperwarpEv;  lookup in file=./testflight<br>13421: symbol=_ZN5scifi9Spaceship17initiateHyperwarpEv;  lookup in file=./libspaceship.so.1 <br>13421: symbol=_ZN5scifi9SpaceshipD1Ev;  lookup in file=./testflight<br>13421: symbol=_ZN5scifi9SpaceshipD1Ev;  lookup in file=./libspaceship.so.1 <br></pre>
</td></tr><tr><td class="title">
Figure 6
</td></tr></table>
<p> This looks more like we'd want this time: the only things being
resolved to the library are the functions that make up the library's
public interface. What's more, if you compare the raw output of the two
runs, you'll notice that there are fewer lookups being performed in
total, because we no longer have the weak symbols to be resolved when
our library is loaded. </p>
<h2> Symbol versioning </h2>
<p> At its simplest, this requires a simple addition to the export map:
</p>
<pre class="programlisting">SPACESHIP_1.0 {<br>  global:<br>    extern &quot;C++&quot; {<br>      *scifi::Spaceship;<br>      scifi::Spaceship::Spaceship*;<br>      scifi::Spaceship::~Spaceship*;<br>      scifi::Spaceship::stabliseIonFluxers*;<br>      scifi::Spaceship::initiateHyperwarp*<br>    };<br>  local<br>    *;<br>};    </pre>
<p> In order to see the effect this has, we need to use <tt
 class="code">objdump</tt>, rather than <tt class="code">nm</tt>,
because <tt class="code">nm</tt> does not display symbol versioning
information. First, if we look at the symbols exported from
libspaceship.so.1, we can see that they are all now marked with a
version. <tt class="code">objdump</tt> doesn't have a filtering option
equivalent to nm's <tt class="code">--defined-only</tt>, so I have
picked out just the relevant lines from its output in Figure 7.<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">&gt; objdump -T -C libspaceship.so.1<br>libspaceship.so.1:     file format elf32-i386<br>DYNAMIC SYMBOL TABLE: <br>00000a92 g    DF .text  0000009d  SPACESHIP_1.0 scifi::Spaceship::Spaceship(std::string const&amp;) <br>00000bde g    DF .text  00000005  SPACESHIP_1.0 scifi::Spaceship::initiateHyperwarp() <br>00000b84 g    DF .text  00000054  SPACESHIP_1.0 scifi::Spaceship::~Spaceship() <br>00000000 g    DO *ABS*  00000000  SPACESHIP_1.0 SPACESHIP_1.0 <br>00000b30 g    DF .text  00000054  SPACESHIP_1.0 scifi::Spaceship::~Spaceship() <br>000009f4 g    DF .text  0000009d  SPACESHIP_1.0 scifi::Spaceship::Spaceship(std::string const&amp;)<br></pre>
</td></tr><tr><td class="title">
Figure 7
</td></tr></table>
<p> Notice how each export is now marked with the version string we
gave. Also, there is a single extra absolute symbol which states that
this shared library provides this version of the ABI. </p>
<p> Now let's look at the imports in the test executable. Again, I'm
going to pick out just the entries (Figure 8) that relate to <tt
 class="code">libspaceship</tt>. If you do this for yourself, you'll
see a lot more entries for <tt class="code">glibc</tt> and <tt
 class="code">libstdc++</tt>.<br>
</p>
<table class="sidebartable"><tr><td>
<pre class="programlisting">&gt; objdump -x testflight<br>Dynamic Section:   NEEDED      libspaceship.so.1 <br>Version References:   <br>required from libspaceship.so.1:     0x04a15a30 0x00 03 SPACESHIP_1.0 <br>SYMBOL TABLE: <br>00000000       F *UND*  00000005          _ZN5scifi9Spaceship17initiateHyperwarpEv@@SPACESHIP_1.0 <br>00000000       F *UND*  00000054          _ZN5scifi9SpaceshipD1Ev@@SPACESHIP_1.0 <br>00000000       F *UND*  0000009d          _ZN5scifi9SpaceshipC1ERKSs@@SPACESHIP_1.0 <br>00000000       F *UND*  00000005           _ZN5scifi9Spaceship19stabiliseIonFluxersEv@@SPACESHIP_1.0<br></pre>
</td></tr><tr><td class="title">
Figure 8
</td></tr></table>
<p> Not only does the executable state, as usual, that it needs <tt
 class="code">libspaceship</tt>, but it now states that it needs a
version of <tt class="code">libspaceship</tt> that provides the right
version of the ABI. In addition and more importantly, each symbol being
imported from our library is now marked with the required version. </p>
<p> There are some more sophisticated things that can be done with
symbol versioning, such as marking which minor version of an interface
symbols were introduced in, by having more than one section in the
export map. See the reference at the end for more information on this. </p>
<h2> Making it easier </h2>
<p> There's only one slight fly in the ointment. For a trivial example,
that export map looks fine, but maintaining it for a real library could
quickly become painful. </p>
<p> One answer, that works for some circumstances, is to think
carefully about how much you actually need to export from your shared
library. If you are programming to interfaces expressed as abstract
base classes, then you probably also have factory functions to create
instances of implementation classes, which return a pointer to the
interface. In that case, it often turns out the the only thing that
needs to be exported from the shared library is that factory function.
On one project that I work on, we have a number of shared libraries
that are loaded dynamically at run time that have this property.
Because the libraries are loaded dynamically and the factory function
is found at runtime using <tt class="code">dlsym()</tt>, the factory
function can have the same name in every such component and so we can
generate the export map at build time and don't have to maintain them
at all. </p>
<p> That is not always a sensible or workable approach though; if you
are writing a C++ class library, then you need to export the classes. </p>
<p> By deciding to write an export map, we have, in effect, created the
same situation that we have on Windows: we are starting with an empty
'global' section in our export map, so that nothing is exported and
we're now trying to get a list of what to export. On Windows, this is
often done by decorating the code with some special directives for the
Microsoft tool-chain. (Note that these directives occur in a different
place in the source code to the GNU <tt class="code">__attribute__</tt>
directive.) These are often hidden away in a macro, because different
directives are needed when building the library and when linking
against it: </p>
<pre class="programlisting">#if defined( _WIN32 )<br>#  if defined(SPACESHIP_EXPORT_INTERFACE)<br>#    define SPACESHIP_API __declspec(dllexport)<br>#  else<br>#    define SPACESHIP_API __declspec(dllimport)<br>#  endif<br>#else<br>#  define SPACESHIP_API<br>#endif<br>class SPACESHIP_API Spaceship<br>{<br>  // etc<br>};<br>void SPACESHIP_API myGlobalFunction();    </pre>
<p> If all the classes that are to be exported are marked this way,
then it ought to be possible to write a tool that will generate the
export map for us. </p>
<p> An approach that I've been experimenting with is to use regular
expression matching to find interesting declarations (namespaces,
classes / structures and functions) in header files. Tracking instances
of opening and closing block braces allows the generation of scoped
names in the export map. Of course, it's not possible (as far as I'm
aware) to write a regular expression that will correctly identify
function declarations in the general case, but we can spot global
functions that are to be exported by the presence of the <tt
 class="code">SPACESHIP_API</tt> tag and if we avoid writing any
serious implementation inside our exportable class declarations, then
we can spot function declarations within the class declaration body.
Looking out for <tt class="code">public</tt> / <tt class="code">protected</tt>
/ <tt class="code">private</tt> tags allows us to avoid exporting
implementation functions. </p>
<p> Anyone fancy a summer project?<br>
</p>
<h2> References </h2>
<ul>
  <li> <i>How To Write Shared Libraries</i>: Ulrich Drepper, 2005 </li>
  <li> Linux man and info pages, nm, objdump, ld: various </li>
</ul>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
