参考:


《程序员的自我修养 ---- 链接,装载与库》

C++ dlopen mini HOWTOhttp://www.tldp.org/HOWTO/pdf/C++-dlopen.pdf


1,使用 c 生成动态链接库mylib.so的简单示例


     声明文件mylib.h

-

#ifndef __MY_LIB_H__
#define __MY_LIB_H__
void foobar(int i);
#endif /* __MY_LIB_H__ */

-

    实现文件mylib.c

-

#include <stdio.h>
#include "mylib.h"
void foobar(int i)
{
  printf("Printing from mylib.so %d\n", i);
}

-

    使用 gcc mylib.c 编译成一个共享对象文件,如下命令:


[steven@sasd c++]$ gcc-fPIC -shared -o mylib.so mylib.c

[steven@sasd c++]$ ll

total 16

-rw-rw-r--. 1 steven steven  109 Apr 13 09:27 mylib.c

-rw-rw-r--. 1 steven steven   90 Apr 13 09:26 mylib.h

-rwxrwxr-x. 1 steven steven 6210 Apr 1309:28 mylib.so

-

注:

-

2这里的共享对象其实全称是动态共享对象文件(Dynamic Shared Objects,简写为DSO);

2-fPIC:地址无关代码(Position-Independent Code),该技术主要用于解决SO中对绝对地址的重定位问题;

-

2,动态链接.so

-

下面在我们的c++程序中链接并使用此.so文件,在C++中,是使用 linkage directive 方式指出任意非C++函数所用的语言,也就是用extern “C”来指示对应的声明是使用C来实现的,这样对于该声明中的文件的编译会采用对应的C编译器来编译处理,如下为使用示例:

-

// use_mylib.cpp
#include <iostream>
#include <string>
using namespace std;
#ifdef __cplusplus
extern "C" {
#endif
#include "mylib.h" // for foobar()
#ifdef __cplusplus
}
#endif
int main()
{
  foobar(100);
  return 0;
}

-

编译及运行结果:

-

[steven@sasd c++]$ g++ -o use_mylibuse_mylib.cpp ./mylib.so

[steven@sasd c++]$ ./use_mylib

Printing from mylib.so 100

[steven@sasd c++]$

-

3,运行时动态加载.so

-

动态库的运行时加载时通过一系列由动态链接器(dynamic linker)提供的API来实现的,这几个API的实现都是在libdl.so中的,且相关的声明和常量定义在头文件<dlfcn.h>中。

-

dlopen():主要用来打开一个.so,并将其加载到进程的地址空间,完成初始化过程,原型如下:

-

void *dlopen(const char *filename, int flag);

-

filenamethe dynamic library file named by the null-terminated string;对于路径设置为相对路径或绝对路径情形下,dlopen()是如何查找该动态库文件的,这个直接参考man

-

flag:用于表示函数符号的解析方式,如:RTLD_LAZY表示使用延迟绑定,只有在函数第一次被使用时才会去绑定,即PLT机制;相对应的为RTLD_NOW表示当模块被加载时即完成所有的函数绑定工作,如果有任何未定义的符号引用的绑定工作无法完成,dlopen都直接返回错误;其他的flag标识可以直接参考man

-

返回值是被加载的模块的句柄,该句柄会被其他的几个API使用到,如果加载失败,则返回NULL

-

dlsym():通过此函数来找到所需要的符号,定义如下:

-

void *dlsym(void *handle, const char *symbol);

-

第一个参数handle就是dlopen返回的句柄,第二个参数symbol表示要查找的符号的名字,一个以‘\0’ 结尾的字符串;

如果dlsym找到了相应的符号,则返回该符号的值;如果没有找到,则返回NULL

对于不同类型的符号,返回的指针意义不同:如果为函数,则为函数地址;如果为变量,则为变量的地址;如果为常量,则应该为常量的值;

-

dlerror():在调用dlopendlsymdlclose之后,都可以通过dlerror()来判断上一次调用是否成功,dlerror的返回值为char*,如果返回NULL,则表示调用成功;否则返回对应的错误消息;函数原型声明如下:

-

char *dlerror(void);

-

dlclose():卸载一个已经加载的模块。

-

