Function calls in dynamic libraries

Environment:

  • Clang version 12.0.1
  • Cmake version 3.21.2
  • G + + 11.1.0 (GCC)

A library link conflict containing two identical functions

Imagine calling a function in your main function, but that function can be implemented in different libraries. Which function should be called in your main function?

Let’s experiment and build the test code:

. ├ ─ ─ CMakeLists. TXT ├ ─ ─ the include │ ├ ─ ─ second. H │ └ ─ ─ work. H ├ ─ ─ main. Cc └ ─ ─ the SRC ├ ─ ─ CMakeLists. TXT ├ ─ ─ second. Cc └ ─ ─ work.ccCopy the code

./ cmakelists. TXT File contents:

Cmake_minimum_required (VERSION 3.14) Project (symbol) include_directories(include) add_subdirectory(SRC) add_executable(${PROJECT_NAME} main.cc) target_link_libraries(${PROJECT_NAME} second work)Copy the code

./include/second.h

#pragma once
#include <iostream>
void SoFunction(a);
void DoFunction(a);

Copy the code

./include/work.h

#pragma once
#include <iostream>
void SoFunction();

Copy the code

SRC/CMakeLists. TXT content:

include_directories(${CMAKE_PROJECT_PATH}/include)
add_library(second SHARED second.cc)
add_library(work SHARED work.cc)
Copy the code

src/second.cc

#include "second.h"
void SoFunction(a)
{
 std::cout << "conflict function call\n";
}
void DoFunction(a)
{
 std::cout<<"DoFunction\n";
 SoFunction(a); }Copy the code

src/work.cc

#include "work.h"
void SoFunction(a) { std::cout << "Call Sofunctiuon\n"; }
Copy the code

./main.cc

#include <iostream>
void SoFunction(a);
int main(a) {
 std::cout<<"1. Main start... \n";
 SoFunction(a); std::cout<<"SoFunction call finished\n";
 return 0;
}

Copy the code

Build run :mkdir build && pushd build && cmake.. && make &&./symbol && popd && rm -rf build

conflict function call
SoFunction call finished
Copy the code

Cc/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt/cmakelists.txt Different results.

Dynamic library call override problem containing two identical functions

If we call second/DoFunction in the main function and it calls SoFunction, will we call SoFunction in second. Cc or SoFunction in work.cc? Modify the main function to test:

#include <iostream>
#include "second.h"
#include "work.h"
int main(a) {
  std::cout<<"1. Main start... \n";
  DoFunction(a); std::cout<<"DoFunction call finished\n";
  SoFunction(a);return 0;
}


Copy the code

We do not change the link order, keep second first link, we get the following result:

1. Main start... 
Second DoFunction
conflict function call
DoFunction call finished
conflict function call

Copy the code

Not surprisingly, the order of function calls is:main.cc/DoFunction->second.cc/DoFunction->second.cc/SoFunction->work.cc/SoFunctionNow let’s modify./CMakeLists.txtTo adjust the link ordertarget_link_libraries(${PROJECT_NAME} work second)Rerun the code, we should get the following result:

1. Main start... 
Second DoFunction
Call Sofunctiuon
DoFunction call finished
Call Sofunctiuon
Copy the code

Functions are called in the following order:main.cc/DoFunction->second.cc/DoFunction->work.cc/SoFunction->work.cc/SoFunctionBecause of the delta functionSoFunctionbecauselibwork.soFirst link, the function uses its. How can functions in the same library still call each other when the link order changes?

Nm-cd SRC /libwork.so

U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ W _ITM_deregisterTMCloneTable W _ITM_registerTMCloneTable 0000000000001129 T SoFunction() U STD :: iOS_BASE ::Init::Init() @glibcXX_3.4U STD :: iOS_BASE ::Init::~Init() @glibcxx_3.4u STD ::cout@GLIBCXX_3.4 U STD ::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, STD :: Char_traits < har> >&, char const*) @glibcxx_3.4Copy the code

Libsecond-so contains the following contents:

