{"id":305,"date":"2026-01-15T14:18:58","date_gmt":"2026-01-15T14:18:58","guid":{"rendered":"https:\/\/haco.club\/?p=305"},"modified":"2026-01-15T14:18:58","modified_gmt":"2026-01-15T14:18:58","slug":"executable-exports-symbols","status":"publish","type":"post","link":"https:\/\/haco.club\/?p=305","title":{"rendered":"Executable Exports Symbols"},"content":{"rendered":"\n<p>There are actually <strong>several critical scenarios<\/strong> where an executable <strong>must<\/strong> export symbols.<\/p>\n\n\n\n<p>The confusion usually lies in the direction of the linking. You are right that <strong>Executable A<\/strong> rarely links dynamically to <strong>Executable B<\/strong> to call functions inside B.<\/p>\n\n\n\n<p>However, the reverse happens frequently: <strong>Dynamic Libraries (Plugins)<\/strong> loaded by <strong>Executable A<\/strong> often need to call functions inside <strong>Executable A<\/strong>.<\/p>\n\n\n\n<p>Here are the specific reasons why an executable needs to keep exported symbols:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. The &#8220;Host-Plugin&#8221; Architecture (Most Common)<\/h3>\n\n\n\n<p>This is the primary reason. If your executable supports plugins (DLLs on Windows, <code>.so<\/code> files on Linux) that are loaded at runtime, those plugins often need to use functionality provided by the main executable.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>How it works:<\/strong> The main executable loads the plugin (using <code>dlopen<\/code> or <code>LoadLibrary<\/code>).<\/li>\n\n\n\n<li><strong>The Problem:<\/strong> The plugin code needs to call <code>GetMainAppVersion()<\/code> or <code>LogMessage()<\/code>, which are defined inside the <code>.exe<\/code>.<\/li>\n\n\n\n<li><strong>The Solution:<\/strong> The executable <strong>exports<\/strong> those symbols. When the dynamic linker loads the plugin, it resolves the plugin&#8217;s undefined references by looking at the executable&#8217;s exported symbol table (the Global Symbol Table).<\/li>\n\n\n\n<li><strong>Linux Flag:<\/strong> On Linux (GCC\/Clang), you often use <code>-rdynamic<\/code> or <code>--export-dynamic<\/code> to force the linker to add internal symbols to the dynamic symbol table so plugins can see them.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Runtime Introspection (<code>dlopen(NULL)<\/code>)<\/h3>\n\n\n\n<p>Sometimes an application needs to look up a function within <strong>itself<\/strong> by string name at runtime.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Scenario:<\/strong> You have a configuration file that says <code>action = \"do_save\"<\/code>. The code reads this string and wants to find the function <code>void do_save()<\/code> in memory to execute it.<\/li>\n\n\n\n<li><strong>Mechanism:<\/strong> The code calls <code>dlsym(dlopen(NULL, ...), \"do_save\")<\/code>.<\/li>\n\n\n\n<li><strong>Requirement:<\/strong> <code>do_save<\/code> must be in the dynamic symbol table (exported) for <code>dlsym<\/code> to find it. If you strip the executable or hide symbols, this look-up fails.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Stack Traces and Crash Reporting<\/h3>\n\n\n\n<p>If you want your application to print a readable stack trace (backtrace) when it crashes <strong>without<\/strong> relying on external debug files (like <code>.pdb<\/code> or separate debug info), the executable needs symbols.<\/p>\n\n\n\n<p>While &#8220;debug symbols&#8221; (DWARF) are usually used for this, many backtrace libraries (like <code>execinfo.h<\/code> on Linux) can fall back to using <strong>exported dynamic symbols<\/strong> (<code>.dynsym<\/code>) to give you at least the function names, even if line numbers are missing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Sanitizers and Profilers (ASan, Valgrind)<\/h3>\n\n\n\n<p>Tools like Google&#8217;s AddressSanitizer (ASan) or Valgrind often intercept function calls (like <code>malloc<\/code>) or need to symbolize addresses at runtime.<br>If an executable is fully stripped of exports, these tools may output addresses like <code>0x4005d3<\/code> instead of <code>main<\/code> or <code>process_request<\/code>, making the output much harder to read or causing the tool to fail to intercept specific calls.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5. Windows Specifics (Hybrid EXE\/DLL)<\/h3>\n\n\n\n<p>On Windows, it is technically possible (though rare) to treat an <code>.exe<\/code> exactly like a <code>.dll<\/code>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You can run <code>LoadLibrary(\"my_app.exe\")<\/code> from another process.<\/li>\n\n\n\n<li>If <code>my_app.exe<\/code> exports functions, the other process can call <code>GetProcAddress<\/code> and use them.<\/li>\n\n\n\n<li><em>Why do this?<\/em> It is sometimes used in resource sharing or niche inter-process communication setups, though standard IPC is preferred.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Summary: Do you need them?<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>If you are building a standard, standalone CLI tool:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>No.<\/strong> You generally do not need exported symbols. You can strip them to save space and obfuscate the code.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>If you are building a program that loads plugins:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Yes.<\/strong> You likely need to export the API that the plugins will use.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>If you use <code>dlsym<\/code> \/ Reflection:<\/strong>\n<ul class=\"wp-block-list\">\n<li><strong>Yes.<\/strong> You need exports to find functions by name.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>Technical Note:<\/strong> There is a difference between <strong>Debug Symbols<\/strong> (used by GDB, not loaded into memory, safe to strip) and <strong>Dynamic Symbols<\/strong> (used by the OS loader, loaded into memory). Your question refers to Dynamic Symbols. Standard executables usually have a very small dynamic symbol table (just libc dependencies) unless configured otherwise.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>The Dynamic Symbol Table (specifically <code>.dynsym<\/code> in ELF files on Linux\/Unix) is essentially a <strong>shopping list for the Dynamic Linker<\/strong>.<\/p>\n\n\n\n<p>It contains two main categories of items: <strong>Things I need (Imports)<\/strong> and <strong>Things I have (Exports)<\/strong>.<\/p>\n\n\n\n<p>Here is the breakdown of exactly what is contained inside.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Imports (Undefined Symbols)<\/h3>\n\n\n\n<p>These are symbols that your executable uses but does not define itself. They represent external dependencies.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What they look like:<\/strong> In the symbol table, they are usually marked as <strong>UND<\/strong> (Undefined).<\/li>\n\n\n\n<li><strong>Purpose:<\/strong> They tell the OS loader: <em>&#8220;Before you run me, please search the loaded shared libraries (like <code>libc.so<\/code>, <code>libm.so<\/code>) to find the address of these functions.&#8221;<\/em><\/li>\n\n\n\n<li><strong>Examples:<\/strong>\n<ul class=\"wp-block-list\">\n<li>Standard C library functions: <code>printf<\/code>, <code>malloc<\/code>, <code>free<\/code>.<\/li>\n\n\n\n<li>External variables: <code>stdout<\/code>, <code>stderr<\/code>.<\/li>\n\n\n\n<li>Third-party library functions: <code>SSL_connect<\/code> (if using OpenSSL).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Exports (Global Defined Symbols)<\/h3>\n\n\n\n<p>These are symbols that your executable defines and has decided to expose to the outside world.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What they look like:<\/strong> They have a specific memory address or offset associated with them.<\/li>\n\n\n\n<li><strong>Purpose:<\/strong> They tell the OS loader: <em>&#8220;If any other object (like a plugin or a library I loaded) asks for this name, point them to this address inside me.&#8221;<\/em><\/li>\n\n\n\n<li><strong>Caveat for Executables:<\/strong> By default, standard executables usually contain <strong>very few<\/strong> exports (often just <code>_start<\/code> or <code>__bss_start<\/code>). As discussed previously, you usually have to force the linker (via <code>-rdynamic<\/code> or <code>__declspec(dllexport)<\/code>) to move your global functions from the &#8220;debug\/static symbol table&#8221; into this &#8220;dynamic symbol table.&#8221;<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Weak Symbols<\/h3>\n\n\n\n<p>These are a special sub-category.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Weak Imports:<\/strong> &#8220;I need this symbol, but if you can&#8217;t find it, don&#8217;t crash the program; just set the address to NULL.&#8221; (Used for optional features).<\/li>\n\n\n\n<li><strong>Weak Exports:<\/strong> &#8220;I provide this function, but if a loaded library provides a function with the same name, use theirs instead of mine.&#8221; (Used for overridable implementations).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Visualization (How to see them)<\/h3>\n\n\n\n<p>You can see exactly what is in your executable&#8217;s dynamic symbol table using the <code>nm<\/code> command with the <code>-D<\/code> (dynamic) flag.<\/p>\n\n\n\n<p><strong>Command:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nm -D my_executable<\/code><\/pre>\n\n\n\n<p><strong>Output Example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>                 U free               &lt;-- IMPORT (Undefined): Needs 'free' from libc\n0000000000404028 B __bss_start        &lt;-- EXPORT (Global Data): Start of BSS section\n                 U malloc             &lt;-- IMPORT (Undefined): Needs 'malloc'\n0000000000401126 T my_plugin_api      &lt;-- EXPORT (Text\/Code): A function I expose\n                 U printf             &lt;-- IMPORT (Undefined): Needs 'printf'<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Important Distinction: <code>.symtab<\/code> vs <code>.dynsym<\/code><\/h3>\n\n\n\n<p>To fully understand the &#8220;necessity&#8221; question, you must distinguish between the two symbol tables inside a binary:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>The Dynamic Symbol Table (<code>.dynsym<\/code>)<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Contents:<\/strong> Only Imports and Exports.<\/li>\n\n\n\n<li><strong>Size:<\/strong> Small.<\/li>\n\n\n\n<li><strong>Necessity:<\/strong> <strong>Required<\/strong> for the program to run (the loader needs it to resolve <code>printf<\/code>).<\/li>\n\n\n\n<li><strong>Strippable?<\/strong> No (mostly). You cannot strip this, or the program breaks.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>The &#8220;Full&#8221; Symbol Table (<code>.symtab<\/code>)<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Contents:<\/strong> Everything above <strong>PLUS<\/strong> local static functions, internal variables, source file names, and compilation units.<\/li>\n\n\n\n<li><strong>Size:<\/strong> Large.<\/li>\n\n\n\n<li><strong>Necessity:<\/strong> Not required for running. Only used for debugging (GDB) and static linking.<\/li>\n\n\n\n<li><strong>Strippable?<\/strong> Yes. When you run the <code>strip<\/code> command, <em>this<\/em> is what gets deleted.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Summary<\/h3>\n\n\n\n<p>So, to answer your specific phrasing:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Does it contain External Library Symbols?<\/strong> Yes (as &#8220;Undefined&#8221; placeholders).<\/li>\n\n\n\n<li><strong>Does it contain Exported Global Symbols?<\/strong> Yes (as &#8220;Defined&#8221; addresses), <em>but only if<\/em> the executable was configured to export them.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>There are actually several critical scenarios where an executable must [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42],"tags":[30,52],"class_list":["post-305","post","type-post","status-publish","format-standard","hentry","category-knowledge-base","tag-binary","tag-symbols"],"_links":{"self":[{"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts\/305","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=305"}],"version-history":[{"count":1,"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts\/305\/revisions"}],"predecessor-version":[{"id":306,"href":"https:\/\/haco.club\/index.php?rest_route=\/wp\/v2\/posts\/305\/revisions\/306"}],"wp:attachment":[{"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=305"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=305"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/haco.club\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=305"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}