在系统内部会维持一个加载引用计数器,每次调用dlopen()来加载模块时,相应的计数器加1;每次调用dlclose()卸载某模块时,相应计数器减1。当计数器等于0时,该模块才会真正被卸载掉。

-

下面直接上示例:

-

// ex_dload_so.cpp
#include <iostream>
using namespace std;
#include <dlfcn.h>
int main()
{
  // 为上面生成的.so文件的绝对路径
  const char *MYLIB_PATH = "/home/steven/test/c++/mylib.so";
                                                            
  // 加载指定的 .so 文件
  void *handle = dlopen(MYLIB_PATH, RTLD_NOW);
  if (handle == NULL)
  {
    cerr << "Open library " << MYLIB_PATH << " error: " << dlerror() << endl;
    return -1;
  }
                                                            
  // 查找函数foobar,并返回函数指针
  void (*fn_foobar)(int);
  fn_foobar = (void (*)(int))dlsym(handle, "foobar");
  char *error = NULL;
  if ((error = dlerror()) != NULL)
  {
    cerr << "Symbol foobar not found: " << error << endl;
    dlclose(handle);
    return -2;
  }
                                                            
  // 调用对应的foobar函数打印输出
  fn_foobar(123);
                                                            
  dlclose(handle);
  return 0;
}

-

编译运行效果:

-

[steven@sasd c++]$ g++-ldl -o ex_dload_so ex_dload_so.cpp

[steven@sasd c++]$ ./ex_dload_so

Printing from mylib.so 123

[steven@sasdc++]$

-

4,使用c++创建和使用含有class.so,采用多态机制实现接口发布,示例摘自《C++ dlopen mini HOWTO

-

1)生成动态链接库triangle.so文件

-

// polygon.h
#ifndef __POLYGON_H__
#define __POLYGON_H__
class Polygon
{
protected:
  double m_side_len;
public:
  Polygon() : m_side_len(0) {}
  virtual ~Polygon() {}
  void set_side_len(double side_len)
  {
    m_side_len = side_len;
  }
                                      
  virtual double area() const = 0;
};
typedef Polygon* create_t();
typedef void destroy_t(Polygon*);
#endif /* __POLYGON_H__ */

-

// triangle.cpp
#include "polygon.h"
#include <cmath>
class Triangle: public Polygon
{
public:
  virtual double area() const
  {
    return m_side_len * m_side_len * sqrt(3) / 2;
  }
};
extern "C" Polygon* create()
{
  return new Triangle;
}
extern "C" void destroy(Polygon *p)
{
  delete p;
}

-

编译生成 triangle.so

-

[steven@sasd c++]$ g++ -fPIC -shared -o triangle.so triangle.cpp

-

2) 使用该动态链接库

-

// ex_dload_cppso.cpp
#include <iostream>
#include <string>
#include <dlfcn.h>
using namespace std;
#include "polygon.h"
int main()
{
  // load triangle library
  void *triangle = dlopen("./triangle.so", RTLD_LAZY);
  if (!triangle)
  {
    cerr << "Can not load library: " << dlerror() << endl;
    return -1;
  }
         
  // reset errors
  dlerror();
         
  // load the symbols "create"
  create_t *create_triangle = (create_t*) dlsym(triangle, "create");
  const char *dlsym_err = dlerror();
  if (dlsym_err)
  {
    cerr << "Can not load symbol create: " << dlsym_err << endl;
    return -2;
  }
         
  // load the symbols "destroy"
  destroy_t* destroy_triangle = (destroy_t*) dlsym(triangle, "destroy");
  dlsym_err = dlerror();
  if (dlsym_err)
  {
    cerr << "Can not load symbol destroy: " << dlsym_err << endl;
    return -2;
  }
         
  // create an instance of class <Trangle>
  Polygon *poly = create_triangle();
         
  // set side length of <Triagle>
  poly->set_side_len(8);
  cout << "The area is " << poly->area() << endl;
         
  // destroy the class
  destroy_triangle(poly);
         
  // unload the triangle.so library
  dlclose(triangle);
         
  return 0;
}

-

编译运行:

-

[steven@sasd c++]$ g++ -ldl -o ex_dload_cppso ex_dload_cppso.cpp

[steven@sasd c++]$ ./ex_dload_cppso

The area is 55.4256

[steven@sasd c++]$