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 24import stat 25 26 27def gen_symbols(tmp_file, sort_lines, symbols_path): 28 modes = stat.S_IWUSR | stat.S_IRUSR | stat.S_IWGRP | stat.S_IRGRP 29 with os.fdopen(os.open(tmp_file, os.O_RDWR | os.O_CREAT, modes), 'w', encoding='utf-8') as output_file: 30 for item in sort_lines: 31 output_file.write('{}\n'.format(item)) 32 33 with os.fdopen(os.open(symbols_path, os.O_RDWR | os.O_CREAT, modes), 'w', encoding='utf-8') as output_file: 34 cmd = 'sort {}'.format(tmp_file) 35 subprocess.run(cmd.split(), stdout=output_file) 36 37 38def create_mini_debug_info(binary_path, stripped_binary_path, root_path, clang_base_dir, adlt_llvm_tool): 39 # temporary file path 40 dynsyms_path = stripped_binary_path + ".dynsyms" 41 funcsysms_path = stripped_binary_path + ".funcsyms" 42 keep_path = stripped_binary_path + ".keep" 43 debug_path = stripped_binary_path + ".debug" 44 mini_debug_path = stripped_binary_path + ".minidebug" 45 46 # llvm tools path 47 host_platform = platform.uname().system.lower() 48 host_cpu = platform.uname().machine.lower() 49 llvm_dir_path = os.path.join( 50 clang_base_dir, host_platform + '-' + host_cpu, 'llvm/bin') 51 if not os.path.exists(llvm_dir_path): 52 llvm_dir_path = os.path.join(root_path, 'out/llvm-install/bin') 53 if adlt_llvm_tool: 54 llvm_dir_path = adlt_llvm_tool 55 llvm_nm_path = os.path.join(llvm_dir_path, "llvm-nm") 56 llvm_objcopy_path = os.path.join(llvm_dir_path, "llvm-objcopy") 57 58 cmd_list = [] 59 60 gen_symbols_cmd = llvm_nm_path + " -D " + binary_path + " --format=posix --defined-only" 61 gen_func_symbols_cmd = llvm_nm_path + " " + binary_path + " --format=posix --defined-only" 62 gen_keep_symbols_cmd = "comm -13 " + dynsyms_path + " " + funcsysms_path 63 gen_keep_debug_cmd = llvm_objcopy_path + \ 64 " --only-keep-debug " + binary_path + " " + debug_path 65 gen_mini_debug_cmd = llvm_objcopy_path + " -S --remove-section .gdb_index --remove-section .comment --keep-symbols=" + \ 66 keep_path + " " + debug_path + " " + mini_debug_path 67 compress_debuginfo = "xz " + mini_debug_path 68 gen_stripped_binary = llvm_objcopy_path + " --add-section .gnu_debugdata=" + \ 69 mini_debug_path + ".xz " + stripped_binary_path 70 71 72 tmp_file1 = '{}.tmp1'.format(dynsyms_path) 73 tmp_file2 = '{}.tmp2'.format(dynsyms_path) 74 modes = stat.S_IWUSR | stat.S_IRUSR | stat.S_IWGRP | stat.S_IRGRP 75 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT, modes), 'w', encoding='utf-8') as output_file: 76 subprocess.run(gen_symbols_cmd.split(), stdout=output_file) 77 78 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT, modes), 'r', encoding='utf-8') as output_file: 79 lines = output_file.readlines() 80 sort_lines = [] 81 for line in lines: 82 columns = line.strip().split() 83 if columns: 84 sort_lines.append(columns[0]) 85 86 gen_symbols(tmp_file2, sort_lines, dynsyms_path) 87 os.remove(tmp_file1) 88 os.remove(tmp_file2) 89 90 91 tmp_file1 = '{}.tmp1'.format(funcsysms_path) 92 tmp_file2 = '{}.tmp2'.format(funcsysms_path) 93 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT, modes), 'w', encoding='utf-8') as output_file: 94 subprocess.run(gen_func_symbols_cmd.split(), stdout=output_file) 95 96 with os.fdopen(os.open(tmp_file1, os.O_RDWR | os.O_CREAT, modes), 'r', encoding='utf-8') as output_file: 97 lines = output_file.readlines() 98 sort_lines = [] 99 for line in lines: 100 columns = line.strip().split() 101 if len(columns) > 2 and ('t' in columns[1] or 'T' in columns[1] or 'd' in columns[1]): 102 sort_lines.append(columns[0]) 103 104 gen_symbols(tmp_file2, sort_lines, funcsysms_path) 105 os.remove(tmp_file1) 106 os.remove(tmp_file2) 107 108 109 with os.fdopen(os.open(keep_path, os.O_RDWR | os.O_CREAT, modes), 'w', encoding='utf-8') as output_file: 110 subprocess.run(gen_keep_symbols_cmd.split(), stdout=output_file) 111 112 113 cmd_list.append(gen_keep_debug_cmd) 114 cmd_list.append(gen_mini_debug_cmd) 115 cmd_list.append(compress_debuginfo) 116 cmd_list.append(gen_stripped_binary) 117 118 # execute each cmd to generate temporary file 119 # which .gnu_debugdata section depends on 120 for cmd in cmd_list: 121 subprocess.call(cmd.split(), shell=False) 122 123 # remove temporary file 124 os.remove(dynsyms_path) 125 os.remove(funcsysms_path) 126 os.remove(keep_path) 127 os.remove(debug_path) 128 os.remove(mini_debug_path + ".xz") 129 130 131def main(): 132 parser = argparse.ArgumentParser(description=__doc__) 133 parser.add_argument("--unstripped-path", 134 help="unstripped binary path") 135 parser.add_argument("--stripped-path", 136 help="stripped binary path") 137 parser.add_argument("--root-path", 138 help="root path is used to search llvm toolchain") 139 parser.add_argument("--clang-base-dir", help="") 140 parser.add_argument("--adlt-llvm-tool", help="") 141 args = parser.parse_args() 142 143 create_mini_debug_info(args.unstripped_path, 144 args.stripped_path, args.root_path, args.clang_base_dir, args.adlt_llvm_tool) 145 146 147if __name__ == "__main__": 148 sys.exit(main()) 149