分享
 
 
 

为Linux 应用程序编写 DLL

王朝system·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

插件和 DLL 通常是用来无须编写整个新应用程序而添加功能的极好方法。在 Linux 中,插件和 DLL 以动态库形式实现。电子商务顾问兼设计师 Allen Wilson 介绍了动态库并且向您演示了如何在某个应用程序正在运行之后使用动态库来改变该应用程序。

Internet 浏览器用户非常熟悉插件的概念。从 Web 上下载插件,通常这些插件为浏览器的音频、视频以及特殊效果提供增强支持。一般来讲,在不更改原有应用程序的情况下,插件为现有应用程序提供新功能。

DLL 是程序函数,它们在设计和构建应用程序时为该程序所知。设计应用程序的主程序时使用程序框架或底板,这些程序框架或底板在运行时选择性地装入所需的 dll,这些 dll 位于磁盘上同主程序分离的一些文件中。这一打包和动态装入提供了灵活的升级、维护、以及许可策略。

随 Linux 一起交付的还有几千条命令和应用程序,它们至少都需要 libc 库函数。如果 libc 函数与每一个应用程序都打包在一起,那么磁盘上将会出现几千个相同函数的副本。Linux 构建这些应用程序,以使用通常所需的系统库的单个系统级副本,而不浪费磁盘空间。Linux 甚至做得更好,每个需要公共系统库函数的进程使用单个的系统级内的副本,一次性将该副本装入到内存并为各进程所共享。

在 Linux 中,插件和 dll 以动态库形式实现。本文的余下部分是在应用程序运行之后使用动态库更改该应用程序的示例。

Linux 动态链接

Linux 中的应用程序以以下两种方式之一链接到外部函数:要么在构建时与静态库(lib*.a)静态地链接,并且将库代码包含在该应用程序的可执行文件里;要么在运行时与共享库(lib*.so)动态地链接。通过动态链接装入器,将动态库映射进应用程序的可执行内存中。在启动应用程序之前,动态链接装入器将所需的共享目标库映射到应用程序的内存,或者使用系统共享的目标并为应用程序解析所需的外部引用。现在应用程序就可以运行了。

作为示例,下面有一个演示 Linux 中对动态链接库的缺省使用的小程序:

main()

