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

c++ cmake 动态库的 std::string 为空,求指点(maocs-12.6)

  •  
  •   wjx0912 · 2023-07-17 15:52:45 +08:00 · 1394 次点击
    这是一个创建于 502 天前的主题,其中的信息可能已经有所发展或是发生改变。
    20 条回复    2023-07-17 17:32:22 +08:00
    wjx0912
        1
    wjx0912  
    OP
       2023-07-17 16:17:24 +08:00
    猜测的原因:__attribute__((constructor))时,c++ runtime 还未初始化,std::string 的一些操作可能不稳定。

    测试:
    ```
    std::string g_test1;
    std::string g_test2;

    __attribute__((constructor))
    static void init() {
    g_test1 = "hello test1";
    printf("init: %s\n", g_test1.c_str());
    }

    void hello_func1(void) {
    g_test2 = "hello test2";
    printf("Hello World: %s, %s\n", g_test1.c_str(), g_test2.c_str());

    return;
    }

    void hello_func2(void) {
    printf("Hello World: %s, %s\n", g_test1.c_str(), g_test2.c_str());

    return;
    }
    ```

    在 hello_func2 里面,g_test1 无法打印,g_test2 正常。

    不知道这个思路是否正确,求大神指点
    chingyat
        2
    chingyat  
       2023-07-17 16:36:56 +08:00
    应该是 init() 在 g_test 初始化之前就被调用了。
    wjx0912
        3
    wjx0912  
    OP
       2023-07-17 16:37:17 +08:00
    ```
    搞定了,把:
    std::string g_test1;
    std::string g_test2;
    改成:
    __attribute__((init_priority(101))) std::string g_test1;
    __attribute__((init_priority(101))) std::string g_test2;

    参考:
    https://stackoverflow.com/questions/43941159/global-static-variables-initialization-issue-with-attribute-constructor-i
    ```
    chingyat
        4
    chingyat  
       2023-07-17 16:38:28 +08:00
    ```c++
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <string>

    #include "hello.h"

    std::string g_test = "initial value";

    __attribute__((constructor))
    static void init()
    {
    g_test = "hello test";
    printf("init: %s\n", g_test.c_str());
    }

    void hello_func(void) {
    printf("Hello World: %s\n", g_test.c_str());

    return;
    }
    ```

    执行结果:

    ```
    init: hello test
    Hello World: initial value
    ```
    wjx0912
        5
    wjx0912  
    OP
       2023-07-17 16:39:33 +08:00
    这个不是编译器 bug ,是未指定行为
    Shatyuka
        6
    Shatyuka  
       2023-07-17 16:40:30 +08:00
    `__attribute__((constructor))`在全局变量初始化之前执行。试试给全局变量添加`__attribute__((init_priority(101)))`属性。
    wjx0912
        7
    wjx0912  
    OP
       2023-07-17 16:41:28 +08:00
    @chingyat 嗯。这个是没问题的。但是 g_test 不是固定值(会从文本读取)。runtime 执行一堆 constructor 的顺序问题。
    wjx0912
        8
    wjx0912  
    OP
       2023-07-17 16:42:39 +08:00
    @Shatyuka 正解。谢谢
    wjx0912
        9
    wjx0912  
    OP
       2023-07-17 16:44:12 +08:00
    @Shatyuka windows 的 DllMain.DLL_PROCESS_ATTACH 执行的比较晚,所以不会有这个问题,对吧
    yulon
        10
    yulon  
       2023-07-17 16:44:22 +08:00
    这个问题和 DllMain 一样,在大部分实现上,C 的部分一般比 C++ 运行时要早或者说更底层运行,不要使用 C 方言,不要混用 C/C++ 关于生存周期的部分。
    zpd2009
        11
    zpd2009  
       2023-07-17 16:47:18 +08:00
    反汇编看了一下,在.init_array 段,执行完 init 以后,又执行了一个函数,把 g_test1 和 g_test2 又初始化为了空
    yulon
        12
    yulon  
       2023-07-17 16:49:35 +08:00
    @yulon MSVC 在 DllMain 里使用 C++ 标准库部分功能会炸或者死锁,Mingw-w64 GCC 相对安全一点,因为包了另一套东西,但是微软一直推荐不要在 DllMain 中执行太复杂的操作,DLL 的核心功能应该放在导出函数中。
    zpd2009
        13
    zpd2009  
       2023-07-17 16:54:48 +08:00
    如果 g_test1 指定了__attribute__((init_priority(101))),在.init_array 段执行的最后一个函数里,只初始化了 g_test2 ,没有重新初始化 g_test1
    wjx0912
        14
    wjx0912  
    OP
       2023-07-17 16:58:45 +08:00
    wjx0912
        15
    wjx0912  
    OP
       2023-07-17 17:00:15 +08:00
    @zpd2009 谢谢。还是运行时没完全理解。但这种坑对新手实在是不友好
    ysc3839
        16
    ysc3839  
       2023-07-17 17:13:30 +08:00 via Android
    @wjx0912 #9 不是,DllMain 是最早执行的,但是 CRT 会在执行用户的 DllMain 之前先执行初始化代码。
    yulon
        17
    yulon  
       2023-07-17 17:17:51 +08:00
    @wjx0912 我说整个标准库,又不是单纯的 std::string ,还有语言层面的特性(线程安全保证之类的)有很多 MSVC 直接用标准库来实现,这种碰到了都会炸😅
    wjx0912
        18
    wjx0912  
    OP
       2023-07-17 17:18:44 +08:00
    @ysc3839 谢谢
    yulon
        19
    yulon  
       2023-07-17 17:25:26 +08:00
    @wjx0912 还有你这个测试,是在同一个编译单元内,一般来说同编译单元在编译时初始化顺序就决定了,这个很好优化,不同编译单元的初始化顺序是完全不可预料的,链接器可能会帮你排好序,但是遇到冲突是不可能完全按照你预料的顺序来初始化,只有 C++ 运行时完全初始化后,才能保证所有全局变量都初始化了。
    lts9165
        20
    lts9165  
       2023-07-17 17:32:22 +08:00
    std::string& get_test1() {
    static std::string test1;
    return test1;
    }

    std::string& get_test2() {
    static std::string test2;
    return test2;
    }

    __attribute__((constructor))
    static void init() {
    get_test1() = "hello test1";
    printf("init: %s\n", get_test1().c_str());
    }

    void hello_func1(void) {
    get_test2() = "hello test2";
    printf("Hello World: %s, %s\n", get_test1().c_str(), get_test2().c_str());

    return;
    }

    void hello_func2(void) {
    printf("Hello World: %s, %s\n", get_test1().c_str(), get_test2().c_str());

    return;
    }


    这是 chatgpt 给出的方案
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2649 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 07:03 · PVG 15:03 · LAX 23:03 · JFK 02:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.