aqtata
V2EX  ›  C++

困扰几天的问题,这是被 gcc 优化了吗?

  •  
  •   aqtata · Jul 4, 2025 · 8296 views
    This topic created in 488 days ago, the information mentioned may be changed or developed.

    一个动态库项目,支持 win32 和 arm64 ,编译器是 msvc 和 gcc ( gcc 是自己从源码编译的,版本 15.1.0 ) 对外暴露一个标准 C API ,其内部实现只有一行代码,调用一个内部名称空间内的方法

    int my_foo()
    {
        return internal::bar();
    }
    

    魔幻的事情是,这个 so 文件,我写一个控制台程序去调用它,能顺利进入到bar()中,放到实际项目中bar()根本就没有被调用,于是尝试打印

    int my_foo()
    {
        std::cout << "111111111111111" << std::endl;
        std::cout << __FUNCTION__ << std::endl;
        std::cout << "internal::bar address: " << (long long)((void*)&internal::bar);
        return internal::bar();
    }
    

    好家伙,在 linux 下只能打印前两行,然后函数返回 0 ,但这种情况只发生在项目引用时出现。写一个简单的命令行程序触发完全没问题,win32 下也都没问题。 我想 so 文件已经是二进制了,还能被链接它的程序优化不成?这里卡住了,不知道怎么办 项目均使用了-O2

    31 replies  •  2025-07-07 10:13:26 +08:00
    OBJECTION
    OBJECTION
    Jul 4, 2025❤️1
    1
    要不还是加个-g gdb 进去看看? 根据你的描述看不出来啥原因啊
    zhyl
    zhyl
    Jul 4, 2025❤️6
    2
    只打印前两行会不会是没有 flush 输出缓冲区
    Niunai
    Niunai
    Jul 4, 2025❤️1
    3
    前两行都有 std::endl ,第三行没有。#2 说的没毛病。
    Niunai
    Niunai
    Jul 4, 2025❤️2
    4
    再说了,有没有被优化,反汇编来看看不就行了,有功夫上 V2 来问,没工夫反汇编吗?
    猜测完了要验证。
    46fo
    46fo
    Jul 4, 2025❤️1
    5
    拖到 IDA 里看一下就知道了
    tusj
    tusj
    Jul 4, 2025❤️1
    6
    你是依据什么判断函数没有被调用的?第 3 行没打印出来,可能是因为你没有 << std::endl;
    aqtata
    aqtata
    OP
    Jul 4, 2025
    7
    没 std::endl 确实,我等下加上。但打印不是问题关键,主要问题时 bar 中的逻辑都没执行,导致接下来其他 api 接口都会调用失败。
    但我这里试出来一个解决方法,给 bar 加上一个参数,比如 bar(int x),这样就能进入到 bar 中了,感觉还是和某种优化有关
    Ming5Ming
    Ming5Ming
    Jul 4, 2025❤️1
    8
    既然是一个方法,有没有可能是重载了?
    minami
    minami
    Jul 4, 2025❤️1
    9
    怀疑优化问题的话可以关闭优化看看。不过盲猜是动态库导出函数调用约定的问题,可以把 internal::bar();提前不要放到 return 里看看,如果能执行就是这个问题了
    Liuuwei
    Liuuwei
    Jul 4, 2025 via iPhone❤️2
    10
    程序都跑完了,缓冲区还会不刷新?
    nooneanyone
    nooneanyone
    Jul 4, 2025❤️1
    11
    是不是动态链接的问题?
    a83223676
    a83223676
    Jul 4, 2025❤️1
    12
    我会先这么写代码试试

    int my_foo()
    {
    std::cout << __FUNCTION__ << std::endl;
    std::cout << "before call bar" << std::endl;
    int ret = internal::bar();
    std::cout << "after call bar.ret:" << ret << std::endl;
    return ret;
    }
    rtv
    rtv
    Jul 4, 2025❤️2
    13
    问 ai 回答可能是符号冲突…你回复也说加一个参数就没问题
    kita
    kita
    Jul 4, 2025❤️1
    14
    你有无 extern "C" {}; 不然可能 link 时候 symbols 不对
    txhwind
    txhwind
    Jul 4, 2025❤️1
    15
    100%是名字冲突了
    Ilavena
    Ilavena
    Jul 4, 2025❤️1
    16
    首先,你自己写了一个控制台程序取调用它,没有问题,说明动态库是正常的,实际项目里面是否存在 internal::bar 接口影响的?
    1 ,在 my_fool 用 exectern C 包装一下,重新编译这个动态库。
    2 ,在 linux 下建议你尝试使用 dlopen 加载动态库,以及 dlsym 调用对应接口
    这样我觉得问题应该能够解决。
    请告诉结果哈
    Ilavena
    Ilavena
    Jul 4, 2025❤️1
    17
    c++还是蛮有魅力的,我搞了 5 年 c++开发,最近再学深度学习,搞 python ,觉得 python 真的简单易学,也觉得 python 真的牛逼。
    weidaizi
    weidaizi
    Jul 4, 2025❤️1
    18
    1. 首先,使用 -O2 也可以加 -g ,遇到这个情况先 debug ,看看代码到底跑哪里去了
    2. 其次,这种简单的问题 99.999999% 不是编译器的问题,所以快去做第 1 件事
    3. 如果在项目中调试也是确实没跑进去,建议开个 sanitizer 看看有没有出现未定义的行为
    hwdq0012
    hwdq0012
    Jul 4, 2025❤️1
    19
    感觉是动态库是旧的,看看编译时间
    cbythe434
    cbythe434
    Jul 4, 2025❤️1
    20
    internal 命名空间覆盖了吧
    yolee599
    yolee599
    Jul 4, 2025 via Android❤️1
    21
    你在第三行打印下面再加一行纯文本打印看看,有时候遇到空指针或者其他情况,不会执行打印的
    wanmyj
    wanmyj
    Jul 4, 2025 via iPhone❤️1
    22
    这个看起来是 rvo 。O2 只是关闭编译器优化,但没有关闭 rvo 。关闭 rvo 有单独的选项的, 加了 fno-elide-constructors 这个参数就 OK 了
    jim9606
    jim9606
    Jul 4, 2025 via Android❤️1
    23
    会不会是因为混用不同 c/c++运行时导致的?
    Gilfoyle26
    Gilfoyle26
    Jul 4, 2025❤️1
    24
    @Niunai #4 同意!!!
    Arthur2e5
    Arthur2e5
    Jul 4, 2025❤️1
    25
    @Liuuwei 又不是 main 跑完了,谁说程序跑完了?

    @jim9606 那直接链接时爆炸没了
    o0DoO0o
    o0DoO0o
    Jul 4, 2025❤️1
    26
    反汇编应该就可以了,看看 myfoo 返回的是寄存器还是常量 0
    johnnyyeen
    johnnyyeen
    Jul 4, 2025❤️1
    27
    看看 internal::bar();实现,如果 internal::bar();什么也没做,多半被优化了。
    aqtata
    aqtata
    OP
    Jul 4, 2025
    28
    我加上了 std::endl ,地址可以打印出来了。然后又将 so 文件拖到 ida 中看了,代码都在,没有被优化。

    根据楼上朋友的提示说可能是符号问题,我找到了项目中另一个 so 文件,确实发现了同名空间下的同名函数!!
    加入输出后果然破案了,在 a.so 内执行 internal::bar() 却串到了 b.so 文件内部的 internal::bar() 中去了!?

    按照我的理解,so 文件已经是二进制了,都有各自的内存范围,不清楚为什么会“串门”
    aqtata
    aqtata
    OP
    Jul 4, 2025❤️1
    29
    解决了,是符号冲突了,gcc 默认把所有符号导出了,这点和 msvc 确实不同。
    Rorysky
    Rorysky
    Jul 6, 2025
    30
    @aqtata 动态链接库是共享的,又叫共享库
    kita
    kita
    Jul 7, 2025
    31
    @aqtata
    https://gcc.gnu.org/wiki/Visibility
    你没有看过这篇吧,贴代码要贴全,不然大家盲猜基本上都认为是 symbols 没有 link 对,但是没有办法告诉你哪里没有 link 对
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2900 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 66ms · UTC 10:57 · PVG 18:57 · LAX 02:57 · JFK 05:57
    ♥ Do have faith in what you're doing.