V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tool2d
V2EX  ›  C++

C++动态库向前兼容的能力,真是一言难尽。

  •  
  •   tool2d · 2023-01-17 09:48:13 +08:00 · 7589 次点击
    这是一个创建于 708 天前的主题,其中的信息可能已经有所发展或是发生改变。
    公司有个运行在外网的生产环境,是 linux 早期版本,本着只要代码能运行,就没人去动的原则,内核一直比较老。

    然后我想加一点小功能,想着现在 clang 那么强大了,跨平台编译应该轻轻松松,然而测试结果让我大跌眼镜。

    目前几乎所有的 clang 交叉编译环境,都是基于新版本的 GLIBC ,相当于 VC 的 Runtime Lib ,而这货竟然严重依赖于系统内核版本?测试了好几个打包编译器,都是顺利生成 ELF ,可运行报错,提示让先升级内核。

    无奈,一点点换老款编译器,从 2022 年的 clang ,到 2020 年的 clang ,到 2012 年的 gcc, 再到 2007 年的 gcc ,最后一个终于运行成功!

    硬着头皮,用最原始的编译器,写最炫酷的 2023 新代码。
    第 1 条附言  ·  2023-01-17 15:11:05 +08:00


    补一张错误图吧,是静态链接,也就一个 hello world ,运行就报"kernel too old"。

    很多人没遇见过 GLIBC 内核不兼容问题。
    53 条回复    2023-02-15 15:04:21 +08:00
    uiosun
        1
    uiosun  
       2023-01-17 09:51:12 +08:00   ❤️ 3
    路过,向前兼容?那个不是 JAVA 的 Slogan 之一吗——JAVA8 永远的神……
    lichao
        2
    lichao  
       2023-01-17 09:52:59 +08:00
    这方面无敌的还是 M$
    Crawping
        3
    Crawping  
       2023-01-17 09:54:12 +08:00   ❤️ 4
    c++ 一大特点
    生产环境与编译环境绑定..
    tool2d
        4
    tool2d  
    OP
       2023-01-17 09:55:42 +08:00
    @uiosun 是向后兼容,不停的换编译器,脑袋都快裂开了。

    内心非常不愿意用老款 GCC ,又不得不用的那种感觉,不知道怎么形容。
    ysc3839
        5
    ysc3839  
       2023-01-17 09:59:07 +08:00 via Android
    glibc 按理来说不依赖内核吧?不过只能新版本 glibc 兼容旧版本编译的程序,不能反过来。
    MSVC 这边,之前是只能同版本兼容,但是能多版本共存。VS2015 开始可以跨版本兼容,但是如果程序使用了新版本加入的函数那就不兼容旧版本。不过 MSVC 一直可以静态链接。
    besto
        6
    besto  
       2023-01-17 10:01:27 +08:00
    1, C++11 之前的 C++ 真的忍不了;
    2, clang 这种有很多默认检查,但基本可以关,这甚至直接导致 github 上一大堆开源代码拉下来编不过;
    3, 一般 gcc 都是谈版本不谈年份。。。交叉编译,难道是嵌入式? linaro 最新的才 7.5...arm 官网有 9.x 的,不过 2012 年的 gcc 不能用是什么鬼
    wingkwanli888
        7
    wingkwanli888  
       2023-01-17 10:03:55 +08:00 via iPhone
    2023 年了为什么 c++ 还没有 npm, maven 之类的包管理器
    DsuineGP
        8
    DsuineGP  
       2023-01-17 10:04:56 +08:00
    @ysc3839 glibc 是内核向用户态暴露的接口, 肯定依赖内核啊
    tool2d
        9
    tool2d  
    OP
       2023-01-17 10:04:57 +08:00
    @ysc3839 依赖的,你 google 搜一下"glibc kernel compatibility",遇到这种问题,完全无解。

    好像可以通过修改 GLIBC 里的 MIN_KERNEL_SUPPORTED 来屏蔽一些功能,支持老内核的 linux ,可这货一般没人去自己编译。

    而且 GLIBC 和 VC Runtime 不一样,加了太多东西,静态编译会有各种奇怪问题,真是心力交瘁。
    lhbc
        10
    lhbc  
       2023-01-17 10:08:32 +08:00 via Android
    想要不依赖系统提供的库,不想用内核的 API ,就自己整齐一套
    又想调用系统库,又要调用系统 API ,还想不依赖版本,这种好事哪有这么容易

    自己把东西整齐活了,就不依赖了
    ysc3839
        11
    ysc3839  
       2023-01-17 10:09:41 +08:00 via Android
    @DsuineGP 是吗?不是 C 运行库吗?按理来说应该不太依赖系统功能的。

    @tool2d 那我就不知道了,我只遇到过新 glibc 版本编译的程序拿到旧系统里运行不了的情况。
    Noicdi
        12
    Noicdi  
       2023-01-17 10:11:06 +08:00 via iPhone   ❤️ 2
    最近在搞公司项目一个老模块依赖的 openssl 升级,体会到了这种兼容问题,整得我狗脑子要炸了
    iold
        13
    iold  
       2023-01-17 10:18:19 +08:00 via iPhone
    @wingkwanli888 #7 vcpkg ,巨硬的,我一直在用
    bitdepth
        14
    bitdepth  
       2023-01-17 10:23:14 +08:00
    試過靜態編譯?
    其實是你 toolchains 有問題
    luvroot
        15
    luvroot  
       2023-01-17 10:24:18 +08:00
    用 Go 把,静态依赖编译,让你不用考虑环境的问题
    DsuineGP
        16
    DsuineGP  
       2023-01-17 10:25:24 +08:00   ❤️ 1
    @tool2d 如果依赖不多编译不麻烦的话, 可以尝试一下静态链接 musl(替换 glibc) 和 libc++(替换 libstdc++)

    @ysc3839 称为「 C 运行库」是因为 libc 会提供大量接口给用户态程序, 比如说 open 等等, 实际上底层就是将内核的 syscall 进行封装并暴露 abi 接口. 并且程序也是可以不依赖「 C 运行库」的, 比如 linux 内核.
    tool2d
        17
    tool2d  
    OP
       2023-01-17 10:27:46 +08:00
    @bitdepth 静态编译后运行,运行也报内核太低,这点就很迷了。

    toolchains 应该没问题,因为能正常生成 ELF ,我想在放到普通桌面 linux 上,大概率也能顺利运行。只是服务器比较特殊,不敢随便动内核。

    想想还是 MS 好啊,静态编译 CRT ,基本上就没什么负担了。
    Reficul
        18
    Reficul  
       2023-01-17 10:31:01 +08:00
    @luvroot #15 Go 也有内核版本依赖,1.18 已经去掉了很多对很多老内核的系统调用兼容。
    bitdepth
        19
    bitdepth  
       2023-01-17 10:34:06 +08:00
    你只是 ABI 相容,不代表 sysycall 相容
    你大過是 timestamp 之類轉換為 64bits 這種 ioctl 出問題了
    junmoxiao
        20
    junmoxiao  
       2023-01-17 11:09:25 +08:00
    有一说一上面提到的问题都可以解决,看看 vc-ltl 那个项目,xp 到最新的 win11 ,就算 api 有变化,一样能兼容。从原理上来说 linux 下也能做到,只是没人做
    zunceng
        21
    zunceng  
       2023-01-17 11:19:32 +08:00
    正常的 c++ 只有代码能跨平台
    geelaw
        22
    geelaw  
       2023-01-17 11:21:39 +08:00 via iPhone
    跨 DLL 请自觉 extern C 或者 COM
    yolee599
        23
    yolee599  
       2023-01-17 11:33:14 +08:00 via Android
    编译器的锅关我 C++ 什么事?:doge:
    yolee599
        24
    yolee599  
       2023-01-17 11:43:19 +08:00 via Android
    我用 gcc 编译过很多代码,从来没遇到过内核版本的问题,无论是 x86 项目,还是 arm 项目,都很顺利啊。OP 文中说的“交叉编译环境”,一般指在 x86 下编译 arm 的环境,这个你得用“交叉编译器”,还要配置好环境变量,指定到你这个交叉编译器
    yolee599
        25
    yolee599  
       2023-01-17 11:49:50 +08:00 via Android
    交叉编译器一般使用 gcc ,除了 NDK ,我还没见过有什么项目使用 clang 作为交叉编译器
    tool2d
        26
    tool2d  
    OP
       2023-01-17 11:57:35 +08:00
    @yolee599 CLANG 比想的要更强,我就是编译 NDK 时发现,改改 taget 就能跨平台编译,就让人感觉很舒适。

    最近 CLang 又加了对 VS IDE 编译和调试支持,生成的 OBJ 可以无缝嵌入 VC2015-VC2022 编译器。于是,我就想 ALl in one 一把,让 CLANG 也支持生成 Linux ELF 。

    结果还是太傻太天真了。
    julyclyde
        27
    julyclyde  
       2023-01-17 13:29:01 +08:00
    @ysc3839 正因为是运行时库,才依赖内核
    C“语言本身”有啥可用库的……
    maxxfire
        28
    maxxfire  
       2023-01-17 13:45:23 +08:00
    没有什么是不能通过增加一层中间层来解决的问题,这个中间层可以理解为转换层、适配层。
    具体技术可以是 函数指针,虚函数表,dlopen 等等,可以参考 COM 组件对象模型。
    nmgwddj
        29
    nmgwddj  
       2023-01-17 13:49:40 +08:00
    目前跨平台项目编译问题我还是推荐使用 conan ,对于三方库的管理和编译工具链没有重度依赖。

    我们现在工程 C++ 标准锁定在 14 ,Windows > MSVC15 (如果没有用一些较新的 API ,使用 7.0 SDK XP 都可以支持)、macOS 、iOS apple-clang > 12.4 ( macOS ARM64 支持) 以上、Android NDK21 clang9 以上、Linux GCC5 以上。GCC5 已经支持大部分 C++14 标准,如果是为了兼容一些 GCC5 没有支持的特性或者旧内核系统,可以考虑 GCC7+ glibc2.23 的 docker 镜像,这样 Ubuntu 16.04 都可以跑。

    主流的 libevent 、openssl 、sqlite3 、libcurl 、zlib 、qt 等都验证编译通过。
    icyalala
        30
    icyalala  
       2023-01-17 14:15:15 +08:00
    @wingkwanli888 包管理 vcpkg 、conan ,国内的 xmake 也可以试试。只是还是一言难尽。。
    duke807
        31
    duke807  
       2023-01-17 14:25:19 +08:00 via Android
    要查清楚是谁报内核太低这个错,就是谁的锅
    weidaizi
        32
    weidaizi  
       2023-01-17 14:37:02 +08:00
    @wingkwanli888 历史问题,如果依赖库的工程用的全是 cmake 的话,FetchContent 也是可以一把梭的
    mingl0280
        33
    mingl0280  
       2023-01-17 16:27:28 +08:00 via Android
    哈哈哈哈,跟我遇到的坑一模一样。
    当然,新版 gcc 还有更坑的……我之前用 elf 编辑绕过了一下(就是去掉签名里面的 @版本),结果新版 gcc 加了几个 @@版本开头的隐藏函数( elf 编辑器里面根本找不着)直接干死了我试图绕过这个问题的方法……
    zhyl
        34
    zhyl  
       2023-01-17 16:46:00 +08:00
    glibc 不能够静态链接的,换 musl 就行了
    newmlp
        35
    newmlp  
       2023-01-17 16:50:55 +08:00
    这方面还是微软兼容性好,ntdll 从 xp 到 11 ,基本没什么问题
    newmlp
        36
    newmlp  
       2023-01-17 16:52:52 +08:00
    @lhbc 微软 nt 就很容易,xp 到 11 ,兼容性杠杠的
    ipwx
        37
    ipwx  
       2023-01-17 17:22:41 +08:00
    你遇到的大概是安全检查。如果没这个拦着你,等 syscall 行为不符合预期的时候,出 bug 你都不知道哪里死的。

    怪就怪 Linux 内核 syscall 一直在升级 ( doge
    icylogic
        38
    icylogic  
       2023-01-17 17:28:57 +08:00   ❤️ 6
    glibc 的兼容性,和 C++ 动态库的兼容性是两件事,而且这两点几乎都和 clang 没什么关系。你提到的这些问题,大部分锅是 glibc 的。

    glibc 几乎很少有人需要最新版的特性,所以只要你去链最老的 glibc 就可以了,一般会推荐让你到所有目标平台中最老的那个去编译。glibc 就是唯一特殊的那个崽,不能静态链接。

    c++ 你想用新的编译器、新的库(包括 std )是完全可以的,既可以静态链接(如果你能搞到静态库),也可以自己打包动态库(比如 /usr/lib/myapp/libxxx.so )然后设置 rpath 到 $ORIGIN 之类的,后者其实就是 Windows 那种一个 exe 带一堆 dll ( vcruntime140.dll, etc) 的风格。

    这两点综合起来就是,比如你需要给 Ubuntu 18.04, 20.04, ... 这些平台提供支持,那你首先准备一台 Ubuntu18.04 的环境,然后通过 toolchain ppa 之类的东西安装或者编译一套新版的 g++/clang ,多新的都可以,只要你能在 18.04 上跑。最后用这套工具链去编译你的 app ,然后把所有依赖 (除了 glibc) 通过静态或者动态的东西打包带走。

    这个你找台环境试一下就知道怎么回事了,比如尝试一下给一台默认 gcc 5.x 的环境编一个使用了 C++17 Filesystem 的应用。







    你要是觉得麻烦就直接 docker 得了。
    lhbc
        39
    lhbc  
       2023-01-17 17:55:03 +08:00 via Android
    @newmlp runtime 也是要装一堆的,还有 .net 也是 n 个版本
    jworg
        40
    jworg  
       2023-01-17 18:10:45 +08:00
    @icylogic 我那公司跑还是原生跑,不过特意准备了好几套不同的 linux 环境的容器然后在 docker 里面编译,配合 jenkins 分发到对应的服务器,都是当初运维偷懒没有升级,最后谁也不敢动,结果各种版本都有。
    ipwx
        41
    ipwx  
       2023-01-17 18:17:46 +08:00   ❤️ 1
    @icylogic glibc 有点像 windows 的 kernelXX.dll ,封装了很多 syscall 相关的东西。

    然后有一种 docker 镜像叫做 manylinux 镜像,目的就是为可移植的 二进制编译 提供帮助。

    https://github.com/pypa/manylinux

    虽然主要用于 python 社区的二进制分发,但 python 你懂的,一堆 C/C++ 扩展,所以 C++ 的工具链是齐的。
    urnoob
        42
    urnoob  
       2023-01-17 18:36:17 +08:00 via Android
    @uiosun
    没听过 java 还有这么一说 从来都是 跨平台 还是回到讨论 cpp 上吧
    Zepp
        43
    Zepp  
       2023-01-18 10:54:22 +08:00 via Android
    https://stackoverflow.com/a/11207190
    看看这个? gcc 有选项可以指定兼容的内核版本的
    Zepp
        44
    Zepp  
       2023-01-18 10:56:23 +08:00 via Android
    @Zepp #43 sorry 我好像搞错了,这里说的是编译 glibc 时的 configure 选项,需要你自己重编译 glibc 才行
    tool2d
        45
    tool2d  
    OP
       2023-01-18 11:00:33 +08:00
    @Zepp 嗯,我也看到了。查过的几个别人编译好的 gcc/clang 参数,基本都是默认值,没人去主动修改。

    也许自己编译 GLIBC 是可行的,只是我没有尝试。

    还有一点我帖子里没提到,实验下来编译 i386 ELF 是不限制内核版本的,只有 x64 位的 ELF 才限制。
    jones2000
        46
    jones2000  
       2023-01-18 17:36:14 +08:00
    项目所有代码在目标机器上源码编译,不就可以了。 除非有些库你没有重新编译
    luoqeng
        47
    luoqeng  
       2023-01-18 19:07:25 +08:00
    还能 golang 有远见,绕过 glibc ,直接 syscall
    nmap
        48
    nmap  
       2023-01-18 19:29:22 +08:00
    这种场景就是 golang 最大的优势之一
    codehz
        49
    codehz  
       2023-01-18 21:19:05 +08:00 via iPhone
    zig 内建的 clang 可以随意指定 glibc 的版本,就当作 abi 三元组的一部分
    -target x86_64-linux-gnu.2.34 就能链接到 glibc 2.34 的版本(其他版本以此类推,最低到 2.16 ,十年前的版本),而且不在乎编译平台的 glibc 版本,甚至 windows 上也可以生成 linux 的 binary
    参考 https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html
    learningman
        50
    learningman  
       2023-01-26 19:08:20 +08:00 via Android
    @luoqeng 然后 cgo 又回去了
    jinsongzhao
        51
    jinsongzhao  
       2023-02-15 14:56:35 +08:00
    @nmap golang 一样,你试试最新版本去低版本操作系统跑一下试试,还是得换最老版本的 go 编译
    jinsongzhao
        52
    jinsongzhao  
       2023-02-15 14:58:09 +08:00
    @luvroot golang 的静态编译一样
    jinsongzhao
        53
    jinsongzhao  
       2023-02-15 15:04:21 +08:00
    golang 自带了很多库,所以好很多而已,但是本质一样,依赖操作系统本身的向下兼容
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3500 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 04:41 · PVG 12:41 · LAX 20:41 · JFK 23:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.