diff --git a/scripts/paths.json b/scripts/paths.json new file mode 100644 index 0000000000000000000000000000000000000000..217f67ad9cfdb8600f5ae80a5cb46bd9d90204e7 --- /dev/null +++ b/scripts/paths.json @@ -0,0 +1,55 @@ +{ + "64": [ + "/system/lib64", + "/vendor/lib64", + "/vendor/lib64/chipsetsdk", + "/vendor/lib64/chipset-sdk", + "/system/lib64/chipset-pub-sdk", + "/system/lib64/chipset-sdk", + "/system/lib64/platformsdk", + "/system/lib64/priv-platformsdk", + "/system/lib64/priv-module", + "/system/lib64/module", + "/system/lib64/module/data", + "/system/lib64/module/multimedia", + "/system/lib64/module/security", + "/system/lib", + "/vendor/lib", + "/system/lib/chipset-pub-sdk", + "/system/lib/chipset-sdk", + "/system/lib/platformsdk", + "/system/lib/priv-platformsdk", + "/system/lib/priv-module", + "/system/lib/module", + "/system/lib/module/data", + "/system/lib/module/multimedia", + "/lib64", + "/lib", + "/usr/local/lib", + "/usr/lib", + "/vendor/lib64/hw", + "/lib64/platformsdk", + "/lib64/chipset-pub-sdk" + ], + "32": [ + "/system/lib", + "/system/lib/ndk", + "/vendor/lib", + "/vendor/lib/chipsetsdk", + "/vendor/lib/chipset-sdk", + "/system/lib/chipset-pub-sdk", + "/system/lib/chipset-sdk", + "/system/lib/platformsdk", + "/system/lib/priv-platformsdk", + "/system/lib/priv-module", + "/system/lib/module", + "/system/lib/module/data", + "/system/lib/module/multimedia", + "/system/lib/module/security", + "/lib", + "/usr/local/lib", + "/usr/lib", + "/lib/platformsdk", + "/lib/chipset-pub-sdk" + ] +} \ No newline at end of file diff --git a/scripts/print_so_deps.py b/scripts/print_so_deps.py new file mode 100755 index 0000000000000000000000000000000000000000..6a9413a58569ed5450ee1825420d85ea3da36300 --- /dev/null +++ b/scripts/print_so_deps.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#/ + + +import argparse +import copy +import json +import os +import re +import sys +import subprocess + +# the absolute path to llvm-readelf +READELF_PATH = "" +# the absolute path to hdc +HDC = "" +DEBUG = 0 + + +class SoParser: + def __init__(self, cpu): + self.soname_deps = {} + self.pulled_so = [] + self.visiting = [] + self.visited = [] + self.saved_so_path = [] + self.search_paths = "" + + @staticmethod + def run_cmd(cmd): + if DEBUG: + print("CMD: " + cmd) + process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + try: + outs, errs = process.communicate(timeout=10) + except subprocess.TimeoutExpired: + process.kill() + print("timeout error:", " ".join(cmd), errs) + return outs.decode().splitlines() + + @staticmethod + def get_soname(line): + m = re.search("[\S\s]+\[(\S+)\]", line) + if not m: + return "" + soname = m.group(1) + return soname + + def pull_so(self, path): + cmd = "{} file recv {} {}".format(HDC, path, self.saved_so_path) + lines = self.run_cmd(cmd) + for line in lines: + if "success" in line: + return 1 + return 0 + + def is_path_exists(self, path): + cmd = "{} shell ls {}".format(HDC, path) + lines = self.run_cmd(cmd) + for line in lines: + if "No such file or directory" in line: + return 0 + return 1 + + def write_results(self, out_format): + if out_format is None: + self.write_dot() + self.write_csv() + self.write_json() + self.write_txt() + if out_format == "csv": + self.write_csv() + if out_format == "txt": + self.write_txt() + if out_format == "json": + self.write_json() + if out_format == "dot": + self.write_dot() + + def write_dot(self): + with os.fdopen(os.open("dep.dot", os.O_CREAT | os.O_WRONLY, 0o755), 'w', encoding='utf-8') as f: + f.write("digraph deps {\n") + dot_format = "\"{}\"->\"{}\" [label={}];\n" + for soname, deps in self.soname_deps.items(): + index = 0 + for so in deps: + index += 1 + f.write(dot_format.format(soname, so, index)) + f.write("}\n") + f.close() + self.run_cmd("dot -Tsvg -o dep.svg dep.dot") + + def write_json(self): + with os.fdopen(os.open("dep.json", os.O_CREAT | os.O_WRONLY, 0o755), 'w', encoding='utf-8') as f: + to_json = json.dumps(self.soname_deps) + f.write(to_json) + f.close() + + def write_txt(self): + with os.fdopen(os.open("dep.ext", os.O_CREAT | os.O_WRONLY, 0o755), 'w', encoding='utf-8') as f: + for soname, deps in self.soname_deps.items(): + f.write("{}:{}\n".format(soname, ",".join(deps))) + f.close() + + def write_csv(self): + with os.fdopen(os.open("dep.csv", os.O_CREAT | os.O_WRONLY, 0o755), 'w', encoding='utf-8') as f: + for soname, deps in self.soname_deps.items(): + f.write("{},{}\n".format(soname, ",".join(deps))) + f.close() + + def read_soinfo(self, so_path_local): + lines = self.run_cmd("{} -dW {}".format(READELF_PATH, so_path_local)) + deps = [] + entry_so = "" + for line in lines: + if "Library soname" in line: + entry_so = self.get_soname(line) + if "NEEDED" in line: + deps.append(self.get_soname(line)) + + self.soname_deps[entry_so] = deps + return deps + + def find_so(self, so_name): + if so_name in self.pulled_so: + print(so_name + " already saved in " + self.saved_so_path) + return 1 + for path in self.search_paths: + abs_path = os.path.join(path, so_name) + if self.is_path_exists(abs_path): + if self.pull_so(abs_path) == 0: + print("pull so failed!") + exit(-1) + self.pulled_so.append(so_name) + return 1 + return 0 + + def bfs(self, so_name, skip_pull_so): + self.visiting.append(so_name) + index = 0 + while len(self.visiting) > 0: + cur = self.visiting.pop(0) + if cur in self.visited: + continue + index += 1 + if not skip_pull_so: + if self.find_so(cur) == 0: + print("can't find {}".format(cur)) + exit(-1) + + deps = self.read_soinfo(self.saved_so_path + cur) + print("\033[1;33m index:{} so:{} deps:[{}]\033[0m \n".format(index, cur, ",".join(deps))) + if deps: + self.visiting.extend(deps) + self.visited.append(cur) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-i', '--input', type=str, required=True, help='Input so name that you want to show its deps.') + parser.add_argument('-f', '--format', type=str, help='Set output file format: csv, json, dot, txt, default is all.') + parser.add_argument('-s', '--skip-pull-so', action='store_true', help='You can skip pull so from device if you have done it.') + parser.add_argument('-c', '--cpu', type=str, required=True, help='Set device type: 64 or 32 system.') + parser.add_argument('-p', '--path', type=str, default="paths.json", help='Set so search paths in your device.') + args = parser.parse_args() + + so_parser = SoParser(args.cpu) + with os.fdopen(os.open(args.path, os.O_RDONLY, 0o755), 'r', encoding='utf-8') as f: + so_parser.search_paths = json.load(f)[args.cpu] + so_parser.saved_so_path = os.path.join(os.getcwd(), "saved/") + if not args.skip_pull_so: + so_parser.run_cmd("rm -rf {} ".format(so_parser.saved_so_path)) + so_parser.run_cmd("mkdir {}".format(so_parser.saved_so_path)) + so_parser.bfs(args.input, args.skip_pull_so) + so_parser.write_results(args.format) + + +if __name__ == "__main__": + sys.exit(main())