免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 792 | 回复: 0
打印 上一主题 下一主题

为 Linux 应用程序编写 DLL [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-17 15:02 |只看该作者 |倒序浏览
插件和 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);
    }

       
                   
                   
                   

    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/17928/showart_2054576.html
  • 您需要登录后才可以回帖 登录 | 注册

    本版积分规则 发表回复

      

    北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年举报专区
    中国互联网协会会员  联系我们:huangweiwei@itpub.net
    感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP