xref: /aosp_15_r20/art/tools/check_cfi.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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