| #!/usr/bin/env python |
| |
| """Validate compact unwind info by cross checking the llvm-objdump |
| reports of the input object file vs final linked output. |
| """ |
| from __future__ import print_function |
| import sys |
| import argparse |
| import re |
| from pprint import pprint |
| |
| |
| def main(): |
| hex = "[a-f\d]" |
| hex8 = hex + "{8}" |
| |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument( |
| "files", |
| metavar="FILES", |
| nargs="*", |
| help="output of (llvm-objdump --unwind-info --syms) for object file(s) plus final linker output", |
| ) |
| parser.add_argument("--debug", action="store_true") |
| args = parser.parse_args() |
| |
| if args.files: |
| objdump_string = "".join([open(f).read() for f in args.files]) |
| else: |
| objdump_string = sys.stdin.read() |
| |
| object_encodings_list = [ |
| (symbol, encoding, personality, lsda) |
| for symbol, encoding, personality, lsda in re.findall( |
| r"start:\s+0x%s+\s+(\w+)\s+" % hex |
| + r"length:\s+0x%s+\s+" % hex |
| + r"compact encoding:\s+0x(%s+)(?:\s+" % hex |
| + r"personality function:\s+0x(%s+)\s+\w+\s+" % hex |
| + r"LSDA:\s+0x(%s+)\s+\w+(?: \+ 0x%s+)?)?" % (hex, hex), |
| objdump_string, |
| re.DOTALL, |
| ) |
| ] |
| object_encodings_map = { |
| symbol: encoding for symbol, encoding, _, _ in object_encodings_list |
| } |
| if not object_encodings_map: |
| sys.exit("no object encodings found in input") |
| |
| # generate-cfi-funcs.py doesn't generate unwind info for _main. |
| object_encodings_map["_main"] = "00000000" |
| |
| program_symbols_map = { |
| address: symbol |
| for address, symbol in re.findall( |
| r"^%s(%s) g\s+F __TEXT,__text (x\1|_main)$" % (hex8, hex8), |
| objdump_string, |
| re.MULTILINE, |
| ) |
| } |
| if not program_symbols_map: |
| sys.exit("no program symbols found in input") |
| |
| program_common_encodings = re.findall( |
| r"^\s+encoding\[(?:\d|\d\d|1[01]\d|12[0-6])\]: 0x(%s+)$" % hex, |
| objdump_string, |
| re.MULTILINE, |
| ) |
| if not program_common_encodings: |
| sys.exit("no common encodings found in input") |
| |
| program_encodings_map = { |
| program_symbols_map[address]: encoding |
| for address, encoding in re.findall( |
| r"^\s+\[\d+\]: function offset=0x(%s+), " % hex |
| + r"encoding(?:\[\d+\])?=0x(%s+)$" % hex, |
| objdump_string, |
| re.MULTILINE, |
| ) |
| } |
| if not object_encodings_map: |
| sys.exit("no program encodings found in input") |
| |
| # Fold adjacent entries from the object file that have matching encodings |
| # TODO(gkm) add check for personality+lsda |
| encoding0 = 0 |
| for symbol in sorted(object_encodings_map): |
| encoding = object_encodings_map[symbol] |
| fold = encoding == encoding0 |
| if fold: |
| del object_encodings_map[symbol] |
| if args.debug: |
| print("%s %s with %s" % ("delete" if fold else "retain", symbol, encoding)) |
| encoding0 = encoding |
| |
| if program_encodings_map != object_encodings_map: |
| if args.debug: |
| print("program encodings map:") |
| pprint(program_encodings_map) |
| print("object encodings map:") |
| pprint(object_encodings_map) |
| sys.exit("encoding maps differ") |
| |
| # Count frequency of object-file folded encodings |
| # and compare with the program-file common encodings table |
| encoding_frequency_map = {} |
| for _, encoding in object_encodings_map.items(): |
| encoding_frequency_map[encoding] = 1 + encoding_frequency_map.get(encoding, 0) |
| encoding_frequencies = [ |
| x |
| for x in sorted( |
| encoding_frequency_map, |
| key=lambda x: (encoding_frequency_map.get(x), x), |
| reverse=True, |
| ) |
| ] |
| del encoding_frequencies[127:] |
| |
| if program_common_encodings != encoding_frequencies: |
| if args.debug: |
| pprint("program common encodings:\n" + str(program_common_encodings)) |
| pprint("object encoding frequencies:\n" + str(encoding_frequencies)) |
| sys.exit("encoding frequencies differ") |
| |
| |
| if __name__ == "__main__": |
| main() |