forked from oceanbase/oceanbase
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbitcode_to_elfobj
More file actions
executable file
·259 lines (224 loc) · 9.32 KB
/
Copy pathbitcode_to_elfobj
File metadata and controls
executable file
·259 lines (224 loc) · 9.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#!/usr/bin/python
# coding=utf8
# author wenxingsen.wxs
import os
import sys
import uuid
import threading
import time
import datetime
import subprocess
import getopt
# 统计开始时间
GLOBAL_START_TIME = datetime.datetime.now()
def get_cost_time():
'''
获取当前运行时间
'''
cost_time_sec = (datetime.datetime.now() - GLOBAL_START_TIME).seconds
return "%dm%.2ds" % (cost_time_sec / 60, cost_time_sec % 60)
def print_log(log_str):
'''
打印日志函数
'''
print("[%s %s] %s" % (time.strftime("%H:%M:%S", time.localtime()), get_cost_time(), log_str))
sys.stdout.flush()
class GlobalConf():
'''
编译配置类
'''
def __init__(self):
# 输入静态库路径
self.origin_static_abs_path = ""
# 输出静态库路径
self.target_static_abs_path = ""
# 链接器路径
self.ld_bin_abs_path = ""
self.workspace = "bitcode_to_elfobj_dir_%s" % str(uuid.uuid1())
GLOBAL_CONF = GlobalConf()
class ERROR_CODE():
'''
错误码
'''
# 通用成功和失败
COMMON_SUCCESS = 0
COMMON_ERROR = 1
def shell_run_command(command_str, need_print_all=True, need_print_output=True, no_time=False):
'''
运行shell命令
'''
if need_print_all:
print_log("[运行命令]: %s" % command_str)
if not need_print_output:
print_log("[运行输出]: 日志过多已经忽略输出")
result = dict()
result["return_code"] = 1
result["return_message"] = []
result["cmd"] = command_str
is_frist = False
ps = subprocess.Popen(command_str,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
close_fds=True,
universal_newlines=True)
while True:
data = ps.stdout.readline()
if not data:
if ps.poll() is not None:
break
result["return_message"].append(data)
if not need_print_all:
continue
if need_print_output and len(data.strip()) > 1:
if not is_frist:
print_log("[运行输出]: %s" % data.replace("\n", ""))
is_frist = True
else:
data_str = " %s" % data.replace("\n", "")
if no_time:
print(data_str)
else:
print_log(" %s" % data.replace("\n", ""))
result["return_code"] = ps.returncode
return result
def print_help():
'''
打印帮助信息
'''
print("使用说明:")
print("./bitcode_to_elfobj --ld=/usr/bin/ld --input=/xxx/absolude_path/demo.a --output=/xxx/absolude_path/demo.a")
def parse_arg():
'''
解析命令行参数
'''
global GLOBAL_CONF
try:
# sys.argv[1:] 过滤掉第一个参数(它是脚本名称,不是参数的一部分)
opts, args = getopt.getopt(sys.argv[1:], "h", ["help", "input=", "output=","ld="])
if args:
print_log("不符合预期输入,请重试\n")
print_help()
exit(ERROR_CODE.COMMON_ERROR)
for cmd, arg in opts:
if cmd in ("-h", "--help"):
print_help()
exit(ERROR_CODE.COMMON_SUCCESS)
elif cmd in ("--input",):
if not arg.startswith("/") or not os.path.exists(arg):
print("[ERROR] 输入路径[%s]不是绝对路径或者不存在" % arg)
exit(ERROR_CODE.COMMON_ERROR)
GLOBAL_CONF.origin_static_abs_path = arg
elif cmd in ("--output",):
if not arg.startswith("/"):
print("[ERROR] 输入路径[%s]不是绝对路径" % arg)
exit(ERROR_CODE.COMMON_ERROR)
GLOBAL_CONF.target_static_abs_path = arg
elif cmd in ("--ld",):
if not arg.startswith("/") or not os.path.exists(arg):
print("[ERROR] 输入路径[%s]不是绝对路径或者不存在" % arg)
exit(ERROR_CODE.COMMON_ERROR)
GLOBAL_CONF.ld_bin_abs_path = arg
except getopt.GetoptError as ex:
print_log("[ERROR] getopt.GetoptError 解析参数失败,请合法输入, %s" % ex)
exit(ERROR_CODE.COMMON_ERROR)
except ValueError as ex:
print_log("[ERROR] ValueError 解析参数失败,请合法输入, %s" % ex)
exit(ERROR_CODE.COMMON_ERROR)
if not GLOBAL_CONF.origin_static_abs_path or not GLOBAL_CONF.target_static_abs_path or not GLOBAL_CONF.ld_bin_abs_path:
print("[ERROR]输入参数不完整")
print_help()
exit(ERROR_CODE.COMMON_ERROR)
def trigger_one_cmd(cmd):
'''
多线程调用命令函数
'''
result = shell_run_command(cmd)
def main():
'''
main函数入口
'''
# 解析参数
parse_arg()
# 问题背景:
# 将一个静态库中的bitcode obj转成 elf obj的难点解决静态库中重名问题
# 例如hello.a 由 a.o b.o b.o三个目标组成,按照普通解压 ar -x hello.a,只会保留 a.o b.o,目标文件将丢失,而且顺序也丢失
# 在OB联合编译之后,存在大量的重复 0_cxx.cxx,1_cxx.cxx等,其外此转化工具自身也要要求解决重复目标文件等能力
# 解决方案:
# 利用ar的解压count指定特定目标文件能力,主要是N参数
# ar -xN 1 hello.a a.o 并放到 0/a.o下面去
# ar -xN 1 hello.a b.o 并放到 1/b.o下面去
# ar -xN 2 hello.a b.o 并放到 2/b.o下面去
# 依次调用 ld.lld -r a.o -o a.o 将bitcode转成elf
# 最后再按照原有顺序拼接静态库:
# ar -qc hello.a 0/a.o 1/b.o 2/b.o
static_lib_name = os.path.basename(GLOBAL_CONF.origin_static_abs_path)
print_log("开始使用链接器(%s)将bitcode obj(%s)转换为elf obj(%s)" % (GLOBAL_CONF.ld_bin_abs_path, GLOBAL_CONF.origin_static_abs_path, GLOBAL_CONF.target_static_abs_path))
time.sleep(0.1)
# 提取 目标文件
print_log("开始分析静态库%s..." % static_lib_name)
result = shell_run_command("ar -t %s" % (GLOBAL_CONF.origin_static_abs_path))
if result['return_code'] != ERROR_CODE.COMMON_SUCCESS:
print(result['return_message'])
print_log("[ERROR]获取目标文件列表失败,运行命令为: %s" % result['cmd'])
exit(ERROR_CODE.COMMON_ERROR)
# 用于统计同名目标文件出现的次数
object_count_dict = {}
# 按照顺序的全部目标文件
all_object_list = []
for one_object in result['return_message']:
one_object = one_object.strip()
# 消除不同的机器空行
if not one_object:
continue
if one_object not in object_count_dict:
object_count_dict[one_object] = 1
else:
object_count_dict[one_object] += 1
all_object_list.append({"id":len(all_object_list), "object": one_object, "count": object_count_dict[one_object]})
# 创建工作目录
result = shell_run_command("rm -rf %s && mkdir -p %s" % (GLOBAL_CONF.workspace, GLOBAL_CONF.workspace), need_print_all=False)
if result['return_code'] != ERROR_CODE.COMMON_SUCCESS:
print_log("[ERROR]创建文件夹失败")
exit(ERROR_CODE.COMMON_ERROR)
# 开始解压文件
print_log("开始提取静态库%s..." % static_lib_name)
ar_after_object_list = []
for one_object in all_object_list:
result = shell_run_command("cd %s && rm -rf %s && mkdir -p %s && cd %s && ar -xN %s %s %s" % (GLOBAL_CONF.workspace, one_object['id'], one_object['id'], one_object['id'], one_object['count'], GLOBAL_CONF.origin_static_abs_path, one_object['object']))
if result['return_code'] != ERROR_CODE.COMMON_SUCCESS:
print(result['return_message'])
print_log("[ERROR]解压静态库失败, 运行命令为: %s" % result['cmd'])
exit(ERROR_CODE.COMMON_ERROR)
ar_after_object_list.append(os.path.join(GLOBAL_CONF.workspace, str(one_object['id']), one_object['object']))
print_log("开始转换%s的目标文件..." % static_lib_name)
thread_list = list()
# 开始bitcode转elfobj过程
for one_object in ar_after_object_list:
cmd = "%s -r %s -o %s" % (GLOBAL_CONF.ld_bin_abs_path, one_object, one_object)
t = threading.Thread(target=trigger_one_cmd, args=(cmd, ))
thread_list.append(t)
t.start()
# 防止内存不足,加一点时延
time.sleep(0.2)
# 等待所有线程结束
for t in thread_list:
t.join()
# 最后链接过程
print_log("开始链接静态库%s..." % static_lib_name)
cmd_ar_finial = "rm -rf %s && ar qc %s " % (GLOBAL_CONF.target_static_abs_path, GLOBAL_CONF.target_static_abs_path)
for one_object in ar_after_object_list:
cmd_ar_finial += "%s " % one_object
result = shell_run_command(cmd_ar_finial)
if result['return_code'] != ERROR_CODE.COMMON_SUCCESS:
print(result['return_message'])
print_log("[ERROR] 生成elf格式obj失败,运行命令为: %s" % result['cmd'])
exit(ERROR_CODE.COMMON_ERROR)
print_log("生成静态库!路径位于位于:%s" % GLOBAL_CONF.target_static_abs_path)
return ERROR_CODE.COMMON_SUCCESS
if __name__ == '__main__':
'''
__main__入口
'''
main()