dwarf-synthesis/tools/check_rbp_rsp_shift/check_rbp_rsp_shift.py

95 lines
2.1 KiB
Python
Executable file

#!/usr/bin/python
from enum import IntEnum
import sys
""" Parse a `readelf -wF` output, trying to locate CFA=f(rbp) to CFA=g(rsp) changes,
and to detect the offset applied to rsp in such cases. """
class Eof(Exception):
pass
program_name = sys.argv[1]
def log_entry(entry):
print("[{}] {}".format(program_name, entry))
def parse_line(line):
spl = line.strip().split(" ")
addr = int(spl[0], 16)
cfa = spl[1]
return addr, cfa
def match_fde_header(line):
spl = line.strip().split()
if len(spl) == 6 and spl[3] == "FDE":
return True
return False
class CfaType(IntEnum):
OTHER = 0
RSP_BASED = 1
RBP_BASED = 2
def get_cfa_type(cfa):
if cfa.startswith("rsp"):
return CfaType.RSP_BASED
if cfa.startswith("rbp"):
return CfaType.RBP_BASED
return CfaType.OTHER
def parse_fde(lines):
# Read until FDE head
for line in lines:
if match_fde_header(line):
break
try:
post_header = next(lines) # Waste a line — FDE columns head
if not post_header.strip(): # Empty FDE — return now
return True
except StopIteration:
return False
# Read each row until an empty line is found
prev_rbp = False # Was the last line rbp indexed?
closed_rbp_block = False # Was there already a rbp-indexed block which is over?
for line in lines:
line = line.strip()
if not line: # Empty line — FDE end
return True
addr, cfa = parse_line(line)
cfa_type = get_cfa_type(cfa)
if cfa_type == CfaType.RSP_BASED and prev_rbp:
closed_rbp_block = True
if cfa != "rsp+8":
log_entry(
"(E) {}: CFA={} after %rbp-based index".format(hex(addr), cfa)
)
if cfa_type == CfaType.RBP_BASED:
prev_rbp = True
if closed_rbp_block:
log_entry("(W) {}: two %rbp blocks in function".format(addr))
else:
prev_rbp = False
return False
if __name__ == "__main__":
handle = sys.stdin
while parse_fde(handle):
pass