Linux多个动态库间的符号冲突问题

发布时间 2023-09-19 18:56:17作者: 寒魔影

背景

今天遇到一个奇怪的问题,在客户车机上客户传入json字符串,使用cjson库cJSON_Parse()函数是成功的,但是通过cJSON_GetObjectItem()获取属性却失败了,代码如下

gtc_nlu_product_t* get_product_config(const char* str, gtc_pool_t* pool)
{
    int                    ret;
    gtc_nlu_product_t    * product;
    cJSON                * root, * node, * skills;

    // 初始化
    ret = GTC_OK;
    product = NULL;

    do 
    {
        // 1.解析json
        root = cJSON_Parse(str);
        if (NULL == root)
        {
            ret = GTC_ERROR;
            hloge("product config parse json failed");
            break;
        }

        // 2.提取技能列表
        node = cJSON_GetObjectItem(root, "config");
        if (NULL == node || cJSON_Object != node->type)
        {
            ret = GTC_ERROR;
            hloge("product config get [config] failed");
            break;
        }

        skills = cJSON_GetObjectItem(node, "skills");
        if (NULL == skills || cJSON_Array != skills->type)
        {
            ret = GTC_ERROR;
            hloge("product config get [skills] failed");
            break;
        }

        // 3.分配内存空间
        product = (gtc_nlu_product_t*)gtc_pcalloc(pool, sizeof(gtc_nlu_product_t));
        if (NULL == product)
        {
            ret = GTC_ERROR;
            hloge("alloc memory failed");
            break;
        }

        product->skills = get_product_skills(skills, pool);
        if (NULL == product->skills)
        {
            ret = GTC_ERROR;
            hloge("product config skills empty");
            break;
        }

        // 提取技能领域
        product->aios_domain_list = get_nlu_product_domain_list(product, pool);

    } while (0);

    // 释放资源
    if (root)
    {
        cJSON_Delete(root);
        root = NULL;
    }

    if (ret)
    {
        product = NULL;
    }

    return product;
    
}

 

分析思路

1.怀疑输入字符串有问题,打印输入字符串,字符串是正确的,本地无法复现;
2.怀疑是字符串编码的问题,打印字符串长度,发现字符串长度和字符串完美匹配;
3.怀疑是内存溢出导致cJSON_GetObjectItem()获取属性异常,但是在cJSON_Parse()与cJSON_GetObjectItem()没有任何内存操作;
             考虑是多线程场景,恰好有别的线程操作内存越界,导致当前线程异常,如果是这个原因,没有道理每次都是cJSON_GetObjectItem()获取属性失败,这种巧合接近于0
             考虑是当前线程内业务逻辑有内存操作问题,通过仔细阅读代码,并没有发现内存操作溢出问题
3.仍然不死心,认为是字符串有问题,使用十六进制打印字符串,发现仍然正常;
4.同事反馈如果不判断cJSON对象的类型似乎是可以的,于是我打印出cJSON对象的类型居然是64,这是不符合常理的,

因为CJOSN库中类型的取值范围是[0,6],粗略看了一下代码,似乎也没有啥地方会给类型赋值64,此时我想起来CJSON似乎有高版本,

我手里这个版本好久没有更新了,去官网看了一下CJSON的最新版本,发现CJOSN库中类型取值范围[0,2,4,8,16,32,64],猜测应该是高版本CJSON引发的符号冲突问题;
5.通过nm命令查看动态库,发现动态库确实没有隐藏函数符合,重新出隐藏符号版本,交付验证;
6.最终使用的方案是编译加入-Wl,-Bsymbolic命令,没有验证隐藏符号方案的原因是那是客户的环境,联调比较麻烦,有一个验证OK的就没有继续进行。

小结

在编译动态库的时候,尽量隐藏函数符号,减少动态库间符号冲突问题。

Linux 动态库符号冲突 - 寒魔影 - 博客园 (cnblogs.com)