1#!/usr/bin/env python
2# coding=utf-8
3
4#
5# Copyright (c) 2024 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 os
20import sys
21import subprocess
22import json
23from collections import OrderedDict
24
25sys.path.append(
26    os.path.dirname(os.path.dirname(os.path.dirname(
27        os.path.abspath(__file__)))))
28from scripts.util.file_utils import read_json_file
29
30
31class KernelPermission():
32
33
34    @staticmethod
35    def run(out_path, root_path):
36        target_out_path = os.path.join(root_path, out_path.lstrip("//"))
37        print("target_out_path", target_out_path)
38        KernelPermission.execute_kernel_permission_cmd(target_out_path, root_path)
39
40
41    @staticmethod
42    def scan_file(out_path):
43        """scan path uild_configs/kernel_permission/
44        return file_list include kernel_permission.json
45        """
46        file_list = []
47        file_path = file_path = os.path.join(out_path, "build_configs/kernel_permission/")
48        for root, subdirs, files in os.walk(file_path):
49            for _filename in files:
50                content = read_json_file(os.path.join(root, _filename))
51                file_list.append(content[0])
52        return file_list
53
54
55    @staticmethod
56    def execute_kernel_permission_cmd(out_path, root_path):
57        """execute cmd
58        llvm-object --add-section .kernelpermission=json_file xx/xx.so
59        """
60        print("begin run kernel permission cmd")
61
62        try:
63            llvm_tool = KernelPermission.regist_llvm_objcopy_path(root_path)
64        except FileNotFoundError as e:
65            print("regist_llvm_objcopy_path failed:{}".format(e))
66        file_list = KernelPermission.scan_file(out_path)
67
68        cmds = KernelPermission.gen_cmds(file_list, out_path, llvm_tool)
69        if cmds:
70            for cmd in cmds:
71                print("llvm cmd: {}".format(cmd))
72                KernelPermission.exec_command(cmd)
73        else:
74            print("There is no kernel permission json file,no need to run llvm-object cmd.")
75
76
77    @staticmethod
78    def regist_llvm_objcopy_path(root_path):
79        """find llvm_objcopy_path executable
80        :raise FileNotFoundError: when can't find the llvm_objcopy_path excutable
81        """
82        llvm_objcopy_path = os.path.join(root_path, "prebuilts/clang/ohos/linux-x86_64/llvm/bin/llvm-objcopy")
83        if os.path.exists(llvm_objcopy_path):
84            return llvm_objcopy_path
85        else:
86            raise FileNotFoundError(
87                'There is no llvm-object executable file at {}'.format(llvm_objcopy_path), '0001')
88
89
90    @staticmethod
91    def gen_cmds(file_list, out_path, llvm_path):
92        """generate cmd
93        llvm-object --add-section .kernelpermission=json_file xx/xx.so
94        """
95        cmds = []
96        cmd = []
97        for info in file_list:
98            kernel_permission_file = os.path.join(out_path, info.get("kernel_permission_path"))
99            if not KernelPermission.check_json_file(kernel_permission_file):
100                raise FileExistsError(
101                    'kernel_permission json file {} invalid!'.format(kernel_permission_file), '0001')
102            target_name = info.get("target_name")
103            output_extension = info.get("gn_output_extension")
104            output_name = info.get("gn_output_name")
105            part_name = info.get("part_name")
106            subsystem_name = info.get("subsystem_name")
107            target_type = info.get("type")
108            module_name = target_name
109            if output_name == "" and output_extension == "":
110                if target_type == "lib" and target_name.startswith("lib"):
111                    module_name = "{}.z.so".format(target_name)
112                elif target_type == "lib" and not target_name.startswith("lib"):
113                    module_name = "lib{}.z.so".format(target_name)
114            print("module_name:{}".format(module_name))
115            module_path = os.path.join(subsystem_name, part_name)
116            module_path = os.path.join(module_path, module_name)
117            print("module_path:{}".format(module_path))
118            target_source = os.path.join(out_path, module_path)
119            if os.path.exists(target_source):
120                cmd = [llvm_path,
121                        "--add-section",
122                        ".kernelpermission=" + kernel_permission_file,
123                        target_source
124                        ]
125                cmds.append(cmd)
126        return cmds
127
128
129    @staticmethod
130    def check_json_file(file_path):
131        try:
132            with os.fdopen(os.open(file_path, os.O_RDWR, 0o640), 'r') as file:
133                json_data = json.load(file)
134                if KernelPermission.check_json_content(json_data):
135                    return True
136                else:
137                    print("encaps.json is invalid")
138                    return False
139        except FileNotFoundError:
140            print("encaps.json is not found")
141            return False
142        except json.JSONDecodeError:
143            print("encaps.json doesn't conform to json format")
144            return False
145        except Exception:
146            print("encaps.json error")
147            return False
148
149
150    @staticmethod
151    def check_json_content(json_data):
152        if len(json_data) == 1 and "encaps" in json_data:
153            return KernelPermission.check_json_value(json_data)
154        else:
155            return False
156
157
158    @staticmethod
159    def check_json_value(json_data):
160        encaps_data = json_data["encaps"]
161        for key, value in encaps_data.items():
162            if not isinstance(value, (bool, int)):
163                return False
164        return True
165
166
167    @staticmethod
168    def exec_command(cmd: list, exec_env=None, **kwargs):
169        process = subprocess.Popen(cmd,
170                                   stdout=subprocess.PIPE,
171                                   stderr=subprocess.STDOUT,
172                                   encoding='utf-8',
173                                   errors='ignore',
174                                   env=exec_env,
175                                   **kwargs)
176        for line in iter(process.stdout.readline, ''):
177            print(line)
178
179        process.wait()
180        ret_code = process.returncode
181
182        if ret_code != 0:
183            raise Exception(
184                'Please check llvm cmd: {}'.format(cmd))
185
186if __name__ == "__main__":
187    pass