Programming Languages on Power

Power Programming Languages

IBM Power, including the AIX, IBM i, and Linux operating systems, support a wide range of programming languages, catering to both traditional enterprise applications and modern development needs.


#Power


#Servers
#Programminglanguages
 View Only
  • 1.  Surprising behavior while linking static libraries with C++ initializations

    Posted 12/20/18 04:06 AM

    Originally posted by: pwaehnert


    Porting our application to the AIX platform we discovered the following crucial difference to all other operating systems which our application supports: The linker always includes object files from static libraries if they contain some initialization code for global C++ objects.

    Example:

        // static.cpp
        #include <string>
        std::string s = "GLOBAL CONSTANT";

     

        // framework.cpp
        int func(int x) { return 2 * x; }

     

        // build.sh
        #! /bin/sh
        set -ex

        xlC -qpic -o static.o -c static.cpp
        ar vr libstatic.a static.o

        xlC -qpic -G -Wl,-brtl -o libframework.so framework.cpp libstatic.a

     

    As framework.cpp doesn't depend on anything from libstatic.a, we expect that the linker would dismiss libstatic.a entirely. But nevertheless

     

        $ dump -Tv libframework.so
        ...
        [7]     0x110000608    .data      EXP     RW SECdef        [noIMid] s
        ...

     

    reveals, that static.o was included anyway.

    Our application consists of several dynamic libraries which in turn are linked together of several static libraries. Since those static libraries contain
    some object files including such initialization code, the final dynamic libraries contain large portions of unnecessary object code and are way too large.

    This behavior strikes us as very odd since on every other platform, which our applications supports (i.e. Windows, macOS and GNU/Linux), the linker would
    ignore those unnecessary object files.

    Questions:

    • Can you confirm the described behavior?
    • Can you provide some explanation why the AIX linker keeps those object files?
    • Are there some compiler or linker flags to mimic the behavior of the other platforms?

    #Ask-Question-Here--General-Compiler-Q-and-A
    #C/C++andFortran


  • 2.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 12/20/18 09:44 AM

    Originally posted by: xlbowler


    I cannot confirm the described behaviour.  If I produce an executable program:

     

         1  int func(int x);
         2
         3  int main() { return func(3); }

     

    This becomes load dependent on libframework.so and the static initialization is run.  If the call to "func" is omitted, there is no load dependency on libframework.so and no problem.  I believe this is what you're expecting and want.

     

    I suspect your final executable has a load dependency on libframework.so, which is causing the static initialization to be picked up.  Can you dump the load information of your executable and see what your load dependency on libframework.so is?  I suspect you're resolvoing a weak symbol in libframework.so, inadvertently available from a header inclusion.

     

    2 quick tips for AIX linker problems:

    1. Use -qfuncsect

    2. Use the linker "weaklocal" option.

    (unfortunately neither is the default and probably what most users will want most of the time, IMO)


    #Ask-Question-Here--General-Compiler-Q-and-A
    #C/C++andFortran


  • 3.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 12/21/18 01:42 PM

    Originally posted by: pwaehnert


    Of course, linking an executable against libframework.so and using its symbols adds a load dependency and thus triggers the static initialization inside libframework.so at load time. That's not the weird part.

    We are rather surprised by the content of libframework.so itself: If you look closely at libframework.so, you'll discover that it contains symbols it technically shouldn't, i.e. the constant string s. It shouldn't contain this symbol since it comes from an object file inside the static library libstatic.a which isn't needed by framework.cpp and thus should be discarded during the linking process of libframework.so. 

    This behavior is different from other systems like GNU/Linux, Windows or macOS. On those systems linking against a static library always discards those objects files whose symbols aren't needed even though they contain a static initialization.

    The flag -qfuncsect let the linker discard all other unused symbols from an object file. Nevertheless, the unneeded static initialization stays.

    The linker flag -Wl,-bweaklocal does not apply here since those symbols in question aren't weak and don't have multiple definitions. Their search order doesn't lead to the described behavior.


    #C/C++andFortran
    #Ask-Question-Here--General-Compiler-Q-and-A


  • 4.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 01/07/19 11:17 AM

    Originally posted by: xlbowler


    Have you had a look at -qtwolink?


    #Ask-Question-Here--General-Compiler-Q-and-A
    #C/C++andFortran


  • 5.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 01/09/19 05:57 AM

    Originally posted by: pwaehnert


    Thank you for pointing out this compiler flag. I haven't noticed it until now. Unfortunately, it helps only with the static constructors, but there're other situations where this flag fails but the surprising linker behavior persists:

    Example: It only takes the following three files to reproduce the said situation.

    // helper.cpp
    #include <string>
    __attribute__((visibility("default"))) int gunc() {
        return std::string("Hello").size();
    }
    
    // framework.cpp
    #include <list>
    __attribute__((visibility("default"))) void run(std::list<int> &l) {
        l.push_back(1);
    }
    
    // do-build.sh
    #!/bin/bash
    
    set -ex
    rm -f *.o *.a *.so
    
    xlC_r -qvisibility=hidden -qpic -o helper.o -c helper.cpp
    ar -rv libhelper.a helper.o
    
    xlC_r -qvisibility=hidden -qpic -o framework.o -c framework.cpp
    xlC_r -qmkshrobj -o libframework.so framework.o -L. -lhelper -qtwolink
    

    Again, if you take a look at the exported symbols of libframework.so, you will stumble upon the function gunc which is defined inside the static library libhelper.a but isn't referenced at all by framework.cpp and should've been discarded during the linking process:

    $ dump -Tv libframework.so
    ...
    [14]    0x110001430    .data      EXP     DS SECdef        [noIMid] gunc__Fv
    ...
    

    This is very unusual compared to other platforms like Windows, macOS or GNU/Linux. Do you know some further compiler or linker flags, which might help in this scenario?


    #Ask-Question-Here--General-Compiler-Q-and-A
    #C/C++andFortran


  • 6.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 01/09/19 08:57 AM

    Originally posted by: xlbowler


    (I respond initially as below, but later noted I could not reproduce the behaviour you described.  What I found was "gunc" was discarded with -qtwolink but remains without it.  I put static init in helper and it was discarded with -qtwolink)

     

    Attribute visibility is problematic in some situations on AIX.  Visibility default causes symbols to be exported in shared libraries.  Typically, I find this is used by library authors to ensure that an inline/template symbol is included in a library build, but this also causes the symbol to be included in consumers of the library that include the header.  As you've noted, it looks like visibility default will defeat -qtwolink from culling otherwise dead modules.  Compiler devs will say you're exporting the symbol so the compiler is doing exactly as it's instructed and keeping the init, nonetheless, I understand this isn't what you want.

     

    The AIX 16.1 compiler, which was released recently adds a -qnovisibility option, which causes attribute visibility to be ignored.  -qnovisibility is the default on the new xlclang compiler.  On AIX, I would recommend using this option and controlling your exported symbols with export lists.  I suspect -qnovisibility was only added for xlclang and not xlC.  Is moving to 16.1 and/or xlclang an option for you? (noting that xlclang++ / xlC are not C++ interoperable.)


    #C/C++andFortran
    #Ask-Question-Here--General-Compiler-Q-and-A


  • 7.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 01/10/19 12:30 PM

    Originally posted by: pwaehnert


    Did you copy the files helper.cpp, framework.cpp and do-build.sh as listed in my post from January 9th and used the script do-build.sh to build the shared library? Which version of XL C/C++ Compiler did you use? We're currently using Version 16.1?

    So far, I considered the visibility of a symbol and the fact that the linker keeps or discards it as independent from each other. At least this is the case for the Visual C++, GCC and clang. These tool-chains link visible symbols from static libraries into the final artifact only if they're needed. The overall project structure and build scripts of our framework heavily relies on this fact.

    I currently try to follow your suggestion and link our framework with export lists rather than visibility attributes. I'm only halfway through but it seems to me, that there're still too many symbols linked into the final library.


    #C/C++andFortran
    #Ask-Question-Here--General-Compiler-Q-and-A


  • 8.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 01/15/19 11:51 AM

    Originally posted by: pwaehnert


    In the meantime I created a further minimal example based on our framework and got the following, as i think, crucial insight: The AIX linker prefers to resolve weak definitions from the first object file and static library from the command line.

    Given the situation I have two static libraries libuseless.a and libhelper.a. The library libuseless.a contains an object file defining a normal symbol useless_func and a weak symbol weak_symbol. The library libhelper.a contains an object file defining another normal symbol helper_func and the same weak symbol weak_symbol.

    Now if we want to use the symbol helper_func of libhelper.a in a binary, we have to link against libhelper.a. The linker pulls in the object file from libhelper.a containing helper_func in order to resolve this symbol. But this object file also contains weak_symbol. The linker now seems to reflect about all locations where he previously saw weak_symbol in order to decide which one he really wants to use. If the linker call contains libuseless.a before libhelper.a, the linker prefers the weak symbol weak_symbol from libuseless.a. But since this weak symbol is defined inside an object file also containing the symbol useless_func, the final binary ends up containing this useless symbol too. If this useless symbol depends on a large tree of further symbols, the resulting binary may contain large portions of dead and useless code. In our case, the final libraries contained 20 MB of dead code on the average.

    On the other hand, if the linker call lists libuseless.a after libhelper.a, the linker prefers weak_symbol from libhelper.a and thus discards all object files from libuseless.a. The final binary does not contain the symbol useless_func. 

    This behavior is different from the linkers on Windows, macOS and GNU/Linux. Those linkers discard the object files from libuseless.a nonetheless and prefer the weak symbols from object files which are already included.

    I can circumvent this behavior of the AIX linker by using the linker flag -bweaklocal, as you already recommended.

    Summary: I had to use both flags you already mentioned together in order to reduce the final binary sizes to something somewhat acceptable:

    • -qtwolink prevents the inclusion of static globals from unused object files from static libraries
    • -bweaklocal prevents the inclusion of weak symbols and neighbor symbols from otherwise completely unneeded object files from static libraries

    Those two flags reduced the overall size of our framework by nearly 1.8 gigabytes. Thank you!


    #C/C++andFortran
    #Ask-Question-Here--General-Compiler-Q-and-A


  • 9.  Re: Surprising behavior while linking static libraries with C++ initializations

    Posted 01/16/19 10:01 AM

    Originally posted by: xlbowler


    Glad I was of help.  I think your explanation leading up to the use of -bweaklocal was what I was trying to convey earlier.  It's exceptionally difficult to describe succinctly.

    To your summary I'll also reiterate for any future readers:

    • Use -qfuncsect.  This greatly improves the linkers ability to cull unused symbols.
    • manage your export lists explicitly (or pay the price for exporting unnecessary weak symbols.)
      • there are also further problems if the inlining policy changes when you update the build compiler
      • and use -qkeepinlines=exports

    There are times I wish these recommendations could be the default but there's a large user base that depends on the current defaults.

    I'm also using the 16.1 compiler and was unable to reproduce the inclusion of "gunc" described earlier, nonetheless, example aside I acknowledge there are headaches with visibility on AIX.


    #Ask-Question-Here--General-Compiler-Q-and-A
    #C/C++andFortran