链接到不同版本的 .dll 的 .lib 可能会产生哪些副作用?

Posted

技术标签:

【中文标题】链接到不同版本的 .dll 的 .lib 可能会产生哪些副作用?【英文标题】:What are possible side-effects of linking against a .lib that is a different version of the .dll? 【发布时间】:2011-05-23 15:59:12 【问题描述】:

我正在链接一个较新的 .lib,但在我的应用程序中使用的是较旧的 .dll。这样做有什么可能的副作用?如果两个版本之间的函数原型相同,难道不应该一切正常吗?如果新版本更改了参数的默认值怎么办?该值是在 .lib 中还是在 .dll 中?

【问题讨论】:

【参考方案1】:

在 C++ 中,默认值是在调用站点编译的 - 因此 DLL 或 .lib 文件与此无关 - 更改标头将产生影响,而 ABI 没有变化。

如果导出函数中的 ABI 没有改变,您应该能够使用旧 DLL 和链接到新 .lib 的程序,只要该程序不是t 使用在新 .lib 中但不在旧 DLL 中的新导出。

影响 ABI 的事情(我并不是说这是一个完整的列表):

- calling convention
- export name
- parameter list (including types)

“libtool 版本控制系统”(http://www.gnu.org/s/libtool/manual/libtool.html#Versioning)是一种识别共享库兼容性的技术。

请注意,如果您没有使用 C 调用约定(即,导出名称将是“C++ 错误”),那么从技术上讲,您几乎无法控制导出的名称。


这里解释了一些 Windows 库(cygwin、pngdll)如何使用遵循 libtool 库版本控制技术的命名约定来管理向后兼容性。这是来自http://home.att.net/~perlspinr/libversioning.html 的网络存档 - 我在此处进行镜像:

几个定义:

入口点在外部 可访问的函数或变量 由 DLL 导出。界面是 所有这些导出的集合 给定的函数和变量 库的版本。关于 libPNG 版本的宏在 makefile.cygwin:

你只需要碰撞PNGDLL,如果 新的 dll 删除一个入口点 提供的旧dll。如果你添加一个新的 入口点,那么新的 dll 是一个 替换旧的, 因为新的提供了一切 旧的。

当然,针对 新版本,它使用了额外的 入口点,不适用于旧的 dll——但从来没有人承诺过 向前兼容,仅向后兼容 兼容性。这是cygwin的方式 DLL 版本控制工作:

1) 遵循 libtool 版本控制 方案从 http://www.gnu.org/software/libtool/manual.html#Versioning:

So, libtool library versions are described by three integers:
current
    The most recent interface number that this library implements.
revision
    The implementation number of the current interface.
age
    The difference between the newest and oldest interfaces that this

图书馆工具。换句话说, 该库实现了所有 接口编号范围为 当前数字 - 年龄到当前。

Updating libtool versioning:

   1.     Start with version information of 0:0:0 for each libtool

图书馆。

   2. Update the version information only immediately before a

公开 发布您的软件。不需要更频繁的更新, 和 只保证当前接口号变大 更快。

   3. If the library source code has changed at all since the last
      update, then increment revision (c:r:a becomes c:r+1:a).

   4. If any interfaces have been added, removed, or changed since the
      last update, increment current, and set revision to 0.

   5. If any interfaces have been added since the last public release,
      then increment age.

   6. If any interfaces have been removed since the last public
      release, then set age to 0. 


Never try to set the interface numbers so that they correspond to the
release number of your package. This is an abuse that only fosters
misunderstanding of the purpose of library versions. Instead, use the
-release flag (see Release numbers), but be warned that every

发布 您的软件包不会与任何其他版本二进制兼容。

2) On windows/cygwin, the DLLVER is 'c - a' (trust me, this is correct,

但它更容易解释 示例)。

所以,这里有一个例子:libtool 版本为 5:4:3,表示 第四次修订的实施 界面5,恰好是 向后兼容这三个 以前的接口定义。 (IE。 对于链接的应用程序是安全的 针对接口 5、4、3 和 2 在运行时加载 5:4:3 dll)。