U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ W _ITM_deregisterTMCloneTable W _ITM_registerTMCloneTable 0000000000001159 T DoFunction() 0000000000001139 T SoFunction() U STD :: iOS_BASE ::Init::Init() @glibcxx_3.4U STD :: iOS_Base ::Init::Init() @glibcxx_3.4U STD :: iOS_BASE ::Init::~Init() @glibcxx_3.4U STD ::cout@GLIBCXX_3.4 U std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, STD :: Char_traits < har> >&, char const*) @glibcxx_3.4Copy the code

Resolve incorrect calls caused by symbol conflicts

Hidden symbols

“Unavailable” dynamic library symbols can be set to unavailable, for example, libwork.so symbols can be unavailable, so that second is available anyway. You can set SRC/cmakelists.txt to the following:

include_directories(${CMAKE_PROJECT_PATH}/include)

if(UNIX AND CMAKE_COMPILER_IS_GNUCC)
    set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
    add_library(work SHARED work.cc)
endif(a)add_library(second SHARED second.cc)


Copy the code

The compilation process above will automatically hide two library symbols (CXX_FLAG in current CMakeLists. TXT has been effective, cannot be directly set a parameter, a plus), at that time we see libwork. So symbol information, perform when run the compiler will go wrong, because I couldn’t find the symbol table DoF Unction and SoFunction, resulting in undefined references.

U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ W _ITM_deregisterTMCloneTable W _ITM_registerTMCloneTable U STD :: iOS_BASE ::Init::Init() @glibcxx_3.4U STD :: iOS_BASE ::Init::Init() @glibcxx_3.4U STD: : cout@GLIBCXX_3.4 U STD: : basic_ostream < char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, Char const *) @ GLIBCXX_3. 4Copy the code

Here we use manual link to generate the final binary :clang++ main.cc -iinclude-l. -lsecond -lwork -o symbol This will mask libwork.so to only call SoFunction in second.cc. The results are as follows:

1. Main start...  
Second DoFunction
conflict function call
SoFunction call finished
conflict function call

Copy the code

Disable in function code

Cc: libsecond. Cc: libsecond. Soclang++ -fvisibility=hidden libsecond -Iinclude src/second.cc -fPIC -shared -o libsecond.so)

#include "second.h"
__attribute__ ((visibility ("default")))  void SoFunction()
{
  std::cout << "conflict function call\n";
}
__attribute__ ((visibility ("default"))) void DoFunction()
{
  std::cout<<"Second DoFunction\n";
  SoFunction();
}

Copy the code

Then open the visibility in SRC/cmakelists. TXT, and the compilation will run normally, because the symbols in work.cc are not exposed, so we will not change the SoFunction in work.cc.

Export function tables through file Settings

Add the following information to the include/export.symb file:

{
global: *SoFunction*;
local: *;
};
Copy the code
  • Global: corresponds to the function you want to export
  • The local area represents symbols that you do not want to export, and the asterisk (*) indicates that all symbols except those in global are not exported

Manual compilation of the dynamic library implementation only preserves SoFunctionclang++ -wl,–version-script=include/ export-symb-s-iinclude SRC/second-.cc-fpic-shared-o Libsecond. So libsecond.

U __cxa_atexit@GLIBC_2.2.5 w __cxa_finalize@GLIBC_2.2.5 w __gmon_start__ W _ITM_deregisterTMCloneTable W _ITM_registerTMCloneTable 0000000000001190 T DoFunction() U STD :: iOS_BASE ::Init::Init() @glibcXX_3.4U STD :: iOS_BASE ::Init::~Init() @glibcxx_3.4u STD ::cout@GLIBCXX_3.4 U STD ::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, STD :: Char_traits < har> >&, char const*) @glibcxx_3.4Copy the code

The output is as follows:

1. Main start...  
Second DoFunction
conflict function call
DoFunction call finished
Call Sofunctiuon
Copy the code

The output here is a bit different (we’ve masked SoFunction in second.cc)

reference

How to resolve symbol conflicts when compiling links