Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 75 additions & 16 deletions chb/astinterface/ASTInterfaceBasicBlock.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,16 @@ def ast_switch_condition(
'''

def assembly_ast(self, astree: "ASTInterface") -> AST.ASTStmt:
instrs: List[AST.ASTInstruction] = []
for (a, i) in sorted(self.instructions.items(), key=lambda p: p[0]):
instrs.extend(i.assembly_ast(astree))
return astree.mk_instr_sequence(instrs)
# A block with control flow of its own is emitted as fragments rather
# than as a single instruction sequence, so that the condition governing
# a fragment is kept in the low-level ast instead of being flattened
# away with it.
if self.basicblock.has_control_flow():
self.basicblock.partition_control_flow()
return self.fragmented_assembly_ast(astree)

return self.linear_assembly_ast(
astree, sorted(self.instructions.values(), key = lambda p:p.iaddr))

def ast_fragment(
self, astree: "ASTInterface", frag: "BasicBlockFragment") -> AST.ASTStmt:
Expand All @@ -208,7 +214,7 @@ def ast_fragment(
cinstr = theninstrs[0]
brcond = cinstr.ast_cc_condition(astree)
if brcond is None:
chklogger.logger.warning(
chklogger.logger.error(
"No instruction predicate expression found at address %s",
cinstr.iaddr)
brcond = astree.mk_temp_lval_expression()
Expand All @@ -224,19 +230,62 @@ def ast_fragment(
instrs = [self.get_instruction(i.iaddr) for i in frag.linear]
return self.linear_ast(astree, instrs)

def fragmented_ast(self, astree: "ASTInterface") -> AST.ASTStmt:

def assembly_ast_fragment(
Comment thread
waskyo marked this conversation as resolved.
self,
astree: "ASTInterface",
frag: "BasicBlockFragment") -> AST.ASTStmt:
if not frag.is_predicated:
instrs = [self.get_instruction(i.iaddr) for i in frag.linear]
return self.linear_assembly_ast(astree, instrs)

# A predicated instruction is lifted as an if-statement, so this 'if'
# has no branch instruction of its own to point at. Build the two
# bodies, take the condition from the first predicated instruction, and
# record which instruction bytes the new statement stands for, since the
# span record is the only thing tying it back to the binary.
theninstrs = [self.get_instruction(i.iaddr) for i in frag.thenbranch]
elseinstrs = [self.get_instruction(i.iaddr) for i in frag.elsebranch]
thenstmt = self.linear_assembly_block_ast(astree, theninstrs)
elsestmt = self.linear_assembly_block_ast(astree, elseinstrs)
spans = [(i.iaddr, i.bytestring) for i in theninstrs + elseinstrs]
Comment thread
waskyo marked this conversation as resolved.
cinstr = theninstrs[0]
brcond = cinstr.assembly_ast_cc_condition(astree)
if brcond is None:
chklogger.logger.error(
"No low-level instruction predicate expression found at "
+ "address %s",
cinstr.iaddr)
return self.linear_assembly_ast(astree, theninstrs + elseinstrs)

instrcount = len(theninstrs) + len(elseinstrs)
# mk_branch creates the if-statement, and add_stmt_span links it to the
# bytes of the instructions it covers, keyed by its locationid.
ifstmt = astree.mk_branch(
brcond, thenstmt, elsestmt, cinstr.iaddr, predicated=instrcount)
astree.add_stmt_span(ifstmt.locationid, spans)
return ifstmt

def fragmented_ast(
self,
astree: "ASTInterface",
ll: bool = False) -> AST.ASTStmt:
if len(self.basicblock.partition) == 0:
raise UF.CHBError("Error in fragmented ast")

stmts: List[AST.ASTStmt] = []

for (a, bf) in sorted(self.basicblock.partition.items()):
stmt = self.ast_fragment(astree, bf)
if ll:
stmt = self.assembly_ast_fragment(astree, bf)
else:
stmt = self.ast_fragment(astree, bf)
stmts.append(stmt)

return astree.mk_block(stmts)

def fragmented_assembly_ast(self, astree: "ASTInterface") -> AST.ASTStmt:
return self.fragmented_ast(astree, ll=True)

def ast(self, astree: "ASTInterface") -> AST.ASTStmt:
if self.is_trampoline:
return self.trampoline_ast(astree)
Expand All @@ -253,22 +302,32 @@ def ast(self, astree: "ASTInterface") -> AST.ASTStmt:
def linear_block_ast(
self,
astree: "ASTInterface",
instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt:
instrs: List[AST.ASTInstruction] = []
for i in instritems:
instrs.extend(i.ast(astree))
instrseq = astree.mk_instr_sequence(instrs)
return astree.mk_block([instrseq])
instritems: List[ASTInterfaceInstruction],
ll: bool = False) -> AST.ASTStmt:
return astree.mk_block([self.linear_ast(astree, instritems, ll=ll)])

def linear_ast(
self,
astree: "ASTInterface",
instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt:
instritems: List[ASTInterfaceInstruction],
ll: bool = False) -> AST.ASTStmt:
instrs: List[AST.ASTInstruction] = []
for i in instritems:
Comment thread
waskyo marked this conversation as resolved.
instrs.extend(i.ast(astree))
instrs.extend(i.assembly_ast(astree) if ll else i.ast(astree))
return astree.mk_instr_sequence(instrs)

def linear_assembly_block_ast(
self,
astree: "ASTInterface",
instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt:
return self.linear_block_ast(astree, instritems, ll=True)

def linear_assembly_ast(
self,
astree: "ASTInterface",
instritems: List[ASTInterfaceInstruction]) -> AST.ASTStmt:
return self.linear_ast(astree, instritems, ll=True)

def trampoline_block_ast(
self,
role: str,
Expand Down
7 changes: 7 additions & 0 deletions chb/astinterface/ASTInterfaceInstruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ def ast_cc_condition(self, astree: "ASTInterface") -> Optional[AST.ASTExpr]:
self.ast_cc_condition_prov(astree)
return self.hl_ast_cc_condition

def assembly_ast_cc_condition(
Comment thread
waskyo marked this conversation as resolved.
self,
astree: "ASTInterface") -> Optional[AST.ASTExpr]:
if self.ll_ast_cc_condition is None:
self.ast_cc_condition_prov(astree)
return self.ll_ast_cc_condition

def ast_switch_condition(
self,
astree: "ASTInterface") -> Optional[AST.ASTExpr]:
Expand Down