1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*795d594fSAndroid Build Coastguard Worker# 3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project 4*795d594fSAndroid Build Coastguard Worker# 5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*795d594fSAndroid Build Coastguard Worker# 9*795d594fSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*795d594fSAndroid Build Coastguard Worker# 11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*795d594fSAndroid Build Coastguard Worker# limitations under the License. 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker""" 18*795d594fSAndroid Build Coastguard WorkerChecks dwarf CFI (unwinding) information by comparing it to disassembly. 19*795d594fSAndroid Build Coastguard WorkerIt is only a simple heuristic check of stack pointer adjustments. 20*795d594fSAndroid Build Coastguard WorkerFully inferring CFI from disassembly is not possible in general. 21*795d594fSAndroid Build Coastguard Worker""" 22*795d594fSAndroid Build Coastguard Worker 23*795d594fSAndroid Build Coastguard Workerimport os, re, subprocess, collections, pathlib, bisect, collections, sys 24*795d594fSAndroid Build Coastguard Workerfrom argparse import ArgumentParser 25*795d594fSAndroid Build Coastguard Workerfrom dataclasses import dataclass 26*795d594fSAndroid Build Coastguard Workerfrom functools import cache 27*795d594fSAndroid Build Coastguard Workerfrom pathlib import Path 28*795d594fSAndroid Build Coastguard Workerfrom typing import Any, List, Optional, Set, Tuple, Dict 29*795d594fSAndroid Build Coastguard Worker 30*795d594fSAndroid Build Coastguard Workerarch: str = "" 31*795d594fSAndroid Build Coastguard WorkerARCHES = ["i386", "x86_64", "arm", "aarch64", "riscv64"] 32*795d594fSAndroid Build Coastguard Worker 33*795d594fSAndroid Build Coastguard WorkerIGNORE : Dict[str, List[str]] = { 34*795d594fSAndroid Build Coastguard Worker # Aligns stack. 35*795d594fSAndroid Build Coastguard Worker "art_quick_osr_stub": ["i386"], 36*795d594fSAndroid Build Coastguard Worker # Unverifiable intermediate CFI after loading the stack pointer from context. 37*795d594fSAndroid Build Coastguard Worker "art_quick_do_long_jump": ["arm", "aarch64", "i386", "x86_64", "riscv64"], 38*795d594fSAndroid Build Coastguard Worker # Saves/restores SP in other register. 39*795d594fSAndroid Build Coastguard Worker "art_quick_generic_jni_trampoline": ["arm", "i386", "x86_64"], 40*795d594fSAndroid Build Coastguard Worker # Starts with non-zero offset at the start of the method. 41*795d594fSAndroid Build Coastguard Worker "art_quick_throw_null_pointer_exception_from_signal": ARCHES, 42*795d594fSAndroid Build Coastguard Worker # Pops stack without static control flow past the opcode. 43*795d594fSAndroid Build Coastguard Worker "nterp_op_return": ["arm", "aarch64", "i386", "x86_64", "riscv64"], 44*795d594fSAndroid Build Coastguard Worker} 45*795d594fSAndroid Build Coastguard Worker 46*795d594fSAndroid Build Coastguard WorkerSP = {"arm": "SP", "aarch64": "WSP", "i386": "ESP", "x86_64": "RSP", "riscv64": "X2"} 47*795d594fSAndroid Build Coastguard WorkerINITIAL_OFFSET = {"i386": 4, "x86_64": 8} 48*795d594fSAndroid Build Coastguard Worker 49*795d594fSAndroid Build Coastguard Worker@cache 50*795d594fSAndroid Build Coastguard Workerdef get_inst_semantics(arch: str) -> List[Any]: 51*795d594fSAndroid Build Coastguard Worker """ List of regex expressions for supported instructions and their behaviour """ 52*795d594fSAndroid Build Coastguard Worker 53*795d594fSAndroid Build Coastguard Worker rexprs = [] 54*795d594fSAndroid Build Coastguard Worker def add(rexpr, adjust_offset=lambda m: 0, adjust_pc=None): 55*795d594fSAndroid Build Coastguard Worker rexprs.append((re.compile(rexpr), adjust_offset, adjust_pc)) 56*795d594fSAndroid Build Coastguard Worker if arch in ["i386", "x86_64"]: 57*795d594fSAndroid Build Coastguard Worker ptr_size = {"i386": 4, "x86_64": 8}[arch] 58*795d594fSAndroid Build Coastguard Worker add(r"push. .*", lambda m: ptr_size) 59*795d594fSAndroid Build Coastguard Worker add(r"pop. .*", lambda m: -ptr_size) 60*795d594fSAndroid Build Coastguard Worker add(r"sub. \$(\w+), (?:%esp|%rsp)( # imm = \w+)?", lambda m: int(m[1], 0)) 61*795d594fSAndroid Build Coastguard Worker add(r"add. \$(\w+), (?:%esp|%rsp)( # imm = \w+)?", lambda m: -int(m[1], 0)) 62*795d594fSAndroid Build Coastguard Worker add(r"call. (0x\w+) <.*", lambda m: ptr_size, adjust_pc=lambda m: int(m[1], 0)) 63*795d594fSAndroid Build Coastguard Worker add(r"j[a-z]* (0x\w+) <.*", adjust_pc=lambda m: int(m[1], 0)) 64*795d594fSAndroid Build Coastguard Worker if arch in ["arm", "aarch64"]: 65*795d594fSAndroid Build Coastguard Worker add(r"sub sp,(?: sp,)? #(\w+)", lambda m: int(m[1], 0)) 66*795d594fSAndroid Build Coastguard Worker add(r"add sp,(?: sp,)? #(\w+)", lambda m: -int(m[1], 0)) 67*795d594fSAndroid Build Coastguard Worker add(r"str \w+, \[sp, #-(\d+)\]!", lambda m: int(m[1])) 68*795d594fSAndroid Build Coastguard Worker add(r"ldr \w+, \[sp\], #(\d+)", lambda m: -int(m[1])) 69*795d594fSAndroid Build Coastguard Worker add(r"stp \w+, \w+, \[sp, #-(\w+)\]!", lambda m: int(m[1], 0)) 70*795d594fSAndroid Build Coastguard Worker add(r"ldp \w+, \w+, \[sp\], #(\w+)", lambda m: -int(m[1], 0)) 71*795d594fSAndroid Build Coastguard Worker add(r"vpush \{([d0-9, ]*)\}", lambda m: 8 * len(m[1].split(","))) 72*795d594fSAndroid Build Coastguard Worker add(r"vpop \{([d0-9, ]*)\}", lambda m: -8 * len(m[1].split(","))) 73*795d594fSAndroid Build Coastguard Worker add(r"v?push(?:\.w)? \{([\w+, ]*)\}", lambda m: 4 * len(m[1].split(","))) 74*795d594fSAndroid Build Coastguard Worker add(r"v?pop(?:\.w)? \{([\w+, ]*)\}", lambda m: -4 * len(m[1].split(","))) 75*795d594fSAndroid Build Coastguard Worker add(r"cb\w* \w+, (0x\w+).*", adjust_pc=lambda m: int(m[1], 0)) 76*795d594fSAndroid Build Coastguard Worker add(r"(?:b|bl|b\w\w) (0x\w+).*", adjust_pc=lambda m: int(m[1], 0)) 77*795d594fSAndroid Build Coastguard Worker if arch in ["riscv64"]: 78*795d594fSAndroid Build Coastguard Worker add(r"addi sp, sp, (-?\w+)", lambda m: -int(m[1], 0)) 79*795d594fSAndroid Build Coastguard Worker add(r"b\w* (?:\w+, )+(0x\w+).*", adjust_pc=lambda m: int(m[1], 0)) 80*795d594fSAndroid Build Coastguard Worker add(r"(?:j|jal) (?:\w+, )?(0x\w+).*", adjust_pc=lambda m: int(m[1], 0)) 81*795d594fSAndroid Build Coastguard Worker return rexprs 82*795d594fSAndroid Build Coastguard Worker 83*795d594fSAndroid Build Coastguard Worker@dataclass(frozen=True) 84*795d594fSAndroid Build Coastguard Workerclass Error(Exception): 85*795d594fSAndroid Build Coastguard Worker address: int 86*795d594fSAndroid Build Coastguard Worker message: str 87*795d594fSAndroid Build Coastguard Worker 88*795d594fSAndroid Build Coastguard Workerdef get_arch(lib: pathlib.Path) -> str: 89*795d594fSAndroid Build Coastguard Worker """ Get architecture of the given library based on the ELF header. """ 90*795d594fSAndroid Build Coastguard Worker 91*795d594fSAndroid Build Coastguard Worker proc = subprocess.run([args.objdump, "--file-headers", lib], 92*795d594fSAndroid Build Coastguard Worker encoding='utf-8', 93*795d594fSAndroid Build Coastguard Worker capture_output=True, 94*795d594fSAndroid Build Coastguard Worker check=True) 95*795d594fSAndroid Build Coastguard Worker 96*795d594fSAndroid Build Coastguard Worker m = re.search("^architecture: *(.*)$", proc.stdout, re.MULTILINE) 97*795d594fSAndroid Build Coastguard Worker assert m, "Can not find ABI of ELF file " + str(lib) 98*795d594fSAndroid Build Coastguard Worker assert m.group(1) in ARCHES, "Unknown arch: " + m.group(1) 99*795d594fSAndroid Build Coastguard Worker return m.group(1) 100*795d594fSAndroid Build Coastguard Worker 101*795d594fSAndroid Build Coastguard WorkerSource = collections.namedtuple("Source", ["pc", "file", "line", "flag"]) 102*795d594fSAndroid Build Coastguard Worker 103*795d594fSAndroid Build Coastguard Workerdef get_src(lib: pathlib.Path) -> List[Source]: 104*795d594fSAndroid Build Coastguard Worker """ Get source-file and line-number for all hand-written assembly code. """ 105*795d594fSAndroid Build Coastguard Worker 106*795d594fSAndroid Build Coastguard Worker proc = subprocess.run([args.dwarfdump, "--debug-line", lib], 107*795d594fSAndroid Build Coastguard Worker encoding='utf-8', 108*795d594fSAndroid Build Coastguard Worker capture_output=True, 109*795d594fSAndroid Build Coastguard Worker check=True) 110*795d594fSAndroid Build Coastguard Worker 111*795d594fSAndroid Build Coastguard Worker section_re = re.compile("^debug_line\[0x[0-9a-f]+\]$", re.MULTILINE) 112*795d594fSAndroid Build Coastguard Worker filename_re = re.compile('file_names\[ *(\d)+\]:\n\s*name: "(.*)"', re.MULTILINE) 113*795d594fSAndroid Build Coastguard Worker line_re = re.compile('0x([0-9a-f]{16}) +(\d+) +\d+ +(\d+)' # pc, line, column, file 114*795d594fSAndroid Build Coastguard Worker ' +\d+ +\d +(.*)') # isa, discriminator, flag 115*795d594fSAndroid Build Coastguard Worker 116*795d594fSAndroid Build Coastguard Worker results = [] 117*795d594fSAndroid Build Coastguard Worker for section in section_re.split(proc.stdout): 118*795d594fSAndroid Build Coastguard Worker files = {m[1]: m[2] for m in filename_re.finditer(section)} 119*795d594fSAndroid Build Coastguard Worker if not any(f.endswith(".S") for f in files.values()): 120*795d594fSAndroid Build Coastguard Worker continue 121*795d594fSAndroid Build Coastguard Worker lines = line_re.findall(section) 122*795d594fSAndroid Build Coastguard Worker results.extend([Source(int(a, 16), files[fn], l, fg) for a, l, fn, fg in lines]) 123*795d594fSAndroid Build Coastguard Worker return sorted(filter(lambda line: "end_sequence" not in line.flag, results)) 124*795d594fSAndroid Build Coastguard Worker 125*795d594fSAndroid Build Coastguard WorkerFde = collections.namedtuple("Fde", ["pc", "end", "data"]) 126*795d594fSAndroid Build Coastguard Worker 127*795d594fSAndroid Build Coastguard Workerdef get_fde(lib: pathlib.Path) -> List[Fde]: 128*795d594fSAndroid Build Coastguard Worker """ Get all FDE blocks (in dumped text-based format) """ 129*795d594fSAndroid Build Coastguard Worker 130*795d594fSAndroid Build Coastguard Worker proc = subprocess.run([args.dwarfdump, "--debug-frame", lib], 131*795d594fSAndroid Build Coastguard Worker encoding='utf-8', 132*795d594fSAndroid Build Coastguard Worker capture_output=True, 133*795d594fSAndroid Build Coastguard Worker check=True) 134*795d594fSAndroid Build Coastguard Worker 135*795d594fSAndroid Build Coastguard Worker section_re = re.compile("\n(?! |\n)", re.MULTILINE) # New-line not followed by indent. 136*795d594fSAndroid Build Coastguard Worker fda_re = re.compile(".* FDE .* pc=([0-9a-f]+)...([0-9a-f]+)") 137*795d594fSAndroid Build Coastguard Worker 138*795d594fSAndroid Build Coastguard Worker results = [] 139*795d594fSAndroid Build Coastguard Worker for section in section_re.split(proc.stdout): 140*795d594fSAndroid Build Coastguard Worker m = fda_re.match(section) 141*795d594fSAndroid Build Coastguard Worker if m: 142*795d594fSAndroid Build Coastguard Worker fde = Fde(int(m[1], 16), int(m[2], 16), section) 143*795d594fSAndroid Build Coastguard Worker if fde.pc != 0: 144*795d594fSAndroid Build Coastguard Worker results.append(fde) 145*795d594fSAndroid Build Coastguard Worker return sorted(results) 146*795d594fSAndroid Build Coastguard Worker 147*795d594fSAndroid Build Coastguard WorkerAsm = collections.namedtuple("Asm", ["pc", "name", "data"]) 148*795d594fSAndroid Build Coastguard Worker 149*795d594fSAndroid Build Coastguard Workerdef get_asm(lib: pathlib.Path) -> List[Asm]: 150*795d594fSAndroid Build Coastguard Worker """ Get all ASM blocks (in dumped text-based format) """ 151*795d594fSAndroid Build Coastguard Worker 152*795d594fSAndroid Build Coastguard Worker proc = subprocess.run( 153*795d594fSAndroid Build Coastguard Worker [ 154*795d594fSAndroid Build Coastguard Worker args.objdump, 155*795d594fSAndroid Build Coastguard Worker "--disassemble", 156*795d594fSAndroid Build Coastguard Worker "--no-show-raw-insn", 157*795d594fSAndroid Build Coastguard Worker "--disassemble-zeroes", 158*795d594fSAndroid Build Coastguard Worker lib, 159*795d594fSAndroid Build Coastguard Worker ], 160*795d594fSAndroid Build Coastguard Worker encoding="utf-8", 161*795d594fSAndroid Build Coastguard Worker capture_output=True, 162*795d594fSAndroid Build Coastguard Worker check=True, 163*795d594fSAndroid Build Coastguard Worker ) 164*795d594fSAndroid Build Coastguard Worker 165*795d594fSAndroid Build Coastguard Worker section_re = re.compile("\n(?! |\n)", re.MULTILINE) # New-line not followed by indent. 166*795d594fSAndroid Build Coastguard Worker sym_re = re.compile("([0-9a-f]+) <(.+)>:") 167*795d594fSAndroid Build Coastguard Worker 168*795d594fSAndroid Build Coastguard Worker results = [] 169*795d594fSAndroid Build Coastguard Worker for section in section_re.split(proc.stdout): 170*795d594fSAndroid Build Coastguard Worker sym = sym_re.match(section) 171*795d594fSAndroid Build Coastguard Worker if sym: 172*795d594fSAndroid Build Coastguard Worker results.append(Asm(int(sym[1], 16), sym[2], section)) 173*795d594fSAndroid Build Coastguard Worker return sorted(results) 174*795d594fSAndroid Build Coastguard Worker 175*795d594fSAndroid Build Coastguard WorkerInst = collections.namedtuple("Inst", ["pc", "inst"]) 176*795d594fSAndroid Build Coastguard Worker 177*795d594fSAndroid Build Coastguard Workerdef get_instructions(asm: Asm) -> List[Inst]: 178*795d594fSAndroid Build Coastguard Worker """ Extract individual instructions from disassembled code block """ 179*795d594fSAndroid Build Coastguard Worker 180*795d594fSAndroid Build Coastguard Worker data = re.sub(r"[ \t]+", " ", asm.data) 181*795d594fSAndroid Build Coastguard Worker inst_re = re.compile(r"([0-9a-f]+): +(?:[0-9a-f]{2} +)*(.*)") 182*795d594fSAndroid Build Coastguard Worker return [Inst(int(pc, 16), inst) for pc, inst in inst_re.findall(data)] 183*795d594fSAndroid Build Coastguard Worker 184*795d594fSAndroid Build Coastguard Worker# PC -> CFA offset (stack size at given PC; None if it not just trivial SP+<integer>) 185*795d594fSAndroid Build Coastguard WorkerCfaOffsets = Dict[int, Optional[int]] 186*795d594fSAndroid Build Coastguard Worker 187*795d594fSAndroid Build Coastguard Workerdef get_dwarf_cfa_offsets(insts: List[Inst], fde: Fde) -> CfaOffsets: 188*795d594fSAndroid Build Coastguard Worker """ Get CFA offsets for all instructions from DWARF """ 189*795d594fSAndroid Build Coastguard Worker 190*795d594fSAndroid Build Coastguard Worker # Parse the CFA offset definitions from the FDE. 191*795d594fSAndroid Build Coastguard Worker sp = SP[arch] 192*795d594fSAndroid Build Coastguard Worker m = re.compile(r"(0x[0-9a-f]+): CFA=(\w*)([^:\n]*)").findall(fde.data) 193*795d594fSAndroid Build Coastguard Worker cfa = collections.deque([(int(a, 0), int(o or "0") if r == sp else None) for a, r, o in m]) 194*795d594fSAndroid Build Coastguard Worker if all(offset is None for add, offset in cfa): 195*795d594fSAndroid Build Coastguard Worker # This would create result that never checks anything. 196*795d594fSAndroid Build Coastguard Worker raise Error(insts[0].pc, "No trivial CFA offsets. Add function to IGNORE list?") 197*795d594fSAndroid Build Coastguard Worker 198*795d594fSAndroid Build Coastguard Worker # Create map from instruction PCs to corresponding CFA offsets. 199*795d594fSAndroid Build Coastguard Worker offset: Optional[int] = INITIAL_OFFSET.get(arch, 0) 200*795d594fSAndroid Build Coastguard Worker result: CfaOffsets = {} 201*795d594fSAndroid Build Coastguard Worker for pc, inst in insts: 202*795d594fSAndroid Build Coastguard Worker while cfa and cfa[0][0] <= pc: 203*795d594fSAndroid Build Coastguard Worker offset = cfa.popleft()[1] 204*795d594fSAndroid Build Coastguard Worker result[pc] = offset 205*795d594fSAndroid Build Coastguard Worker return result 206*795d594fSAndroid Build Coastguard Worker 207*795d594fSAndroid Build Coastguard Workerdef get_inferred_cfa_offsets(insts: List[Inst]) -> CfaOffsets: 208*795d594fSAndroid Build Coastguard Worker """ Get CFA offsets for all instructions from static analysis """ 209*795d594fSAndroid Build Coastguard Worker 210*795d594fSAndroid Build Coastguard Worker rexprs = get_inst_semantics(arch) 211*795d594fSAndroid Build Coastguard Worker offset: Optional[int] = INITIAL_OFFSET.get(arch, 0) 212*795d594fSAndroid Build Coastguard Worker result: CfaOffsets = {} 213*795d594fSAndroid Build Coastguard Worker for pc, inst in insts: 214*795d594fSAndroid Build Coastguard Worker # Set current offset for PC, unless branch already set it. 215*795d594fSAndroid Build Coastguard Worker offset = result.setdefault(pc, offset) 216*795d594fSAndroid Build Coastguard Worker 217*795d594fSAndroid Build Coastguard Worker # Adjust PC and offset based on the current instruction. 218*795d594fSAndroid Build Coastguard Worker for rexpr, adjust_offset, adjust_pc in rexprs: 219*795d594fSAndroid Build Coastguard Worker m = rexpr.fullmatch(inst) 220*795d594fSAndroid Build Coastguard Worker if m: 221*795d594fSAndroid Build Coastguard Worker new_offset = offset + adjust_offset(m) 222*795d594fSAndroid Build Coastguard Worker if adjust_pc: 223*795d594fSAndroid Build Coastguard Worker new_pc = adjust_pc(m) 224*795d594fSAndroid Build Coastguard Worker if insts[0].pc <= new_pc <= insts[-1].pc: 225*795d594fSAndroid Build Coastguard Worker if new_pc in result and result[new_pc] != new_offset: 226*795d594fSAndroid Build Coastguard Worker raise Error(pc, "Inconsistent branch (old={} new={})" 227*795d594fSAndroid Build Coastguard Worker .format(result[new_pc], new_offset)) 228*795d594fSAndroid Build Coastguard Worker result[new_pc] = new_offset 229*795d594fSAndroid Build Coastguard Worker else: 230*795d594fSAndroid Build Coastguard Worker offset = new_offset 231*795d594fSAndroid Build Coastguard Worker break # First matched pattern wins. 232*795d594fSAndroid Build Coastguard Worker return result 233*795d594fSAndroid Build Coastguard Worker 234*795d594fSAndroid Build Coastguard Workerdef check_fde(fde: Fde, insts: List[Inst], srcs) -> None: 235*795d594fSAndroid Build Coastguard Worker """ Compare DWARF offsets to assembly-inferred offsets. Report differences. """ 236*795d594fSAndroid Build Coastguard Worker 237*795d594fSAndroid Build Coastguard Worker dwarf_cfa_offsets = get_dwarf_cfa_offsets(insts, fde) 238*795d594fSAndroid Build Coastguard Worker inferred_cfa_offsets = get_inferred_cfa_offsets(insts) 239*795d594fSAndroid Build Coastguard Worker 240*795d594fSAndroid Build Coastguard Worker for pc, inst in insts: 241*795d594fSAndroid Build Coastguard Worker if dwarf_cfa_offsets[pc] is not None and dwarf_cfa_offsets[pc] != inferred_cfa_offsets[pc]: 242*795d594fSAndroid Build Coastguard Worker if pc in srcs: # Only report if it maps to source code (not padding or literals). 243*795d594fSAndroid Build Coastguard Worker for inst2 in insts: 244*795d594fSAndroid Build Coastguard Worker print("0x{:08x} [{}]: dwarf={} inferred={} {}".format( 245*795d594fSAndroid Build Coastguard Worker inst2.pc, srcs.get(inst2.pc, ""), 246*795d594fSAndroid Build Coastguard Worker str(dwarf_cfa_offsets[inst2.pc]), str(inferred_cfa_offsets[inst2.pc]), 247*795d594fSAndroid Build Coastguard Worker inst2.inst.strip())) 248*795d594fSAndroid Build Coastguard Worker raise Error(pc, "DWARF offset does not match inferred offset") 249*795d594fSAndroid Build Coastguard Worker 250*795d594fSAndroid Build Coastguard Workerdef check_lib(lib: pathlib.Path) -> int: 251*795d594fSAndroid Build Coastguard Worker global arch 252*795d594fSAndroid Build Coastguard Worker arch = get_arch(lib) 253*795d594fSAndroid Build Coastguard Worker 254*795d594fSAndroid Build Coastguard Worker assert lib.exists() 255*795d594fSAndroid Build Coastguard Worker fdes = get_fde(lib) 256*795d594fSAndroid Build Coastguard Worker asms = collections.deque(get_asm(lib)) 257*795d594fSAndroid Build Coastguard Worker srcs = {src.pc: src.file + ":" + src.line for src in get_src(lib)} 258*795d594fSAndroid Build Coastguard Worker seen = set() # Used to verify the we have covered all assembly source lines. 259*795d594fSAndroid Build Coastguard Worker fail = 0 260*795d594fSAndroid Build Coastguard Worker assert srcs, "No sources found" 261*795d594fSAndroid Build Coastguard Worker 262*795d594fSAndroid Build Coastguard Worker for fde in fdes: 263*795d594fSAndroid Build Coastguard Worker if fde.pc not in srcs: 264*795d594fSAndroid Build Coastguard Worker continue # Ignore if it is not hand-written assembly. 265*795d594fSAndroid Build Coastguard Worker 266*795d594fSAndroid Build Coastguard Worker # Assembly instructions (one FDE can cover several assembly chunks). 267*795d594fSAndroid Build Coastguard Worker all_insts, name = [], "" 268*795d594fSAndroid Build Coastguard Worker while asms and asms[0].pc < fde.end: 269*795d594fSAndroid Build Coastguard Worker asm = asms.popleft() 270*795d594fSAndroid Build Coastguard Worker if asm.pc < fde.pc: 271*795d594fSAndroid Build Coastguard Worker continue 272*795d594fSAndroid Build Coastguard Worker insts = get_instructions(asm) 273*795d594fSAndroid Build Coastguard Worker if any(asm.name.startswith(n) and arch in a for n, a in IGNORE.items()): 274*795d594fSAndroid Build Coastguard Worker seen.update([inst.pc for inst in insts]) 275*795d594fSAndroid Build Coastguard Worker continue 276*795d594fSAndroid Build Coastguard Worker all_insts.extend(insts) 277*795d594fSAndroid Build Coastguard Worker name = name or asm.name 278*795d594fSAndroid Build Coastguard Worker if not all_insts: 279*795d594fSAndroid Build Coastguard Worker continue # No assembly 280*795d594fSAndroid Build Coastguard Worker 281*795d594fSAndroid Build Coastguard Worker # Compare DWARF data to assembly instructions 282*795d594fSAndroid Build Coastguard Worker try: 283*795d594fSAndroid Build Coastguard Worker check_fde(fde, all_insts, srcs) 284*795d594fSAndroid Build Coastguard Worker except Error as e: 285*795d594fSAndroid Build Coastguard Worker print("0x{:08x} [{}]: ERROR in {}: {}\n" 286*795d594fSAndroid Build Coastguard Worker .format(e.address, srcs.get(e.address, ""), name, e.message)) 287*795d594fSAndroid Build Coastguard Worker fail += 1 288*795d594fSAndroid Build Coastguard Worker seen.update([inst.pc for inst in all_insts]) 289*795d594fSAndroid Build Coastguard Worker for pc in sorted(set(srcs.keys()) - seen): 290*795d594fSAndroid Build Coastguard Worker print("ERROR: Missing CFI for {:08x}: {}".format(pc, srcs[pc])) 291*795d594fSAndroid Build Coastguard Worker fail += 1 292*795d594fSAndroid Build Coastguard Worker return fail 293*795d594fSAndroid Build Coastguard Worker 294*795d594fSAndroid Build Coastguard Worker 295*795d594fSAndroid Build Coastguard Workerdef main(argv): 296*795d594fSAndroid Build Coastguard Worker """ Check libraries provided on the command line, or use the default build output """ 297*795d594fSAndroid Build Coastguard Worker 298*795d594fSAndroid Build Coastguard Worker libs = args.srcs 299*795d594fSAndroid Build Coastguard Worker if not libs: 300*795d594fSAndroid Build Coastguard Worker apex = Path(os.environ["OUT"]) / "symbols/apex/" 301*795d594fSAndroid Build Coastguard Worker libs = list(apex.glob("**/libart.so")) 302*795d594fSAndroid Build Coastguard Worker assert libs, "Can not find any libart.so in " + str(apex) 303*795d594fSAndroid Build Coastguard Worker for lib in libs: 304*795d594fSAndroid Build Coastguard Worker fail = check_lib(pathlib.Path(lib)) 305*795d594fSAndroid Build Coastguard Worker if fail > 0: 306*795d594fSAndroid Build Coastguard Worker print(fail, "ERROR(s) in", str(lib)) 307*795d594fSAndroid Build Coastguard Worker sys.exit(1) 308*795d594fSAndroid Build Coastguard Worker if args.out: 309*795d594fSAndroid Build Coastguard Worker args.out.write_bytes(b"") 310*795d594fSAndroid Build Coastguard Worker 311*795d594fSAndroid Build Coastguard Workerif __name__ == "__main__": 312*795d594fSAndroid Build Coastguard Worker parser = ArgumentParser(description=__doc__) 313*795d594fSAndroid Build Coastguard Worker parser.add_argument("--out", type=Path, help="Output (will just generate empty file)") 314*795d594fSAndroid Build Coastguard Worker parser.add_argument("--dwarfdump", type=Path, default="llvm-dwarfdump") 315*795d594fSAndroid Build Coastguard Worker parser.add_argument("--objdump", type=Path, default="llvm-objdump") 316*795d594fSAndroid Build Coastguard Worker parser.add_argument("srcs", nargs="*", type=Path) 317*795d594fSAndroid Build Coastguard Worker args = parser.parse_args() 318*795d594fSAndroid Build Coastguard Worker 319*795d594fSAndroid Build Coastguard Worker main(sys.argv) 320