Linux多个动态库间的符号冲突问题
背景
今天遇到一个奇怪的问题,在客户车机上客户传入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的就没有继续进行。
小结
在编译动态库的时候,尽量隐藏函数符号,减少动态库间符号冲突问题。