链接不同版本的.dll文件会有哪些可能的副作用?

4

我在我的应用程序中链接了一个更新的.lib文件,但是却使用了一个较旧的.dll文件。这样做可能会有哪些副作用?如果两个版本之间的函数原型相同,那么一切都应该正常工作,对吗?如果更新的版本更改了参数的默认值,那么该值是在.lib文件中还是.dll文件中?

4个回答

5
在C++中,默认值是在调用点编译的,因此DLL或.lib文件与之无关 - 更改头文件将具有效果,而ABI不会更改。如果导出函数的ABI未更改,则应该可以使用旧的DLL与使用新的.lib链接的程序一起运行,只要程序没有使用新的导出,这些导出在新的.lib中存在但在旧的DLL中不存在。以下是可能影响ABI的因素(本列表并非全面):
- calling convention
- export name
- parameter list (including types)

“libtool版本控制系统”(http://www.gnu.org/s/libtool/manual/libtool.html#Versioning)是一种用于识别共享库兼容性的技术。
请注意,如果您没有使用C调用约定(即,导出名称将是“C ++ mangled”),则从技术上讲,您对导出名称的控制很少。
以下是有关Windows库(cygwin,pngdll)如何使用遵循libtool库版本控制技术的命名约定来管理向后兼容性的说明。这是从http://home.att.net/~perlspinr/libversioning.html的Web档案中提取的 - 我在此处进行镜像。

A couple of definitions:

entry points are externally accessible functions or variables exported by the DLL. The interface is the set of all these exported functions and variables in a given version of the library. Regarding the libPNG version macros in makefile.cygwin:

You ONLY need to bump PNGDLL if the new dll REMOVES an entry point that the old dll provided. If you ADD a new entry point, then the new dll is a drop in replacement for the old one, since the new one provides everything the old one did.

Of course, an app compiled against the new version, which uses the additional entry points, won't work with the old dll -- but nobody ever promised FORWARD compatibility, only BACKWARD compatibility. This is the way cygwin DLL versioning works:

1) follow the libtool versioning scheme From 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

library implements. In other words, the library implements all the interface numbers in the range from number current - age to current.

Updating libtool versioning:

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

library.

   2. Update the version information only immediately before a

public release of your software. More frequent updates are unnecessary, and only guarantee that the current interface number gets larger faster.

   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

release of your package will not be binary compatible with any other release.

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

but it's easier to explain by example).

So, here's an example: the libtool version is 5:4:3, which indicates revision 4 of the implementation of interface 5, which happens to be backwards compatible with the three previous interface definitions. (ie. it is safe for applications linked against interfaces 5, 4, 3 and 2 to load the 5:4:3 dll at runtime).

So, let's look at the likely history of the mystery dll. I am following the c:r:a update rules described above.

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)    

All of these DLLs with DLLVER = 2 (2:0:0, 3:0:1, 4:0:2, 5:0:3, 5:1:3, 5:2:3, 5:3:3, and 5:4:3) are all strictly backwards compatible: it is guaranteed that any newer DLL in the series can be loaded by an exe that was compiled against an older DLL in the series.

In 1.2.3, the DLLVER was 12. Let's pretend that was a 'c' - 'a' of 12, and that 'c' = 12 and 'a' = 0.

In [libpng] 1.2.4, you simply added some new functions -- but did NOT remove any. So, the new libtool number is 13:0:1 -- and DLLVER remains 12.


对于 C++,虚函数表是 ABI 的一部分,因此虚函数表的任何更改(添加或删除虚函数)都会导致二进制兼容性发生变化。 - doron

3

不要这样做。

我之前遇到过“新库,旧动态链接库”问题,它们真的很让人头疼。只有当每个公开可见类型具有完全相同的签名时,才算是“安全”的,这基本上意味着库作者必须把二进制兼容性作为优先考虑。否则,你最终会遇到奇怪的堆损坏错误,通常直到程序运行了一段时间才会显现出来。

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


1

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

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


1

只有在旧版本和新版本的dll之间保持了二进制兼容性时,这才是可行的。

简单来说,如果:

  • 测试工具使用的dll头文件没有更改
  • 没有添加或删除任何导出符号,那么你可能没问题。

如果以上条件不满足,则很有可能会出现一些非常有趣的错误/崩溃/内存泄漏等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接