zoukankan      html  css  js  c++  java
  • 程序员的自我修养——第八、九章——windows下的动态链接

    第八章不是很感兴趣,直接跳到第九章。

      DLL即动态链接库(Dynamic Link Library)的缩写.

      一个DLL中有两个数据段,一个进程间共享,另一个私有.

     

          当我们使用“__declspec(dllexport)”时表示该符号是从本DLL导出的符号。“__declspec(dllimport)”表示该符号是从别的DLL导入的符号。

    创建DLL:

    /*math.c*/

    __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;

    }

    使用cl编译器进行编译:

    cl   /LDd  Math.c

     

    参数/LDd表示产生Debug版的DLL,不加任何参数则表示产生EXE可执行文件,可以使用/LD来编译生成Release版的DLL。

    通过dumpbin 查看DLL的导出符号:

    D:\Program Files\home\*>dumpbin /EXPORTS Math.dll

    Microsoft (R) COFF/PE Dumper Version 11.00.50214.1

    Copyright (C) Microsoft Corporation.  All rights reserved.

    Dump of file Math.dll

    File Type: DLL

      Section contains the following exports for math.dll

        00000000 characteristics

        4F878A19 time date stamp Fri Apr 13 10:06:17 2012

            0.00 version

               1 ordinal base

               3 number of functions

               3 number of names

        ordinal hint RVA      name  //三个导出函数即相对地址

              1    0 00001000 Add

              2    1 00001040 Mul

              3    2 00001020 Sub

      Summary

            3000 .data

            9000 .rdata

            3000 .reloc

           1E000 .text

     

    使用DLL:

    /*test_math.c*/

    #include <stdio.h>

    __declspec(dllexport) double Sub(double a, double b);

    int main(int argc, char *argv[])

    {

    double result=Sub(3.0, 2.0);

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

    return 0;

    }

    编译:

    D:\Program Files\home\*>cl    /c test_math.c

    D:\Program Files\home\*> link  test_math.obj  math.lib

    声明DLL中的某个函数为导出函数的办法有两种,使用“__declspec(dllexport)”扩展;另一种就是采用模块定义(.def)文件声明。

    创建math.def文件:

    /*math.def*/

    LIBRARY Math

    EXPORTS

    Add

    Sub

    Mul

    Div

    编译:

    D:\Program Files\home\*>cl  math.c /LD  /DEF  math.def

     

    最终输出跟第一种方法相同的结果。

    DLL支持运行时链接,Windows提供了3个API为:

    ·LoadLibrary,这个函数用来装载一个DLL到进程的地址空间,它的功能跟dlopen类似。

    ·GetProcAddress, 用来查找某个符号的地址,与dlsym类似

    ·FreeLibrary, 用来卸载某个已加载的模块,与dlclose类似

     

    /*RunDllSimple.c*/

    #include <windows.h>

    #include <stdio.h>

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

    int main(int argc, char *argv[])

    {

      Func function;

      double result;

      HINSTANCE hinstLib=LoadLibrary("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;

    }

    D:\Program Files\home\*>cl RunDllSimple.c

    D:\Program Files\home\*>RunDllSimple.exe

    result = 3.000000

     

    符号导出导入表:

          当一个PE需要将一些函数或变量提供给其他PE文件使用时,我们把这种行为叫做符号导出。导出表提供了一个符号名与符号地址的映射关系。

          在ELF中,“.rel.dyn”和“.rel.plt”两个段中分别保存了该模块所需要导入的变量和函数的符号以及所在的模块等信息。而“.got”和“.got.plt”则保存着这些变量和函数的真正地址。

    查看依赖了哪些DLL:

    D:\Program Files\home\*>dumpbin    /IMPORTS   math.dll

    我们的math.dll导入了KERNEL32.dll

    在PE文件中,导入表是一个IMAGE_IMPORT_DESCRIPTOR的结构数组,每个IMAGE_IMPORT_DESCRIPTOR结构对应一个被导入的DLL。

    EXE文件的基地址默认为0x00400000,而DLL文件基地址默认为0x10000000

     

    DLL绑定:把导出函数的地址保存到模块的导入表中,可以省去每次启动时符号解析的过程。

    DLL绑定的方法:

    D:\Program Files\home\*>editbin   /BIND   test_math.exe

    Microsoft (R) COFF/PE Editor Version 11.00.50214.1

    Copyright (C) Microsoft Corporation.  All rights reserved.

    D:\Program Files\home\*>dumpbin  /IMPORTS   test_math.exe

    Microsoft (R) COFF/PE Dumper Version 11.00.50214.1

    Copyright (C) Microsoft Corporation.  All rights reserved.

    Dump of file test_math.exe

    File Type: EXECUTABLE IMAGE

      Section contains the following imports:

        math.dll

                    40C100 Import Address Table

                    40DF30 Import Name Table

                  FFFFFFFF time date stamp

                  FFFFFFFF Index of first forwarder reference

          10001020      2 Sub

        KERNEL32.dll

                    40C000 Import Address Table

                    40DE30 Import Name Table

                  FFFFFFFF time date stamp

                  FFFFFFFF Index of first forwarder reference

          77E330E2    540 SetUnhandledExceptionFilter

          77E30AFD     D0 CreateFileW

          77E3296C    1DA GetCommandLineA

    ….

    ….

    DLL绑定实现,editbin对被绑定的程序的导入符号进行遍历查找,找到以后就把符号的运行时的目标地址写入到被绑定程序的导入表内。

     

    c++与动态链接

     

    指导意见:

    (1)所有接口函数都应该是抽象的。所有的方法都应该是纯虚的。(或者inline的方法也可以)

    (2)所有的全局函数都应该使用extern “C”来防止名字修饰的不兼容。并且导出函数的都应该是__stdcall调用规范(COM的DLL都使用这样的规范)。这样即使用户本身的程序默认以__cdecl方式编译的,对于DLL的调用也能正确。

    (3)不要使用C++标准库STL

    (4)不要使用异常

    (5)不要使用虚析构函数。可以创建一个destroy()方法并且重载delete操作符并且调用destroy()

    (6)不要在DLL里面申请内存。而且在DLL外释放(或者相反)。不同的DLL和可执行文件可能使用不同的堆,在一个堆里面申请内存而在另外一个堆里面释放会导致错误。比如,对于内存分配相关的函数不应该是inline的,以防止它在编译时被展开到不同的DLL和可执行文件

    (7)不要在接口中使用重载方法。因为不同的编译器对于vtable的安排可能不同。

     

    3. DLL HELL(dll噩梦)

     

    预防方法:

    静态链接

    防止DLL覆盖(windows文件保护实现)

    避免DLL冲突(让每个应用程序拥有自己依赖的DLL)

    .Net下DLL Hell的解决方案(Manifest,XML描述文件,(side-by-side manager)SxS Manager实现对相应版本的DLL的加载)

     

  • 相关阅读:
    bzoj3884: 上帝与集合的正确用法(数论)
    洛谷10月月赛R2·浴谷八连测R3题解
    bzoj5055: 膜法师(BIT)
    bzoj2213: [Poi2011]Difference(思维题)
    bzoj1016: [JSOI2008]最小生成树计数(kruskal+dfs)
    一模 (2) day2
    一模 (2) day1
    Prime Palindromes
    常州培训 day5 解题报告
    一模 (1) day2
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2496658.html
Copyright © 2011-2022 走看看