在c/c++大项目中我们会使用到其他部门提供的so动态库,如果两个部门函数命名不规范出现符号冲突的几率非常大。历史原因我们需要在几十万代码中对函数重命名,手工来改肯定是无法接受的,在解决问题过程中发现,clang的分词配合sed命令来修改很好提高了效率。
学习地址: https://www.cnblogs.com/skynet/p/3372855.html
书籍: 程序员的自我修养——链接、装载与库
解决:https://blog.csdn.net/found/article/details/105263450
#include
#include
static void test(int a)
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__, a);
}
void test(int a)
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__, a);
}
#include
#include
static void test(int a)
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__, a);
}
static void test(int a)
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__, a);
}
//test1.c gcc -shared -fPIC -o libtest1.so test1.c
#include
#include
void test(int a)
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__, a);
}
//test2.c gcc -shared -fPIC -o libtest2.so test2.c
#include
#include
void test(int a)
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__, a);
}
//main.c gcc main.c -o main -L./ -ltest1 -ltest2 -Xlinker --rpath ./
//输出
#include
#include
void test(int a);
void test_a();
void test_b();
void main()
{
test(1);
}
2). 连接库加载是按照广度优先加载的 比如 下面依赖会先加载 liba.so libb.so libc.so 最后加载libd.so
main -->libtest1.so
—> libtest2.so—>lib.so
—> libtest3.so
//gcc -shared -fPIC -o lib.so test3.c
#include
#include
void test()
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__);
}
3). 编译时库的加载顺序由 -l的顺序确定
//gcc -shared -fPIC -o libd.so d.c
#include
#include
void test(int a)
{
printf("file:%s func:%s %d\n",__FILE__, __FUNCTION__, a);
}
//gcc -shared -fPIC -o liba.so a.c -L./ -ld
#include
#include
void test();
void test_a()
{
test(2);
printf("file:%s func:%s, test\n",__FILE__, __FUNCTION__);
}
//gcc -shared -fPIC -o libb.so b.c
#include
#include
void test(char *b)
{
printf("file:%s func:%s %s\n",__FILE__, __FUNCTION__, b);
}
void test_b()
{
printf("file:%s func:%s, test\n",__FILE__, __FUNCTION__);
}
#include
#include
void test(int a);
void test_a();
void test_b();
void main()
{
test(1);
test_a();
test_b();
}
/*
gcc main.c -o test -L./ -lb -la -Xlinker --rpath ./
输出
Segmentation fault (core dumped)
gcc main.c -o test -L./ -la -lb -Xlinker --rpath ./
输出
Segmentation fault (core dumped)
gcc main.c -o test -L./ -ld -la -lb -Xlinker --rpath ./
输出
file:d.c func:test 1
file:d.c func:test 2
file:a.c func:test_a, test
file:b.c func:test_b, test
*/
# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
"""
实现 c c++ .h 的文件中文件中函数定义和调用批量重命名
用到clang的分词功能 可以指定头文件(包含头文件的才进行替换)
原理
1. 找出包含目标头文件的所有.h文件,包括间接引用的 (使用 grep 过滤#include )
2. 找出包含头文件的所有c c++ 文件
3. 替换头文件和源文件中的 函数定义和调用
4. 函数定义 根据词法 分析得到 所有token 标识符 后面跟 左括号可判断为 函数调用或者函数申明
使用需要安装clang python库才能使用
1.安装dnf install clang 或 yum install clang
2.下载clang 源码https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang-11.0.0.src.tar.xz
3.解压拷贝clang-master\bindings\python\clang 到python库安装目录/usr/lib64/python3.6/site-packages/
4.Config.set_library_file 根据libclang的库来设置
"""
import json
import os
import sys
import subprocess
import clang
import logging
import argparse
from clang.cindex import Index, Config
Config.set_library_file('/usr/lib64/libclang.so.9')
logging.basicConfig(level=logging.DEBUG,
format='%(message)s')
def chang_func_name(src_file, rename_map):
cmds = set()
index = Index.create()
tu = index.parse(src_file)
try:
tokens = tu.cursor.get_tokens()
token_objs = []
for token in tokens:
token_objs.append({"name": token.spelling, "kind": token.kind, "line": token.location.line})
cnt = len(token_objs) - 1
for index in range(cnt):
token_obj = token_objs[index]
next_token_obj = token_objs[index + 1]
for old_name, new_name in rename_map.items():
if token_obj["kind"] == clang.cindex.TokenKind.IDENTIFIER and token_obj["name"] == old_name \
and next_token_obj['name'] == "(":
cmd = "sed -i '{} s/{}/{}/g' {}".format(token_obj['line'], token_obj['name'], new_name,
src_file)
# 使用set 放着 一行有两个函数调用 执行两次sed
cmds.add(cmd)
for cmd in cmds:
logging.info(cmd)
# ret_code, out = subprocess.getstatusoutput(cmd)
# if ret_code != 0:
# logging.error(out)
except Exception as e:
# logging.warning(e)
pass
def gather_file(root, file, output, endStr):
exclude = []
if os.path.islink(root):
return
if file in exclude:
return
if file.endswith(endStr):
output.append(root + "/" + file)
def get_include_header_files(header_file, all_headers):
todo = [os.path.basename(header_file)]
done_headers = [header_file]
tmp_file = "/tmp/aaa"
fd = open(tmp_file, 'w')
for line in all_headers:
fd.writelines(line + "\n")
fd.close()
while True:
if len(todo) == 0:
break
tmp_header_file = todo.pop(0)
match_string = tmp_header_file.replace(".", "\\.")
cmd = "cat {} | xargs grep -l '#include *[\"<]\\(.*/\\)*{}[\">]'".format(tmp_file, match_string)
logging.debug(cmd)
ret_code, out = subprocess.getstatusoutput(cmd)
if out == "":
continue
for file_name in out.split('\n'):
if file_name not in done_headers:
todo.append(os.path.basename(file_name))
done_headers.append(file_name)
return done_headers
def get_all_src_files(include_headers, all_src_files):
tmp_file = "/tmp/aaa"
fd = open(tmp_file, 'w')
for line in all_src_files:
fd.writelines(line + "\n")
fd.close()
need_handle_src_files = []
for header_file in include_headers:
match_string = os.path.basename(header_file).replace(".", "\\.")
cmd = "cat {} | xargs grep -l '#include *[\"<]\\(.*/\\)*{}[\">]'".format(tmp_file, match_string)
ret_code, out = subprocess.getstatusoutput(cmd)
logging.debug(cmd)
if out == "":
continue
need_handle_src_files += out.split('\n')
return list(set(need_handle_src_files))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.description = '批量重命名c/c++代码函数名'
parser.add_argument('--code_path', help='项目工程目录 如/home/', required=True)
parser.add_argument('--header_file', help='头文件路径 只有包含此头文件才会替换 不指定替换所有文件', required=False)
parser.add_argument('--func_map_file', required=True,
help='指定需要替换的函数名文件全路径 如/home/rename_funcs 文件内容格式为 {"old_func_name1":"new_func_name1", "old_func_name2":"new_func_name2"}')
args = parser.parse_args()
code_path = args.code_path
header_file = args.header_file
func_map_file = args.func_map_file
if not os.path.exists(code_path):
logging.error("code path: {} not exist".format(code_path))
exit(-1)
if header_file is not None and not os.path.exists(header_file):
logging.error("header_file: {} not exist".format(header_file))
exit(-1)
try:
with open(func_map_file) as fp:
rename_map = json.load(fp)
except FileNotFoundError as e:
logging.error("func_map_file: {} not exist".format(func_map_file))
parser.print_help()
exit(-1)
except json.decoder.JSONDecodeError as e:
logging.error("func_map_file: {} format error".format(func_map_file))
parser.print_help()
exit(-1)
all_headers = []
all_src_files = []
for root, dirs, files in os.walk(code_path):
for file in files:
gather_file(root, file, all_headers, ".h")
gather_file(root, file, all_src_files, ".c")
gather_file(root, file, all_src_files, ".cpp")
need_hande_files = all_headers + all_src_files
if header_file is not None:
include_headers = get_include_header_files(header_file, all_headers)
logging.debug(include_headers)
need_hande_files = get_all_src_files(include_headers, all_src_files)
logging.debug(need_hande_files)
need_hande_files += include_headers
for src_file in need_hande_files:
chang_func_name(src_file, rename_map)