{

printf("Hello world

");

}

当使用 gcc 编译 hello.c 时,就创建了一个名为 a.out 的可执行文件。通过使用 Linux 命令 ldd a.out(该命令打印出共享库的相互依赖性),可以看出所需的共享库是:

libc.so.6 => /lib/libc.so.6 (0x4001d000)

/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

使用相同的动态链接装入器在应用程序运行之后将 dll 映射进应用程序的内存。通过使用 Linux 动态装入器例程,应用程序控制装入哪一个动态库以及调用库中的哪一个函数,以执行装入和链接以及返回所需入口点的地址。

Linux dll 函数

Linux 提供 4 个库函数(dlopen, dlerror, dlsym 和 dlclose),一个 include 文件(dlfcn.h)以及两个共享库(静态库 libdl.a 和动态库 libdl.so),以支持动态链接装入器。这些库函数是:

dlopen 将共享目标文件打开并且映射到内存中,并且返回句柄

dlsym 返回一个指向被请求入口点的指针

dlerror 返回 NULL 或者一个指向描述最近错误的 ASCII 字符串的指针

dlclose 关闭句柄并且取消共享目标文件的映射

动态链接装入器例程 dlopen 需要在文件系统中查找共享目标文件以打开文件并创建句柄。有 4 种方式用以指定文件的位置:

dlopen call 中的绝对文件路径

在 LD_LIBRARY_PATH 环境变量中指定的目录中

在 /etc/ld.so.cache 中指定的库列表之中

先在 /usr/lib 之中,然后在 /lib 之中

dll 示例:小的 C 程序和 dlTest

动态链接装入器示例程序是一个小的 C 程序,该程序被设计用来练习 dl 例程。该程序基于每个人都编写过的一个 C 程序,它将“Hello World”打印到控制台上。最初打印的消息是“HeLlO WoRlD”。该测试程序链接到再次打印该消息的两个函数上:第一次都用大写字符,第二次都用小写字符。

以下是该程序的概要:

定义 dll include 文件 dlfcn.h 和所需的变量。 至少需要这些变量:

到共享库文件的句柄

指向被映射函数入口点的指针

指向错误字符串的指针

打印初始消息,“HeLlO WoRlD”。

使用绝对路径“/home/dlTest/UPPERCASE.so”和选项 RTLD_LAZY,dlopen 打开 UPPERCASE dll 的共享目标文件并返回句柄。

选项 RTLD_LAZY 推迟解析 dll 的外部引用,直到 dll 被执行。

选项 RTLD_NOW 在 dlopen 返回之前解析所有的外部引用。

dlsym 返回入口点 printUPPERCASE 的地址。

调用 printUPPERCASE 并且打印修改过的消息“HELLO WORLD”。

dlclose 关闭到 UPPERCASE.so 的句柄,并且从内存中取消 dll 映射。

dlopen 使用基于环境变量 LD_LIBRARY_PATH 的相对路径查找共享目标路径,来打开 lowercase dll 的共享目标文件 lowercase.so,并且返回句柄。

dlsym 返回入口点 printLowercase 的地址。

调用 printLowercase 并且打印修改过的信息“hello world”。

dlclose 关闭到 lowercase.so 的句柄,并且从内存中取消 dll 映射。

注意,每次调用 dlopen、dlsym 或 dlclose 之后,调用 dlerror 以获取最后的错误信息,并且打印该错误信息字符串。以下是 dlTest 的测试运行:

dlTest 2-Original message

HeLlO WoRlD

dlTest 3-Open Library with absolute path return-(null)-

dlTest 4-Find symbol printUPPERCASE return-(null)-

HELLO WORLD

dlTest 5-printUPPERCASE return-(null)-

dlTest 6-Close handle return-(null)-

dlTest 7-Open Library with relative path return-(null)-

dlTest 8-Find symbol printLowercase return-(null)-

hello world

dlTest 9-printLowercase return-(null)-

dlTest 10-Close handle return-(null)-

完整的 dlTest.c、UPPERCASE.c 和 lowercase.c 源代码清单在本文后面的清单里。

构建 dlTest

启用运行时动态链接需要三步:

将 dll 编译为位置无关代码

创建 dll 共享目标文件

编译主程序并同 dl 库相链接

编译 UPPERCASE.c 和 lowercase.c 的 gcc 命令包含 -fpic 选项。选项 -fpic 和 -fPIC 导致生成的代码是位置无关的,重建共享目标库需要位置无关。-fPIC 选项产生位置无关的代码,这类代码支持大偏移。用于 UPPERCASE.o 和 lowercase.o 的第二个 gcc 命令,带有 -shared 选项,该选项产生适合于动态链接的共享目标文件 a*.so。

用于编译和执行 dltest 的 ksh 脚本如下:

#!/bin/ksh

# Build shared library

#

#set -x

clear

#

# Shared library for dlopen absolute path test

#

if [ -f UPPERCASE.o ]; then rm UPPERCASE.o

fi

gcc -c -fpic UPPERCASE.c

if [ -f UPPERCASE.so ]; then rm UPPERCASE.so

fi

gcc -shared -lc -o UPPERCASE.so UPPERCASE.o

#

# Shared library for dlopen relative path test

#

export LD_LIBRARY_PATH=`pwd`

if [ -f lowercase.o ]; then rm lowercase.o

fi

gcc -c -fpic lowercase.c

if [ -f lowercase.so ]; then rm lowercase.so

fi

gcc -shared -lc -o lowercase.so lowercase.o

#

# Rebuild test program

#

if [ -f dlTest ]; then rm dlTest

fi

gcc -o dlTest dlTest.c -ldl

echo Current LD_LIBRARY_PATH=$LD_LIBRARY_PATH

dlTest

结束语

创建能在运行时被动态链接到 Linux 系统上的应用程序的共享目标代码是一项非常简单的练习。应用程序通过使用对动态链接装入器的 dlopen、dlsym 和 dlclose 函数调用来获取对共享目标文件的访问。dlerror 以字符串的形式返回任何错误,这些错误信息字符串描述 dl 函数碰到的最后一个错误。在运行时,主应用程序使用绝对路径或相对于 LD_LIBRARY_PATH 的相对路径找到共享目标库,并且请求所需的 dll 入口点的地址。当需要时,也可对 dll 进行间接函数调用,最后,关闭到共享目标文件的句柄,并且从内存中取消该目标文件映射,使之不可用。

使用附加选项 -fpic 或 -fPIC 编译共享目标代码,以产生位置无关的代码,使用 -shared 选项将目标代码放进共享目标库中。

Linux 中的共享目标代码库和动态链接装入器向应用程序提供了额外的功能。减少了磁盘上和内存里的可执行文件的大小。可以在需要时,装入可选的应用程序功能,可以在无须重新构建整个应用程序的情况下修正缺陷,并且应用程序可以包含第三方的插件。

清单(应用程序和 dll)

dlTest.c:

/*************************************************************/

/* Test Linux Dynamic Function Loading */

/* */

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

/* Opens dynamic library and return handle */

/* */

/* const char *dlerror(void) */

/* Returns string describing the last error. */

/* */

/* void *dlsym(void *handle, char *symbol) */

/* Return pointer to symbol's load point. */

/* If symbol is undefined, NULL is returned. */

/* */

/* int dlclose (void *handle) */

/* Close the dynamic library handle. */

/* */

/* */

/* */

/*************************************************************/

#include

#include

/* */

/* 1-dll include file and variables */

/* */

#include

void *FunctionLib; /* Handle to shared lib file */

int (*Function)(); /* Pointer to loaded routine */

const char *dlError; /* Pointer to error string */

main( argc, argv )

{

int rc; /* return codes */

char HelloMessage[] = "HeLlO WoRlD\n";

/* */

/* 2-print the original message */

/* */

printf(" dlTest 2-Original message \n");

printf("%s", HelloMessage);

/* */

/* 3-Open Dynamic Loadable Libary with absolute path */

/* */

FunctionLib = dlopen("/home/dlTest/UPPERCASE.so",RTLD_LAZY);

dlError = dlerror();

printf(" dlTest 3-Open Library with absolute path return-%s- \n", dlError);

if( dlError ) exit(1);

/* */

/* 4-Find the first loaded function */

/* */

Function = dlsym( FunctionLib, "printUPPERCASE");

dlError = dlerror();

printf(" dlTest 4-Find symbol printUPPERCASE return-%s- \n", dlError);

if( dlError ) exit(1);

/* */

/* 5-Execute the first loaded function */

/* */

rc = (*Function)( HelloMessage );

printf(" dlTest 5-printUPPERCASE return-%s- \n", dlError);

/* */

/* 6-Close the shared library handle */

/* Note: after the dlclose, "printUPPERCASE" is not loaded */

/* */

rc = dlclose(FunctionLib);

dlError = dlerror();

printf(" dlTest 6-Close handle return-%s-\n",dlError);

if( rc ) exit(1);

/* */

/* 7-Open Dynamic Loadable Libary using LD_LIBRARY path */

/* */

FunctionLib = dlopen("lowercase.so",RTLD_LAZY);

dlError = dlerror();

printf(" dlTest 7-Open Library with relative path return-%s- \n", dlError);

if( dlError ) exit(1);

/* */

/* 8-Find the second loaded function */

/* */

Function = dlsym( FunctionLib, "printLowercase");

dlError = dlerror();

printf(" dlTest 8-Find symbol printLowercase return-%s- \n", dlError);

if( dlError ) exit(1);

/* */

/* 8-execute the second loaded function */

/* */

rc = (*Function)( HelloMessage );

printf(" dlTest 9-printLowercase return-%s- \n", dlError);

/* */

/* 10-Close the shared library handle */

/* */

rc = dlclose(FunctionLib);

dlError = dlerror();

printf(" dlTest 10-Close handle return-%s-\n",dlError);

if( rc ) exit(1);

return(0);

}

UPPERCASE.c:

/************************************************/

/* Function to print input string as UPPER case. */

/* Returns 1. */

/*********************************************** */

int printUPPERCASE ( inLine )

char inLine[];

{

char UPstring[256];

char *inptr, *outptr;

inptr = inLine;

outptr = UPstring;

while ( *inptr != '\0' )

*outptr++ = toupper(*inptr++);

*outptr++ = '\0';

printf(UPstring);

return(1);

}

lowercase.c

/********************************************/

/* Function to print input string as lower case. */

/* Returns 2. */

/******************************************* */

int printLowercase( inLine )

char inLine[];

{

char lowstring[256];

char *inptr, *outptr;

inptr = inLine;

outptr = lowstring;

while ( *inptr != '' )

*outptr++ = tolower(*inptr++);

*outptr++ = '';

printf(lowstring);

return(2);

}

参考资料

在 IBM 的 Linux 应用程序工具 zSeries 和 S/390 系统版的列表中,将会发现许多有用的工具箱和库。

下载 IBM developer kit for Linux.

同样在 developerWorks 上,请阅读相关文章 "shared objects for the object disoriented!".

在 developerWorks 上浏览更多 Linux 参考资料。

在 developerWorks 上浏览更多开放[url=http://www.pccode.net].net" class="wordstyle"

[1] [2] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有