【发布时间】:2022-11-29 04:39:40
【问题描述】:
我正在使用一个古老而复杂的系统,该系统在数十个(有时数百个)Win32 进程之间共享内存。代码主要是几年前移植到 Delphi 的非常古老的 Pascal。
(几乎)所有代码都在一个 DLL 中,所有进程都加载它。目前,我们已强制该 DLL 的固定加载地址。 Image base 已定义,并且在链接器设置中禁用了 ASLR。每个进程在启动时都会检查 DLL 加载地址,如果 DLL 不能在所有进程中加载到完全相同的地址,则整个系统将拒绝工作。这当然是一个有问题的解决方案。有时,客户拥有各种影响地址空间并阻止我们的产品获得 DLL 所需地址的第 3 方小工具。
固定DLL加载地址的原因如下。我想知道是否有办法解决这个问题。
我一直在尝试介绍面向对象的编程。问题是,如果我在共享内存中实例化一个 Delphi 类,该实例现在似乎依赖于 DLL 加载地址。例如,如果另一个进程试图销毁那个对象,它将崩溃,除非这两个进程恰好具有相同的 DLL 地址。 Delphi 运行时似乎将函数地址保存在对象实例中,假设它们在对象的生命周期内保持不变。
一种可能的解决方案可能是将 DLL 内容复制到共享内存,然后对 DLL_PROCESS_ATTACH 进行某种魔术操作,使进程运行该代码副本而不是加载的 DLL 地址。我们拥有的共享内存总是映射到相同的地址。 (是的,这有时也是一个问题,但很少见,因为共享内存可以映射到容易获得的高地址(2 GB 以上)。)
或者有没有办法告诉 Delphi 编译器“请不要假设与此类相关的函数的地址是固定的”?我正在使用 Delphi 11.1。
【问题讨论】:
-
回答你的最后一个问题:Delphi 编译器没有这样的选项。不过,我不确定如何解决您的一般问题,可能是因为我仍然不太了解该程序中发生的事情。
-
您的解决方案问题是DCOM。您将不得不评估恢复多年前做出的错误决定的难度。
-
你不能分享对象跨进程边界,只有数据.这是一个非常糟糕的设计,需要重写。话虽这么说,而不是为 DLL 假设任何特定的加载地址,只需让 DLL 分配一个块共享内存在运行时,多个 DLL 实例可以共享,然后根据需要委托出该内存块的一部分。如果需要,创建一个自定义内存管理器来处理它。
-
第二种解决方案是使用像 Microsoft Detours 这样的工具来拦截对 DLL 的调用,并将它们重定向到另一个进程中的正确地址。这是一个更复杂的解决方案,但它可以让您保留现有代码。
-
附带说明一下,这种架构有几个历史原因: 1. 该软件已有将近 40 年的历史,最初构建它的操作系统没有线程。并行运行事物的唯一方法是运行多个进程。 2.代码是32位的,不能轻易翻译成64位。通过运行一堆进程,您可以使用大约 2 GB 的共享内存,外加每个进程的健康本地内存块 - 总共有效使用远远超过 4 GB。 3、DCOM自带的开销很大,不能在系统内部使用。
标签: delphi winapi dll shared-memory