Skip to content
Open
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
84 changes: 68 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,55 @@ 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(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not something from your changes, but trying to keep track of the distinction between ast_fragment and fragmented_ast is making my head spin 😵‍💫

If you're feeling magnanimous, some comments would be amaze 🎅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but how about in a separate PR?

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)

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]
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)
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 +295,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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is replacing the original code from def assembly_ast but that one looked at the instructions in sorted order:

for (a, i) in sorted(self.instructions.items(), key=lambda p: p[0]):

Does that difference matter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ordering is actually moved out into the callers, so the ordering has not changed.

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(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#til q: what does the cc stand for in the function name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc is "condition code". The ARM instruction's conditional-execution suffix and the NZCV flags it tests.

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