Note that there is a triple indirection involved with personalities and compact unwind: 1. Two bits of each CU encoding are used as an offset into the personality array. 2. Each entry of the personality array is an offset from the image base. The resulting address (after adding the image base) should point within the GOT. 3. The corresponding GOT entry contains the actual pointer to the personality function. To further complicate things, when the personality function is in the object file (as opposed to a dylib), its references in `__compact_unwind` may refer to it via a section + offset relocation instead of a symbol relocation. Since our GOT implementation can only create entries for symbols, we have to create a synthetic symbol at the given section offset. Reviewed By: clayborg Differential Revision: https://reviews.llvm.org/D95809
102 lines
3.7 KiB
Python
Executable File
102 lines
3.7 KiB
Python
Executable File
#!/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")
|
|
|
|
program_symbols_map = {address:symbol
|
|
for address, symbol in
|
|
re.findall(r"^%s(%s) g\s+F __TEXT,__text (x\1)$" % (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()
|