Решить проблему конфликта символов при динамической компоновке библиотек.

задняя часть

вызов функции в динамической библиотеке

окрестности:

  • clang version 12.0.1
  • cmake version 3.21.2
  • g++ (GCC) 11.1.0

Конфликт библиотечных ссылок, содержащий две идентичные функции

Представьте, что в вашей основной функции вызывается функция, но эта функция может быть реализована в разных библиотеках, какую из них следует вызывать в основной функции?

link.drawio.pngДавайте попробуем и создадим тестовый код:

.
├── CMakeLists.txt
├── include
│   ├── second.h
│   └── work.h
├── main.cc
└── src
   ├── CMakeLists.txt
   ├── second.cc
   └── work.cc

./CMakeLists.txtсодержание документа:

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)

./include/second.h

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

./include/work.h

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

src/CMakeLists.txtсодержание:

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

src/second.cc

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

src/work.cc

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

./main.cc

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

Прогон сборки:mkdir build && pushd build && cmake .. && make && ./symbol && popd && rm -rf buildРезультат выполнения следующий:

conflict function call
SoFunction call finished

Вы можете видеть, что основная функция вызываетсяsecond.cc里面的SoFunction, потому что когда мы сначала связываемсяlibsecond.soДинамическая библиотека, естественно по ссылкеlibwork.soбудет перезаписан, если мы находимся в./CMakeLists.txtЕсли вы отрегулируете порядок ссылок в , вы получите другие результаты.

Проблема покрытия вызовов динамической библиотеки с двумя идентичными функциями

Если мы вызовем основную функциюsecond/DoFunctionэто позвонитSoFunction, то вызоветsecond.ccизSoFunctionили позвоните по телефонуwork.ccизSoFunction? Измените основную функцию для проверки:

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


В это время мыне изменятьПорядок ссылок, сохраняя вторую ссылку первой, мы получаем следующий результат:

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

link.drawio (1).pngНеудивительно, что последовательность вызова функции такова:main.cc/DoFunction->second.cc/DoFunction->second.cc/SoFunction->work.cc/SoFunctionТеперь мы модифицируем./CMakeLists.txtнастроить порядок ссылокtarget_link_libraries(${PROJECT_NAME} work second), перезапустим код, получим следующий результат:

1. Main start... 
Second DoFunction
Call Sofunctiuon
DoFunction call finished
Call Sofunctiuon

link.drawio (2).pngПоследовательность вызова функций такова:main.cc/DoFunction->second.cc/DoFunction->work.cc/SoFunction->work.cc/SoFunction, то поскольку функцияSoFunctionтак какlibwork.soСначала ссылка, функция использует свою.Тогда как еще реализовать функцию одной библиотеки для вызова друг друга при изменении последовательности ссылок?

Давайте сначала посмотрим на информацию о символах в двух библиотеках: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.4
                U std::ios_base::Init::~Init()@GLIBCXX_3.4
                U 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<c
har> >&, char const*)@GLIBCXX_3.4

Содержимое libsecond.so:

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.4
                U std::ios_base::Init::~Init()@GLIBCXX_3.4
                U 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<c
har> >&, char const*)@GLIBCXX_3.4

Устранение ошибочных вызовов, вызванных конфликтами символов

скрытые символы

Мы можем сделать символы динамической библиотеки видимыми для внешнего мира, установив невидимые, например, мы можем сделатьlibwork.soСимвол не виден внешнему миру, тогда мы можем вызвать функцию секунды несмотря ни на что.Вы можете установитьsrc/CMakeLists.txtСодержание следующее:

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()
add_library(second SHARED second.cc)


Описанный выше процесс компиляции автоматическиСкрытие символов для обеих библиотек(CXX_FLAG всегда действует в текущем CMakeLists.txt, вы не можете напрямую установить один плюсовой параметр, один не добавляется), в это время мы видим символьную информацию libwork.so, произойдет ошибка при выполнении вышеуказанной компиляции и запустить команду, потому что символ не может быть найден на поверхностиDoFunctionиSoFunction, что приводит к неопределенной ссылке.

                 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.4
                 U std::ios_base::Init::~Init()@GLIBCXX_3.4
                 U 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.4

Здесь мы используем ручную ссылку для создания окончательного двоичного файла:clang++ main.cc -Iinclude -L. -lsecond -lwork -o symbolВ это время мы будем блокировать, когда будем выполнятьlibwork.soвнутри символа, так что реализация вызывает толькоsecond.ccвнутриSoFunction, Результат следующий:

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

Отключено в функциональном коде

существуетlibsecond.soКитайцы открываютSoFunctionиDoFunctionДве функции видны внешнему миру и могут быть измененыsecond.cc(компилировать second.cc отдельно как libsecond.soclang++ -fvisibility=hidden -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();
}

затем вsrc/CMakeLists.txtПри включенной видимости компиляция может проходить нормально, т.к.work.ccСимволы внутри не подвергаются воздействию внешнего мира, поэтому мы не будем их передавать в работу.ccSoFunction.

Экспорт таблицы функций через настройки файла

Добавьте следующую информацию в файл include/export.symb:

{
global: *SoFunction*;
local: *;
};
  • global: соответствует функции, которую вы хотите экспортировать
  • Локальная область представляет собой символы, которые не нужно экспортировать, а знак * означает, что все символы, кроме глобальных, не экспортируются.

Вручную скомпилируйте реализацию динамической библиотеки, чтобы сохранить только SoFunctionclang++ -Wl,--version-script=include/export.symb -s -Iinclude src/second.cc -fPIC -shared -o libsecond.so, содержимое libsecond.so следующее:

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.4
                U std::ios_base::Init::~Init()@GLIBCXX_3.4
                U 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<c
har> >&, char const*)@GLIBCXX_3.4

Результат выглядит следующим образом:

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

Вывод здесь немного другой (мы заблокировали SoFunction в second.cc)

Ссылаться на

Часть содержания относится к:Как решить проблему конфликта символов при компиляции и линковке