1#!/usr/bin/python3 2 3# Parsing dwarfdump's output to determine whether the location list for the 4# parameter "b" covers all of the function. The script searches for information 5# in the input file to determine the [prologue, epilogue) range for the 6# function, the location list range for "b", and checks that the latter covers 7# the entirety of the former. 8import re 9import sys 10 11DebugInfoPattern = r"\.debug_info contents:" 12DebugLinePattern = r"\.debug_line contents:" 13ProloguePattern = r"^\s*0x([0-9a-f]+)\s.+prologue_end" 14EpiloguePattern = r"^\s*0x([0-9a-f]+)\s.+epilogue_begin" 15FormalPattern = r"^0x[0-9a-f]+:\s+DW_TAG_formal_parameter" 16LocationPattern = r"DW_AT_location\s+\[DW_FORM_([a-z_]+)\](?:.*0x([a-f0-9]+))" 17DebugLocPattern = r'\[0x([a-f0-9]+),\s+0x([a-f0-9]+)\) ".text": (.+)$' 18 19SeenDebugInfo = False 20SeenDebugLine = False 21LocationRanges = None 22PrologueEnd = None 23EpilogueBegin = None 24 25# The dwarfdump output should contain the DW_AT_location for "b" first, then the 26# line table which should contain prologue_end and epilogue_begin entries. 27with open(sys.argv[1], "r") as dwarf_dump_file: 28 dwarf_iter = iter(dwarf_dump_file) 29 for line in dwarf_iter: 30 if not SeenDebugInfo and re.match(DebugInfoPattern, line): 31 SeenDebugInfo = True 32 if not SeenDebugLine and re.match(DebugLinePattern, line): 33 SeenDebugLine = True 34 # Get the range of DW_AT_location for "b". 35 if LocationRanges is None: 36 if match := re.match(FormalPattern, line): 37 # Go until we either find DW_AT_location or reach the end of this entry. 38 location_match = None 39 while location_match is None: 40 if (line := next(dwarf_iter, "")) == "\n": 41 raise RuntimeError( 42 ".debug_info output is missing DW_AT_location for 'b'" 43 ) 44 location_match = re.search(LocationPattern, line) 45 # Variable has whole-scope location, represented by an empty tuple. 46 if location_match.group(1) == "exprloc": 47 LocationRanges = () 48 continue 49 if location_match.group(1) != "sec_offset": 50 raise RuntimeError( 51 f"Unhandled form for DW_AT_location: DW_FORM_{location_match.group(1)}" 52 ) 53 # Variable has location range list. 54 if ( 55 debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, "")) 56 ) is None: 57 raise RuntimeError(f"Invalid location range list for 'b'") 58 LocationRanges = ( 59 int(debug_loc_match.group(1), 16), 60 int(debug_loc_match.group(2), 16), 61 ) 62 while ( 63 debug_loc_match := re.search(DebugLocPattern, next(dwarf_iter, "")) 64 ) is not None: 65 match_loc_start = int(debug_loc_match.group(1), 16) 66 match_loc_end = int(debug_loc_match.group(2), 16) 67 match_expr = debug_loc_match.group(3) 68 if match_loc_start != LocationRanges[1]: 69 raise RuntimeError( 70 f"Location list for 'b' is discontinuous from [0x{LocationRanges[1]:x}, 0x{match_loc_start:x})" 71 ) 72 if "stack_value" in match_expr: 73 raise RuntimeError( 74 f"Location list for 'b' contains a stack_value expression: {match_expr}" 75 ) 76 LocationRanges = (LocationRanges[0], match_loc_end) 77 # Get the prologue_end address. 78 elif PrologueEnd is None: 79 if match := re.match(ProloguePattern, line): 80 PrologueEnd = int(match.group(1), 16) 81 # Get the epilogue_begin address. 82 elif EpilogueBegin is None: 83 if match := re.match(EpiloguePattern, line): 84 EpilogueBegin = int(match.group(1), 16) 85 break 86 87if not SeenDebugInfo: 88 raise RuntimeError(".debug_info section not found.") 89if not SeenDebugLine: 90 raise RuntimeError(".debug_line section not found.") 91 92if LocationRanges is None: 93 raise RuntimeError(".debug_info output is missing parameter 'b'") 94if PrologueEnd is None: 95 raise RuntimeError(".debug_line output is missing prologue_end") 96if EpilogueBegin is None: 97 raise RuntimeError(".debug_line output is missing epilogue_begin") 98 99if len(LocationRanges) == 2 and ( 100 LocationRanges[0] > PrologueEnd or LocationRanges[1] < EpilogueBegin 101): 102 raise RuntimeError( 103 f"""Location list for 'b' does not cover the whole function:") 104 Prologue to Epilogue = [0x{PrologueEnd:x}, 0x{EpilogueBegin:x}) 105 Location range = [0x{LocationRanges[0]:x}, 0x{LocationRanges[1]:x}) 106""" 107 ) 108