4

我正在使用 NASM 在 x86 程序集中构建一个 COM 对象。我非常了解 COM,我也非常了解 x86 程序集,但是让两者相互融合让我挂断了……(顺便说一句,如果你想劝阻我不要使用 x86 程序集,请不要,我有非常特殊的原因为什么要在 x86 程序集中构建它!)

我正在尝试构建一个 vtable 以在我的 COM 对象中使用,但我不断收到奇怪的指针,而不是指向我的函数的实际指针。(我在想我得到了相对偏移量,或者 NASM 在其中嵌入了临时值,并且在链接期间它们没有被实际值替换)

我目前正在尝试构建的接口是IClassFactory接口,代码如下:

%define S_OK 0x00000000
%define E_NOINTERFACE 0x80004002

section .text

; All of these have very simple shells rather than implementations, but that is just until I can get the vtable worked out

ClassFactory_QueryInterface:
    mov eax, E_NOINTERFACE
    retn 12

ClassFactory_AddRef:
    mov eax, 1
    retn 4

ClassFactory_Release:
    mov eax, 1
    retn 4

ClassFactory_CreateInstance:
    mov eax, E_NOINTERFACE
    retn 16

ClassFactory_LockServer:
    mov eax, S_OK
    retn 8

global ClassFactory_vtable
ClassFactory_vtable dd ClassFactory_QueryInterface, ClassFactory_AddRef, ClassFactory_Release, ClassFactory_CreateInstance, ClassFactory_LockServer

global ClassFactory_object
ClassFactory_object dd ClassFactory_vtable

注意:这不是所有代码,我在不同的文件中有 DllGetClassObject、DllMain 等。

但是当我组装(使用 NASM: nasm -f win32 comobject.asm)和链接(使用 MS Link: link /dll /subsystem:windows /out:comobject.dll comobject.obj),并使用 OllyDbg 检查可执行文件时,vtable 会出现奇怪的值。例如,在我上次构建中,函数的实际地址如下:

  • 查询接口 - 0x00381012
  • AddRef - 0x0038101A
  • 发布 - 0x00381020
  • 创建实例 - 0x00381026
  • LockServer - 0x0038102E

但是 vtable 有这些值:

  • 查询接口 - 0x00F51012
  • AddRef - 0x00F5101A
  • 发布 - 0x00F51020
  • 创建实例 - 0x00F51026
  • 锁定服务器 - 0x00F5102E

这些值看起来非常可疑......几乎就像搬迁没有采取一样。此外,vtable 显示为 0x00F5104A,所有这些都是不可访问的内存地址。(出于信息目的,这些值每次都不同

我尝试使用 Visual Studio 2010 Express 在 C++ 中做同样的事情,一切都很好。所以我假设这只是我在装配中缺少的东西......


谁能向我指出为什么这些值没有正确显示?

4

3 回答 3

2

我必须道歉,问题原来是我自己的错......在所有构建这个东西的混战中,我已经/dll从链接器调用中删除了它,导致它被构建为一个 EXE,而不是一个 DLL......


让我为下一个遇到此问题的人更好地解释一下。

所有 Windows 可执行文件都有一个基地址,该基地址被假定为可执行文件将被加载到的虚拟地址。在大多数情况下,加载到正在运行的进程中的可执行文件不会加载到“首选”基地址,因为另一个 DLL(或应用程序本身)可能已经占用了该地址。出于这个原因,Windows PE 可执行文件使用所谓的重定位表。重定位表告诉 Windows 在重定位到新的基地址时需要重写可执行文件中的哪些位置。

然而,随着Virtual Memory的出现,大多数链接器将忽略 EXE 中的重定位表作为优化,因为可执行文件将始终在其基地址加载(除非它与保留的内核地址冲突,在这种情况下它将无法一起加载)。因此,因为我停止编译为 DLL,我的可执行文件没有获得重定位表,因此无法正确加载到正在运行的进程的地址空间中。


更新:

默认情况下,MSVC 仅在 DLL 项目中包含重定位表,如MSDN中所述:

By default, /FIXED:NO is the default when building a DLL, and /FIXED is the default for any other project type.

/FIXED:NO可以通过将开关提供给链接器来更改此行为。非 DLL 项目的默认设置是/FIXED告诉链接器目标具有固定的基地址并且不需要重定位表。

于 2010-08-02T05:55:38.333 回答
1

您是否尝试过在 C 中构建存根 COM 接口并反汇编结果?这应该给你一个线索,你的实现中出了什么问题。

于 2010-07-26T00:42:35.507 回答
1

您是否尝试过将您的全局变量也声明为导出?好久没做x86了。但是阅读 nasm 的文档似乎意味着您需要全局和导出才能使 DLL 重定位修复工作。

于 2010-07-27T11:25:07.530 回答