关于 Windows 下 C/C++ Runtime 兼容性的问题

2022-03-08 05:32:01 +08:00
 jim9606

目前 Windows 有两种 C 运行时:所有 NT 版本都有的 MSVCRT(msvcrt.dll)和最低支持 Vista 的 UCRT(ucrtbase.dll)。在使用 MSVC 时,还会引入主版本关联的 VC++ Runtime(msvcpXXX.dll,vcruntimeXXX.dll)现在有几个问题:

  1. UCRT 会有更好的性能吗?无需支持 XP 的话选择链接 UCRT 有什么缺点吗?
  2. 使用 MSVC 或者 MSYS2 ( mingw64/ucrt64/clang64)编译的程序必须链接以上两个 CRT 之一,据说是因为这部分跟系统有较深耦合无法避免,那为什么 golang 可以做到不依赖 CRT ?
  3. 在保证导出 API 函数都是 C 风格时,可以安全混用两种 CRT 吗?例如链接 UCRT 的 exe 加载链接 MSVCRT 的 dll ,或者链接 MSVCRT 的 exe 加载链接 UCRT 的 dll ?
  4. 如果我希望给别人用的 dll 有较好的兼容性,在保证导出 API 函数都是 C 风格时,可以将 VC++ Runtime 静态链接进 dll 吗?如果可以,怎么配置?
  5. 在保证导出 API 函数都是 C 风格时,可以在一个 exe 中安全混用依赖不同主版本 VC++ Runtime 的 dll 吗?
2209 次点击
所在节点    问与答
18 条回复
hackpro
2022-03-08 05:36:12 +08:00
如果希望兼容性的话 考虑静态链接吗
thedrwu
2022-03-08 05:59:26 +08:00
1.没用过不好回答
2.可以不链接任何 crt ,用纯 Windows api 。尤其是为了把体积缩小到极致的时候。golang 怎么做不清楚。
3.申请释放内存用同一个 crt
4.同上,即使静态链接
5.同上

欢迎补充
thedrwu
2022-03-08 06:16:22 +08:00
补充上一楼。

以前为了兼容性, 小程序我链的都是 msvcrt.dll 。其中的 bug 都成 feature 将错就错了。后来随着宽带普及,软盘淘汰,不需要榨干最后一点体积。再后来 visual studio 也免费了,根据第三方库下载安装对应版本的 vc 编译不是个事了。

既然都跟 golang 比了,不在乎这点体积。
0o0O0o0O0o
2022-03-08 08:39:56 +08:00
ysc3839
2022-03-08 09:15:40 +08:00
1.理论上是的,因为 msvcrt 为了兼容性可能有些妥协。ucrt 可能更加符合新的 C 标准
2.否的,只是 mingw 项目不开发 libc ,直接用了微软的 libc ,msys2/cygwin(有 POSIX 兼容层那个模式)就是使用自己开发的 libc ,不使用系统的
3.一直可以,许多系统 DLL 就是依赖 msvcrt 的,但一直都不可以互操作
4.可以,msvc 是用 /MT /MTd 这两个选项 https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library
5.一直可以,同样也是不可用互操作
littlefishcc
2022-03-08 09:19:28 +08:00
我会回答第五条:
依赖不同版本,可以用 GetProcAddress 指向不同版本的运行库。
3dwelcome
2022-03-08 09:48:25 +08:00
“如果我希望给别人用的 dll 有较好的兼容性,在保证导出 API 函数都是 C 风格时,可以将 VC++ Runtime 静态链接进 dll 吗?如果可以,怎么配置?”

楼主没搞懂,使用动态 VC Runtime DLL 这点,对于程序开发很重要的。

你用静态 Runtime 库的话,由 malloc 分配的内存,是没办法在各个模块之间共享的。

