import sys class NotFDE(Exception): pass def parse_fde_head(line): spl = line.strip().split() assert len(spl) >= 2 if spl[1] == "ZERO": raise NotFDE assert len(spl) >= 4 typ = spl[3] if typ != "FDE": raise NotFDE assert len(spl) == 6 pc_range = spl[5][3:] pc_beg, pc_end = map(lambda x: int(x, 16), pc_range.split("..")) return pc_beg, pc_end def parse_fde_row(line, ra_col): vals = list(map(lambda x: x.strip(), line.split())) assert len(vals) > ra_col # ra is the rightmost useful column out = {"LOC": int(vals[0], 16), "CFA": vals[1], "ra": vals[ra_col]} return out def clean_rows(rows): # Merge equivalent contiguous rows if not rows: return rows assert len(rows) > 0 out_rows = [rows[0]] for row in rows[1:]: if not row == out_rows[-1]: out_rows.append(row) return out_rows def parse_fde(lines): assert len(lines) > 0 try: pc_beg, pc_end = parse_fde_head(lines[0]) except NotFDE: return rows = [{"LOC": 0, "CFA": "rsp+8", "ra": "c-8"}] # Implicit CIE row if len(lines) >= 2: # Has content head_row = list(map(lambda x: x.strip(), lines[1].split())) ra_col = head_row.index("ra") for line in lines[2:]: rows.append(parse_fde_row(line, ra_col)) return {"beg": pc_beg, "end": pc_end, "rows": clean_rows(rows)} def parse_eh_frame(handle): output = [] cur_lines = [] for line in handle: line = line.strip() if line == "===": return output if line.startswith("Contents of"): continue if line == "": if cur_lines != []: infos = parse_fde(cur_lines) if infos: output.append(infos) cur_lines = [] else: cur_lines.append(line) return sorted(output, key=lambda x: x["beg"]) def match_segments(orig_eh, synth_eh): out = [] matches = [[False] * len(orig_eh), [False] * len(synth_eh)] for orig_id, orig_fde in enumerate(orig_eh): is_plt = False for row in orig_fde["rows"]: if row["CFA"] == "exp": is_plt = True for synth_id, synth_fde in enumerate(synth_eh): if orig_fde["beg"] == synth_fde["beg"]: if is_plt: matches[1][synth_id] = True # PLT -- fake match continue if matches[1][synth_id]: print("Multiple matches (synth)") if matches[0][orig_id]: print( "Multiple matches (orig) {}--{}".format( hex(orig_fde["beg"]), hex(orig_fde["end"]) ) ) else: matches[0][orig_id] = True matches[1][synth_id] = True out.append((orig_fde, synth_fde)) elif ( is_plt and orig_fde["beg"] <= synth_fde["beg"] and synth_fde["end"] <= orig_fde["end"] ): matches[1][synth_id] = True # PLT -- fake match if is_plt: matches[0][orig_id] = True # plt -- fake match unmatched_orig, unmatched_synth = [], [] for orig_id, orig_match in enumerate(matches[0]): if not orig_match: unmatched_orig.append(orig_eh[orig_id]) for synth_id, synth_match in enumerate(matches[1]): if not synth_match: unmatched_synth.append(synth_eh[synth_id]) return out, unmatched_orig, unmatched_synth def fde_pos(fde): return "{}--{}".format(hex(fde["beg"]), hex(fde["end"])) def dump_light_fdes(fdes): for fde in fdes: print("FDE: {}".format(fde_pos(fde))) def match_fde(orig, synth): def vals_of(row): return {"CFA": row["CFA"], "ra": row["ra"]} def loc_of(rch): return rch[1]["LOC"] rows = [orig["rows"], synth["rows"]] cur_val = [vals_of(rows[0][0]), vals_of(rows[1][0])] rowchanges = [] for typ in [0, 1]: for row in rows[typ]: rowchanges.append((typ, row)) rowchanges.sort(key=loc_of) for rowid, rowch in enumerate(rowchanges): typ, row = rowch[0], rowch[1] cur_val[typ] = vals_of(row) if len(rowchanges) > rowid + 1 and loc_of(rowch) == loc_of( rowchanges[rowid + 1] ): continue if cur_val[0] != cur_val[1]: print("Mis {} ; {}".format(cur_val[0], cur_val[1])) return False return True def main(): orig_eh = parse_eh_frame(sys.stdin) synth_eh = parse_eh_frame(sys.stdin) matched, unmatched_orig, unmatched_synth = match_segments(orig_eh, synth_eh) print(len(matched), len(unmatched_orig), len(unmatched_synth)) dump_light_fdes(unmatched_orig) print("==") dump_light_fdes(unmatched_synth) mismatches = 0 for (orig, synth) in matched: if not match_fde(orig, synth): print("MISMATCH: {} ; {}".format(fde_pos(orig), fde_pos(synth))) mismatches += 1 print("TOTAL: {}/{}".format(mismatches, len(matched))) if __name__ == "__main__": main()