1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# 5# Copyright (c) 2022 Huawei Device Co., Ltd. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19import sys 20import argparse 21import os 22import platform 23import subprocess 24 25 26def gen_symbols(tmp_file, sort_lines, symbols_path): 27 with os.fdopen(os.open(tmp_file, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file: 28 for item in sort_lines: 29 output_file.write('{}\n'.format(item)) 30 31 with os.fdopen(os.open(symbols_path, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file: 32 cmd = 'sort {}'.format(tmp_file) 33 subprocess.run(cmd.split(), stdout=output_file) 34 35 36def create_mini_debug_info(binary_path, stripped_binary_path, root_path, clang_base_dir): 37 # temporary file path 38 dynsyms_path = stripped_binary_path + ".dynsyms" 39 funcsysms_path = stripped_binary_path + ".funcsyms" 40 keep_path = stripped_binary_path + ".keep" 41 debug_path = stripped_binary_path + ".debug" 42 mini_debug_path = stripped_binary_path + ".minidebug" 43 44 # llvm tools path 45 host_platform = platform.uname().system.lower() 46 host_cpu = platform.uname().machine.lower() 47 llvm_dir_path = os.path.join( 48 clang_base_dir, host_platform + '-' + host_cpu, 'llvm/bin') 49 if not os.path.exists(llvm_dir_path): 50 llvm_dir_path = os.path.join(root_path, 'out/llvm-install/bin') 51 llvm_nm_path = os.path.join(llvm_dir_path, "llvm-nm") 52 llvm_objcopy_path = os.path.join(llvm_dir_path, "llvm-objcopy") 53 54 cmd_list = [] 55 56 gen_symbols_cmd = llvm_nm_path + " -D " + binary_path + " --format=posix --defined-only" 57 gen_func_symbols_cmd = llvm_nm_path + " " + binary_path + " --format=posix --defined-only" 58 gen_keep_symbols_cmd = "comm -13 " + dynsyms_path + " " + funcsysms_path 59 gen_keep_debug_cmd = llvm_objcopy_path + \ 60 " --only-keep-debug " + binary_path + " " + debug_path 61 gen_mini_debug_cmd = llvm_objcopy_path + " -S --remove-section .gbd_index --remove-section .comment --keep-symbols=" + \ 62 keep_path + " " + debug_path + " " + mini_debug_path 63 compress_debuginfo = "xz " + mini_debug_path 64 gen_stripped_binary = llvm_objcopy_path + " --add-section .gnu_debugdata=" + \ 65 mini_debug_path + ".xz " + stripped_binary_path 66 67 68 tmp_file1 = '{}.tmp1'.format(dynsyms_path) 69 tmp_file2 = '{}.tmp2'.format(dynsyms_path) 70 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file: 71 subprocess.run(gen_symbols_cmd.split(), stdout=output_file) 72 73 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'r', encoding='utf-8') as output_file: 74 lines = output_file.readlines() 75 sort_lines = [] 76 for line in lines: 77 columns = line.strip().split() 78 if columns: 79 sort_lines.append(columns[0]) 80 81 gen_symbols(tmp_file2, sort_lines, dynsyms_path) 82 os.remove(tmp_file1) 83 os.remove(tmp_file2) 84 85 86 tmp_file1 = '{}.tmp1'.format(funcsysms_path) 87 tmp_file2 = '{}.tmp2'.format(funcsysms_path) 88 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file: 89 subprocess.run(gen_func_symbols_cmd.split(), stdout=output_file) 90 91 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT), 'r', encoding='utf-8') as output_file: 92 lines = output_file.readlines() 93 sort_lines = [] 94 for line in lines: 95 columns = line.strip().split() 96 if len(columns) > 2 and ('t' in columns[1] or 'T' in columns[1] or 'd' in columns[1]): 97 sort_lines.append(columns[0]) 98 99 gen_symbols(tmp_file2, sort_lines, funcsysms_path) 100 os.remove(tmp_file1) 101 os.remove(tmp_file2) 102 103 104 with os.fdopen(os.open(keep_path, os.O_RDWR | os.O_CREAT), 'w', encoding='utf-8') as output_file: 105 subprocess.run(gen_keep_symbols_cmd.split(), stdout=output_file) 106 107 108 cmd_list.append(gen_keep_debug_cmd) 109 cmd_list.append(gen_mini_debug_cmd) 110 cmd_list.append(compress_debuginfo) 111 cmd_list.append(gen_stripped_binary) 112 113 # execute each cmd to generate temporary file 114 # which .gnu_debugdata section depends on 115 for cmd in cmd_list: 116 subprocess.call(cmd.split(), shell=False) 117 118 # remove temporary file 119 os.remove(dynsyms_path) 120 os.remove(funcsysms_path) 121 os.remove(keep_path) 122 os.remove(debug_path) 123 os.remove(mini_debug_path + ".xz") 124 125 126def main(): 127 parser = argparse.ArgumentParser(description=__doc__) 128 parser.add_argument("--unstripped-path", 129 help="unstripped binary path") 130 parser.add_argument("--stripped-path", 131 help="stripped binary path") 132 parser.add_argument("--root-path", 133 help="root path is used to search llvm toolchain") 134 parser.add_argument("--clang-base-dir", help="") 135 args = parser.parse_args() 136 137 create_mini_debug_info(args.unstripped_path, 138 args.stripped_path, args.root_path, args.clang_base_dir) 139 140 141if __name__ == "__main__": 142 sys.exit(main()) 143