apply from: "$rootProject.projectDir/gradle/javaProject.gradle" apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle" apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle" apply from: "$rootProject.projectDir/gradle/nativeProject.gradle" apply from: "$rootProject.projectDir/gradle/helpProject.gradle" apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle" apply plugin: 'eclipse' eclipse.project.name = 'Features Decompiler' dependencies { compile project(':Base') compile project(':SoftwareModeling') // include Base src/test/resources when running decompiler integration tests (uses defaultTools) integrationTestRuntime project(path: ':Base', configuration: 'testArtifacts') testCompile "org.jmockit:jmockit:1.44" helpPath project(path: ":Base", configuration: 'helpPath') } /** * This task calls bison to process the xml.y and grammar.y file into cpp files that will then * be compiled with the rest of the decompiler code. */ task yaccDecompiler { Task t1 = createBisonTask("xml", "decompile", true); Task t2 = createBisonTask("grammar", "decompile", true); dependsOn t1, t2 // these variables are used by the cpp sources sets as an input to the decompiler native build ext.sourceDir = file("build/generated/decompile/cpp") ext.headerDir = file("build/generated/decompile/headers") } /** * This task calls bison to process the xml.y file (yacc) into an cpp file that will then * be compiled with the rest of the decompiler code. */ task yaccSleigh { Task t1 = createBisonTask("slghparse", "sleigh", false); Task t2 = createBisonTask("pcodeparse", "sleigh", true); Task t3 = createBisonTask("xml", "sleigh", true); dependsOn t1,t2,t3 ext.sourceDir = file("build/generated/sleigh/cpp") ext.headerDir = file("build/generated/sleigh/headers") } /** * This task calls flex to process the slghscan. file (yacc) into an cpp file that will then * be compiled with the rest of the decompiler code. */ task lexSleigh { Task t1 = createLexTask("slghscan", "sleighlex") dependsOn t1 ext.sourceDir = file("build/generated/sleighlex") ext.headerDir = file("build/generated/sleighlex") } def buildDir = "../../../build" // Relative to the 'workingDir' Exec task property. def installPoint = "$rootDir/GhidraDocs/languages/html" /** * Build the pdfs docs for the decompiler and place them in the '$buildDir' directory. * A build (ex: 'gradle buildGhidra') will place the pdfs in the distribution zip file. * This task will fail gracefully and allow any distribution task (like buildGhidra) to continue, * without pdfs in the distribution zip. * There is an associated, auto-generated clean task. */ task buildDecompilerDocumentationPdfs(type: Exec) { // Check the OS before enabling task. if (!(org.gradle.internal.os.OperatingSystem.current().isLinux() || org.gradle.internal.os.OperatingSystem.current().isMacOsX())) { it.enabled = false } workingDir 'src/main/doc' // Gradle will provide a cleanBuildDecompilerDocumentationPdfs task that will remove these // declared outputs. outputs.file "$workingDir/$buildDir/pcoderef.fo" outputs.file "$workingDir/$buildDir/pcoderef.pdf" outputs.file "$workingDir/$buildDir/sleigh.fo" outputs.file "$workingDir/$buildDir/sleigh.pdf" // 'which' returns the number of failed arguments // Using 'which' first will allow the entire command to fail if the required // executables are not installed. // // The bash commands end with "2>&1" to redirect stderr to stdout and have all // messages print in sequence // // 'commandLine' takes one command, so wrap multiple commands in bash. commandLine 'bash', '-e', '-c', """ echo '** Checking if required executables are installed. **' which fop 2>&1 which xsltproc 2>&1 mkdir -p $buildDir 2>&1 cp $installPoint/Diagram*.png $buildDir 2>&1 echo '** Building sleigh.fo **' xsltproc --output $buildDir/sleigh.fo sleigh_pdf.xsl sleigh.xml 2>&1 echo '** Building sleigh.pdf **' fop $buildDir/sleigh.fo $buildDir/sleigh.pdf 2>&1 echo '** Building pcoderef.fo **' xsltproc --output $buildDir/pcoderef.fo pcoderef_pdf.xsl pcoderef.xml 2>&1 echo '** Building pcoderef.pdf **' fop $buildDir/pcoderef.fo $buildDir/pcoderef.pdf 2>&1 echo '** Done. **' """ // Allows doLast block regardless of exit value. Task does not fail if bash command fails. ignoreExitValue true // Store the output instead of printing to the console. standardOutput = new ByteArrayOutputStream() ext.output = { standardOutput.toString() } ext.errorOutput = { standardOutput.toString() } // Print the output of the commands and check the return value. doLast { println output() if (execResult.exitValue) { println "$it.name: An error occurred with this task. Here is the output:\n" + output() println "Skipping task $it.name\n" } } } /** * Build the html docs for the decompiler and place them in the '$installPoint' directory. * This gradle task will overwrite the html docs currently in the git repo. * A build (ex: 'gradle buildGhidra') will place the html files in the distribution, but buildGhidra * does not depend on buildDecompilerDocumentationHtml. * There is an associated, auto-generated clean task. **/ task buildDecompilerDocumentationHtml(type: Exec) { workingDir 'src/main/doc' // Gradle will provide a cleanBuildDecompilerDocumentationHtml task that will remove these // declared outputs. outputs.file "$workingDir/$buildDir/index.html" outputs.dir "$workingDir/$buildDir/html" // 'which' returns the number of failed arguments // Using the 'which' command first will allow the task to fail if the required // executables are not installed. // // The bash commands end with "2>&1" to redirect stderr to stdout and have all // messages print in sequence // // 'commandLine' takes one command, so wrap multiple commands in bash. commandLine 'bash', '-e', '-c', """ echo '** Checking if required executables are installed. **' which sed 2>&1 which xsltproc 2>&1 echo -e '** Building index.html **' xsltproc --output $buildDir/index.html main_html.xsl main.xml 2>&1 sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' $buildDir/index.html echo '** Building html/sleigh.html **' xsltproc --stringparam base.dir $buildDir/html/ --stringparam root.filename sleigh sleigh_html.xsl sleigh.xml 2>&1 sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' $buildDir/html/sleigh*.html cp $installPoint/Frontpage.css $buildDir/html 2>&1 cp $installPoint/languages.css $buildDir/html cp $installPoint/Diagram1.png $buildDir/html cp $installPoint/Diagram2.png $buildDir/html cp $installPoint/Diagram3.png $buildDir/html echo '** Building html/pcoderef.html **' xsltproc --stringparam base.dir $buildDir/html/ --stringparam root.filename pcoderef pcoderef_html.xsl pcoderef.xml 2>&1 sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' $buildDir/html/pcoderef.html sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' $buildDir/html/pcodedescription.html sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' $buildDir/html/pseudo-ops.html sed -i -e '/Frontpage.css/ { p; s/Frontpage.css/languages.css/; }' $buildDir/html/reference.html cp $installPoint/Frontpage.css $buildDir/html cp $installPoint/languages.css $buildDir/html echo '** Installing html documentation. **' cp $buildDir/index.html $installPoint/index.html rm $installPoint/*.html $installPoint/*.png cp $buildDir/html/*.html $buildDir/html/*.png $installPoint/ echo '** Done. **' """ // Allows doLast block regardless of exit value. ignoreExitValue true // Store the output instead of printing to the console. standardOutput = new ByteArrayOutputStream() ext.output = { standardOutput.toString() } ext.errorOutput = { standardOutput.toString() } // Check the OS before executing command. doFirst { if ( !(org.gradle.internal.os.OperatingSystem.current().isLinux() || org.gradle.internal.os.OperatingSystem.current().isMacOsX())) { throw new TaskExecutionException( it, new Exception( "The '$it.name' task only works on Linux or Mac Os X" )) } } // Print the output of the commands and check the return value. doLast { println output() if (execResult.exitValue) { logger.error("$it.name: An error occurred. Here is the output:\n" + output()) throw new TaskExecutionException( it, new Exception( "$it.name: The command: '${commandLine.join(' ')}'" + "\nfailed with exit code $execResult.exitValue; see task output for details." ) ) } } } /** * Define the "native build model" for building the decompiler executables. */ model { // Define the source files that are compiled and linked to become the decompiler. // The decompiler source is a bit weird in that all the cpp and headers all live in // the same directory with other files that are not used by the decompiler. // That is why we have to list every cpp file that makes up the decomplier. components { decompile(NativeExecutableSpec) { baseName "decompile" // these tell gradle for which platforms to build a decompiler executable. targetPlatform "win64" targetPlatform "linux64" targetPlatform "osx64" sources { cpp { source { srcDir "src/decompile/cpp" // include "xml.cc" // generated by yacc task include "space.cc" include "float.cc" include "address.cc" include "pcoderaw.cc" include "translate.cc" include "opcodes.cc" include "globalcontext.cc" include "capability.cc" include "architecture.cc" include "options.cc" include "graph.cc" include "cover.cc" include "block.cc" include "cast.cc" include "typeop.cc" include "database.cc" include "cpool.cc" include "comment.cc" include "fspec.cc" include "action.cc" include "loadimage.cc" //include "grammar.cc" // doesn't seem to be used include "varnode.cc" include "op.cc" include "type.cc" include "variable.cc" include "varmap.cc" include "jumptable.cc" include "emulate.cc" include "emulateutil.cc" include "flow.cc" include "userop.cc" include "funcdata.cc" include "funcdata_block.cc" include "funcdata_varnode.cc" include "funcdata_op.cc" include "pcodeinject.cc" include "heritage.cc" include "prefersplit.cc" include "rangeutil.cc" include "ruleaction.cc" include "subflow.cc" include "transform.cc" include "blockaction.cc" include "merge.cc" include "double.cc" include "coreaction.cc" include "condexe.cc" include "override.cc" include "dynamic.cc" include "crc32.cc" include "prettyprint.cc" include "printlanguage.cc" include "printc.cc" include "printjava.cc" include "memstate.cc" include "opbehavior.cc" include "paramid.cc" include "ghidra_arch.cc" include "inject_ghidra.cc" include "ghidra_translate.cc" include "loadimage_ghidra.cc" include "typegrp_ghidra.cc" include "database_ghidra.cc" include "ghidra_context.cc" include "cpool_ghidra.cc" include "ghidra_process.cc" include "comment_ghidra.cc" // include "callgraph.cc" // uncomment for debug // include "ifacedecomp.cc" // uncomment for debug // include "ifaceterm.cc" // uncomment for debug // include "interface.cc" // uncomment for debug } exportedHeaders { srcDir "src/decompile/cpp" } } // end cpp // this creates a source set that is basically the output of bison yacc task // defined above that compiles the xml.y file to a cpp file. yacc(CppSourceSet) { generatedBy yaccDecompiler exportedHeaders { srcDir "src/decompile/cpp" } } } // end sources }// end decompile sleigh(NativeExecutableSpec) { targetPlatform "win64" targetPlatform "linux64" targetPlatform "osx64" sources { cpp { source { srcDir "src/decompile/cpp" //include "xml.cc" include "space.cc" include "float.cc" include "address.cc" include "pcoderaw.cc" include "translate.cc" include "opcodes.cc" include "globalcontext.cc" include "sleigh.cc" //include "pcodeparse.cc" include "pcodecompile.cc" include "sleighbase.cc" include "slghsymbol.cc" include "slghpatexpress.cc" include "slghpattern.cc" include "semantics.cc" include "context.cc" include "filemanage.cc" include "slgh_compile.cc" //include "slghparse.cc" //include "slghscan.cc" } exportedHeaders { srcDir "src/decompile/cpp" } } yacc(CppSourceSet) { generatedBy yaccSleigh exportedHeaders { srcDir "src/decompile/cpp" } } lex(CppSourceSet) { generatedBy lexSleigh builtBy yaccSleigh // Requires headers produced by bison exportedHeaders { srcDir "src/decompile/cpp" srcDir yaccSleigh.headerDir srcDir lexSleigh.headerDir } } } // end sources (sleigh) } } // end components binaries { all{ b -> if (b.toolChain in Gcc) { b.cppCompiler.args "-Wall" b.cppCompiler.args "-O2" // for DEBUG, comment this line out // b.cppCompiler.args "-g" // for DEBUG, uncomment this line b.cppCompiler.args "-Wno-sign-compare" if (b.targetPlatform.operatingSystem.linux) { // b.linker.args "-static" b.cppCompiler.define "LINUX" b.cppCompiler.define "_LINUX" } } else if (b.toolChain in VisualCpp) { b.cppCompiler.args "/EHsc" b.cppCompiler.define "_SECURE_SCL=0" b.cppCompiler.define "_HAS_ITERATOR_DEBUGGING=0" // b.cppCompiler.args "/Zi" // for DEBUG, uncomment this line // b.cppCompiler.args "/FS" // for DEBUG, uncomment this line // b.linker.args "/DEBUG" // for DEBUG, uncomment this line if (b.targetPlatform.operatingSystem.windows) { b.cppCompiler.define "WINDOWS" b.cppCompiler.define "_WINDOWS" b.cppCompiler.define "WIN32" b.cppCompiler.define "_WIN32" if (b.targetPlatform.name == "win64") { b.cppCompiler.define "WIN64" b.cppCompiler.define "_WIN64" } } } else if (b.toolChain in Clang) { b.cppCompiler.args "-Wall" b.cppCompiler.args "-O2" // for DEBUG, comment this line out // b.cppCompiler.args "-g" // for DEBUG, uncomment this line b.cppCompiler.args "-Wno-sign-compare" b.cppCompiler.args "-w" if (b.targetPlatform.operatingSystem.linux) { // b.linker.args "-static" } } } } } // end model /** * create a bison task to compile a yacc file for the decompiler */ Task createBisonTask(String filename, String binaryName, boolean qualifyVariables) { return task("bison_${binaryName}_$filename", type: Exec) { String inputFile = "src/decompile/cpp/${filename}.y" String outputCppDir = "build/generated/$binaryName/cpp" String outputHeadersDir = "build/generated/$binaryName/headers" inputs.file inputFile outputs.dir outputCppDir // make sure output directories exist. Must be done in execution phase. Otherwise, // if a clean task is executed it will wipe them out doFirst { file(outputCppDir).mkdirs() file(outputHeadersDir)mkdirs() } executable 'bison' // use bison program to process yacc files // specify the bison's output file args "-o", "$outputCppDir/${filename}.tab.cc" // most of the yacc files should be compiled with a variable qualifyer to avoid dupes. // Unfortunately there is one (slghparse) that can't use a qualifyer because it // declares a variable used by other files. if (qualifyVariables) { args "-p", filename } // tell bison where to put the hh file. args "--defines=${outputHeadersDir}/${filename}.tab.hh" // tell bison the file to compile args inputFile } } /** * create a lex task to compile a yacc file for the decompiler */ Task createLexTask(String filename, String binaryName) { return task("lex_${binaryName}_$filename", type: Exec) { String outputDir = "build/generated/${binaryName}/cpp" String inputFile = "src/decompile/cpp/${filename}.l" executable 'flex' // the program to execute // set up inputs and outputs so that gradle knows when this needs to be rebuilt inputs.file inputFile outputs.dir outputDir // make sure the output dirs are created, but do it in the execution phase. // Can't do it in the configure phase, otherwise // a clean will remove it during the execution phase doFirst { file(outputDir).mkdirs(); } // tell flex where to put the output args "-o", "$outputDir/${filename}.yy.cc" // tell flex the input file args inputFile } } rootProject.createInstallationZip { dependsOn buildDecompilerDocumentationPdfs def decompilerPdfZipPath = rootProject.ext.ZIP_DIR_PREFIX + "/docs/languages/" // Add decompiler pdf files to zip. If the pdf files do not exist during execution time // (if there was an error or wrong platform), the zip task will move on. buildDecompilerDocumentationPdfs.outputs.each { output -> output.files.each { file -> if (file.name.endsWith("pdf")) { logger.debug("$project.name: Adding Decompiler documentation (if it exists) $file.name to $decompilerPdfZipPath") rootProject.createInstallationZip.from (file) { into { decompilerPdfZipPath } } } } } }