那么,让我们看看可能的历史 的神秘dll。我正在关注 c:r:a 更新规则如上所述。

oldest: interface definition 0, initial release:
0:0:0 (DLLVER = 0)    
removed an entry point:
1:0:0 (DLLVER = 1)    NOT backwards compatible!
but DLLVER does the right thing.
source code changed, but no added or removed entry points:
1:1:0 (DLLVER = 1)    
more source code changes:
1:2:0 (DLLVER = 1)    

In all of the previous three releases, 'c' - 'a' = DLLVER = 1.
removed an entry point (or renamed it):
2:0:0 (DLLVER = 2)    This is INCOMPATIBLE.
(But look: 'c' - 'a' = 2, so the DLLVER does the right thing)
added a new function:
3:0:1 (DLLVER = 2)    (this is BACKWARDS but not FORWARDS compatible.
However, the DLLVER 'c' - 'a' still is 2, so that is good.)
add eight more exported functions all at once
4:0:2 (DLLVER = 2)    
add another function:
5:0:3 (DLLVER = 2)
source code changes, but no new interfaces:
5:1:3 (DLLVER = 2)    
again:
5:2:3 (DLLVER = 2)    
again:
5:3:3 (DLLVER = 2)    
again:
5:4:3 (DLLVER = 2)    

DLLVER = 2 的所有这些 DLL (2:0:0, 3:0:1, 4:0:2, 5:0:3, 5:1:3, 5:2:3、5:3:3 和 5:4:3) 都是 严格向后兼容:它是 保证在任何较新的 DLL 系列可以由一个exe加载 是针对旧的 DLL 编译的 系列。

在 1.2.3 中,DLLVER 是 12。让我们 假装那是一个 'c' - 'a' 的 12, 'c' = 12 和 'a' = 0。

在 [libpng] 1.2.4 中,您只需添加 一些新功能——但没有 删除任何。所以,新的 libtool 编号 是 13:0:1 -- DLLVER 仍然是 12。

【讨论】:

对于部分 ABI 的 C++ vtables,因此 vtable 中的任何更改(添加或删除的虚拟函数)都将导致二进制兼容性更改【参考方案2】:

不要这样做。

我以前处理过“新的 LIB,旧的 DLL”问题,诊断起来真的非常烦人。只有当 每个 公开可见的类型具有完全相同的签名时,它才是“安全的”,这基本上意味着库作者必须优先考虑二进制兼容性。否则,您最终会遇到奇怪的堆损坏错误,这些错误通常在程序运行一段时间后才会出现。

回答您的具体问题:默认参数实际上是头文件的属性,而不是库。

【讨论】:

【参考方案3】:

默认参数应该在.h文件中,所以那部分应该没问题。

否则,可能的结果是当 .DLL 未按预期执行时通常很难找到错误。

【讨论】:

【参考方案4】:

当且仅当 dll 的旧版本和新版本之间保持二进制兼容性时,这是可以的。

简单来说。如果:

harness 和 使用的 dll 头文件没有变化 没有添加或删除导出的符号,你可能会没事的。

如果不满足这两个条件中的任何一个,你很有可能会遇到一些非常有趣的错误/崩溃/内存涂鸦等。

【讨论】:

以上是关于链接到不同版本的 .dll 的 .lib 可能会产生哪些副作用?的主要内容,如果未能解决你的问题,请参考以下文章

VC运行库版本不同导致链接.LIB静态库时发生重复定义问题的一个案例分析和总结

VC运行库版本不同导致链接.LIB静态库时发生重复定义问题的一个案例分析和总结

VC运行库版本不同导致链接.LIB静态库时发生重复定义问题的一个案例分析和总结

windows中LIB和DLL的区别与使用

将 DLL 加载到 MFC 中的不同方法?

CUDA 链接错误(Lib 到 Dll)