Benefits of exporting symbols on demand

  1. Reduce the amount of time of loading the library.
  2. Reduce the size of library.
  3. Optimize the code generation.
  4. Improve library maintainability and make the library easier to use.
  5. Reduce the potential for symbol collision.

List the symbols of shared library

1. nm

https://llvm.org/docs/CommandGuide/llvm-nm.html

nm -gD libxxx.so

2. objdump

https://llvm.org/docs/CommandGuide/llvm-objdump.html

objdump -T libxxx.so

3. readelf

https://man7.org/linux/man-pages/man1/readelf.1.html

readelf -Ws libxxx.so

Option 1. the visibility attribute

1
2
3
4
5
6
7
8
9
#if HAVE_VISIBILITY && BUILDING_LIBFOO
#define LIBFOO_EXPORTED __attribute__((__visibility__("default")))
#elif (defined _WIN32 && !defined __CYGWIN__) && BUILDING_SHARED && BUILDING_LIBFOO
#define LIBFOO_EXPORTED __declspec(dllexport)
#elif (defined _WIN32 && !defined __CYGWIN__) && BUILDING_SHARED
#define LIBFOO_EXPORTED __declspec(dllimport)
#else
#define LIBFOO_EXPORTED
#endif

Compile the code with -fvisibility=hidden flag. This option forces the default visibility of all symbols to be hidden.

1
c++ -I. -fvisibility=hidden -o a.o -c a.cpp

On Windows, using the keyword __declspec(dllexport) to export symbols . See https://learn.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-declspec-dllexport?view=msvc-170

See more details: https://gcc.gnu.org/wiki/Visibility

Option 2. linker version scripts for ELF platforms (like Linux and Android)

For example, there is a file only_jni_exports.map :

1
2
3
4
5
6
7
{
global:
JNI_*;
Java_*;
local:
*;
};
  • After global , which symbols should be exported.
  • After local , which symbols should not be exported.
  • wildcards: * matches 0 or more characters, ? matches a single character.
  • This file specifies not to export all symbols except those explicitly exported by global.

Using this file in link options:

1
2
3
4
gcc -shared a.o b.o \
-Wl,--version-script,only_jni_exports.map \
-Wl,-soname,libxxx.so \
-o libxxx.so

Using this scripts file in CMakeLists.txt, e.g:

1
2
3
4
5
6
if (ANDROID)
add_library(${TARGET} SHARED ${SOURCE})
set(VERSION_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/only_jni_exports.map)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script=${VERSION_SCRIPT}")
set_target_properties(${TARGET} PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
endif()

Option 3. exported symbols list file for Mach-O (macOS/iOS/…)

File libapp.exp :

1
2
3
# Exported following symbols
_foo
_bar

The list is a file with the names of symbols to export. The symbol names must include the underscore (_) prefix

Using this file in comand line, e.g.:

1
clang -dynamiclib a.c -exported_symbols_list libapp.exp -o libxxx.dylib

Using this file in CMake, e.g:

1
2
3
4
5
6
7
8
if (BUILDING_SHARED)
add_library(${TARGET} SHARED ${SOURCE})
if (APPLE)
set(EXPORTED_SYMBOLS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/libapp.exp)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -exported_symbols_list ${EXPORTED_SYMBOLS_FILE}")
set_target_properties(${TARGET} PROPERTIES LINK_DEPENDS ${EXPORTED_SYMBOLS_FILE})
endif (APPLE)
endif (BUILDING_SHARED)

Refs