import sys
import argparse
import os
import platform
import subprocess
import re
def load_removelist():
removelist_path = os.path.join(os.path.dirname(__file__), 'symbol_remove.txt')
patterns = []
if os.path.exists(removelist_path):
with open(removelist_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
patterns.append(re.compile(line))
return patterns
def gen_symbols(tmp_file, sort_lines, symbols_path):
with os.fdopen(os.open(tmp_file, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file:
for item in sort_lines:
output_file.write('{}\n'.format(item))
with os.fdopen(os.open(symbols_path, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file:
cmd = 'sort {}'.format(tmp_file)
subprocess.run(cmd.split(), stdout=output_file)
def create_mini_debug_info(binary_path, stripped_binary_path, root_path, clang_base_dir):
dynsyms_path = stripped_binary_path + ".dynsyms"
funcsysms_path = stripped_binary_path + ".funcsyms"
keep_path = stripped_binary_path + ".keep"
debug_path = stripped_binary_path + ".debug"
mini_debug_path = stripped_binary_path + ".minidebug"
host_platform = platform.uname().system.lower()
host_cpu = platform.uname().machine.lower()
llvm_dir_path = os.path.join(clang_base_dir, 'bin')
llvm_nm_path = os.path.join(llvm_dir_path, "llvm-nm")
llvm_objcopy_path = os.path.join(llvm_dir_path, "llvm-objcopy")
cmd_list = []
gen_symbols_cmd = llvm_nm_path + " -D " + binary_path + " --format=posix --defined-only"
gen_func_symbols_cmd = llvm_nm_path + " " + binary_path + " --format=posix --defined-only"
gen_keep_symbols_cmd = "comm -13 " + dynsyms_path + " " + funcsysms_path
gen_keep_debug_cmd = llvm_objcopy_path + \
" --only-keep-debug " + binary_path + " " + debug_path
gen_mini_debug_cmd = llvm_objcopy_path + " -S --remove-section .gdb_index --remove-section .comment --keep-symbols=" + \
keep_path + " " + debug_path + " " + mini_debug_path
compress_debuginfo = "xz " + mini_debug_path
gen_stripped_binary = llvm_objcopy_path + " --add-section .gnu_debugdata=" + \
mini_debug_path + ".xz " + stripped_binary_path
tmp_file1 = '{}.tmp1'.format(dynsyms_path)
tmp_file2 = '{}.tmp2'.format(dynsyms_path)
with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file:
subprocess.run(gen_symbols_cmd.split(), stdout=output_file)
with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'r', encoding='utf-8') as output_file:
lines = output_file.readlines()
sort_lines = []
for line in lines:
columns = line.strip().split()
if columns:
sort_lines.append(columns[0])
gen_symbols(tmp_file2, sort_lines, dynsyms_path)
os.remove(tmp_file1)
os.remove(tmp_file2)
tmp_file1 = '{}.tmp1'.format(funcsysms_path)
tmp_file2 = '{}.tmp2'.format(funcsysms_path)
with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file:
subprocess.run(gen_func_symbols_cmd.split(), stdout=output_file)
with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'r', encoding='utf-8') as output_file:
lines = output_file.readlines()
sort_lines = []
removelist_patterns = load_removelist()
for line in lines:
columns = line.strip().split()
if len(columns) > 2 and ('t' in columns[1] or 'T' in columns[1] or 'd' in columns[1]):
symbol_name = columns[0]
if any(pattern.search(symbol_name) for pattern in removelist_patterns):
continue
sort_lines.append(symbol_name)
gen_symbols(tmp_file2, sort_lines, funcsysms_path)
os.remove(tmp_file1)
os.remove(tmp_file2)
with os.fdopen(os.open(keep_path, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file:
subprocess.run(gen_keep_symbols_cmd.split(), stdout=output_file)
cmd_list.append(gen_keep_debug_cmd)
cmd_list.append(gen_mini_debug_cmd)
cmd_list.append(compress_debuginfo)
cmd_list.append(gen_stripped_binary)
for cmd in cmd_list:
subprocess.call(cmd.split(), shell=False)
os.remove(dynsyms_path)
os.remove(funcsysms_path)
os.remove(keep_path)
os.remove(debug_path)
os.remove(mini_debug_path + ".xz")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--unstripped-path",
help="unstripped binary path")
parser.add_argument("--stripped-path",
help="stripped binary path")
parser.add_argument("--root-path",
help="root path is used to search llvm toolchain")
parser.add_argument("--clang-base-dir", help="")
args = parser.parse_args()
create_mini_debug_info(args.unstripped_path,
args.stripped_path, args.root_path, args.clang_base_dir)
if __name__ == "__main__":
sys.exit(main())