1*33f37583SAndroid Build Coastguard Worker#!/usr/bin/env python 2*33f37583SAndroid Build Coastguard Worker# 3*33f37583SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 4*33f37583SAndroid Build Coastguard Worker# 5*33f37583SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*33f37583SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*33f37583SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*33f37583SAndroid Build Coastguard Worker# 9*33f37583SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*33f37583SAndroid Build Coastguard Worker# 11*33f37583SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*33f37583SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*33f37583SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*33f37583SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*33f37583SAndroid Build Coastguard Worker# limitations under the License. 16*33f37583SAndroid Build Coastguard Worker"""apex_elf_checker checks if ELF files in the APEX 17*33f37583SAndroid Build Coastguard Worker 18*33f37583SAndroid Build Coastguard WorkerUsage: apex_elf_checker [--unwanted <names>] <apex> 19*33f37583SAndroid Build Coastguard Worker 20*33f37583SAndroid Build Coastguard Worker --unwanted <names> 21*33f37583SAndroid Build Coastguard Worker 22*33f37583SAndroid Build Coastguard Worker Fail if any of ELF files in APEX has any of unwanted names in NEEDED ` 23*33f37583SAndroid Build Coastguard Worker""" 24*33f37583SAndroid Build Coastguard Worker 25*33f37583SAndroid Build Coastguard Workerimport argparse 26*33f37583SAndroid Build Coastguard Workerimport os 27*33f37583SAndroid Build Coastguard Workerimport re 28*33f37583SAndroid Build Coastguard Workerimport subprocess 29*33f37583SAndroid Build Coastguard Workerimport sys 30*33f37583SAndroid Build Coastguard Workerimport tempfile 31*33f37583SAndroid Build Coastguard Worker 32*33f37583SAndroid Build Coastguard Worker 33*33f37583SAndroid Build Coastguard Worker_DYNAMIC_SECTION_NEEDED_PATTERN = re.compile( 34*33f37583SAndroid Build Coastguard Worker '^ 0x[0-9a-fA-F]+\\s+NEEDED\\s+Shared library: \\[(.*)\\]$' 35*33f37583SAndroid Build Coastguard Worker) 36*33f37583SAndroid Build Coastguard Worker 37*33f37583SAndroid Build Coastguard Worker 38*33f37583SAndroid Build Coastguard Worker_ELF_MAGIC = b'\x7fELF' 39*33f37583SAndroid Build Coastguard Worker 40*33f37583SAndroid Build Coastguard Worker 41*33f37583SAndroid Build Coastguard Workerdef ParseArgs(): 42*33f37583SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 43*33f37583SAndroid Build Coastguard Worker parser.add_argument('apex', help='Path to the APEX') 44*33f37583SAndroid Build Coastguard Worker parser.add_argument( 45*33f37583SAndroid Build Coastguard Worker '--tool_path', 46*33f37583SAndroid Build Coastguard Worker help='Tools are searched in TOOL_PATH/bin. Colon-separated list of paths', 47*33f37583SAndroid Build Coastguard Worker ) 48*33f37583SAndroid Build Coastguard Worker parser.add_argument( 49*33f37583SAndroid Build Coastguard Worker '--unwanted', 50*33f37583SAndroid Build Coastguard Worker help='Names not allowed in DT_NEEDED. Colon-separated list of names', 51*33f37583SAndroid Build Coastguard Worker ) 52*33f37583SAndroid Build Coastguard Worker return parser.parse_args() 53*33f37583SAndroid Build Coastguard Worker 54*33f37583SAndroid Build Coastguard Worker 55*33f37583SAndroid Build Coastguard Workerdef InitTools(tool_path): 56*33f37583SAndroid Build Coastguard Worker if tool_path is None: 57*33f37583SAndroid Build Coastguard Worker exec_path = os.path.realpath(sys.argv[0]) 58*33f37583SAndroid Build Coastguard Worker if exec_path.endswith('.py'): 59*33f37583SAndroid Build Coastguard Worker script_name = os.path.basename(exec_path)[:-3] 60*33f37583SAndroid Build Coastguard Worker sys.exit( 61*33f37583SAndroid Build Coastguard Worker f'Do not invoke {exec_path} directly. Instead, use {script_name}' 62*33f37583SAndroid Build Coastguard Worker ) 63*33f37583SAndroid Build Coastguard Worker tool_path = os.environ['PATH'] 64*33f37583SAndroid Build Coastguard Worker 65*33f37583SAndroid Build Coastguard Worker def ToolPath(name): 66*33f37583SAndroid Build Coastguard Worker for p in tool_path.split(':'): 67*33f37583SAndroid Build Coastguard Worker path = os.path.join(p, name) 68*33f37583SAndroid Build Coastguard Worker if os.path.exists(path): 69*33f37583SAndroid Build Coastguard Worker return path 70*33f37583SAndroid Build Coastguard Worker sys.exit(f'Required tool({name}) not found in {tool_path}') 71*33f37583SAndroid Build Coastguard Worker 72*33f37583SAndroid Build Coastguard Worker return { 73*33f37583SAndroid Build Coastguard Worker tool: ToolPath(tool) 74*33f37583SAndroid Build Coastguard Worker for tool in [ 75*33f37583SAndroid Build Coastguard Worker 'deapexer', 76*33f37583SAndroid Build Coastguard Worker 'debugfs_static', 77*33f37583SAndroid Build Coastguard Worker 'fsck.erofs', 78*33f37583SAndroid Build Coastguard Worker 'llvm-readelf', 79*33f37583SAndroid Build Coastguard Worker ] 80*33f37583SAndroid Build Coastguard Worker } 81*33f37583SAndroid Build Coastguard Worker 82*33f37583SAndroid Build Coastguard Worker 83*33f37583SAndroid Build Coastguard Workerdef IsElfFile(path): 84*33f37583SAndroid Build Coastguard Worker with open(path, 'rb') as f: 85*33f37583SAndroid Build Coastguard Worker buf = bytearray(len(_ELF_MAGIC)) 86*33f37583SAndroid Build Coastguard Worker f.readinto(buf) 87*33f37583SAndroid Build Coastguard Worker return buf == _ELF_MAGIC 88*33f37583SAndroid Build Coastguard Worker 89*33f37583SAndroid Build Coastguard Worker 90*33f37583SAndroid Build Coastguard Workerdef ParseElfNeeded(path, tools): 91*33f37583SAndroid Build Coastguard Worker output = subprocess.check_output( 92*33f37583SAndroid Build Coastguard Worker [tools['llvm-readelf'], '-d', '--elf-output-style', 'LLVM', path], 93*33f37583SAndroid Build Coastguard Worker text=True, 94*33f37583SAndroid Build Coastguard Worker stderr=subprocess.PIPE, 95*33f37583SAndroid Build Coastguard Worker ) 96*33f37583SAndroid Build Coastguard Worker 97*33f37583SAndroid Build Coastguard Worker needed = [] 98*33f37583SAndroid Build Coastguard Worker for line in output.splitlines(): 99*33f37583SAndroid Build Coastguard Worker match = _DYNAMIC_SECTION_NEEDED_PATTERN.match(line) 100*33f37583SAndroid Build Coastguard Worker if match: 101*33f37583SAndroid Build Coastguard Worker needed.append(match.group(1)) 102*33f37583SAndroid Build Coastguard Worker return needed 103*33f37583SAndroid Build Coastguard Worker 104*33f37583SAndroid Build Coastguard Worker 105*33f37583SAndroid Build Coastguard Workerdef ScanElfFiles(work_dir): 106*33f37583SAndroid Build Coastguard Worker for parent, _, files in os.walk(work_dir): 107*33f37583SAndroid Build Coastguard Worker for file in files: 108*33f37583SAndroid Build Coastguard Worker path = os.path.join(parent, file) 109*33f37583SAndroid Build Coastguard Worker # Skip symlinks for APEXes with symlink optimization 110*33f37583SAndroid Build Coastguard Worker if os.path.islink(path): 111*33f37583SAndroid Build Coastguard Worker continue 112*33f37583SAndroid Build Coastguard Worker if IsElfFile(path): 113*33f37583SAndroid Build Coastguard Worker yield path 114*33f37583SAndroid Build Coastguard Worker 115*33f37583SAndroid Build Coastguard Worker 116*33f37583SAndroid Build Coastguard Workerdef CheckElfFiles(args, tools): 117*33f37583SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as work_dir: 118*33f37583SAndroid Build Coastguard Worker subprocess.check_output( 119*33f37583SAndroid Build Coastguard Worker [ 120*33f37583SAndroid Build Coastguard Worker tools['deapexer'], 121*33f37583SAndroid Build Coastguard Worker '--debugfs_path', 122*33f37583SAndroid Build Coastguard Worker tools['debugfs_static'], 123*33f37583SAndroid Build Coastguard Worker '--fsckerofs_path', 124*33f37583SAndroid Build Coastguard Worker tools['fsck.erofs'], 125*33f37583SAndroid Build Coastguard Worker 'extract', 126*33f37583SAndroid Build Coastguard Worker args.apex, 127*33f37583SAndroid Build Coastguard Worker work_dir, 128*33f37583SAndroid Build Coastguard Worker ], 129*33f37583SAndroid Build Coastguard Worker text=True, 130*33f37583SAndroid Build Coastguard Worker stderr=subprocess.PIPE, 131*33f37583SAndroid Build Coastguard Worker ) 132*33f37583SAndroid Build Coastguard Worker 133*33f37583SAndroid Build Coastguard Worker if args.unwanted: 134*33f37583SAndroid Build Coastguard Worker unwanted = set(args.unwanted.split(':')) 135*33f37583SAndroid Build Coastguard Worker for file in ScanElfFiles(work_dir): 136*33f37583SAndroid Build Coastguard Worker needed = set(ParseElfNeeded(file, tools)) 137*33f37583SAndroid Build Coastguard Worker if unwanted & needed: 138*33f37583SAndroid Build Coastguard Worker sys.exit( 139*33f37583SAndroid Build Coastguard Worker f'{os.path.relpath(file, work_dir)} has unwanted NEEDED:' 140*33f37583SAndroid Build Coastguard Worker f' {",".join(unwanted & needed)}' 141*33f37583SAndroid Build Coastguard Worker ) 142*33f37583SAndroid Build Coastguard Worker 143*33f37583SAndroid Build Coastguard Worker 144*33f37583SAndroid Build Coastguard Workerdef main(): 145*33f37583SAndroid Build Coastguard Worker args = ParseArgs() 146*33f37583SAndroid Build Coastguard Worker tools = InitTools(args.tool_path) 147*33f37583SAndroid Build Coastguard Worker try: 148*33f37583SAndroid Build Coastguard Worker CheckElfFiles(args, tools) 149*33f37583SAndroid Build Coastguard Worker except subprocess.CalledProcessError as e: 150*33f37583SAndroid Build Coastguard Worker sys.exit('Result:' + str(e.stderr)) 151*33f37583SAndroid Build Coastguard Worker 152*33f37583SAndroid Build Coastguard Worker 153*33f37583SAndroid Build Coastguard Workerif __name__ == '__main__': 154*33f37583SAndroid Build Coastguard Worker main() 155