Google

星期三, 一月 23, 2008

PE Format(19)

7.输入符号(importedsymbols)

  ------------------------------

  当编译器发现一个对别的可执行文件(大多数是DLL文件)中的函数调用时,在最简单化的情况下,它会对此情况一无所知,只是简单地输出一个对那个符号的正常调用指令。链接器不得不修正那个符号的地址,就象它为任何其它的外部符号所做的那样。

  链接器使用一个输入库来查找从哪个DLL文件输入了哪个符号,并为所有的输入符号都建立存根,每个存根包含一个跳转指令;存根就是实际的调用目标。这些跳转指令实际上将跳往从所谓的输入地址表中提取的一个地址。在更复杂的应用程序(使用“__declspec(dllimport)”时)中,编译器会知道函数是输入的,并直接输出一个位于输入地址表中的地址的调用,绕过跳转。

  不管怎样,DLL文件中的函数地址总是必要的,并将于应用程序载入时,由加载器从输出DLL文件的输出目录中提供。加载器知道哪个库中的哪些符号需要被查找以及哪些地址需要通过搜索输入目录来修正。

  我最好给你一个例子。有或无__declspec(dllimport)的调用如下所示:

  源文件:
    intsymbol(char*);
     __declspec(dllimport)intsymbol2(char*);
     voidfoo(void)
     {
       inti=symbol("bar");
       intj=symbol2("baz");
     }

   汇编:
    ...
     call_symbol        ;没有declspec(dllimport)的
     ...
     call[__imp__symbol2]    ;含有declspec(dllimport)的
     ...

  在第一种(没有__declspec(dllimport))情况下,编译器不知道“_symbol”位于一个DLL文件中,因此链接器必须要提供 “_symbol”函数。因为此函数不存在,它就为输入符号提供一个存根函数,即一个间接跳转。所有输入存根的集合被称为“转移区”(有时也叫做“跳板”,因为你跳到那里的目的是为了跳到别的地方)。

  典型地,此转移区位于代码节中(它不是输入目录的一部分)。每一个函数存根都是一个跳往DLL文件中的实际函数的跳转。转移区的形式象这样:
  _symbol:    jmp [__imp__symbol]
   _other_symbol: jmp [__imp__other__symbol]
   ...

  这意味着:如果你不指定“__declspec(dllimport)”来使用输入符号,那么链接器将会为它们产生一个由间接跳转所组成的转移区。如果你真指定了“__declspec(dllimport)”,那么编译器就会自己做间接(跳转),转移区也就不需要了。(这也意味着:如果你输入的是变量或其它东西,你就必须指定“__declspec(dllimport)”,因为一个具有jmp指令的存根只合适于函数。)

标签: , ,

0 条评论:

发表评论

<< 主页

辽ICP备05003652号
流风洄雪听天籁,轻云蔽日看落花

Powered by Blogger