From c9a6dc74b888383b0a457f01659993a60024c52d Mon Sep 17 00:00:00 2001 From: Alistair Michael Date: Wed, 20 Sep 2023 17:02:03 +1000 Subject: [PATCH 1/4] add basic human readable il output --- src/main/scala/Main.scala | 5 +- src/main/scala/ir/Expr.scala | 5 +- src/main/scala/ir/Program.scala | 8 +- src/main/scala/translating/ILtoIL.scala | 279 ++++++++++++++++++ src/main/scala/util/RunUtils.scala | 13 +- .../scala/MemoryRegionAnalysisMiscTest.scala | 2 +- 6 files changed, 306 insertions(+), 6 deletions(-) create mode 100644 src/main/scala/translating/ILtoIL.scala diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala index 86d347557..0701789b0 100644 --- a/src/main/scala/Main.scala +++ b/src/main/scala/Main.scala @@ -30,6 +30,8 @@ object Main { analyse: Flag, @arg(name="interpret", doc="Run BASIL IL interpreter.") interpret: Flag, + @arg(name="dump-il", doc="Dump the Intermediate Language to text.") + dumpIL: Flag, @arg(name="help", short='h', doc="Show this help message.") help: Flag ) @@ -55,7 +57,8 @@ object Main { Logger.setLevel(LogLevel.DEBUG) } - val program: BProgram = RunUtils.loadAndTranslate(conf.adtFileName, conf.relfFileName, conf.specFileName, conf.analyse.value, conf.interpret.value) + + val program: BProgram = RunUtils.loadAndTranslate(conf.adtFileName, conf.relfFileName, conf.specFileName, conf.analyse.value, conf.interpret.value, conf.dumpIL.value) RunUtils.writeToFile(program, conf.outFileName) } diff --git a/src/main/scala/ir/Expr.scala b/src/main/scala/ir/Expr.scala index 8a2801dbd..956b545c3 100644 --- a/src/main/scala/ir/Expr.scala +++ b/src/main/scala/ir/Expr.scala @@ -356,6 +356,9 @@ sealed trait Variable extends Expr { case b: BitVecType => b.size case _ => throw new Exception("tried to get size of non-bitvector") } + + override def toString: String = s"Variable($name, $irType)" + override def acceptVisit(visitor: Visitor): Variable = throw new Exception("visitor " + visitor + " unimplemented for: " + this) } @@ -372,4 +375,4 @@ case class LocalVar(override val name: String, override val irType: IRType) exte override def toBoogie: BVar = BVariable(s"$name", irType.toBoogie, Scope.Local) override def toString: String = s"LocalVar($name, $irType)" override def acceptVisit(visitor: Visitor): Variable = visitor.visitLocalVar(this) -} \ No newline at end of file +} diff --git a/src/main/scala/ir/Program.scala b/src/main/scala/ir/Program.scala index d34c9e998..f8b71aee9 100644 --- a/src/main/scala/ir/Program.scala +++ b/src/main/scala/ir/Program.scala @@ -96,6 +96,12 @@ class Program(var procedures: ArrayBuffer[Procedure], var initialMemory: ArrayBu p.stackIdentification() } } + + + override def toString() : String = { + procedures.map(_.toString).mkString("\n") + } + } class Procedure(var name: String, var address: Option[Int], var blocks: ArrayBuffer[Block], var in: ArrayBuffer[Parameter], var out: ArrayBuffer[Parameter]) { @@ -179,4 +185,4 @@ class Parameter(var name: String, var size: Int, var value: Register) { def toGamma: BVariable = BParam(s"Gamma_$name", BoolBType) } -case class MemorySection(name: String, address: Int, size: Int, bytes: Seq[Literal]) \ No newline at end of file +case class MemorySection(name: String, address: Int, size: Int, bytes: Seq[Literal]) diff --git a/src/main/scala/translating/ILtoIL.scala b/src/main/scala/translating/ILtoIL.scala new file mode 100644 index 000000000..70d186fde --- /dev/null +++ b/src/main/scala/translating/ILtoIL.scala @@ -0,0 +1,279 @@ +package translating +import ir._ + + +private class ILSerialiser extends Visitor { + var program: StringBuilder = StringBuilder() + + var indentLevel = 0 + + def getIndent(): String = { + " " * indentLevel + } + + def blockIdentifier(block: Block) : String = { + val i = block.address match { + case Some(addr) => f"${addr}:${block.label}" + case None => f"?:${block.label}" + } + '"' + i + '"' + } + + def procedureIdentifier(proc: Procedure): String = { + val i = proc.address match { + case Some(addr) => f"${addr}:${proc.name}" + case None => f"?:${proc.name}" + } + '"' + i + '"' + } + + + override def visitExpr(node: Expr): Expr = { + program ++= "Expr(" + val r = node.acceptVisit(this) + program ++= ")" + r + } + + override def visitStatement(node: Statement): Statement = node.acceptVisit(this) + + override def visitLocalAssign(node: LocalAssign): Statement = { + program ++= "LocalAssign(" + node.lhs = visitVariable(node.lhs) + program ++= " := " + node.rhs = visitExpr(node.rhs) + program ++= ")" + node + } + + override def visitMemoryAssign(node: MemoryAssign): Statement = { + program ++= "MemoryAssign(" + node.lhs = node.lhs + node.rhs = visitMemoryStore(node.rhs) + program ++= ")" + node + } + + override def visitAssert(node: Assert): Statement = { + program ++= "Assert(" + node.body = visitExpr(node.body) + program ++= ")" + node + } + + override def visitJump(node: Jump): Jump = { + val n = node.acceptVisit(this) + n + } + + override def visitGoTo(node: GoTo): Jump = { + program ++= "GoTo(" + // TODO + program ++= blockIdentifier(node.target) + program ++= ", " + program ++= "Condition(" + node.condition = node.condition.map(visitExpr) + program ++= ")" // Condition + program ++= ")" // GoTo + node + } + + override def visitDirectCall(node: DirectCall): Jump = { + program ++= "DirectCall(" + program ++= procedureIdentifier(node.target) + program ++= ", " + program ++= "Condition(" + node.condition = node.condition.map(visitExpr) + program ++= ")" // Condition + program ++= ")" // DirectCall + node + } + + override def visitIndirectCall(node: IndirectCall): Jump = { + program ++= "IndirectCall(" + node.target = visitVariable(node.target) + program ++= ", " + program ++= "Condition(" + node.condition = node.condition.map(visitExpr) + program ++= ")" // Condition + program ++= ")" // IndirectCall + node + } + + override def visitBlock(node: Block): Block = { + program ++= getIndent() + program ++= "Block(" + blockIdentifier(node) + ",\n" + indentLevel += 1 + program ++= getIndent() + program ++= "statements(\n" + indentLevel += 1 + + for (i <- node.statements.indices) { + program ++= getIndent() + node.statements(i) = visitStatement(node.statements(i)) + program ++= "\n" + } + indentLevel -= 1 + program ++= getIndent() + "),\n" + program ++= getIndent() + "jumps(\n" + indentLevel += 1 + for (i <- node.jumps.indices) { + program ++= getIndent() + node.jumps(i) = visitJump(node.jumps(i)) + program ++= "\n" + } + indentLevel -= 1 + program ++= getIndent() + ")\n" + indentLevel -= 1 + program ++= getIndent() + program ++= ")\n" + node + } + + override def visitProcedure(node: Procedure): Procedure = { + program ++= "Procedure(" + procedureIdentifier(node) + ", " + indentLevel += 1 + + program ++= "in(" + for (i <- node.in.indices) { + node.in(i) = visitParameter(node.in(i)) + if (i != node.in.size - 1) + program ++= ", " + } + program ++= "), " + program ++= "out(" + for (i <- node.out.indices) { + node.out(i) = visitParameter(node.out(i)) + if (i != node.out.size - 1) + program ++= ", " + } + program ++= "), " + program ++= "blocks(\n" + for (i <- node.blocks.indices) { + node.blocks(i) = visitBlock(node.blocks(i)) + } + program ++= "))\n" + indentLevel -= 1 + node + } + + override def visitParameter(node: Parameter): Parameter = { + program ++= "Parameter(" + node.value = visitRegister(node.value) + program ++= ")" + node + } + + override def visitProgram(node: Program): Program = { + for (i <- node.procedures.indices) { + val updatedProcedure = visitProcedure(node.procedures(i)) + val targetProcedure = node.procedures(i) + if (targetProcedure == node.mainProcedure) { + node.mainProcedure = updatedProcedure + } + node.procedures(i) = updatedProcedure + } + node + } + + override def visitExtract(node: Extract): Expr = { + program ++= "Extract(" + node.body = visitExpr(node.body) + program ++= ")" + node + } + + override def visitRepeat(node: Repeat): Expr = { + program ++= "Repeat(" + node.body = visitExpr(node.body) + program ++= ")" + node + } + + override def visitZeroExtend(node: ZeroExtend): Expr = { + program ++= "ZeroExtend(" + node.body = visitExpr(node.body) + program ++= ")" + node + } + + override def visitSignExtend(node: SignExtend): Expr = { + program ++= "SignExtend(" + node.body = visitExpr(node.body) + program ++= ")" + node + } + + override def visitUnaryExpr(node: UnaryExpr): Expr = { + program ++= "UnaryExpr(" + node.arg = visitExpr(node.arg) + program ++= ")" + node + } + + override def visitBinaryExpr(node: BinaryExpr): Expr = { + program ++= "BinaryExpr(" + node.arg1 = visitExpr(node.arg1) + program ++= ", " + node.arg2 = visitExpr(node.arg2) + program ++= ")" + node + } + + override def visitMemoryStore(node: MemoryStore): MemoryStore = { + program ++= "MemoryStore(" + visitMemory(node.mem) + program ++= "[" + node.index = visitExpr(node.index) + program ++= "] := " + node.value = visitExpr(node.value) + program ++= ")" + node + } + + override def visitMemoryLoad(node: MemoryLoad): Expr = { + program ++= "MemoryLoad(" + node.mem = visitMemory(node.mem) + program ++= ", [" + node.index = visitExpr(node.index) + program ++= "])" + node + } + + override def visitMemory(node: Memory): Memory = { + program ++= "Memory(" + program ++= node.toString() + program ++= ")" + node + } + + override def visitVariable(node: Variable): Variable = { + program ++= node.toString() + node + //node.acceptVisit(this) + } + + override def visitRegister(node: Register): Register = { + program ++= node.toString() + node + } + + override def visitLocalVar(node: LocalVar): LocalVar = { + program ++= node.toString() + node + } + + override def visitLiteral(node: Literal): Literal = { + program ++= node.toString() + node + } + + +} + +def serialiseIL(p: Program): String = { + val s = ILSerialiser() + s.visitProgram(p) + s.program.toString() +} + diff --git a/src/main/scala/util/RunUtils.scala b/src/main/scala/util/RunUtils.scala index 9b1a3a426..2f3797b74 100644 --- a/src/main/scala/util/RunUtils.scala +++ b/src/main/scala/util/RunUtils.scala @@ -54,7 +54,7 @@ object RunUtils { } } - def loadAndTranslate(BAPFileName: String, readELFFileName: String, specFileName: Option[String], performAnalysis: Boolean, performInterpret: Boolean): BProgram = { + def loadAndTranslate(BAPFileName: String, readELFFileName: String, specFileName: Option[String], performAnalysis: Boolean, performInterpret: Boolean, dumpIL: Boolean): BProgram = { val bapProgram = loadBAP(BAPFileName) val (externalFunctions, globals, globalOffsets, mainAddress) = loadReadELF(readELFFileName) @@ -74,10 +74,19 @@ object RunUtils { IRProgram = externalRemover.visitProgram(IRProgram) IRProgram = renamer.visitProgram(IRProgram) + if (dumpIL) { + dump_file(serialiseIL(IRProgram), "before-analysis.il") + } + if (performAnalysis) { analyse(IRProgram, externalFunctions, globals, globalOffsets) + if (dumpIL) { + dump_file(serialiseIL(IRProgram), "after-analysis.il") + } } + + IRProgram.stripUnreachableFunctions() IRProgram.stackIdentification() IRProgram.setModifies() @@ -113,7 +122,7 @@ object RunUtils { val cfg = IntraproceduralProgramCfg.generateFromProgram(IRProgram) // Output.output(OtherOutput(OutputKindE.cfg), cfg.toDot({ x => - // x.toString + // x.toStrin // }, Output.dotIder)) //Output.output(OtherOutput(OutputKindE.cfg), cfg.toDot(x => x.toString, Output.dotIder), "intra_cfg") diff --git a/src/test/scala/MemoryRegionAnalysisMiscTest.scala b/src/test/scala/MemoryRegionAnalysisMiscTest.scala index ab88ff4bc..2d4a913f9 100644 --- a/src/test/scala/MemoryRegionAnalysisMiscTest.scala +++ b/src/test/scala/MemoryRegionAnalysisMiscTest.scala @@ -14,7 +14,7 @@ class MemoryRegionAnalysisMiscTest extends AnyFunSuite with OneInstancePerTest { var expected = "" var actual = "" var output: Option[Map[analysis.CfgNode, ?]] = None - RunUtils.loadAndTranslate(examplesPath + s"${name}/${name}.adt", examplesPath + s"${name}/${name}.relf", None, true, false) + RunUtils.loadAndTranslate(examplesPath + s"${name}/${name}.adt", examplesPath + s"${name}/${name}.relf", None, true, false, false) try { // create dump folder if it does not exist val dumpFolder = File(tempPath) From e3c3573abb920c7ed1d5e260df9fe1b8e57a0d92 Mon Sep 17 00:00:00 2001 From: Alistair Michael Date: Wed, 20 Sep 2023 17:12:23 +1000 Subject: [PATCH 2/4] minor cleanup --- src/main/scala/ir/Program.scala | 4 ---- src/main/scala/translating/ILtoIL.scala | 7 +++---- src/main/scala/util/RunUtils.scala | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/scala/ir/Program.scala b/src/main/scala/ir/Program.scala index f8b71aee9..4aeff086a 100644 --- a/src/main/scala/ir/Program.scala +++ b/src/main/scala/ir/Program.scala @@ -98,10 +98,6 @@ class Program(var procedures: ArrayBuffer[Procedure], var initialMemory: ArrayBu } - override def toString() : String = { - procedures.map(_.toString).mkString("\n") - } - } class Procedure(var name: String, var address: Option[Int], var blocks: ArrayBuffer[Block], var in: ArrayBuffer[Parameter], var out: ArrayBuffer[Parameter]) { diff --git a/src/main/scala/translating/ILtoIL.scala b/src/main/scala/translating/ILtoIL.scala index 70d186fde..2686745a6 100644 --- a/src/main/scala/translating/ILtoIL.scala +++ b/src/main/scala/translating/ILtoIL.scala @@ -8,7 +8,7 @@ private class ILSerialiser extends Visitor { var indentLevel = 0 def getIndent(): String = { - " " * indentLevel + " " * indentLevel } def blockIdentifier(block: Block) : String = { @@ -93,7 +93,7 @@ private class ILSerialiser extends Visitor { program ++= "IndirectCall(" node.target = visitVariable(node.target) program ++= ", " - program ++= "Condition(" + program ++= "condition(" node.condition = node.condition.map(visitExpr) program ++= ")" // Condition program ++= ")" // IndirectCall @@ -152,7 +152,7 @@ private class ILSerialiser extends Visitor { for (i <- node.blocks.indices) { node.blocks(i) = visitBlock(node.blocks(i)) } - program ++= "))\n" + program ++= ")),\n" indentLevel -= 1 node } @@ -250,7 +250,6 @@ private class ILSerialiser extends Visitor { override def visitVariable(node: Variable): Variable = { program ++= node.toString() node - //node.acceptVisit(this) } override def visitRegister(node: Register): Register = { diff --git a/src/main/scala/util/RunUtils.scala b/src/main/scala/util/RunUtils.scala index 2f3797b74..20c557a02 100644 --- a/src/main/scala/util/RunUtils.scala +++ b/src/main/scala/util/RunUtils.scala @@ -122,7 +122,7 @@ object RunUtils { val cfg = IntraproceduralProgramCfg.generateFromProgram(IRProgram) // Output.output(OtherOutput(OutputKindE.cfg), cfg.toDot({ x => - // x.toStrin + // x.toString // }, Output.dotIder)) //Output.output(OtherOutput(OutputKindE.cfg), cfg.toDot(x => x.toString, Output.dotIder), "intra_cfg") From cd4d74c3f0fabe446fce88fae899c5ce4fe981b3 Mon Sep 17 00:00:00 2001 From: Alistair Michael Date: Thu, 21 Sep 2023 14:11:51 +1000 Subject: [PATCH 3/4] use readonly, add operators, fix memory --- src/main/scala/translating/ILtoIL.scala | 91 +++++++++++++------------ 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/src/main/scala/translating/ILtoIL.scala b/src/main/scala/translating/ILtoIL.scala index 2686745a6..72f305c0c 100644 --- a/src/main/scala/translating/ILtoIL.scala +++ b/src/main/scala/translating/ILtoIL.scala @@ -2,7 +2,7 @@ package translating import ir._ -private class ILSerialiser extends Visitor { +private class ILSerialiser extends ReadOnlyVisitor { var program: StringBuilder = StringBuilder() var indentLevel = 0 @@ -30,40 +30,39 @@ private class ILSerialiser extends Visitor { override def visitExpr(node: Expr): Expr = { program ++= "Expr(" - val r = node.acceptVisit(this) + node.acceptVisit(this) program ++= ")" - r + node } override def visitStatement(node: Statement): Statement = node.acceptVisit(this) override def visitLocalAssign(node: LocalAssign): Statement = { program ++= "LocalAssign(" - node.lhs = visitVariable(node.lhs) + visitVariable(node.lhs) program ++= " := " - node.rhs = visitExpr(node.rhs) + visitExpr(node.rhs) program ++= ")" node } override def visitMemoryAssign(node: MemoryAssign): Statement = { program ++= "MemoryAssign(" - node.lhs = node.lhs - node.rhs = visitMemoryStore(node.rhs) + visitMemoryStore(node.rhs) program ++= ")" node } override def visitAssert(node: Assert): Statement = { program ++= "Assert(" - node.body = visitExpr(node.body) + visitExpr(node.body) program ++= ")" node } override def visitJump(node: Jump): Jump = { - val n = node.acceptVisit(this) - n + node.acceptVisit(this) + node } override def visitGoTo(node: GoTo): Jump = { @@ -71,8 +70,8 @@ private class ILSerialiser extends Visitor { // TODO program ++= blockIdentifier(node.target) program ++= ", " - program ++= "Condition(" - node.condition = node.condition.map(visitExpr) + program ++= "condition(" + node.condition.map(visitExpr) program ++= ")" // Condition program ++= ")" // GoTo node @@ -82,8 +81,8 @@ private class ILSerialiser extends Visitor { program ++= "DirectCall(" program ++= procedureIdentifier(node.target) program ++= ", " - program ++= "Condition(" - node.condition = node.condition.map(visitExpr) + program ++= "condition(" + node.condition.map(visitExpr) program ++= ")" // Condition program ++= ")" // DirectCall node @@ -91,10 +90,10 @@ private class ILSerialiser extends Visitor { override def visitIndirectCall(node: IndirectCall): Jump = { program ++= "IndirectCall(" - node.target = visitVariable(node.target) + visitVariable(node.target) program ++= ", " program ++= "condition(" - node.condition = node.condition.map(visitExpr) + node.condition.map(visitExpr) program ++= ")" // Condition program ++= ")" // IndirectCall node @@ -110,16 +109,16 @@ private class ILSerialiser extends Visitor { for (i <- node.statements.indices) { program ++= getIndent() - node.statements(i) = visitStatement(node.statements(i)) + visitStatement(node.statements(i)) program ++= "\n" } indentLevel -= 1 program ++= getIndent() + "),\n" program ++= getIndent() + "jumps(\n" indentLevel += 1 - for (i <- node.jumps.indices) { + for (j <- node.jumps) { program ++= getIndent() - node.jumps(i) = visitJump(node.jumps(i)) + visitJump(j) program ++= "\n" } indentLevel -= 1 @@ -136,21 +135,22 @@ private class ILSerialiser extends Visitor { program ++= "in(" for (i <- node.in.indices) { - node.in(i) = visitParameter(node.in(i)) - if (i != node.in.size - 1) + visitParameter(node.in(i)) + if (i != node.in.size - 1) { program ++= ", " + } } program ++= "), " program ++= "out(" for (i <- node.out.indices) { - node.out(i) = visitParameter(node.out(i)) + visitParameter(node.out(i)) if (i != node.out.size - 1) program ++= ", " } program ++= "), " program ++= "blocks(\n" for (i <- node.blocks.indices) { - node.blocks(i) = visitBlock(node.blocks(i)) + visitBlock(node.blocks(i)) } program ++= ")),\n" indentLevel -= 1 @@ -159,63 +159,64 @@ private class ILSerialiser extends Visitor { override def visitParameter(node: Parameter): Parameter = { program ++= "Parameter(" - node.value = visitRegister(node.value) + visitRegister(node.value) program ++= ")" node } override def visitProgram(node: Program): Program = { - for (i <- node.procedures.indices) { - val updatedProcedure = visitProcedure(node.procedures(i)) - val targetProcedure = node.procedures(i) - if (targetProcedure == node.mainProcedure) { - node.mainProcedure = updatedProcedure - } - node.procedures(i) = updatedProcedure + for (i <- node.procedures) { + visitProcedure(i) } node } override def visitExtract(node: Extract): Expr = { program ++= "Extract(" - node.body = visitExpr(node.body) + visitExpr(node.body) + program ++= f"[${node.end}:${node.start}]" program ++= ")" node } override def visitRepeat(node: Repeat): Expr = { program ++= "Repeat(" - node.body = visitExpr(node.body) + visitExpr(node.body) + program ++= f", ${node.repeats}" program ++= ")" node } override def visitZeroExtend(node: ZeroExtend): Expr = { program ++= "ZeroExtend(" - node.body = visitExpr(node.body) + visitExpr(node.body) + program ++= f", ${node.extension}" program ++= ")" node } override def visitSignExtend(node: SignExtend): Expr = { program ++= "SignExtend(" - node.body = visitExpr(node.body) + visitExpr(node.body) + program ++= f", ${node.extension}" program ++= ")" node } override def visitUnaryExpr(node: UnaryExpr): Expr = { program ++= "UnaryExpr(" - node.arg = visitExpr(node.arg) + program ++= '"' + f"${node.op}" + '"' + ", " + visitExpr(node.arg) program ++= ")" node } override def visitBinaryExpr(node: BinaryExpr): Expr = { program ++= "BinaryExpr(" - node.arg1 = visitExpr(node.arg1) + program ++= "\"" + node.op + '"' + ", " + visitExpr(node.arg1) program ++= ", " - node.arg2 = visitExpr(node.arg2) + visitExpr(node.arg2) program ++= ")" node } @@ -224,26 +225,26 @@ private class ILSerialiser extends Visitor { program ++= "MemoryStore(" visitMemory(node.mem) program ++= "[" - node.index = visitExpr(node.index) + visitExpr(node.index) program ++= "] := " - node.value = visitExpr(node.value) + visitExpr(node.value) program ++= ")" node } override def visitMemoryLoad(node: MemoryLoad): Expr = { program ++= "MemoryLoad(" - node.mem = visitMemory(node.mem) + visitMemory(node.mem) program ++= ", [" - node.index = visitExpr(node.index) + visitExpr(node.index) program ++= "])" node } override def visitMemory(node: Memory): Memory = { - program ++= "Memory(" - program ++= node.toString() - program ++= ")" + program ++= "Memory(" + program ++= '"' + node.name + '"' + program ++= f", ${node.addressSize}, ${node.valueSize})" node } From c20c1ba7dba0e7ff7ad68524e9673648eb8e7cde Mon Sep 17 00:00:00 2001 From: Alistair Michael Date: Fri, 22 Sep 2023 10:58:17 +1000 Subject: [PATCH 4/4] remove redundant Expr --- src/main/scala/translating/ILtoIL.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/scala/translating/ILtoIL.scala b/src/main/scala/translating/ILtoIL.scala index 72f305c0c..8c8255679 100644 --- a/src/main/scala/translating/ILtoIL.scala +++ b/src/main/scala/translating/ILtoIL.scala @@ -29,10 +29,7 @@ private class ILSerialiser extends ReadOnlyVisitor { override def visitExpr(node: Expr): Expr = { - program ++= "Expr(" node.acceptVisit(this) - program ++= ")" - node } override def visitStatement(node: Statement): Statement = node.acceptVisit(this)