1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project 4*9e94795aSAndroid Build Coastguard Worker# 5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*9e94795aSAndroid Build Coastguard Worker# 11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Worker""" 18*9e94795aSAndroid Build Coastguard WorkerA tool to extract kernel information from a kernel image. 19*9e94795aSAndroid Build Coastguard Worker""" 20*9e94795aSAndroid Build Coastguard Worker 21*9e94795aSAndroid Build Coastguard Workerimport argparse 22*9e94795aSAndroid Build Coastguard Workerimport subprocess 23*9e94795aSAndroid Build Coastguard Workerimport sys 24*9e94795aSAndroid Build Coastguard Workerimport re 25*9e94795aSAndroid Build Coastguard Worker 26*9e94795aSAndroid Build Coastguard WorkerCONFIG_PREFIX = b'IKCFG_ST' 27*9e94795aSAndroid Build Coastguard WorkerGZIP_HEADER = b'\037\213\010' 28*9e94795aSAndroid Build Coastguard WorkerCOMPRESSION_ALGO = ( 29*9e94795aSAndroid Build Coastguard Worker (["gzip", "-d"], GZIP_HEADER), 30*9e94795aSAndroid Build Coastguard Worker (["xz", "-d"], b'\3757zXZ\000'), 31*9e94795aSAndroid Build Coastguard Worker (["bzip2", "-d"], b'BZh'), 32*9e94795aSAndroid Build Coastguard Worker (["lz4", "-d", "-l"], b'\002\041\114\030'), 33*9e94795aSAndroid Build Coastguard Worker 34*9e94795aSAndroid Build Coastguard Worker # These are not supported in the build system yet. 35*9e94795aSAndroid Build Coastguard Worker # (["unlzma"], b'\135\0\0\0'), 36*9e94795aSAndroid Build Coastguard Worker # (["lzop", "-d"], b'\211\114\132'), 37*9e94795aSAndroid Build Coastguard Worker) 38*9e94795aSAndroid Build Coastguard Worker 39*9e94795aSAndroid Build Coastguard Worker# "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" 40*9e94795aSAndroid Build Coastguard Worker# LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n"; 41*9e94795aSAndroid Build Coastguard WorkerLINUX_BANNER_PREFIX = b'Linux version ' 42*9e94795aSAndroid Build Coastguard WorkerLINUX_BANNER_REGEX = LINUX_BANNER_PREFIX.decode() + \ 43*9e94795aSAndroid Build Coastguard Worker r'(?P<release>(?P<version>[0-9]+[.][0-9]+[.][0-9]+).*) \(.*@.*\) \((?P<compiler>.*)\) .*\n' 44*9e94795aSAndroid Build Coastguard Worker 45*9e94795aSAndroid Build Coastguard Worker 46*9e94795aSAndroid Build Coastguard Workerdef get_from_release(input_bytes, start_idx, key): 47*9e94795aSAndroid Build Coastguard Worker null_idx = input_bytes.find(b'\x00', start_idx) 48*9e94795aSAndroid Build Coastguard Worker if null_idx < 0: 49*9e94795aSAndroid Build Coastguard Worker return None 50*9e94795aSAndroid Build Coastguard Worker try: 51*9e94795aSAndroid Build Coastguard Worker linux_banner = input_bytes[start_idx:null_idx].decode() 52*9e94795aSAndroid Build Coastguard Worker except UnicodeDecodeError: 53*9e94795aSAndroid Build Coastguard Worker return None 54*9e94795aSAndroid Build Coastguard Worker mo = re.match(LINUX_BANNER_REGEX, linux_banner) 55*9e94795aSAndroid Build Coastguard Worker if mo: 56*9e94795aSAndroid Build Coastguard Worker return mo.group(key) 57*9e94795aSAndroid Build Coastguard Worker return None 58*9e94795aSAndroid Build Coastguard Worker 59*9e94795aSAndroid Build Coastguard Worker 60*9e94795aSAndroid Build Coastguard Workerdef dump_from_release(input_bytes, key): 61*9e94795aSAndroid Build Coastguard Worker """ 62*9e94795aSAndroid Build Coastguard Worker Helper of dump_version and dump_release 63*9e94795aSAndroid Build Coastguard Worker """ 64*9e94795aSAndroid Build Coastguard Worker idx = 0 65*9e94795aSAndroid Build Coastguard Worker while True: 66*9e94795aSAndroid Build Coastguard Worker idx = input_bytes.find(LINUX_BANNER_PREFIX, idx) 67*9e94795aSAndroid Build Coastguard Worker if idx < 0: 68*9e94795aSAndroid Build Coastguard Worker return None 69*9e94795aSAndroid Build Coastguard Worker 70*9e94795aSAndroid Build Coastguard Worker value = get_from_release(input_bytes, idx, key) 71*9e94795aSAndroid Build Coastguard Worker if value: 72*9e94795aSAndroid Build Coastguard Worker return value.encode() 73*9e94795aSAndroid Build Coastguard Worker 74*9e94795aSAndroid Build Coastguard Worker idx += len(LINUX_BANNER_PREFIX) 75*9e94795aSAndroid Build Coastguard Worker 76*9e94795aSAndroid Build Coastguard Worker 77*9e94795aSAndroid Build Coastguard Workerdef dump_version(input_bytes): 78*9e94795aSAndroid Build Coastguard Worker """ 79*9e94795aSAndroid Build Coastguard Worker Dump kernel version, w.x.y, from input_bytes. Search for the string 80*9e94795aSAndroid Build Coastguard Worker "Linux version " and do pattern matching after it. See LINUX_BANNER_REGEX. 81*9e94795aSAndroid Build Coastguard Worker """ 82*9e94795aSAndroid Build Coastguard Worker return dump_from_release(input_bytes, "version") 83*9e94795aSAndroid Build Coastguard Worker 84*9e94795aSAndroid Build Coastguard Worker 85*9e94795aSAndroid Build Coastguard Workerdef dump_compiler(input_bytes): 86*9e94795aSAndroid Build Coastguard Worker """ 87*9e94795aSAndroid Build Coastguard Worker Dump kernel version, w.x.y, from input_bytes. Search for the string 88*9e94795aSAndroid Build Coastguard Worker "Linux version " and do pattern matching after it. See LINUX_BANNER_REGEX. 89*9e94795aSAndroid Build Coastguard Worker """ 90*9e94795aSAndroid Build Coastguard Worker return dump_from_release(input_bytes, "compiler") 91*9e94795aSAndroid Build Coastguard Worker 92*9e94795aSAndroid Build Coastguard Worker 93*9e94795aSAndroid Build Coastguard Workerdef dump_release(input_bytes): 94*9e94795aSAndroid Build Coastguard Worker """ 95*9e94795aSAndroid Build Coastguard Worker Dump kernel release, w.x.y-..., from input_bytes. Search for the string 96*9e94795aSAndroid Build Coastguard Worker "Linux version " and do pattern matching after it. See LINUX_BANNER_REGEX. 97*9e94795aSAndroid Build Coastguard Worker """ 98*9e94795aSAndroid Build Coastguard Worker return dump_from_release(input_bytes, "release") 99*9e94795aSAndroid Build Coastguard Worker 100*9e94795aSAndroid Build Coastguard Worker 101*9e94795aSAndroid Build Coastguard Workerdef dump_configs(input_bytes): 102*9e94795aSAndroid Build Coastguard Worker """ 103*9e94795aSAndroid Build Coastguard Worker Dump kernel configuration from input_bytes. This can be done when 104*9e94795aSAndroid Build Coastguard Worker CONFIG_IKCONFIG is enabled, which is a requirement on Treble devices. 105*9e94795aSAndroid Build Coastguard Worker 106*9e94795aSAndroid Build Coastguard Worker The kernel configuration is archived in GZip format right after the magic 107*9e94795aSAndroid Build Coastguard Worker string 'IKCFG_ST' in the built kernel. 108*9e94795aSAndroid Build Coastguard Worker """ 109*9e94795aSAndroid Build Coastguard Worker 110*9e94795aSAndroid Build Coastguard Worker # Search for magic string + GZip header 111*9e94795aSAndroid Build Coastguard Worker idx = input_bytes.find(CONFIG_PREFIX + GZIP_HEADER) 112*9e94795aSAndroid Build Coastguard Worker if idx < 0: 113*9e94795aSAndroid Build Coastguard Worker return None 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Worker # Seek to the start of the archive 116*9e94795aSAndroid Build Coastguard Worker idx += len(CONFIG_PREFIX) 117*9e94795aSAndroid Build Coastguard Worker 118*9e94795aSAndroid Build Coastguard Worker sp = subprocess.Popen(["gzip", "-d", "-c"], stdin=subprocess.PIPE, 119*9e94795aSAndroid Build Coastguard Worker stdout=subprocess.PIPE, stderr=subprocess.PIPE) 120*9e94795aSAndroid Build Coastguard Worker o, _ = sp.communicate(input=input_bytes[idx:]) 121*9e94795aSAndroid Build Coastguard Worker if sp.returncode == 1: # error 122*9e94795aSAndroid Build Coastguard Worker return None 123*9e94795aSAndroid Build Coastguard Worker 124*9e94795aSAndroid Build Coastguard Worker # success or trailing garbage warning 125*9e94795aSAndroid Build Coastguard Worker assert sp.returncode in (0, 2), sp.returncode 126*9e94795aSAndroid Build Coastguard Worker 127*9e94795aSAndroid Build Coastguard Worker return o 128*9e94795aSAndroid Build Coastguard Worker 129*9e94795aSAndroid Build Coastguard Worker 130*9e94795aSAndroid Build Coastguard Workerdef try_decompress_bytes(cmd, input_bytes): 131*9e94795aSAndroid Build Coastguard Worker sp = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, 132*9e94795aSAndroid Build Coastguard Worker stderr=subprocess.PIPE) 133*9e94795aSAndroid Build Coastguard Worker o, _ = sp.communicate(input=input_bytes) 134*9e94795aSAndroid Build Coastguard Worker # ignore errors 135*9e94795aSAndroid Build Coastguard Worker return o 136*9e94795aSAndroid Build Coastguard Worker 137*9e94795aSAndroid Build Coastguard Worker 138*9e94795aSAndroid Build Coastguard Workerdef try_decompress(cmd, search_bytes, input_bytes): 139*9e94795aSAndroid Build Coastguard Worker idx = 0 140*9e94795aSAndroid Build Coastguard Worker while True: 141*9e94795aSAndroid Build Coastguard Worker idx = input_bytes.find(search_bytes, idx) 142*9e94795aSAndroid Build Coastguard Worker if idx < 0: 143*9e94795aSAndroid Build Coastguard Worker return 144*9e94795aSAndroid Build Coastguard Worker 145*9e94795aSAndroid Build Coastguard Worker yield try_decompress_bytes(cmd, input_bytes[idx:]) 146*9e94795aSAndroid Build Coastguard Worker idx += 1 147*9e94795aSAndroid Build Coastguard Worker 148*9e94795aSAndroid Build Coastguard Worker 149*9e94795aSAndroid Build Coastguard Workerdef decompress_dump(func, input_bytes): 150*9e94795aSAndroid Build Coastguard Worker """ 151*9e94795aSAndroid Build Coastguard Worker Run func(input_bytes) first; and if that fails (returns value evaluates to 152*9e94795aSAndroid Build Coastguard Worker False), then try different decompression algorithm before running func. 153*9e94795aSAndroid Build Coastguard Worker """ 154*9e94795aSAndroid Build Coastguard Worker o = func(input_bytes) 155*9e94795aSAndroid Build Coastguard Worker if o: 156*9e94795aSAndroid Build Coastguard Worker return o 157*9e94795aSAndroid Build Coastguard Worker for cmd, search_bytes in COMPRESSION_ALGO: 158*9e94795aSAndroid Build Coastguard Worker for decompressed in try_decompress(cmd, search_bytes, input_bytes): 159*9e94795aSAndroid Build Coastguard Worker if decompressed: 160*9e94795aSAndroid Build Coastguard Worker o = decompress_dump(func, decompressed) 161*9e94795aSAndroid Build Coastguard Worker if o: 162*9e94795aSAndroid Build Coastguard Worker return o 163*9e94795aSAndroid Build Coastguard Worker # Force decompress the whole file even if header doesn't match 164*9e94795aSAndroid Build Coastguard Worker decompressed = try_decompress_bytes(cmd, input_bytes) 165*9e94795aSAndroid Build Coastguard Worker if decompressed: 166*9e94795aSAndroid Build Coastguard Worker o = decompress_dump(func, decompressed) 167*9e94795aSAndroid Build Coastguard Worker if o: 168*9e94795aSAndroid Build Coastguard Worker return o 169*9e94795aSAndroid Build Coastguard Worker 170*9e94795aSAndroid Build Coastguard Worker 171*9e94795aSAndroid Build Coastguard Workerdef dump_to_file(f, dump_fn, input_bytes, desc): 172*9e94795aSAndroid Build Coastguard Worker """ 173*9e94795aSAndroid Build Coastguard Worker Call decompress_dump(dump_fn, input_bytes) and write to f. If it fails, return 174*9e94795aSAndroid Build Coastguard Worker False; otherwise return True. 175*9e94795aSAndroid Build Coastguard Worker """ 176*9e94795aSAndroid Build Coastguard Worker if f is not None: 177*9e94795aSAndroid Build Coastguard Worker o = decompress_dump(dump_fn, input_bytes) 178*9e94795aSAndroid Build Coastguard Worker if o: 179*9e94795aSAndroid Build Coastguard Worker f.write(o) 180*9e94795aSAndroid Build Coastguard Worker else: 181*9e94795aSAndroid Build Coastguard Worker sys.stderr.write( 182*9e94795aSAndroid Build Coastguard Worker "Cannot extract kernel {}".format(desc)) 183*9e94795aSAndroid Build Coastguard Worker return False 184*9e94795aSAndroid Build Coastguard Worker return True 185*9e94795aSAndroid Build Coastguard Worker 186*9e94795aSAndroid Build Coastguard Workerdef to_bytes_io(b): 187*9e94795aSAndroid Build Coastguard Worker """ 188*9e94795aSAndroid Build Coastguard Worker Make b, which is either sys.stdout or sys.stdin, receive bytes as arguments. 189*9e94795aSAndroid Build Coastguard Worker """ 190*9e94795aSAndroid Build Coastguard Worker return b.buffer if sys.version_info.major == 3 else b 191*9e94795aSAndroid Build Coastguard Worker 192*9e94795aSAndroid Build Coastguard Workerdef main(): 193*9e94795aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 194*9e94795aSAndroid Build Coastguard Worker formatter_class=argparse.RawTextHelpFormatter, 195*9e94795aSAndroid Build Coastguard Worker description=__doc__ + 196*9e94795aSAndroid Build Coastguard Worker "\nThese algorithms are tried when decompressing the image:\n " + 197*9e94795aSAndroid Build Coastguard Worker " ".join(tup[0][0] for tup in COMPRESSION_ALGO)) 198*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--input', 199*9e94795aSAndroid Build Coastguard Worker help='Input kernel image. If not specified, use stdin', 200*9e94795aSAndroid Build Coastguard Worker metavar='FILE', 201*9e94795aSAndroid Build Coastguard Worker type=argparse.FileType('rb'), 202*9e94795aSAndroid Build Coastguard Worker default=to_bytes_io(sys.stdin)) 203*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--output-configs', 204*9e94795aSAndroid Build Coastguard Worker help='If specified, write configs. Use stdout if no file ' 205*9e94795aSAndroid Build Coastguard Worker 'is specified.', 206*9e94795aSAndroid Build Coastguard Worker metavar='FILE', 207*9e94795aSAndroid Build Coastguard Worker nargs='?', 208*9e94795aSAndroid Build Coastguard Worker type=argparse.FileType('wb'), 209*9e94795aSAndroid Build Coastguard Worker const=to_bytes_io(sys.stdout)) 210*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--output-version', 211*9e94795aSAndroid Build Coastguard Worker help='If specified, write version. Use stdout if no file ' 212*9e94795aSAndroid Build Coastguard Worker 'is specified.', 213*9e94795aSAndroid Build Coastguard Worker metavar='FILE', 214*9e94795aSAndroid Build Coastguard Worker nargs='?', 215*9e94795aSAndroid Build Coastguard Worker type=argparse.FileType('wb'), 216*9e94795aSAndroid Build Coastguard Worker const=to_bytes_io(sys.stdout)) 217*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--output-release', 218*9e94795aSAndroid Build Coastguard Worker help='If specified, write kernel release. Use stdout if ' 219*9e94795aSAndroid Build Coastguard Worker 'no file is specified.', 220*9e94795aSAndroid Build Coastguard Worker metavar='FILE', 221*9e94795aSAndroid Build Coastguard Worker nargs='?', 222*9e94795aSAndroid Build Coastguard Worker type=argparse.FileType('wb'), 223*9e94795aSAndroid Build Coastguard Worker const=to_bytes_io(sys.stdout)) 224*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--output-compiler', 225*9e94795aSAndroid Build Coastguard Worker help='If specified, write the compiler information. Use stdout if no file ' 226*9e94795aSAndroid Build Coastguard Worker 'is specified.', 227*9e94795aSAndroid Build Coastguard Worker metavar='FILE', 228*9e94795aSAndroid Build Coastguard Worker nargs='?', 229*9e94795aSAndroid Build Coastguard Worker type=argparse.FileType('wb'), 230*9e94795aSAndroid Build Coastguard Worker const=to_bytes_io(sys.stdout)) 231*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--tools', 232*9e94795aSAndroid Build Coastguard Worker help='Decompression tools to use. If not specified, PATH ' 233*9e94795aSAndroid Build Coastguard Worker 'is searched.', 234*9e94795aSAndroid Build Coastguard Worker metavar='ALGORITHM:EXECUTABLE', 235*9e94795aSAndroid Build Coastguard Worker nargs='*') 236*9e94795aSAndroid Build Coastguard Worker args = parser.parse_args() 237*9e94795aSAndroid Build Coastguard Worker 238*9e94795aSAndroid Build Coastguard Worker tools = {pair[0]: pair[1] 239*9e94795aSAndroid Build Coastguard Worker for pair in (token.split(':') for token in args.tools or [])} 240*9e94795aSAndroid Build Coastguard Worker for cmd, _ in COMPRESSION_ALGO: 241*9e94795aSAndroid Build Coastguard Worker if cmd[0] in tools: 242*9e94795aSAndroid Build Coastguard Worker cmd[0] = tools[cmd[0]] 243*9e94795aSAndroid Build Coastguard Worker 244*9e94795aSAndroid Build Coastguard Worker input_bytes = args.input.read() 245*9e94795aSAndroid Build Coastguard Worker 246*9e94795aSAndroid Build Coastguard Worker ret = 0 247*9e94795aSAndroid Build Coastguard Worker if not dump_to_file(args.output_configs, dump_configs, input_bytes, 248*9e94795aSAndroid Build Coastguard Worker "configs in {}".format(args.input.name)): 249*9e94795aSAndroid Build Coastguard Worker ret = 1 250*9e94795aSAndroid Build Coastguard Worker if not dump_to_file(args.output_version, dump_version, input_bytes, 251*9e94795aSAndroid Build Coastguard Worker "version in {}".format(args.input.name)): 252*9e94795aSAndroid Build Coastguard Worker ret = 1 253*9e94795aSAndroid Build Coastguard Worker if not dump_to_file(args.output_release, dump_release, input_bytes, 254*9e94795aSAndroid Build Coastguard Worker "kernel release in {}".format(args.input.name)): 255*9e94795aSAndroid Build Coastguard Worker ret = 1 256*9e94795aSAndroid Build Coastguard Worker 257*9e94795aSAndroid Build Coastguard Worker if not dump_to_file(args.output_compiler, dump_compiler, input_bytes, 258*9e94795aSAndroid Build Coastguard Worker "kernel compiler in {}".format(args.input.name)): 259*9e94795aSAndroid Build Coastguard Worker ret = 1 260*9e94795aSAndroid Build Coastguard Worker 261*9e94795aSAndroid Build Coastguard Worker return ret 262*9e94795aSAndroid Build Coastguard Worker 263*9e94795aSAndroid Build Coastguard Worker 264*9e94795aSAndroid Build Coastguard Workerif __name__ == '__main__': 265*9e94795aSAndroid Build Coastguard Worker sys.exit(main()) 266