gcccmakelto

Using GCC's link-time optimization with static linked libraries


I'm trying to use link-time optimizations with the -flto flag of GCC (6.1.1).

While it works fine with my code, it doesn't link with a static linked library I'm also building and linking with my project (which is Engine and the library is glsl-optimizer, just for reference).

Here is the output:

...
/usr/bin/ranlib: ir_expression_flattening.cpp.o: plugin needed to handle lto object
/usr/bin/ranlib: opt_function_inlining.cpp.o: plugin needed to handle lto object
/usr/bin/ranlib: opt_copy_propagation_elements.cpp.o: plugin needed to handle lto object
...

And after that, of course, I get several "undefined references" to some functions.

I did some research and found out that it might be because of ar, and I should try to use gcc-ar, but I'm not sure how I might do that.

Also, I'm using CMake that does not support lto (except on Intel's compiler on some platforms, so I read...). Even though, I tried using:

set_property(TARGET glsl_optimizer PROPERTY INTERPROCEDURAL_OPTIMIZATION True)

Which didn't work.

Also, I tried GCC's -fuse-linker-plugin flag which didn't work.

I guess I'll have to do it manually the old way directly using gcc-ar, or maybe there's some other method?


Solution

  • Here is an MCVE CMake project that reproduces the problem:

    $ ls -R hellow
    hellow:
    CMakeLists.txt  hello.c  libhello.c
    
    $ cat hellow/CMakeLists.txt 
    cmake_minimum_required (VERSION 2.6)
    project (hellow)
    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto")
    SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto")
    #SET(CMAKE_AR  "gcc-ar")
    #SET(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
    #SET(CMAKE_C_ARCHIVE_FINISH   true)
    add_library(hello STATIC libhello.c) 
    add_executable(hellow hello.c)
    target_link_libraries(hellow hello)
    add_dependencies(hellow hello)
    
    
    $ cat hellow/hello.c 
    extern void hello(void);
    
    int main(void)
    {
        hello();
        return 0;
    }
    
    $ cat hellow/libhello.c 
    #include <stdio.h>
    
    void hello(void)
    {
        puts("Hello");
    }
    

    Configuration is good:

    $ mkdir build_hellow
    $ cd build_hellow/
    $ cmake ../hellow
    -- The C compiler identification is GNU 5.4.0
    -- The CXX compiler identification is GNU 5.4.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/imk/dev/so/build_hellow
    

    Build fails as per problem:

    $ make
    Scanning dependencies of target hello
    [ 25%] Building C object CMakeFiles/hello.dir/libhello.c.o
    [ 50%] Linking C static library libhello.a
    /usr/bin/ar: CMakeFiles/hello.dir/libhello.c.o: plugin needed to handle lto object
    /usr/bin/ranlib: libhello.c.o: plugin needed to handle lto object
    [ 50%] Built target hello
    Scanning dependencies of target hellow
    [ 75%] Building C object CMakeFiles/hellow.dir/hello.c.o
    [100%] Linking C executable hellow
    /tmp/ccV0lG36.ltrans0.ltrans.o: In function `main':
    <artificial>:(.text+0x5): undefined reference to `hello'
    collect2: error: ld returned 1 exit status
    CMakeFiles/hellow.dir/build.make:95: recipe for target 'hellow' failed
    make[2]: *** [hellow] Error 1
    CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/hellow.dir/all' failed
    make[1]: *** [CMakeFiles/hellow.dir/all] Error 2
    Makefile:83: recipe for target 'all' failed
    make: *** [all] Error 2
    

    There is more than one solution. One is to uncomment the 3 commented lines in CMakeLists.txt above. Then:

    $ cmake ../hellow/
    -- The C compiler identification is GNU 5.4.0
    -- The CXX compiler identification is GNU 5.4.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/imk/dev/so/build_hellow
    
    $ make
    Scanning dependencies of target hello
    [ 25%] Building C object CMakeFiles/hello.dir/libhello.c.o
    [ 50%] Linking C static library libhello.a
    [ 50%] Built target hello
    Scanning dependencies of target hellow
    [ 75%] Building C object CMakeFiles/hellow.dir/hello.c.o
    [100%] Linking C executable hellow
    [100%] Built target hellow
    
    $ ./hellow 
    Hello
    

    This fix makes use of the following facts.

    The build-breaking problem:

    /usr/bin/ar: CMakeFiles/hello.dir/libhello.c.o: plugin needed to handle lto object
    ...
    /usr/bin/ranlib: libhello.c.o: plugin needed to handle lto object
    

    can solved by giving ar and ranlib the option:

    --plugin=$(gcc --print-file-name=liblto_plugin.so)
    

    However, GNU ranlib is merely a synonym for ar -s, and gcc-ar is a wrapper for ar that supplies that plugin.

    CMake's build template for a C static library is:

    CMAKE_C_ARCHIVE_CREATE ( = <CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>)
    CMAKE_C_ARCHIVE_FINISH ( = <CMAKE_RANLIB> <TARGET>)
    

    which for GNU ar is equivalent to:

    CMAKE_C_ARCHIVE_CREATE ( = <CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>)
    CMAKE_C_ARCHIVE_FINISH ( = true) # Or any other no-op command
    

    So with these settings plus:

    SET(CMAKE_AR  "gcc-ar")
    

    we're good.

    For a C++ project, of course, set CMAKE_CXX_ARCHIVE_CREATE and CMAKE_CXX_ARCHIVE_FINISH