您好,欢迎来到爱问旅游网。
搜索
您的当前位置:首页C++ linux动态库so导出及使用

C++ linux动态库so导出及使用

来源:爱问旅游网


第一次尝试导出linux动态库,包装log4cpp,遇到的问题做个记录。

log4cpp linux下编译安装

在官网上下下来包过后,官网的安装说明不全:

  • ./autogen.sh
  • ./configure
  • make
  • make check
  • sudo make install

使用宏定义进行区分windows & linux

// stdcall & cdecl
#if defined(_MSC_VER) || defined(_WIN32) || defined(_WIN)
#define TCE_API __stdcall
// TCELOGGER_DLL_EXPORTS
#ifdef TCELOGGER_DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
#else
#define TCE_API
#define DLL_API
#endif

多参数宏定义

主要是linux:
args… 对应 ##args // QT会提示这种表示是GNU扩展
windows:
… 对应 ##__VA_ARGS__

// linux下:
#define Info(logFormat, args...) Info(__FUNCTION__, __LINE__, logFormat, ##args)
// windows下:
#define Info(logFormat, ...) Info(__FUNCTION__, __LINE__, logFormat, ##__VA_ARGS__)

参考:

存在不兼容的函数

sprint_s snprintf

发现有的函数是windows平台的。sprint_s是windows平台下线程安全的格式化字符串函数并非标准C函数,因此linux下无法使用,但可以使用snprintf函数代替。

int snprintf(char *dest, size_t n, const char *fmt, ...); 
// 函数原型相同,替换即可
#define sprintf_s snprintf

控制linux动态库的导出函数

windows下通过__declspec(dllexport)来声明DLL动态库导出的接口(函数或类),__declspec(dllimport)来声明为动态库加载的接口。linux下不可用。
linux下,GCC帮助文档 -fvisibility=default|internal|hidden|protected 参数下有这么一段话:

  • a superior solution made possible by this option to marking things hidden when the default is public is to make the default hidden and mark things public. This is the norm with DLL’s on Windows and with -fvisibility=hidden and “attribute ((visibility(“default”)))” instead of “__declspec(dllexport)” you get almost identical semantics with identical syntax. This is a great boon to those working with cross-platform projects.

总结是:

查看文件属性:

  • readelf -s *.so

查看导出函数:

  • nm -D *.so
  • objdump -tT *.so

使用linux动态库

Linux提供4个库函数、一个头文件dlfcn.h以及两个共享库()支持动态链接。

  • dlopen:打开动态共享目标文件并将其映射到内存中,返回其首地址
  • dlsym:返回锁请求的入口点的指针
  • dlerror:返回NULL或者指向描述最近错误的字符串
  • dlclose:关闭动态共享文件

动态加载

使用linux的库,使用方法和windows下一致。

#include <iostream>
#include <dlfcn.h>
#include "TceLogger.h"
using namespace std;
using namespace tce;

// declare function pointer
typedef TceLogger& (*type_pSo_TLGI)(void);

int main()
{
    // dynamic load .so
    // declare a void* handle (in windows HINSTANCE)
    void *pSo_handle;
    // use dlopen to load .so (in windows LoadLibrary)
    pSo_handle = dlopen("../qtProject/libTceLogger.so", RTLD_NOW | RTLD_DEEPBIND);
    if (!pSo_handle)
    {
        cout<<"can't open .so"<<endl;
        cout<<"dlopen - "<<dlerror()<<endl;
        return -1;
    }
    // use dlsym to get function address (in windows GetProcAddress)
    type_pSo_TLGI pSo_TLGI = (type_pSo_TLGI)dlsym(pSo_handle,"TceLoggerGetInstance");
    if (!pSo_TLGI)
    {
        cout<<"can't cast function"<<endl;
        return -1;
    }
    // use function to get instance
    TceLogger& logger = pSo_TLGI();
    logger.Info("ok");
    return 0;
}
动态加载cmakelist

动态加载时,需要使用dl库,在cmakelist中添加链接dl库。

cmake_minimum_required(VERSION 2.8)

project(SoCaller)

// 设置debug模式
SET(CMAKE_BUILD_TYPE DEBUG)

//设置C++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

// qt工程中显示h文件
FILE(GLOB_RECURSE LibFiles "TceLogger.h")
add_custom_target(headers SOURCES ${LibFiles})

// 设置include目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
// 设置库目录
link_directories(${CMAKE_CURRENT_SOURCE_DIR})

// 设置生成内容
add_executable(${PROJECT_NAME} "SoCaller.cpp")

// 设置要链接的库
target_link_libraries(${PROJECT_NAME} 
	dl 
	TceLogger //当静态加载时直接链接
)

参考:

静态加载

在cmakelist中设置链接该动态库后,并包含头文件,可以直接使用。

// 使用动态库导出的C接口函数获取实例
TceLogger& tceLogger = TceLoggerGetInstance();
tceLogger.Crit("ok");
return 0;

其他问题

qt的cmakelists工程进行debug调试

在cmakelist中添加SET(CMAKE_BUILD_TYPE DEBUG)

cmakelists链接静态库和动态库

在用测试demo debug时发现,,原来是生成so时,链接的log4cpp库没有后缀:

TARGET_LINK_LIBRARIES(${PROJECT_NAME} log4cpp)

,修改为

TARGET_LINK_LIBRARIES(${PROJECT_NAME} log4cpp.a)

报错relocation R_X86__32 against `.rodata’ can not be used when making a shared object

修改链接log4cpp.a静态库后,报错relocation R_X86__32 against `.rodata’ can not be used when making a shared object。
原因是如果将编译的静态库链接进动态库使用,也就是我的应用情景,需要在编译静态库时,加上编译指令 -fPIC 。
因此在log4cpp源码的cmakelist中,增加指令:

SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")

然后不使用configure,直接cmake make,生成静态库,解决。

参考:

cmakelist 编写规则:

dlopen报错 undefined symbol: pthread_key_create

应该是没有链接pthread库,在log4cpp的cmakelist中,以及使用log4cpp.a的包装类中,添加链接pthread:

TARGET_LINK_LIBRARIES(${LOG4CPP_LIBRARY_NAME} pthread)

解决问题。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- awee.cn 版权所有 湘ICP备2023022495号-5

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务