95 lines
2.1 KiB
Python
Executable file
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
|