4

最近我反汇编了一个 DLL(用 c/c++ 编写),发现代码段中有很多“跳转存根”。这些存根除了跳转到 DLL 中的函数之外什么都不做。

例如:

jmp foo() 
jmp foo2()
...

为什么编译器(Visual Studio 2012)在二进制文件中包含这些函数存根?

谢谢!

4

3 回答 3

6

在所有存根之后是否有一大堆 0xCC 字节?如果是这样,您正在查看已在启用增量链接的情况下编译的代码(调试版本的默认设置)。

在为增量链接进行编译时,编译器为每个函数创建一个存根,并确保所有调用都通过存根进行。如果需要用更新的代码替换函数,可以在末尾添加新代码,只需修补跳转 thunk - 所有现有调用都将重定向到新代码。额外的 CC 保留用于更多存根,以防添加新功能。

有关更多背景信息,请参阅 MSDN

于 2013-06-06T10:57:57.027 回答
3

这就是链接器和 DLL 的符号“混合在一起”的方式。它保证在符号表中使用正确的偏移量,可以由加载 DLL 的加载器解析(并因此更新 DLL 中函数的地址),并且编译后的代码仍然可以处理例如函数指针:

void (*fptr)() = foo;

如果foo只是对 DLL 中某个位置的引用,那么如何解析此地址将取决于加载程序。解决这个问题要比解决“这里有一个 foo() 入口点,让你找到真正的 foo”的问题要复杂得多。

于 2013-06-06T09:09:40.063 回答
1

DLL 是可重定位的,这意味着它们可能最终位于内存中的任何位置。这意味着对它们的所有调用都必须重写。通过将所有这些调用放在一个小的跳转表中,在重定位的情况下只需要重写一页。这很重要,因为可以跨进程共享未更改的代码页面,但每个进程都有自己的已修改页面的副本。

于 2013-06-06T12:19:52.757 回答