动态库显式运行时链接

1. Windows

Microsoft Visual C++ 编译器提供了一系列的 C/C++ 的扩展来指定符号的导入导出。

__declspec(dllexport) 表示该符号是从本 DLL 导出。

__declspec(dllimport) 表示该符号是从其他 DLL 导入。

在 C++ 中,如果希望导出或者导入的符号符合 C 语言的符号修饰规范,那么必须在这个符号的定义之前加上 extern "C",以防止 C++ 编译器进行符号修饰。

1.1 编译 DLL 

> VS2010: 文件 -> 新建 -> 项目-> Win32 控制台应用程序 -> 应用程序类型:DLL;附加选项:空项目

// Math.c ->  Math.dll
__declspec(dllexport) double Add(double a, double b)
{
	return a + b;
}

__declspec(dllexport) double Sub(double a, double b)
{
	return a - b;
}

__declspec(dllexport) double Mul(double a, double b)
{
	return a * b;
}

1.2 DLL 显式运行时链接

  • LoadLibrary,这个函数用来装载一个 DLL 到进程的地址空间。

  • GetProcAddress,用来查找某个符号的地址。

  • FreeLibrary,用来卸载某个已加载的某块。

#include <Windows.h>
#include <stdio.h>

typedef double (*Func)(double, double);

int main(int argc, char *argv[])
{
	Func function;
	double result;

	HINSTANCE hinstLib = LoadLibrary(L"Math.dll");
	if (hinstLib == NULL) {
		printf("ERROR: unable to load DLL\n");
		return 1;
	}

	function = (Func)GetProcAddress(hinstLib, "Add");
	if (function == NULL) {
		printf("ERROR: unable to find DLL function\n");
		FreeLibrary(hinstLib);
		return 1;
	}

	result = function(1.0, 2.0);
	FreeLibrary(hinstLib);

	printf("Result = %f\n", result);

	return 0;
}

2. Linux

2.1 编译生成动态库

// Math.c -> Math.so
double Add(double a, double b)
{
	return a + b;
}

double Sub(double a, double b)
{
	return a - b;
}

double Mul(double a, double b)
{
	return a * b;
}

使用如下命令,可以编译生成动态库:

$ gcc -fPIC -shared -o Math.so Math.c

2.2 运行时装载动态库

  • dlopen() 打开一个动态库,并将其加载到进程的地址空间,完成初始化过程。

  • dlsym() 查询指定的符号。

  • dlerror() 判断 dlopen()、dlsym()、dlclose() 上一次调用是否出错,获得出错信息。

  • dlclose() 卸载模块。

#include <stdio.h>
#include <dlfcn.h>

typedef double (*Func)(double, double);

int main(int argc, char *argv[])
{
	void *handle;
	char *error;
	Func func;
	double result;

	handle = dlopen("./Math.so", RTLD_NOW);
	if (handle == NULL) {
		printf("ERROR: unable to load .so\n");
		return 1;
	}

	func = dlsym(handle, "Add");
	if ( (error = dlerror()) != NULL) {
		printf("Symbol Add not found: %s\n", error);
		dlclose(handle);
		return 2;
	}

	result = func(1.0, 2.0);
	printf("Result = %f\n", result);

	dlclose(handle);

	return 0;
}

编译,执行

$ gcc -o main main.c -ldl
$ ./main

PS

在 Linux 环境下,查看函数信息

man 函数名

---

参考

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


你可能感兴趣的:(动态链接)