1#!/usr/bin/env python
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 string
20import sys
21import os
22
23from .elf_file import ElfFile
24from .elf_walker import ELFWalker
25from .module_info import CompileInfoLoader
26from .hdi import HdiParser
27from .sa import SAParser
28from .innerapi import InnerAPILoader
29
30
31class ElfFileWithDepsInfo(ElfFile):
32    def __init__(self, file, prefix):
33        super(ElfFileWithDepsInfo, self).__init__(file, prefix)
34        self["deps"] = []
35        self["dependedBy"] = []
36
37    def __eq__(self, other):
38        if not isinstance(other, ElfFileWithDepsInfo):
39            return NotImplemented
40
41        return self["id"] == other["id"]
42
43    def __repr__(self):
44        return self.__str__()
45
46    def __str__(self):
47        return "%s:%d deps(%d) dependedBy(%d)" % (self["name"], self["id"], len(self["deps"]), len(self["dependedBy"]))
48
49    def depends_on(self, mod):
50        for dep in self["deps"]:
51            if dep["callee"] == mod:
52                return True
53        return False
54
55
56class Dependency(dict):
57    def __init__(self, idx, caller, callee):
58        self["id"] = idx
59        self["caller_id"] = caller["id"]
60        self["callee_id"] = callee["id"]
61        self["caller"] = caller
62        self["callee"] = callee
63        self["external"] = False
64        self["calls"] = 0
65
66    def __eq__(self, other):
67        if not isinstance(other, Dependency):
68            return NotImplemented
69
70        return self["id"] == other["id"]
71
72    def __repr__(self):
73        return self.__str__()
74
75    def __str__(self):
76        return "(%s:%s[%d] -%d:%d-> %s:%s[%d])" % (self["caller"]["componentName"], self["caller"]["name"], self["caller"]["id"], int(self["external"]), self["calls"], self["callee"]["componentName"], self["callee"]["name"], self["callee"]["id"])
77
78
79class ElfFileMgr(object):
80    def __init__(self, product_out_path=None, elf_file_class=None, dependence_class=None):
81        self._elf_files = []
82        self._path_dict = {}
83        self._basename_dict = {}
84        if elf_file_class:
85            self._elf_file_class = elf_file_class
86        else:
87            self._elf_file_class = ElfFileWithDepsInfo
88
89        self._deps = []
90        if dependence_class:
91            self._dependence_class = dependence_class
92        else:
93            self._dependence_class = Dependency
94        self._dep_idx = 1
95        self._elf_idx = 1
96
97        self._not_found_depened_files = []
98
99        walker = ELFWalker(product_out_path)
100        self._prefix = walker.get_product_images_path()
101        self._product_out_path = walker.get_product_out_path()
102        self._link_file_map = walker.get_link_file_map()
103
104    def scan_all_files(self):
105        walker = ELFWalker(self._product_out_path)
106
107        self._scan_all_elf_files(walker)
108        self._build_deps_tree()
109
110        self._max_depth = 0
111        self._max_total_depends = 0
112
113        print("Load compile information now ...")
114        CompileInfoLoader.load(self, self._product_out_path)
115        HdiParser.load(self, self._product_out_path)
116        SAParser.load(self, self._product_out_path)
117
118    def get_product_images_path(self):
119        return self._prefix
120
121    def get_product_out_path(self):
122        return self._product_out_path
123
124    def add_elf_file(self, elf):
125        # Append to array in order
126        elf["id"] = self._elf_idx
127        self._elf_idx = self._elf_idx + 1
128        self._elf_files.append(elf)
129
130        # Add to dictionary with path as key
131        self._path_dict[elf["path"]] = elf
132
133        # Add to dictionary with basename as key
134        if elf["name"] in self._basename_dict:
135            self._basename_dict[elf["name"]].append(elf)
136        else:
137            self._basename_dict[elf["name"]] = [elf]
138
139    def add_dependence(self, caller, callee):
140        dep = self._dependence_class(self._dep_idx, caller, callee)
141        caller["deps"].append(dep)
142        callee["dependedBy"].append(dep)
143
144        self._deps.append(dep)
145        self._dep_idx = self._dep_idx + 1
146        return dep
147
148    def get_elf_by_path(self, path):
149        if path not in self._path_dict and path.find("/lib64/") > 0:
150            path = path.replace("/lib64/", "/lib/")
151        if path in self._path_dict:
152            return self._path_dict[path]
153        if path.find("/platformsdk/") > 0:
154            return None
155
156        if path.startswith("system/lib64/"):
157            path = path.replace("system/lib64/", "system/lib64/platformsdk/")
158        elif path.startswith("system/lib/"):
159            path = path.replace("system/lib/", "system/lib/platformsdk/")
160        else:
161            return None
162
163        if path not in self._path_dict and path.find("/lib64/") > 0:
164            path = path.replace("/lib64/", "/lib/")
165        if path in self._path_dict:
166            return self._path_dict[path]
167        return None
168
169    def get_elf_by_idx(self, idx):
170        if idx < 1 or idx > len(self._elf_files):
171            return None
172        return self._elf_files[idx - 1]
173
174    def get_elf_by_name(self, name):
175        if name in self._basename_dict:
176            return self._basename_dict[name][0]
177
178        return self.__get_link_file(name)
179
180    def get_all(self):
181        return self._elf_files
182
183    def get_all_deps(self):
184        return self._deps
185
186    def _scan_all_elf_files(self, walker):
187        print("Scanning %d ELF files now ..." % len(walker.get_elf_files()))
188        for f in walker.get_elf_files():
189            elf = self._elf_file_class(f, self._prefix)
190            if elf["path"] in self._path_dict:
191                print("Warning: duplicate " + elf.get_file() + ' skipped.')
192                continue
193
194            # Ignore these files
195            if elf["name"] in ["hdc_std"]:
196                continue
197
198            self.add_elf_file(elf)
199
200        # Reorder libraries with same name as defined by LD_LIBRARY_PATH
201        for bname, val in self._basename_dict.items():
202            if len(val) < 2:
203                continue
204            self._basename_dict[bname] = self.__reorder_library(val)
205
206    def __reorder_library(self, val):
207        orders = []
208        idx = 0
209        for p in val:
210            orders.append((self.__get_library_order(p["path"]), idx))
211            idx = idx + 1
212        orders.sort()
213
214        res = []
215        for item in orders:
216            res.append(val[item[1]])
217
218        return res
219
220    def __get_library_order(self, path):
221        if not path.startswith("/"):
222            path = "/" + path
223        if path.find("/lib64/") > 0:
224            path_order = "/system/lib64:/vendor/lib64:/vendor/lib64/chipsetsdk:/system/lib64/ndk:/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/lib:/vendor/lib:/system/lib/ndk:/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"
225        else:
226            path_order = "/system/lib:/vendor/lib:/vendor/lib/chipsetsdk:/system/lib/ndk:/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:/lib:/usr/local/lib:/usr/lib"
227
228        if path.rfind("/") < 0:
229            return 1000
230
231        path = path[:path.rfind("/")]
232        paths = path_order.split(':')
233        idx = 0
234        for p in paths:
235            if p == path:
236                return idx
237            idx = idx + 1
238        return 1000
239
240    def _build_deps_tree(self):
241        print("Build dependence tree for %d ELF files now ..." % len(self._elf_files))
242        for elf in self._elf_files:
243            self.__build_deps_tree_for_one_elf(elf)
244        print("    Got %d dependencies" % self._dep_idx)
245
246    def __build_deps_tree_for_one_elf(self, elf):
247        for lib in elf.library_depends():
248            dep_elf = self.get_elf_by_name(lib)
249            if not dep_elf:
250                self._not_found_depened_files.append({"caller": elf["name"], "callee": lib})
251                print("Warning: can not find depended library [" + lib + "] for " + elf["name"])
252                break
253
254            self.add_dependence(elf, dep_elf)
255
256    def __get_link_file(self, name):
257        for src, target in self._link_file_map.items():
258            tmp_name = os.path.basename(src)
259            if name != tmp_name:
260                continue
261            tmp_name = os.path.dirname(src)
262            tmp_name = os.path.join(tmp_name, target)
263            if name in ["libc.so"]:
264                tmp_name = os.path.normpath(tmp_name)
265                norm_prefix = os.path.normpath(self._prefix) + "/"
266                link_elf = ElfFile(tmp_name, norm_prefix)
267            else:
268                link_elf = ElfFile(tmp_name, self._prefix)
269            return self.get_elf_by_path(link_elf["path"])
270
271
272if __name__ == '__main__':
273    mgr = ElfFileMgr("./demo/archinfo/assets/rk3568/3.2.7.5")
274    mgr.scan_all_files()
275    elf = mgr.get_elf_by_path("system/lib/libskia_ohos.z.so")
276    print("Get skia now ...")
277
278    res = mgr.get_elf_by_path("system/lib/platformsdk/libhmicui18n.z.so")
279    print(res)
280