要共享,就只能让所有模块,都使用同一个 VC Runtime DLL 。换句话说,你写代码肯定不是为了兼容性去用静态链接库.
ysc3839
2022-03-08 10:02:14 +08:00
@3dwelcome 但是没谁规定一定要用 libc 提供的 malloc 来分配内存,Windows 有很多可跨模块的分配器,比如 process heap allocator 和 COM task allocator ,甚至可以不传递所有权。
lonewolfakela
2022-03-08 16:17:16 +08:00
4. 会导致使用你的 dll 的程序也必须静态链接 VC++ Runtime ;然而如果那个程序本身用的 Runtime 和你版本不一样的话,全部静态链接到一个 exe 里之后,鬼知道会发生什么后果……
ysc3839
2022-03-08 16:46:45 +08:00
@lonewolfakela 不一定的,不涉及 malloc 以及 C++的互操作的话就不需要。比如说调用方传数据进来的情况,可以把数据拷一份,或者让调用方提供个回调函数,在调用回调函数前都需要保持数据有效。传数据出去的情况,可以让调用方提供缓冲区。以上两种情况还可以都用系统提供的进程级的内存分配器。
lonewolfakela
2022-03-08 19:24:16 +08:00
@ysc3839 可是,微软的 C++链接器会直接拒绝混用两种模式的情况啊……
https://stackoverflow.com/questions/3469841/mixing-code-compiled-with-mt-and-md
ysc3839
2022-03-08 20:12:26 +08:00
@lonewolfakela 你发的这是静态链接时的情况呀。楼主说的是自己编译一个 DLL 提供给别人用,就没有涉及静态链接。
lonewolfakela
2022-03-08 20:51:31 +08:00
@ysc3839 哦抱歉,是我搞混了。
jim9606
2022-03-08 23:12:46 +08:00
@3dwelcome
@lonewolfakela
关于是否混用 VC++ Runtime 的问题,我的理解(从 Linux 迁移过来的理解,可能有错)是如果使用共享 VC++ Runtime dll 会导致 VC++导出的符号对所有 dll/exe 可见,不同主版本的 VC++符号会被互相覆盖,会导致某个链接 VC12 的调用实际上调用了 VC14 的符号,从而产生问题。如果将 VC++静态链接进 dll ,这些 VC++符号就是对其他 dll/exe 不可见的私有符号。我用 VS2022 测试了下,使用 /MT /MTd 时 dumpbin 显示只依赖 kernel32.dll ,所以应该是把 VC14 和 UCRT 都静态链接进去了。那么在满足 ABI 稳定和谁分配谁释放的前提下,这个 dll 是不是可以安全地给 MSVC13 编译?

另外,这种混用 Runtime 的方法是不是在 Linux 下不可能实现?例如 musl 的程序加载静态链接 glibc 的 so ?
3dwelcome
2022-03-09 00:21:22 +08:00
@jim9606 Linux 的 so ,和 windows 的 dll 设计不太一样。

dll 就是一个完整的闭环体系,缺函数没办法编译通过。

而 so 编译时缺胳膊少腿都没问题,只要 elf 符号完整,最后代码就和变形金刚一样,能组合在一起完整运行。

dll 也很少会覆盖符号,这个情况只在 linux 见过,windows 上我是没见过。
lonewolfakela
2022-03-09 00:50:18 +08:00
不过话又说回来,真的要想稳定性兼容性都比较好、少出各种诡异问题的话,我觉得还是给比如 MSVC10 、11 、12 、14 各自编译一份比较好——其实也不太多,只需要 4 份就能从 VS2010 一直支持到 VS2022 了。或者如果可以选择不支持那么老的版本的 VS 的话,直接只针对 MSVC14.0 编译一份动态链接 Runtime 的 DLL ,就能支持从 VS2015 到 VS2022 ,在很多时候已经挺够用的了。
ysc3839
2022-03-09 01:38:50 +08:00
@jim9606 不会覆盖,不同版本的 vcrt dll 可以在同一进程中共存。
@3dwelcome 你是指链接时即使 so 里没有某个符号,也能通过吗?这个好像跟操作系统关系不大,是链接器的逻辑,msvc 的链接器需要你明确告诉它某个符号对应某个 dll 中的某个函数。实际上即使没有对应 lib ,也可以自己生成一个。
jim9606
2022-03-09 02:28:30 +08:00
@lonewolfakela
那当然是各自编译一份最好,这个毋庸置疑。现在 UCRT 和 VC++14 提供向后兼容是不错,但谁知道以后 UCRT 会不会变成 MSVCRT 那样的包袱呢。
其实还有一点,在 Windows 上升级 VC++ Redist 是相对简单安全的事情,实在不行就费点内存磁盘用静态链接 /本地部署,也很安全。尽管 glibc 、libstdc++、libgcc 在近几年也提供类似的兼容性,但在 Linux 发行版上升级这些库是件挺有风险的事,反正我是没胆做这种尝试。特别是 glibc 旧版兼容的问题还不好通过静态链接来解决。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/838759

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX