вызов функции в динамической библиотеке
окрестности:
- clang version 12.0.1
- cmake version 3.21.2
- g++ (GCC) 11.1.0
Конфликт библиотечных ссылок, содержащий две идентичные функции
Представьте, что в вашей основной функции вызывается функция, но эта функция может быть реализована в разных библиотеках, какую из них следует вызывать в основной функции?
Давайте попробуем и создадим тестовый код:
.
├── 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
Неудивительно, что последовательность вызова функции такова:
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
Последовательность вызова функций такова:
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)
Ссылаться на
Часть содержания относится к:Как решить проблему конфликта символов при компиляции и линковке