linkershared-librariesldgnulinker-scripts

Is there a robust way to version all exported symbols with version script without changing the visibility?


GNU documentation is really poor and is worded in a way that seem to imply that you can only assign version only to symbols that are listed explicitly either under global: or local:, but those are also function to define what symbols to export/hide. Is there a syntax to assign version to all exported symbols without explicitly listing all of them? I tried

VERS_1.1 {
     *;
};

And it was accepted by the linker/compiler, but I'm not sure if it does what I want and I couldn't find this syntax documented anywhere.


Solution

  • Your hunch that a version-script such as:

    VERS_1.1 {
         *;
    };
    

    will accomplish what you want is correct. If the file containing it is, say, versions.map, you would normally invoke it in your GCC linkage command by adding the option:

    -Wl,--version-script=versions.map
    

    Alternatively, if you are using your own linker script, then you can include the version script in the linker script as a VERSION command:

    VERSION {
        VERS_1.1 {
             *;
        };
    } 
    

    Either way, the version script will apply version tag @@VERS_1.1 to every dynamically exported symbol and does not affect which symbols are exported.

    I think the best documentation is 3.9 VERSION Command in 3 Linker Scripts in the GNU ld manual. Indeed, it does not not expressly define the syntax of version commands and by way of excuse says:

    The easiest way to demonstrate the version script language is with a few examples.

    If this is the documentation you are referring to (though I suspect it is not), I would agree that the excuse is not wholly satisfactory. Many questions are left for us to answer experimentally.

    A few of the facts that can be discovered that way are:

    1. If the global: and local: keywords are both omitted before initially listed symbols then global: is the default, until and unless local: occurs. global: is only necessary when used to cancel local:.
    2. global cannot make a symbol to which it is applied dynamically visible in the output file if it otherwise would not be - i.e. if it is static in the source, or in an anonymous C++ namespace, or declared with __attribute__((visibility("hidden"))).
    3. local, on the contrary, forces a symbol to which it is applied to be local in the output file, as if were static in the source, or in an anonymous C++ namespace, or declared with __attribute__((visibility("hidden"))).

    Point 2 disposes of your worry:

    but those [global and local] also function to define what symbols to export/hide.

    global does not define what symbols to export: it just tags symbols that already are exported for versioning. Per point 3, local coerces symbol visibility, but you don't need it.

    Points 1 and 2 suffice to make your version-script fit for purpose.