3
votes

I have a problem with setting up a visibility of my shared library. I would like to ask for help in resolving my issue as described below:

I have a bunch of source files which I would like to build as shared library:

+Base
|_Parameter
| |_Parameter.h
| |_Parameter_Exception.h
| |_Parameter_Exception.cpp
|_Data
| |_DataInput.h
| |_DataInput_Exception.h
| |_DataInput_Exception.cpp
...

Well there are some more files in there but I think it does not influence my problem description.

I am using CMake in order to build this SHARED library. Here is part of my CMakeLists.txt which deals with building the library.

project( Base )
cmake_minimum_required(VERSION 3.5

set( Base_HEADERS
    Parameter/Parameter.h
    Parameter/Parameter_Exception.h
    Data/DataInput.h
    Data/DataInput_Exception.h
)
set( Base_SOURCES
    Parameter/Parameter_Exception.cpp
    Data/DataInput_Exception.cpp
)
set( LIBRARY_OUTPUT_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} )

add_library( Base SHARED ${Base_SOURCES} ${Base_HEADERS} )

generate_export_header( ${PROJECT_NAME} EXPORT_FILE_NAME ${PROJECT_NAME}_Export.h )

This setup builds libBase.so shared library without any problem. It is important to mention that I have set some visibility related flags to be used during linking:

set( CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined -fvisibility=hidden -fvisibility-inlines-hidden" )

As configured in Base library CMakeLists.txt partly listed above:

  1. Base_Export.h header containig GCC visibility attributes macros is generated by CMake. It is then included in my sources listed in the tree up there.
  2. By default all the symbols shall be hidden as set in linker flags (-fvisibility=hidden)

For example a piece of Parameter_Exception.h:

#include "Base_Export.h"

class SomeException : public std::exception
{
public:
    SomeException( void ) {}
...
};

Please notice I have not set any visibility attribute here (by a macro defined in Base_Export). So I would assume, the symbol of SomeException class shall be hidden to all the DSO "users" (due to -fvisibility=hidden)

But it doesn't seem so. Unfortunately:

Once I use the library, it the exception is usable although the symbol should be hidden. I would expect link failure cause by this code snippet "outside the library":

#include "Parameter/Parameter_Exception.h"
...
try
{
    ...
}
/* THIS SHOULD FAIL IN LINKING DUE TO UNDEFINED SYMBOL, RIGHT? */
catch( const SomeException & e )
{
    ...
}

I have also tried to look for the symbol in the libBase.so using the command: readelf -Ws libBase.so

it lists enormously long text:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
  1: 000000000008b7e0     0 SECTION LOCAL  DEFAULT    9 
....

but what I have noticed here is that in "Vis" column up there there is always DEFAULT which implies a default visibility ( __attribute__((visibility("default"))) ) to me BUT I would expect to see HIDDEN in there ( __attribute__((visibility("hidden"))) )

So what am I doing wrong? Or my understanding is not correct? I know all the linked flags are propagated correctly but seem to have no effect.

My toolchain configuration as listed by CMake configuration script (GCC version that supports visibility attributes):

...
-- The CXX compiler identification is GNU 5.3.1
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
...
-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY
-- Performing Test COMPILER_HAS_HIDDEN_VISIBILITY - Success
-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY
-- Performing Test COMPILER_HAS_HIDDEN_INLINE_VISIBILITY - Success
-- Performing Test COMPILER_HAS_DEPRECATED_ATTR
-- Performing Test COMPILER_HAS_DEPRECATED_ATTR - Success
-- Configuring done
-- Generating done

Many thanks in advance to anybody willing to help me and possibly others having the same trouble as I do.

Thanks again, Martin

2

2 Answers

6
votes

CMake has direct support for symbol visibility handling, so you shouldn't need to touch CMAKE_SHARED_LINKER_FLAGS or CMAKE_CXX_FLAGS at all. You can set the visibility on a per-target basis like this:

set_target_properties(Base PROPERTIES
    C_VISIBILITY_PRESET       hidden
    CXX_VISIBILITY_PRESET     hidden
    VISIBILITY_INLINES_HIDDEN YES
)

CMake will then ensure the relevant flags are added for you. Rather than having to set these properties for each target, it is usually more convenient to set the associated variables to set the defaults for all subsequently created targets (and this is what I generally recommend and use in projects I work on):

cmake_minimum_required(VERSION 3.5)
project(Base)

set(CMAKE_C_VISIBILITY_PRESET       hidden)
set(CMAKE_CXX_VISIBILITY_PRESET     hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)

add_library(Base SHARED ...)

When used in conjunction with the GenerateExportHeader module (which you already appear to be using), you get support not just for gcc/clang, but also Visual Studio, making it a nice cross-platform way of controlling symbol visibility. You don't need to know the relevant compiler flags or attributes, CMake will handle them for you and give you equivalent behavior across all the compilers.

3
votes

-fvisibility=hidden and -fvisibility-inlines-hidden are compiler options, not linker options. Set them in CMAKE_CXX_FLAGS