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

2 篇文章 1 订阅


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

log4cpp linux下编译安装

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

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

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

// stdcall & cdecl
#if defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64)
#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__)

参考:
https://blog.csdn.net/test1280/article/details/71155800
https://www.linuxidc.com/Linux/2014-06/102923.htm

存在不兼容的函数

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.

总结是:

  1. linux下源文件中的所有函数都有一个默认的visibility属性,默认为public,即默认导出。如果要隐藏,则在GCC编译指令中加入 -fvisibility=hidden 参数,会将默认的public属性变为hidden。
  2. 隐藏函数导出后,所有的导出都隐藏,再在源码中,在需要导出的函数前添加 __attribute__ ((visibility(“default”))) ,使其仍按默认的public属性处理。

查看文件属性:

  • readelf -s *.so

查看导出函数:

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

使用linux动态库

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

  • 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 //当静态加载时直接链接
)

参考:https://blog.csdn.net/shijiu2012/article/details/82978881

静态加载

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

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

其他问题

qt的cmakelists工程进行debug调试

在cmakelist中添加SET(CMAKE_BUILD_TYPE DEBUG)

cmakelists链接静态库和动态库

在用测试demo debug时发现,dlopen失败报错是没有找到log4cpp.so,原来是生成so时,链接的log4cpp库没有后缀:

TARGET_LINK_LIBRARIES(${PROJECT_NAME} log4cpp)

这样会默认链接动态库log4cpp.so,修改为

TARGET_LINK_LIBRARIES(${PROJECT_NAME} log4cpp.a)

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

修改链接log4cpp.a静态库后,报错relocation R_X86_64_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,生成静态库,解决。

参考:
https://blog.csdn.net/u010312436/article/details/52486811/
cmakelist 编写规则:
https://www.cnblogs.com/flyinggod/archive/2017/11/08/7805587.html

dlopen报错 undefined symbol: pthread_key_create

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

TARGET_LINK_LIBRARIES(${LOG4CPP_LIBRARY_NAME} pthread)

解决问题。

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值