summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/arch/arm/jni_entrypoints_arm.S2
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S16
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S6
-rw-r--r--runtime/interpreter/mterp/arm64ng/control_flow.S1
-rw-r--r--runtime/interpreter/mterp/arm64ng/main.S2
-rw-r--r--runtime/interpreter/mterp/armng/control_flow.S1
-rw-r--r--runtime/interpreter/mterp/armng/main.S13
-rwxr-xr-xtools/check_cfi.py247
8 files changed, 282 insertions, 6 deletions
diff --git a/runtime/arch/arm/jni_entrypoints_arm.S b/runtime/arch/arm/jni_entrypoints_arm.S
index 96b6241a20..fc57df76d3 100644
--- a/runtime/arch/arm/jni_entrypoints_arm.S
+++ b/runtime/arch/arm/jni_entrypoints_arm.S
@@ -114,11 +114,13 @@ ENTRY art_jni_dlsym_lookup_stub
add sp, #12 @ restore stack pointer
.cfi_adjust_cfa_offset -12
cbz r0, 1f @ is method code null?
+ .cfi_remember_state
pop {r0, r1, r2, r3, lr} @ restore regs
.cfi_adjust_cfa_offset -20
.cfi_restore lr
bx r12 @ if non-null, tail call to method's code
1:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, 20
pop {r0, r1, r2, r3, pc} @ restore regs and return to caller to handle exception
END art_jni_dlsym_lookup_stub
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 7e11d32656..d6f129be50 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -686,6 +686,7 @@ ENTRY art_quick_aput_obj
mov r0, r3
bl artIsAssignableFromCode
cbz r0, .Lthrow_array_store_exception
+ .cfi_remember_state
pop {r0-r2, lr}
.cfi_restore r0
.cfi_restore r1
@@ -700,8 +701,13 @@ ENTRY art_quick_aput_obj
strb r3, [r3, r0]
blx lr
.Lthrow_array_store_exception:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, 16
pop {r0-r2, lr}
- /* No need to repeat restore cfi directives, the ones above apply here. */
+ .cfi_restore r0
+ .cfi_restore r1
+ .cfi_restore r2
+ .cfi_restore lr
+ .cfi_adjust_cfa_offset -16
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r3
mov r1, r2
mov r2, rSELF @ pass Thread::Current
@@ -780,7 +786,7 @@ ENTRY \name
RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
REFRESH_MARKING_REGISTER
bx lr
- .cfi_restore_state
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END \name
@@ -1356,12 +1362,14 @@ ENTRY art_quick_resolution_trampoline
mov r3, sp @ pass SP
blx artQuickResolutionTrampoline @ (Method* called, receiver, Thread*, SP)
cbz r0, 1f @ is code pointer null? goto exception
+ .cfi_remember_state
mov r12, r0
ldr r0, [sp, #0] @ load resolved method in r0
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
bx r12 @ tail-call into actual code
1:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_REFS_AND_ARGS
RESTORE_SAVE_REFS_AND_ARGS_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_resolution_trampoline
@@ -1502,6 +1510,7 @@ ENTRY art_quick_instrumentation_entry
mov r3, sp @ pass SP
blx artInstrumentationMethodEntryFromCode @ (Method*, Object*, Thread*, SP)
cbz r0, .Ldeliver_instrumentation_entry_exception
+ .cfi_remember_state
@ Deliver exception if we got nullptr as function.
mov r12, r0 @ r12 holds reference to code
ldr r0, [sp, #4] @ restore r0
@@ -1511,6 +1520,7 @@ ENTRY art_quick_instrumentation_entry
REFRESH_MARKING_REGISTER
bx r12 @ call method with lr set to art_quick_instrumentation_exit
.Ldeliver_instrumentation_entry_exception:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_REFS_AND_ARGS
@ Deliver exception for art_quick_instrumentation_entry placed after
@ art_quick_instrumentation_exit so that the fallthrough works.
RESTORE_SAVE_REFS_AND_ARGS_FRAME
@@ -1529,6 +1539,7 @@ ENTRY art_quick_instrumentation_exit
cbz r0, .Ldo_deliver_instrumentation_exception
@ Deliver exception if we got nullptr as function.
+ .cfi_remember_state
cbnz r1, .Ldeoptimize
// Normal return.
str r0, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
@@ -1537,6 +1548,7 @@ ENTRY art_quick_instrumentation_exit
REFRESH_MARKING_REGISTER
bx lr
.Ldo_deliver_instrumentation_exception:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
DELIVER_PENDING_EXCEPTION_FRAME_READY
.Ldeoptimize:
str r1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 4]
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index a20d558240..d8c91e11b9 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1645,11 +1645,13 @@ ENTRY art_quick_proxy_invoke_handler
bl artQuickProxyInvokeHandler // (Method* proxy method, receiver, Thread*, SP)
ldr x2, [xSELF, THREAD_EXCEPTION_OFFSET]
cbnz x2, .Lexception_in_proxy // success if no exception is pending
+ .cfi_remember_state
RESTORE_SAVE_REFS_AND_ARGS_FRAME // Restore frame
REFRESH_MARKING_REGISTER
fmov d0, x0 // Store result in d0 in case it was float or double
ret // return on success
.Lexception_in_proxy:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_REFS_AND_ARGS
RESTORE_SAVE_REFS_AND_ARGS_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
@@ -1692,12 +1694,14 @@ ENTRY art_quick_resolution_trampoline
mov x3, sp
bl artQuickResolutionTrampoline // (called, receiver, Thread*, SP)
cbz x0, 1f
+ .cfi_remember_state
mov xIP0, x0 // Remember returned code pointer in xIP0.
ldr x0, [sp, #0] // artQuickResolutionTrampoline puts called method in *SP.
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
br xIP0
1:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_REFS_AND_ARGS
RESTORE_SAVE_REFS_AND_ARGS_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_resolution_trampoline
@@ -1924,6 +1928,7 @@ ENTRY art_quick_instrumentation_exit
bl artInstrumentationMethodExitFromCode // (Thread*, SP, gpr_res*, fpr_res*)
cbz x0, .Ldo_deliver_instrumentation_exception
+ .cfi_remember_state
// Handle error
cbnz x1, .Ldeoptimize
// Normal return.
@@ -1933,6 +1938,7 @@ ENTRY art_quick_instrumentation_exit
REFRESH_MARKING_REGISTER
br lr
.Ldo_deliver_instrumentation_exception:
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
DELIVER_PENDING_EXCEPTION_FRAME_READY
.Ldeoptimize:
str x1, [sp, #FRAME_SIZE_SAVE_EVERYTHING - 8]
diff --git a/runtime/interpreter/mterp/arm64ng/control_flow.S b/runtime/interpreter/mterp/arm64ng/control_flow.S
index f2d0559064..7873ca6b7c 100644
--- a/runtime/interpreter/mterp/arm64ng/control_flow.S
+++ b/runtime/interpreter/mterp/arm64ng/control_flow.S
@@ -168,6 +168,7 @@
RESTORE_ALL_CALLEE_SAVES
ret
.cfi_restore_state
+ CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -8, CALLEE_SAVES_SIZE
%def op_return_object():
% op_return(is_object="1", is_void="0", is_wide="0")
diff --git a/runtime/interpreter/mterp/arm64ng/main.S b/runtime/interpreter/mterp/arm64ng/main.S
index 8ada63c03e..89de81f5e4 100644
--- a/runtime/interpreter/mterp/arm64ng/main.S
+++ b/runtime/interpreter/mterp/arm64ng/main.S
@@ -1917,6 +1917,8 @@ artNterpAsmInstructionStart = .L_op_nop
% return "nterp_"
%def opcode_start():
NAME_START nterp_${opcode}
+ # Explicitly restore CFA, just in case the previous opcode clobbered it (by .cfi_def_*).
+ CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -8, CALLEE_SAVES_SIZE
%def opcode_end():
NAME_END nterp_${opcode}
// Advance to the end of this handler. Causes error if we are past that point.
diff --git a/runtime/interpreter/mterp/armng/control_flow.S b/runtime/interpreter/mterp/armng/control_flow.S
index ab05228c2c..689b245729 100644
--- a/runtime/interpreter/mterp/armng/control_flow.S
+++ b/runtime/interpreter/mterp/armng/control_flow.S
@@ -168,6 +168,7 @@
.cfi_def_cfa sp, CALLEE_SAVES_SIZE
RESTORE_ALL_CALLEE_SAVES lr_to_pc=1
.cfi_restore_state
+ CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -4, CALLEE_SAVES_SIZE
%def op_return_object():
% op_return(is_object="1", is_void="0", is_wide="0")
diff --git a/runtime/interpreter/mterp/armng/main.S b/runtime/interpreter/mterp/armng/main.S
index 5a086f597d..310a3fd8f1 100644
--- a/runtime/interpreter/mterp/armng/main.S
+++ b/runtime/interpreter/mterp/armng/main.S
@@ -488,6 +488,7 @@ END \name
.cfi_adjust_cfa_offset -4
.if \lr_to_pc
pop {r9-r11, pc} @ 9 words of callee saves and args.
+ .cfi_adjust_cfa_offset -16
.else
pop {r9-r11, lr} @ 9 words of callee saves and args.
.cfi_adjust_cfa_offset -16
@@ -1908,7 +1909,7 @@ EndExecuteNterpImpl:
* expected common case is a "reasonable" value that converts directly
* to modest integer. The EABI convert function isn't doing this for us.
*/
-nterp_d2l_doconv:
+ENTRY nterp_d2l_doconv
ubfx r2, r1, #20, #11 @ grab the exponent
movw r3, #0x43e
cmp r2, r3 @ MINLONG < x > MAXLONG?
@@ -1931,6 +1932,7 @@ d2l_maybeNaN:
mov r0, #0
mov r1, #0
bx lr @ return 0 for NaN
+END nterp_d2l_doconv
/*
* Convert the float in r0 to a long in r0/r1.
@@ -1939,7 +1941,7 @@ d2l_maybeNaN:
* expected common case is a "reasonable" value that converts directly
* to modest integer. The EABI convert function isn't doing this for us.
*/
-nterp_f2l_doconv:
+ENTRY nterp_f2l_doconv
ubfx r2, r0, #23, #8 @ grab the exponent
cmp r2, #0xbe @ MININT < x > MAXINT?
bhs f2l_special_cases
@@ -1960,6 +1962,7 @@ f2l_maybeNaN:
mov r0, #0
mov r1, #0
bx lr @ return 0 for NaN
+END nterp_f2l_doconv
// Entrypoints into runtime.
NTERP_TRAMPOLINE nterp_get_static_field, NterpGetStaticField
@@ -1975,7 +1978,7 @@ NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
- .type artNterpAsmInstructionEnd, #object
+ .type artNterpAsmInstructionEnd, #function
.hidden artNterpAsmInstructionEnd
.global artNterpAsmInstructionEnd
artNterpAsmInstructionEnd:
@@ -1986,7 +1989,7 @@ artNterpAsmInstructionEnd:
%def instruction_start():
- .type artNterpAsmInstructionStart, #object
+ .type artNterpAsmInstructionStart, #function
.hidden artNterpAsmInstructionStart
.global artNterpAsmInstructionStart
artNterpAsmInstructionStart = .L_op_nop
@@ -1996,6 +1999,8 @@ artNterpAsmInstructionStart = .L_op_nop
% return "nterp_"
%def opcode_start():
NAME_START nterp_${opcode}
+ # Explicitly restore CFA, just in case the previous opcode clobbered it (by .cfi_def_*).
+ CFI_DEF_CFA_BREG_PLUS_UCONST CFI_REFS, -4, CALLEE_SAVES_SIZE
%def opcode_end():
NAME_END nterp_${opcode}
// Advance to the end of this handler. Causes error if we are past that point.
diff --git a/tools/check_cfi.py b/tools/check_cfi.py
new file mode 100755
index 0000000000..ac258c28aa
--- /dev/null
+++ b/tools/check_cfi.py
@@ -0,0 +1,247 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Checks dwarf CFI (unwinding) information by comparing it to disassembly.
+It is only a simple heuristic check of stack pointer adjustments.
+Fully inferring CFI from disassembly is not possible in general.
+"""
+
+import os, re, subprocess, collections, pathlib, bisect, collections
+from typing import List, Optional, Set, Tuple
+
+Source = collections.namedtuple("Source", ["addr", "file", "line", "flag"])
+
+def get_source(lib: pathlib.Path) -> List[Source]:
+ """ Get source-file and line-number for all hand-written assembly code. """
+
+ proc = subprocess.run(["llvm-dwarfdump", "--debug-line", lib],
+ encoding='utf-8',
+ capture_output=True,
+ check=True)
+
+ section_re = re.compile("^debug_line\[0x[0-9a-f]+\]$", re.MULTILINE)
+ filename_re = re.compile('file_names\[ *(\d)+\]:\n\s*name: "(.*)"', re.MULTILINE)
+ line_re = re.compile('0x([0-9a-f]{16}) +(\d+) +\d+ +(\d+)' # addr, line, column, file
+ ' +\d+ +\d +(.*)') # isa, discriminator, flag
+
+ results = []
+ for section in section_re.split(proc.stdout):
+ files = {m[1]: m[2] for m in filename_re.finditer(section)}
+ if not any(f.endswith(".S") for f in files.values()):
+ continue
+ lines = line_re.findall(section)
+ results.extend([Source(int(a, 16), files[fn], l, fg) for a, l, fn, fg in lines])
+ return sorted(filter(lambda line: "end_sequence" not in line.flag, results))
+
+Fde = collections.namedtuple("Fde", ["addr", "end", "data"])
+
+def get_fde(lib: pathlib.Path) -> List[Fde]:
+ """ Get all unwinding FDE blocks (in dumped text-based format) """
+
+ proc = subprocess.run(["llvm-dwarfdump", "--debug-frame", lib],
+ encoding='utf-8',
+ capture_output=True,
+ check=True)
+
+ section_re = re.compile("\n(?! |\n)", re.MULTILINE) # New-line not followed by indent.
+ fda_re = re.compile(".* FDE .* pc=([0-9a-f]+)...([0-9a-f]+)")
+
+ results = []
+ for section in section_re.split(proc.stdout):
+ m = fda_re.match(section)
+ if m:
+ fde = Fde(int(m[1], 16), int(m[2], 16), section)
+ if fde.addr != 0:
+ results.append(fde)
+ return sorted(results)
+
+Asm = collections.namedtuple("Asm", ["addr", "name", "data"])
+
+def get_asm(lib: pathlib.Path) -> List[Asm]:
+ """ Get disassembly for all methods (in dumped text-based format) """
+
+ proc = subprocess.run(["llvm-objdump", "--disassemble", lib],
+ encoding='utf-8',
+ capture_output=True,
+ check=True)
+
+ section_re = re.compile("\n(?! |\n)", re.MULTILINE) # New-line not followed by indent.
+ sym_re = re.compile("([0-9a-f]+) <(.+)>:")
+
+ results = []
+ for section in section_re.split(proc.stdout):
+ sym = sym_re.match(section)
+ if sym:
+ results.append(Asm(int(sym[1], 16), sym[2], section))
+ return sorted(results)
+
+Cfa = collections.namedtuple("Cfa", ["addr", "cfa"])
+
+def get_cfa(fde: Fde) -> List[Cfa]:
+ """ Extract individual CFA (SP+offset) entries from the FDE block """
+
+ cfa_re = re.compile("0x([0-9a-f]+): CFA=([^\s:]+)")
+ return [Cfa(int(addr, 16), cfa) for addr, cfa in cfa_re.findall(fde.data)]
+
+Inst = collections.namedtuple("Inst", ["addr", "inst", "symbol"])
+
+def get_instructions(asm: Asm) -> List[Inst]:
+ """ Extract individual instructions from disassembled code block """
+
+ data = re.sub(r"[ \t]+", " ", asm.data)
+ inst_re = re.compile(r"([0-9a-f]+): +(?:[0-9a-f]{2} +)*(.*)")
+ return [Inst(int(addr, 16), inst, asm.name) for addr, inst in inst_re.findall(data)]
+
+CfaOffset = collections.namedtuple("CfaOffset", ["addr", "offset"])
+
+def get_dwarf_cfa_offsets(cfas: List[Cfa]) -> List[CfaOffset]:
+ """ Parse textual CFA entries into integer stack offsets """
+
+ result = []
+ for addr, cfa in cfas:
+ if cfa == "WSP" or cfa == "SP":
+ result.append(CfaOffset(addr, 0))
+ elif cfa.startswith("WSP+") or cfa.startswith("SP+"):
+ result.append(CfaOffset(addr, int(cfa.split("+")[1])))
+ else:
+ result.append(CfaOffset(addr, None))
+ return result
+
+def get_infered_cfa_offsets(insts: List[Inst]) -> List[CfaOffset]:
+ """ Heuristic to convert disassembly into stack offsets """
+
+ # Regular expressions which find instructions that adjust stack pointer.
+ rexprs = []
+ def add(rexpr, adjust_offset):
+ rexprs.append((re.compile(rexpr), adjust_offset))
+ add(r"sub sp,(?: sp,)? #(\d+)", lambda m: int(m[1]))
+ add(r"add sp,(?: sp,)? #(\d+)", lambda m: -int(m[1]))
+ add(r"str \w+, \[sp, #-(\d+)\]!", lambda m: int(m[1]))
+ add(r"ldr \w+, \[sp\], #(\d+)", lambda m: -int(m[1]))
+ add(r"stp \w+, \w+, \[sp, #-(\d+)\]!", lambda m: int(m[1]))
+ add(r"ldp \w+, \w+, \[sp\], #(\d+)", lambda m: -int(m[1]))
+ add(r"vpush \{([d0-9, ]*)\}", lambda m: 8 * len(m[1].split(",")))
+ add(r"vpop \{([d0-9, ]*)\}", lambda m: -8 * len(m[1].split(",")))
+ add(r"v?push(?:\.w)? \{([\w+, ]*)\}", lambda m: 4 * len(m[1].split(",")))
+ add(r"v?pop(?:\.w)? \{([\w+, ]*)\}", lambda m: -4 * len(m[1].split(",")))
+
+ # Regular expression which identifies branches.
+ jmp_re = re.compile(r"cb\w* \w+, 0x(\w+)|(?:b|bl|b\w\w) 0x(\w+)")
+
+ offset, future_offset = 0, {}
+ result = [CfaOffset(insts[0].addr, offset)]
+ for addr, inst, symbol in insts:
+ # Previous code branched here, so us that offset instead.
+ # This likely identifies slow-path which is after return.
+ if addr in future_offset:
+ offset = future_offset[addr]
+
+ # Add entry to output (only if the offset changed).
+ if result[-1].offset != offset:
+ result.append(CfaOffset(addr, offset))
+
+ # Adjust offset if the instruction modifies stack pointer.
+ for rexpr, adjust_offset in rexprs:
+ m = rexpr.match(inst)
+ if m:
+ offset += adjust_offset(m)
+ break # First matched pattern wins.
+
+ # Record branches. We only support forward edges for now.
+ m = jmp_re.match(inst)
+ if m:
+ future_offset[int(m[m.lastindex], 16)] = offset
+ return result
+
+def check_fde(fde: Fde, insts: List[Inst], srcs, verbose: bool = False) -> Tuple[str, Set[int]]:
+ """ Compare DWARF offsets to assembly-inferred offsets. Report differences. """
+
+ error, seen_addrs = None, set()
+ cfas = get_cfa(fde)
+ i, dwarf_cfa = 0, get_dwarf_cfa_offsets(cfas)
+ j, infered_cfa = 0, get_infered_cfa_offsets(insts)
+ for inst in insts:
+ seen_addrs.add(inst.addr)
+ while i+1 < len(dwarf_cfa) and dwarf_cfa[i+1].addr <= inst.addr:
+ i += 1
+ while j+1 < len(infered_cfa) and infered_cfa[j+1].addr <= inst.addr:
+ j += 1
+ if verbose:
+ print("{:08x}: dwarf={:4} infered={:4} {:40} // {}".format(
+ inst.addr, str(dwarf_cfa[i].offset), str(infered_cfa[j].offset),
+ inst.inst.strip(), srcs.get(inst.addr, "")))
+ if dwarf_cfa[i].offset is not None and dwarf_cfa[i].offset != infered_cfa[j].offset:
+ if inst.addr in srcs: # Only report if it maps to source code (not padding or literals).
+ error = error or "{:08x} {}".format(inst.addr, srcs.get(inst.addr, ""))
+ return error, seen_addrs
+
+def check_lib(lib: pathlib.Path):
+ assert lib.exists()
+ IGNORE = [
+ "art_quick_throw_null_pointer_exception_from_signal", # Starts with non-zero offset.
+ "art_quick_generic_jni_trampoline", # Saves/restores SP in other register.
+ "nterp_op_", # Uses calculated CFA due to dynamic stack size.
+ "$d.", # Data (literals) interleaved within code.
+ ]
+ fdes = get_fde(lib)
+ asms = collections.deque(get_asm(lib))
+ srcs = {src.addr: src.file + ":" + src.line for src in get_source(lib)}
+ seen = set() # Used to verify the we have covered all assembly source lines.
+
+ for fde in fdes:
+ if fde.addr not in srcs:
+ continue # Ignore if it is not hand-written assembly.
+
+ # Assembly instructions (one FDE can cover several assembly chunks).
+ all_insts, name = [], None
+ while asms and asms[0].addr < fde.end:
+ asm = asms.popleft()
+ if asm.addr < fde.addr:
+ continue
+ insts = get_instructions(asm)
+ if any(asm.name.startswith(i) for i in IGNORE):
+ seen.update([inst.addr for inst in insts])
+ continue
+ all_insts.extend(insts)
+ name = name or asm.name
+ if not all_insts:
+ continue # No assembly
+
+ # Compare DWARF data to assembly instructions
+ error, seen_addrs = check_fde(fde, all_insts, srcs)
+ if error:
+ print("ERROR at " + name + " " + error)
+ check_fde(fde, all_insts, srcs, True)
+ print("")
+ seen.update(seen_addrs)
+ for addr in sorted(set(srcs.keys()) - seen):
+ print("Missing CFI for {:08x}: {}".format(addr, srcs[addr]))
+
+
+def main(argv):
+ """ Check libraries provided on the command line, or use the default build output """
+
+ libs = argv[1:]
+ if not libs:
+ out = os.environ["OUT"]
+ libs.append(out + "/symbols/apex/com.android.art/lib/libart.so")
+ libs.append(out + "/symbols/apex/com.android.art/lib64/libart.so")
+ for lib in libs:
+ check_lib(pathlib.Path(lib))
+
+if __name__ == "__main__":
+ main(os.sys.argv)