diff --git a/DevGuide.md b/DevGuide.md index e227291def..addc98bdcd 100644 --- a/DevGuide.md +++ b/DevGuide.md @@ -34,8 +34,8 @@ You may not need all of these, depending on which portions you are building or d - https://adoptopenjdk.net/releases.html?variant=openjdk11&jvmVariant=hotspot - Amazon Corretto - https://docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html -* Gradle 5.x or 6.x - We use version 5.0, and have tested with up to 6.8.3. - - https://gradle.org/next-steps/?version=5.0&format=bin +* Gradle 6.0 or later - We use version 6.9, and tested with up to 7.0.1. + - https://gradle.org/releases/ * A C/C++ compiler - We use GCC on Linux, Xcode (Clang) on macOS, and Visual Studio (2017 or later) on Windows. - https://gcc.gnu.org/ - https://developer.apple.com/xcode/ diff --git a/GPL/DMG/build.gradle b/GPL/DMG/build.gradle index 4098009221..6211ef390a 100644 --- a/GPL/DMG/build.gradle +++ b/GPL/DMG/build.gradle @@ -16,7 +16,7 @@ if (findProject(':Generic') != null) { } } else { - apply plugin: 'java' + apply plugin: 'java-library' } apply plugin: 'eclipse' @@ -39,16 +39,14 @@ sourceSets { } } -eclipse.classpath.plusConfigurations += [configurations.dmgCompile] - repositories { flatDir name: "flat", dirs:["data/lib"] } dependencies { - dmgCompile ':csframework@jar' - dmgCompile ':hfsx@jar' - dmgCompile ':hfsx_dmglib@jar' + dmgImplementation ':csframework@jar' + dmgImplementation ':hfsx@jar' + dmgImplementation ':hfsx_dmglib@jar' } /*************************************************************************************** @@ -58,6 +56,6 @@ dependencies { ***************************************************************************************/ jar { - destinationDir = file("build/data/lib") + destinationDirectory = file("build/data/lib") from sourceSets.dmg.output } diff --git a/GPL/DemanglerGnu/build.gradle b/GPL/DemanglerGnu/build.gradle index 0e9f80bb3f..bf413418c8 100644 --- a/GPL/DemanglerGnu/build.gradle +++ b/GPL/DemanglerGnu/build.gradle @@ -60,8 +60,8 @@ task zipBuildableSource(type:Zip) { group 'private' description "Collects the source files needed to build this module." - baseName project.name + "-src-for-build" - extension 'zip' + archiveBaseName = project.name + "-src-for-build" + archiveExtension = 'zip' // // Version 2.33.1 diff --git a/Ghidra/Debug/AnnotationValidator/build.gradle b/Ghidra/Debug/AnnotationValidator/build.gradle index 4016854294..a92ab6b4a8 100644 --- a/Ghidra/Debug/AnnotationValidator/build.gradle +++ b/Ghidra/Debug/AnnotationValidator/build.gradle @@ -22,7 +22,7 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug AnnotationValidator' dependencies { - compile project(':ProposedUtils') + api project(':ProposedUtils') } // no-dep jar for experiments in loading annotation processor into Eclipse diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle b/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle index 6b15549169..162c3089f0 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle +++ b/Ghidra/Debug/Debugger-agent-dbgeng/build.gradle @@ -23,14 +23,16 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-dbgeng' dependencies { - compile project(":Framework-AsyncComm") - compile project(":Framework-Debugging") - compile project(":Debugger-gadp") - compile "net.java.dev.jna:jna:5.4.0" - compile "net.java.dev.jna:jna-platform:5.4.0" + api project(":Framework-AsyncComm") + api project(":Framework-Debugging") + api project(":Debugger-gadp") + api "net.java.dev.jna:jna:5.4.0" + api "net.java.dev.jna:jna-platform:5.4.0" - //testCompile project(":Base") - testCompile project(path: ":Debugger-gadp", configuration: 'testArtifacts') + //testImplementation project(":Base") + testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts') + testImplementation project(path: ":Debugger-gadp", configuration: 'testArtifacts') } def boolean filterJar(File jarfile) { @@ -52,7 +54,7 @@ jar { task configureNodepJar { doLast { - configurations.runtime.files.forEach { + configurations.runtimeOnly.files.forEach { if (filterJar(it)) { nodepJar.from(zipTree(it)) } diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle index a6994d370c..7c97953e24 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle +++ b/Ghidra/Debug/Debugger-agent-dbgmodel-traceloader/build.gradle @@ -22,7 +22,7 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-dbgmodel-traceloader' dependencies { - compile project(":Debugger") - compile project(":Debugger-agent-dbgeng") - compile project(':Debugger-agent-dbgmodel') + api project(":Debugger") + api project(":Debugger-agent-dbgeng") + api project(':Debugger-agent-dbgmodel') } diff --git a/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle b/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle index 4b71f04ac3..1d7de0d1cc 100644 --- a/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle +++ b/Ghidra/Debug/Debugger-agent-dbgmodel/build.gradle @@ -23,9 +23,12 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-dbgmodel' dependencies { - compile project(":Debugger-agent-dbgeng") + api project(":Debugger-agent-dbgeng") - testCompile project(path: ":Debugger-agent-dbgeng", configuration: 'testArtifacts') + testImplementation project(path: ":Debugger-agent-dbgeng", configuration: 'testArtifacts') + testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts') + testImplementation project(path: ":Debugger-gadp", configuration: 'testArtifacts') } def boolean filterJar(File jarfile) { @@ -47,7 +50,7 @@ jar { task configureNodepJar { doLast { - configurations.runtime.files.forEach { + configurations.runtimeOnly.files.forEach { if (filterJar(it)) { nodepJar.from(zipTree(it)) } diff --git a/Ghidra/Debug/Debugger-agent-gdb/build.gradle b/Ghidra/Debug/Debugger-agent-gdb/build.gradle index 8b4fc90f50..ddbf7d277b 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/build.gradle +++ b/Ghidra/Debug/Debugger-agent-gdb/build.gradle @@ -23,12 +23,14 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-agent-gdb' dependencies { - compile project(':Framework-AsyncComm') - compile project(':Framework-Debugging') - compile project(':Debugger-gadp') - compile 'org.python:jython-standalone:2.7.1' + api project(':Framework-AsyncComm') + api project(':Framework-Debugging') + api project(':Debugger-gadp') + api 'org.python:jython-standalone:2.7.1' - testCompile project(path: ':Debugger-gadp', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts') + testImplementation project(path: ':Debugger-gadp', configuration: 'testArtifacts') } def boolean filterJar(File jarfile) { @@ -50,7 +52,7 @@ jar { task configureNodepJar { doLast { - configurations.runtime.files.forEach { + configurations.runtimeOnly.files.forEach { if (filterJar(it)) { nodepJar.from(zipTree(it)) } diff --git a/Ghidra/Debug/Debugger-gadp/build.gradle b/Ghidra/Debug/Debugger-gadp/build.gradle index 8b0b90cfa7..248542d00e 100644 --- a/Ghidra/Debug/Debugger-gadp/build.gradle +++ b/Ghidra/Debug/Debugger-gadp/build.gradle @@ -47,12 +47,13 @@ dependencies { protocArtifact 'com.google.protobuf:protoc:3.11.1:osx-x86_64@exe' } - compile 'com.google.protobuf:protobuf-java:3.11.1' - compile project(':Framework-AsyncComm') - compile project(':Framework-Debugging') - compile project(':ProposedUtils') + api 'com.google.protobuf:protobuf-java:3.11.1' + api project(':Framework-AsyncComm') + api project(':Framework-Debugging') + api project(':ProposedUtils') - testCompile project(path: ':Framework-Debugging', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts') } /*protobuf { diff --git a/Ghidra/Debug/Debugger-jpda/build.gradle b/Ghidra/Debug/Debugger-jpda/build.gradle index fc1e97672f..bd5bdb0e33 100644 --- a/Ghidra/Debug/Debugger-jpda/build.gradle +++ b/Ghidra/Debug/Debugger-jpda/build.gradle @@ -22,9 +22,10 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger-jpda' dependencies { - compile project(':Framework-AsyncComm') - compile project(':Framework-Debugging') - compile project(':ProposedUtils') + api project(':Framework-AsyncComm') + api project(':Framework-Debugging') + api project(':ProposedUtils') - testCompile project(path: ':Framework-Debugging', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts') } diff --git a/Ghidra/Debug/Debugger/build.gradle b/Ghidra/Debug/Debugger/build.gradle index c81bbd04c9..0a6feff55a 100644 --- a/Ghidra/Debug/Debugger/build.gradle +++ b/Ghidra/Debug/Debugger/build.gradle @@ -23,15 +23,16 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Debugger' dependencies { - compile project(':Framework-AsyncComm') - compile project(':Framework-Debugging') - compile project(':Framework-TraceModeling') - compile project(':Base') - compile project(':ByteViewer') - compile project(':ProposedUtils') + api project(':Framework-AsyncComm') + api project(':Framework-Debugging') + api project(':Framework-TraceModeling') + api project(':Base') + api project(':ByteViewer') + api project(':ProposedUtils') helpPath project(path: ':Base', configuration: 'helpPath') - testCompile project(path: ':Framework-Debugging', configuration: 'testArtifacts') - testCompile project(path: ':Framework-TraceModeling', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-Debugging', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-TraceModeling', configuration: 'testArtifacts') } diff --git a/Ghidra/Debug/Framework-AsyncComm/build.gradle b/Ghidra/Debug/Framework-AsyncComm/build.gradle index 4808063437..0bfa2d3451 100644 --- a/Ghidra/Debug/Framework-AsyncComm/build.gradle +++ b/Ghidra/Debug/Framework-AsyncComm/build.gradle @@ -22,9 +22,9 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Framework-AsyncComm' dependencies { - compile project(':Generic') - compile project(':Graph') - compile project(':ProposedUtils') + api project(':Generic') + api project(':Graph') + api project(':ProposedUtils') } task genTestPy(type: JavaExec) { diff --git a/Ghidra/Debug/Framework-Debugging/build.gradle b/Ghidra/Debug/Framework-Debugging/build.gradle index 85a584e05b..6031083387 100644 --- a/Ghidra/Debug/Framework-Debugging/build.gradle +++ b/Ghidra/Debug/Framework-Debugging/build.gradle @@ -23,12 +23,12 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Framework-Debugging' dependencies { - compile project(':Framework-AsyncComm') - compile project(':Generic') - compile project(':SoftwareModeling') - compile project(':ProposedUtils') + api project(':Framework-AsyncComm') + api project(':Generic') + api project(':SoftwareModeling') + api project(':ProposedUtils') - testCompile project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') + testImplementation project(path: ':Framework-AsyncComm', configuration: 'testArtifacts') } task testSpecimenWin64 { diff --git a/Ghidra/Debug/Framework-TraceModeling/build.gradle b/Ghidra/Debug/Framework-TraceModeling/build.gradle index d529b0bf41..878990745c 100644 --- a/Ghidra/Debug/Framework-TraceModeling/build.gradle +++ b/Ghidra/Debug/Framework-TraceModeling/build.gradle @@ -22,11 +22,11 @@ apply plugin: 'eclipse' eclipse.project.name = 'Debug Framework-TraceModeling' dependencies { - compile project(':Generic') - compile project(':SoftwareModeling') - compile project(':ProposedUtils') + api project(':Generic') + api project(':SoftwareModeling') + api project(':ProposedUtils') annotationProcessor project(':AnnotationValidator') - testCompile project(':Base') - testRuntime project(':ARM') // For its emulator state modifier + testImplementation project(':Base') + testRuntimeOnly project(':ARM') // For its emulator state modifier } diff --git a/Ghidra/Debug/ProposedUtils/build.gradle b/Ghidra/Debug/ProposedUtils/build.gradle index 5fe63f731a..e6920c4033 100644 --- a/Ghidra/Debug/ProposedUtils/build.gradle +++ b/Ghidra/Debug/ProposedUtils/build.gradle @@ -24,16 +24,16 @@ eclipse.project.name = 'Debug ProposedUtils' // val autoServiceVersion = "1.0-rc5" dependencies { - compile project(':DB') - compile project(':Project') - compile project(':SoftwareModeling') - compile project(':Utility') + api project(':DB') + api project(':Project') + api project(':SoftwareModeling') + api project(':Utility') - compile project(':Base') // Boo!: (Where to put DefaultEnumeratedColumnProgramTableModel?) + api project(':Base') // Boo!: (Where to put DefaultEnumeratedColumnProgramTableModel?) // TODO: Evaluate these dependencies - // compile("com.google.auto.service:auto-service-annotations:$autoServiceVersion") + // api("com.google.auto.service:auto-service-annotations:$autoServiceVersion") // annotationProcessor("com.google.auto.service:auto-service:$autoServiceVersion") - testCompile project(':Base') + testImplementation project(':Base') } diff --git a/Ghidra/Extensions/SampleTablePlugin/build.gradle b/Ghidra/Extensions/SampleTablePlugin/build.gradle index 5950dcfe29..33f40360d1 100644 --- a/Ghidra/Extensions/SampleTablePlugin/build.gradle +++ b/Ghidra/Extensions/SampleTablePlugin/build.gradle @@ -23,5 +23,5 @@ eclipse.project.name = 'Xtra SampleTablePlugin' dependencies { - compile project(':Base') + api project(':Base') } diff --git a/Ghidra/Extensions/SleighDevTools/build.gradle b/Ghidra/Extensions/SleighDevTools/build.gradle index 4dbcca1cae..7e829c0cd2 100644 --- a/Ghidra/Extensions/SleighDevTools/build.gradle +++ b/Ghidra/Extensions/SleighDevTools/build.gradle @@ -20,5 +20,5 @@ apply plugin: 'eclipse' eclipse.project.name = 'Xtra SleighDevTools' dependencies { - compile project(':Base') + api project(':Base') } diff --git a/Ghidra/Extensions/bundle_examples/build.gradle b/Ghidra/Extensions/bundle_examples/build.gradle index 4fce7fc287..24194f0b3b 100644 --- a/Ghidra/Extensions/bundle_examples/build.gradle +++ b/Ghidra/Extensions/bundle_examples/build.gradle @@ -30,7 +30,7 @@ eclipse.project.name = 'Xtra Bundle Examples' dependencies { - compile project(':Base') + api project(':Base') } @@ -50,8 +50,8 @@ srcDirs.each {dirName -> // create and return a jar task for the given source directory def makeJarTask(dirName) { return tasks.create("build${dirName}", Jar) { - baseName dirName - archiveName "${dirName}.jar" + archiveBaseName = dirName + archiveFileName = "${dirName}.jar" ext.dirName=dirName @@ -91,8 +91,8 @@ eclipse { // "$rootProject.projectDir/gradle/support/extensionCommon.gradle" task zipExtensions(type: Zip, dependsOn:jarTasks) { def p = this.project - archiveName "${rootProject.ext.ZIP_NAME_PREFIX}_${p.name}.zip" - destinationDir rootProject.ext.DISTRIBUTION_DIR + archiveFileName = "${rootProject.ext.ZIP_NAME_PREFIX}_${p.name}.zip" + destinationDirectory = rootProject.ext.DISTRIBUTION_DIR duplicatesStrategy 'exclude' diff --git a/Ghidra/Extensions/sample/build.gradle b/Ghidra/Extensions/sample/build.gradle index a6a9893a0c..34ade0bd9f 100644 --- a/Ghidra/Extensions/sample/build.gradle +++ b/Ghidra/Extensions/sample/build.gradle @@ -22,5 +22,5 @@ eclipse.project.name = 'Xtra sample' dependencies { - compile project(':Base') + api project(':Base') } diff --git a/Ghidra/Features/Base/build.gradle b/Ghidra/Features/Base/build.gradle index f306645e5f..f8fbfd252f 100644 --- a/Ghidra/Features/Base/build.gradle +++ b/Ghidra/Features/Base/build.gradle @@ -33,28 +33,28 @@ configurations { } dependencies { - compile project(':Utility') - compile project(':Generic') - compile project(':Docking') - compile project(':Graph') - compile project(':SoftwareModeling') - compile project(':DB') - compile project(':Help') + api project(':Utility') + api project(':Generic') + api project(':Docking') + api project(':Graph') + api project(':SoftwareModeling') + api project(':DB') + api project(':Help') - compile 'org.apache.felix:org.apache.felix.framework:6.0.3' - compile 'com.github.rotty3000:phidias:0.3.7' - compile 'biz.aQute.bnd:biz.aQute.bndlib:5.1.2' - compile 'org.slf4j:slf4j-api:1.7.25' + api 'org.apache.felix:org.apache.felix.framework:6.0.3' + api 'com.github.rotty3000:phidias:0.3.7' + api 'biz.aQute.bnd:biz.aQute.bndlib:5.1.2' + api 'org.slf4j:slf4j-api:1.7.25' - runtime "org.slf4j:slf4j-nop:1.7.25" + runtimeOnly "org.slf4j:slf4j-nop:1.7.25" compileOnly "junit:junit:4.12" // These have abstract test classes and stubs needed by this module - testCompile project(path: ':Docking', configuration: 'testArtifacts') - testCompile project(path: ':Generic', configuration: 'testArtifacts') - testCompile project(path: ':Project', configuration: 'testArtifacts') - testCompile project(path: ':SoftwareModeling', configuration: 'testArtifacts') + testImplementation project(path: ':Docking', configuration: 'testArtifacts') + testImplementation project(path: ':Generic', configuration: 'testArtifacts') + testImplementation project(path: ':Project', configuration: 'testArtifacts') + testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts') javacc 'net.java.dev.javacc:javacc:5.0' } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java index 18903f7a4a..a448d5ca7e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java @@ -26,6 +26,7 @@ import java.util.stream.Collectors; import org.apache.felix.framework.FrameworkFactory; import org.apache.felix.framework.util.FelixConstants; +import org.apache.felix.framework.wiring.BundleRequirementImpl; import org.jgrapht.graph.DirectedMultigraph; import org.jgrapht.traverse.TopologicalOrderIterator; import org.osgi.framework.*; @@ -691,7 +692,13 @@ public class BundleHost { Map> requirementMap = new HashMap<>(); for (GhidraBundle bundle : bundles) { try { - requirementMap.put(bundle, bundle.getAllRequirements()); + List requirements = bundle.getAllRequirements(); + // remove optional requirements + requirements.removeIf(r -> { + BundleRequirementImpl rimpl = (BundleRequirementImpl) r; + return rimpl.isOptional(); + }); + requirementMap.put(bundle, requirements); } catch (GhidraBundleException e) { fireBundleException(e); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java index 9c2fd5d4cd..c914d881bd 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java @@ -129,6 +129,10 @@ public class OSGiUtils { static List parseImportPackage(String importPackageString) throws BundleException { Map headerMap = new HashMap<>(); + // assume version 2 for a more robust parse + headerMap.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + // symbolic name is required for version 2 bundle manifest + headerMap.put(Constants.BUNDLE_SYMBOLICNAME, Constants.SYSTEM_BUNDLE_SYMBOLICNAME); headerMap.put(Constants.IMPORT_PACKAGE, importPackageString); ManifestParser manifestParser = new ManifestParser(null, null, null, headerMap); return manifestParser.getRequirements(); @@ -144,6 +148,10 @@ public class OSGiUtils { static List parseExportPackage(String exportPackageString) throws BundleException { Map headerMap = new HashMap<>(); + // assume version 2 for a more robust parse + headerMap.put(Constants.BUNDLE_MANIFESTVERSION, "2"); + // symbolic name is required for version 2 bundle manifest + headerMap.put(Constants.BUNDLE_SYMBOLICNAME, Constants.SYSTEM_BUNDLE_SYMBOLICNAME); headerMap.put(Constants.EXPORT_PACKAGE, exportPackageString); ManifestParser manifestParser = new ManifestParser(null, null, null, headerMap); return manifestParser.getCapabilities(); @@ -205,15 +213,64 @@ public class OSGiUtils { } } + static private boolean hasEvenQuoteCount(String s) { + return s.chars().filter(c -> c == '"').count() % 2 == 0; + } + static void collectPackagesFromJar(Path jarPath, Set packages) { try { - try (JarFile j = new JarFile(jarPath.toFile())) { - j.stream().filter(entry -> entry.getName().endsWith(".class")).forEach(jarEntry -> { - String entryName = jarEntry.getName(); - int lastSlash = entryName.lastIndexOf('/'); - packages.add( - lastSlash > 0 ? entryName.substring(0, lastSlash).replace('/', '.') : ""); - }); + try (JarFile jarFile = new JarFile(jarPath.toFile())) { + // if this jar is an OSGi bundle, use its declared exports + String exportPackageString = + jarFile.getManifest().getMainAttributes().getValue(Constants.EXPORT_PACKAGE); + if (exportPackageString != null) { + String saved = null; + /* + * split on commas not contained in quotes. + * + * e.g. + * org.foo,org.bar;uses="org.baz,org.qux" + * ^- should split here ^- not here + * + * We first split on all commas. The first entry, + * org.foo + * has an even number of quotes, so it's added as is to packages. + * The second entry, + * org.bar;uses="org.baz + * has an odd number of quotes, so we save + * org.bar;uses="org.baz, + * Then the third entry, + * org.qux" + * is appended, and the result has an even number of quotes, so is added. + */ + + for (String packageName : exportPackageString.split(",")) { + boolean evenQuoteCount = hasEvenQuoteCount(packageName); + if (saved != null) { + packageName = saved + packageName; + evenQuoteCount = !evenQuoteCount; + saved = null; + } + if (evenQuoteCount) { + packages.add(packageName); + } + else { + saved = packageName + ','; + } + } + } + else { + jarFile.stream() + .filter(entry -> entry.getName().endsWith(".class")) + .forEach(jarEntry -> { + String entryName = jarEntry.getName(); + int lastSlash = entryName.lastIndexOf('/'); + if (lastSlash > 0) { + packages.add( + entryName.substring(0, lastSlash).replace('/', '.')); + } + }); + } } } catch (IOException e) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java index a8d41a0317..4939373bc6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java @@ -132,6 +132,10 @@ public class SegmentCommand extends LoadCommand { public long getFileOffset() { return fileoff; } + + public void setFileOffset(long fileOffset) { + fileoff = fileOffset; + } public long getFileSize() { return filesize; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java index 0e1d4b1883..6969d45c56 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java @@ -34,6 +34,10 @@ import utilities.util.FileUtilities; public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest { private static final String TEMP_NAME_PREFIX = "sourcebundle"; + + // the version of Guava Ghidra is currently using. + private static final int GUAVA_MAJOR_VERSION = 19; + private BundleHost bundleHost; private CapturingBundleHostListener capturingBundleHostListener; @@ -333,6 +337,52 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest { // @formatter:on } + @Test + public void testImportFromExtraSystemPackagesWithVersionConstraint() throws Exception { + // @formatter:off + String goodRange = String.format("[%d,%d)", GUAVA_MAJOR_VERSION, GUAVA_MAJOR_VERSION+1); + addClass( + "//@importpackage com.google.common.io;version=\""+goodRange+"\"\n", + "import com.google.common.io.BaseEncoding;", + "AClass", + "@Override\n" + + "public String toString() {\n" + + " return BaseEncoding.base16().encode(new byte[] {0x42});\n" + + "}\n" + ); + + buildAndActivate(); + assertEquals("wrong response from instantiated class", "42", + getInstance("AClass").toString()); + // @formatter:on + } + + @Test + public void testImportFromExtraSystemPackagesWithBadVersionConstraint() throws Exception { + // @formatter:off + String badRange = String.format("[%d,%d)", GUAVA_MAJOR_VERSION+1, GUAVA_MAJOR_VERSION+2); + addClass( + "//@importpackage com.google.common.io;version=\""+badRange+"\"\n", + "import com.google.common.io.BaseEncoding;", + "AClass", + "@Override\n" + + "public String toString() {\n" + + " return BaseEncoding.base16().encode(new byte[] {0x42});\n" + + "}\n" + ); + + buildWithExpectations( + "1 import requirement remains unresolved:\n" + + " [null] osgi.wiring.package; (&(osgi.wiring.package=com.google.common.io)" + + "(version>="+(GUAVA_MAJOR_VERSION+1)+".0.0)" + + "(!(version>="+(GUAVA_MAJOR_VERSION+2)+".0.0))), " + + "from /tmp/ghidra.dev2tmp/sourcebundle000/AClass.java\n", + "1 missing package import:com.google.common.io (version>="+(GUAVA_MAJOR_VERSION+1)+".0.0)" + + ", 1 source file with errors" + ); + // @formatter:on + } + @Test public void testLoadLibraryFromOtherBundleWithManifest() throws Exception { // @formatter:off diff --git a/Ghidra/Features/BytePatterns/build.gradle b/Ghidra/Features/BytePatterns/build.gradle index cc529c7b50..923e3b0d14 100644 --- a/Ghidra/Features/BytePatterns/build.gradle +++ b/Ghidra/Features/BytePatterns/build.gradle @@ -23,8 +23,8 @@ eclipse.project.name = 'Features BytePatterns' dependencies { - compile project(':Base') - compile project(':Utility') + api project(':Base') + api project(':Utility') helpPath project(path: ":Base", configuration: 'helpPath') diff --git a/Ghidra/Features/ByteViewer/build.gradle b/Ghidra/Features/ByteViewer/build.gradle index ad81ae6ff5..a5a9b62913 100644 --- a/Ghidra/Features/ByteViewer/build.gradle +++ b/Ghidra/Features/ByteViewer/build.gradle @@ -23,9 +23,9 @@ eclipse.project.name = 'Features ByteViewer' dependencies { - compile project(':Base') + api project(':Base') - testCompile project(path: ':SoftwareModeling', configuration: 'testArtifacts') + testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts') helpPath project(path: ':Base', configuration: 'helpPath') // this module's help has links to Base help files } diff --git a/Ghidra/Features/DebugUtils/build.gradle b/Ghidra/Features/DebugUtils/build.gradle index 3dac0627cd..5dac164218 100644 --- a/Ghidra/Features/DebugUtils/build.gradle +++ b/Ghidra/Features/DebugUtils/build.gradle @@ -22,5 +22,5 @@ eclipse.project.name = 'Features DebugUtils' dependencies { - compile project(':Base') + api project(':Base') } diff --git a/Ghidra/Features/Decompiler/build.gradle b/Ghidra/Features/Decompiler/build.gradle index 85a49229f9..12eea15bec 100644 --- a/Ghidra/Features/Decompiler/build.gradle +++ b/Ghidra/Features/Decompiler/build.gradle @@ -24,11 +24,11 @@ apply plugin: 'eclipse' eclipse.project.name = 'Features Decompiler' dependencies { - compile project(':Base') - compile project(':SoftwareModeling') + api project(':Base') + api 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" + integrationTestImplementation project(path: ':Base', configuration: 'testArtifacts') + testImplementation "org.jmockit:jmockit:1.44" helpPath project(path: ":Base", configuration: 'helpPath') } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc index 86bbf61bc2..b40acf50e6 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/block.cc @@ -823,7 +823,6 @@ void BlockGraph::forceOutputNum(int4 i) void BlockGraph::selfIdentify(void) { - vector::iterator tmp; vector::iterator iter; FlowBlock *mybl,*otherbl; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh index c01f95de11..31a877e8c3 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/double.hh @@ -183,7 +183,7 @@ class LessThreeWay { Varnode *vnhil1,*vnhil2,*vnhie1,*vnhie2; Varnode *vnlo1,*vnlo2; Varnode *hi,*lo,*hi2,*lo2; - int4 hislot,loslot; + int4 hislot; bool hiflip,equalflip,loflip; bool lolessiszerocomp; bool lolessequalform,hilessequalform,signcompare; @@ -240,7 +240,7 @@ public: class MultForm { SplitVarnode in; PcodeOp *add1,*add2; - PcodeOp *subhi,*sublo; + PcodeOp *subhi; PcodeOp *multlo,*multhi1,*multhi2; Varnode *midtmp,*lo1zext,*lo2zext; Varnode *hi1,*lo1,*hi2,*lo2; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh index 561a2245eb..c289890038 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/subflow.hh @@ -172,7 +172,6 @@ class LaneDivide : public TransformManager { /// \brief Description of a large Varnode that needs to be traced (in the worklist) class WorkNode { friend class LaneDivide; - Varnode *vn; ///< The underlying Varnode with lanes TransformVar *lanes; ///< Lane placeholders for underyling Varnode int4 numLanes; ///< Number of lanes in the particular Varnode int4 skipLanes; ///< Number of lanes to skip in the global description diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index b05109d696..02fdcf36d9 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -764,6 +764,9 @@ int4 TypeEnum::compareDependency(const Datatype &op) const const TypeEnum *te = (const TypeEnum *) &op; map::const_iterator iter1,iter2; + if (namemap.size() != te->namemap.size()) { + return (namemap.size() < te->namemap.size()) ? -1 : 1; + } iter1 = namemap.begin(); iter2 = te->namemap.begin(); while(iter1 != namemap.end()) { @@ -1287,18 +1290,21 @@ void TypeCode::saveXml(ostream &s) const void TypeCode::restoreXml(const Element *el,TypeFactory &typegrp) { + const List &list(el->getChildren()); + List::const_iterator iter; + iter = list.begin(); + if (iter != list.end()) { + // Traditionally a tag implies variable length, without a "varlength" attribute + flags |= variable_length; + } restoreXmlBasic(el); if (proto != (FuncProto *)0) { delete proto; proto = (FuncProto *)0; } - const List &list(el->getChildren()); - List::const_iterator iter; - iter = list.begin(); if (iter == list.end()) return; // No underlying prototype Architecture *glb = typegrp.getArch(); factory = &typegrp; - flags |= variable_length; proto = new FuncProto(); proto->setInternal( glb->defaultfp, typegrp.getTypeVoid() ); proto->restoreXml(*iter,glb); diff --git a/Ghidra/Features/DecompilerDependent/build.gradle b/Ghidra/Features/DecompilerDependent/build.gradle index a822d75ba4..bf9c36055e 100644 --- a/Ghidra/Features/DecompilerDependent/build.gradle +++ b/Ghidra/Features/DecompilerDependent/build.gradle @@ -23,8 +23,8 @@ eclipse.project.name = 'Features DecompilerDependent' dependencies { - compile project(':Base') - compile project(':Decompiler') + api project(':Base') + api project(':Decompiler') } diff --git a/Ghidra/Features/FileFormats/build.gradle b/Ghidra/Features/FileFormats/build.gradle index d979e482c0..1046713237 100644 --- a/Ghidra/Features/FileFormats/build.gradle +++ b/Ghidra/Features/FileFormats/build.gradle @@ -23,28 +23,28 @@ apply plugin: 'eclipse' eclipse.project.name = 'Features FileFormats' dependencies { - compile project(':Base') - compile project(':Recognizers') - compile project(':Python') + api project(':Base') + api project(':Recognizers') + api project(':Python') - compile ':dex-ir:2.0' - compile ':dex-reader:2.0' - compile ':dex-reader-api:2.0' - compile ':dex-translator:2.0' + api ':dex-ir:2.0' + api ':dex-reader:2.0' + api ':dex-reader-api:2.0' + api ':dex-translator:2.0' - compile 'org.ow2.asm:asm-debug-all:4.1' + api 'org.ow2.asm:asm-debug-all:4.1' - compile 'org.smali:baksmali:1.4.0' // TODO: upgrade to 2.2.6 - compile 'org.smali:dexlib:1.4.0' - compile 'org.smali:util:1.4.0' + api 'org.smali:baksmali:1.4.0' // TODO: upgrade to 2.2.6 + api 'org.smali:dexlib:1.4.0' + api 'org.smali:util:1.4.0' - compile 'net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01' - compile ':AXMLPrinter2' + api 'net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01' + api ':AXMLPrinter2' - runtime 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01' + runtimeOnly 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01' helpPath project(path: ":Base", configuration: 'helpPath') // include code from src/test/slow in Base - testCompile project(path: ':Base', configuration: 'integrationTestArtifacts') + testImplementation project(path: ':Base', configuration: 'integrationTestArtifacts') } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheDylibExtractor.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheDylibExtractor.java new file mode 100644 index 0000000000..d2c73caf71 --- /dev/null +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheDylibExtractor.java @@ -0,0 +1,307 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.file.formats.ios.dyldcache; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; + +import generic.continues.RethrowContinuesFactory; +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.ByteProvider; +import ghidra.app.util.bin.format.macho.*; +import ghidra.app.util.bin.format.macho.commands.*; +import ghidra.util.*; +import ghidra.util.exception.NotFoundException; +import ghidra.util.task.TaskMonitor; + +/** + * A class for extracting DYLIB files from a {@link DyldCacheFileSystem} + */ +public class DyldCacheDylibExtractor { + + /** + * Gets an {@link InputStream} that reads a DYLIB from a {@link DyldCacheFileSystem}. The + * DYLIB's header will be altered to account for its segment bytes being packed down. + * + * @param dylibOffset The offset of the DYLIB in the given provider + * @param provider The DYLD + * @param monitor A cancellable {@link TaskMonitor} + * @return An {@link InputStream} that reads the specified DYLIB from the given DYLD + * {@link ByteProvider} + * @throws IOException If there was an IO-related issue with extracting the DYLIB + * @throws MachException If there was an error parsing the DYLIB headers + */ + public static InputStream extractDylib(long dylibOffset, ByteProvider provider, + TaskMonitor monitor) throws IOException, MachException { + + // Make sure Mach-O header is valid + MachHeader header = MachHeader.createMachHeader(RethrowContinuesFactory.INSTANCE, provider, + dylibOffset, false); + header.parse(); + + // Pack the DYLIB + PackedDylib packedDylib = new PackedDylib(header, dylibOffset, provider); + + // Fixup indices, offsets, etc in the packed DYLIB's header + for (LoadCommand cmd : header.getLoadCommands()) { + if (monitor.isCancelled()) { + break; + } + switch (cmd.getCommandType()) { + case LoadCommandTypes.LC_SEGMENT: + fixupSegment((SegmentCommand) cmd, packedDylib, false, monitor); + break; + case LoadCommandTypes.LC_SEGMENT_64: + fixupSegment((SegmentCommand) cmd, packedDylib, true, monitor); + break; + case LoadCommandTypes.LC_SYMTAB: + fixupSymbolTable((SymbolTableCommand) cmd, packedDylib); + break; + case LoadCommandTypes.LC_DYSYMTAB: + fixupDynamicSymbolTable((DynamicSymbolTableCommand) cmd, packedDylib); + break; + case LoadCommandTypes.LC_DYLD_INFO: + case LoadCommandTypes.LC_DYLD_INFO_ONLY: + fixupDyldInfo((DyldInfoCommand) cmd, packedDylib); + break; + } + } + + return packedDylib.getInputStream(); + } + + /** + * Fixes-up the old DYLD file offsets in the given segment so they are correct for the newly + * packed DYLIB + * + * @param cmd The segment to fix-up + * @param packedDylib The packed DYLIB + * @param is64bit True if the segment is 64-bit; false if 32-bit + * @param monitor A cancellable {@link TaskMonitor} + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private static void fixupSegment(SegmentCommand cmd, PackedDylib packedDylib, boolean is64bit, + TaskMonitor monitor) throws IOException { + if (cmd.getFileOffset() > 0 && cmd.getFileSize() > 0) { + packedDylib.fixup(cmd.getStartIndex() + (is64bit ? 0x28 : 0x20), is64bit ? 8 : 4); + } + long sectionStartIndex = cmd.getStartIndex() + (is64bit ? 0x48 : 0x38); + for (Section section : cmd.getSections()) { + if (monitor.isCancelled()) { + break; + } + if (section.getOffset() > 0 && section.getSize() > 0) { + packedDylib.fixup(sectionStartIndex + (is64bit ? 0x30 : 0x28), 4); + } + if (section.getRelocationOffset() > 0) { + packedDylib.fixup(sectionStartIndex + (is64bit ? 0x38 : 0x30), 4); + } + sectionStartIndex += is64bit ? 0x50 : 0x44; + } + } + + /** + * Fixes-up the old DYLD file offsets in the given symbol table so they are correct for the + * newly packed DYLIB + * + * @param cmd The symbol table to fix-up + * @param packedDylib The packed DYLIB + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private static void fixupSymbolTable(SymbolTableCommand cmd, PackedDylib packedDylib) + throws IOException { + if (cmd.getSymbolOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x8, 4); + } + if (cmd.getStringTableOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x10, 4); + } + } + + /** + * Fixes-up the old DYLD file offsets in the given dynamic symbol table so they are correct for + * the newly packed DYLIB + * + * @param cmd The dynamic symbol table to fix-up + * @param packedDylib The packed DYLIB + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private static void fixupDynamicSymbolTable(DynamicSymbolTableCommand cmd, + PackedDylib packedDylib) throws IOException { + if (cmd.getTableOfContentsOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x20, 4); + } + if (cmd.getModuleTableOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x28, 4); + } + if (cmd.getReferencedSymbolTableOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x30, 4); + } + if (cmd.getIndirectSymbolTableOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x38, 4); + } + if (cmd.getExternalRelocationOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x40, 4); + } + if (cmd.getLocalRelocationOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x48, 4); + } + } + + /** + * Fixes-up the old DYLD file offsets in the given DYLD Info command so they are correct for the + * newly packed DYLIB + * + * @param cmd The DYLD Info command to fix-up + * @param packedDylib The packed DYLIB + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private static void fixupDyldInfo(DyldInfoCommand cmd, PackedDylib packedDylib) + throws IOException { + if (cmd.getRebaseOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x8, 4); + } + if (cmd.getBindOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x10, 4); + } + if (cmd.getWeakBindOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x18, 4); + } + if (cmd.getLazyBindOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x20, 4); + } + if (cmd.getExportOffset() > 0) { + packedDylib.fixup(cmd.getStartIndex() + 0x28, 4); + } + } + + /** + * A packed DYLIB that was once living inside of a DYLD. The DYLIB is said to be packed + * because its segment file bytes, which were not adjacent in its containing DYLD, are now + * adjacent in its new array. + */ + private static class PackedDylib { + + private BinaryReader reader; + private Map packedStarts; + private byte[] packed; + + /** + * Creates a new {@link PackedDylib} object + * + * @param header The DYLD's DYLIB's Mach-O header + * @param dylibOffset The offset of the DYLIB in the given provider + * @param provider The DYLD's bytes + * @throws IOException If there was an IO-related error + */ + public PackedDylib(MachHeader header, long dylibOffset, ByteProvider provider) + throws IOException { + reader = new BinaryReader(provider, true); + packedStarts = new HashMap<>(); + int size = 0; + for (SegmentCommand segment : header.getAllSegments()) { + packedStarts.put(segment, size); + size += segment.getFileSize(); + + // Some older DYLDs use relative file offsets for only their __TEXT segment. + // Adjust these segments to be consistent with all the other segments. + if (segment.getFileOffset() == 0) { + segment.setFileOffset(dylibOffset); + } + } + packed = new byte[size]; + for (SegmentCommand segment : header.getAllSegments()) { + long segmentSize = segment.getFileSize(); + if (segment.getFileOffset() + segmentSize > provider.length()) { + segmentSize = provider.length() - segment.getFileOffset(); + Msg.warn(this, segment.getSegmentName() + + " segment extends beyond end of file. Truncating..."); + } + byte[] bytes = provider.readBytes(segment.getFileOffset(), segmentSize); + System.arraycopy(bytes, 0, packed, packedStarts.get(segment), bytes.length); + } + } + + /** + * Gets an {@link InputStream} that reads the packed DYLIB + * + * @return An {@link InputStream} that reads the packed DYLIB + */ + public InputStream getInputStream() { + return new ByteArrayInputStream(packed); + } + + /** + * Fixes up the bytes at the given DYLD file offset to map to the correct offset in the + * packed DYLIB + * + * @param fileOffset The DYLD file offset to fix-up + * @param size The number of bytes to fix-up (must be 4 or 8) + * @throws IOException If there was an IO-related error + * @throws IllegalArgumentException if size is an unsupported value + */ + public void fixup(long fileOffset, int size) throws IOException { + if (size != 4 && size != 8) { + throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); + } + long orig = reader.readUnsignedValue(fileOffset, size); + try { + byte[] newBytes = toBytes(getPackedOffset(orig), size); + System.arraycopy(newBytes, 0, packed, (int) getPackedOffset(fileOffset), + newBytes.length); + } + catch (NotFoundException e) { + Msg.warn(this, e.getMessage()); + } + } + + /** + * Converts the given DYLD file offset to an offset into the packed DYLIB + * + * @param fileOffset The DYLD file offset to convert + * @return An offset into the packed DYLIB + * @throws NotFoundException If there was no corresponding DYLIB offset + */ + private long getPackedOffset(long fileOffset) throws NotFoundException { + for (SegmentCommand segment : packedStarts.keySet()) { + if (fileOffset >= segment.getFileOffset() && + fileOffset < segment.getFileOffset() + segment.getFileSize()) { + return fileOffset - segment.getFileOffset() + packedStarts.get(segment); + } + } + throw new NotFoundException( + "Failed to convert DYLD file offset to packed DYLIB offset: " + + Long.toHexString(fileOffset)); + } + + /** + * Converts the given value to a byte array + * + * @param value The value to convert to a byte array + * @param size The number of bytes to convert (must be 4 or 8) + * @return The value as a byte array of the given size + * @throws IllegalArgumentException if size is an unsupported value + */ + private byte[] toBytes(long value, int size) throws IllegalArgumentException { + if (size != 4 && size != 8) { + throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); + } + DataConverter converter = LittleEndianDataConverter.INSTANCE; + return size == 8 ? converter.getBytes(value) : converter.getBytes((int) value); + } + } +} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java index 5d593b7dc4..d67eb9ae02 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java @@ -57,19 +57,8 @@ public class DyldCacheFileSystem extends GFileSystemBase { } long machHeaderStartIndexInProvider = data.getAddress() - header.getBaseAddress(); try { - /* - * //check to make sure mach-o header is valid MachHeader header = - * MachHeader.createMachHeader( RethrowContinuesFactory.INSTANCE, - * provider, machHeaderStartIndexInProvider, false ); - * header.parse(); - * - * return new ByteProviderInputStream( provider, - * machHeaderStartIndexInProvider, provider.length() - - * machHeaderStartIndexInProvider ); - */ - - FixupMacho32bitArmOffsets fixer = new FixupMacho32bitArmOffsets(); - return fixer.fix(file, machHeaderStartIndexInProvider, provider, monitor); + return DyldCacheDylibExtractor.extractDylib(machHeaderStartIndexInProvider, provider, + monitor); } catch (MachException e) { throw new IOException("Invalid Mach-O header detected at 0x" + @@ -164,11 +153,8 @@ public class DyldCacheFileSystem extends GFileSystemBase { monitor.incrementProgress(1); - GFileImpl file = GFileImpl.fromPathString(this, root, data.getPath(), null, false, - 0/*TODO compute length?*/ ); + GFileImpl file = GFileImpl.fromPathString(this, root, data.getPath(), null, false, -1); storeFile(file, data); - - file.setLength(provider.length() - (data.getAddress() - header.getBaseAddress())); } } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/FixupMacho32bitArmOffsets.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/FixupMacho32bitArmOffsets.java deleted file mode 100644 index a1f91b1c64..0000000000 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/FixupMacho32bitArmOffsets.java +++ /dev/null @@ -1,177 +0,0 @@ -/* ### - * IP: GHIDRA - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.file.formats.ios.dyldcache; - -import java.io.*; -import java.util.*; - -import generic.continues.RethrowContinuesFactory; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.format.macho.*; -import ghidra.app.util.bin.format.macho.commands.*; -import ghidra.formats.gfilesystem.GFile; -import ghidra.util.*; -import ghidra.util.exception.DuplicateNameException; -import ghidra.util.task.TaskMonitor; - -public class FixupMacho32bitArmOffsets { - private DataConverter converter = LittleEndianDataConverter.INSTANCE; - - public InputStream fix(GFile file, long offsetAdjustment, ByteProvider provider, - TaskMonitor monitor) throws IOException, MachException { - Map changeMap = new HashMap(); - - //check to make sure mach-o header is valid - MachHeader header = MachHeader.createMachHeader( RethrowContinuesFactory.INSTANCE, provider, offsetAdjustment, false ); - header.parse(); - - //fix up index, offsets, etc in the header - List commands = header.getLoadCommands(); - for ( LoadCommand loadCommand : commands ) { - if ( monitor.isCancelled() ) { - break; - } - switch ( loadCommand.getCommandType() ) { - case LoadCommandTypes.LC_SEGMENT: { - SegmentCommand segmentCommand = (SegmentCommand) loadCommand; - if ( segmentCommand.getFileOffset() > 0 ) { - long newOffset = segmentCommand.getFileOffset() - offsetAdjustment; - changeMap.put( segmentCommand.getStartIndex() + 0x20 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( segmentCommand.getNumberOfSections() > 0 ) { - long sectionStartIndex = segmentCommand.getStartIndex() + 0x38 - offsetAdjustment; - for ( Section section : segmentCommand.getSections() ) { - if ( monitor.isCancelled() ) { - break; - } - if ( section.getOffset() > 0 && section.getOffset() > offsetAdjustment ) { - long newOffset = Conv.intToLong( section.getOffset() ) - offsetAdjustment; - changeMap.put( sectionStartIndex + 0x28, converter.getBytes( (int)newOffset ) ); - } - if ( section.getRelocationOffset() > 0 && section.getRelocationOffset() > offsetAdjustment ) { - long newOffset = Conv.intToLong( section.getRelocationOffset() ) - offsetAdjustment; - changeMap.put( sectionStartIndex + 0x30, converter.getBytes( (int)newOffset ) ); - } - try { - sectionStartIndex += section.toDataType().getLength(); - } - catch ( DuplicateNameException e ) { - throw new IOException( e ); - } - } - } - break; - } - case LoadCommandTypes.LC_SYMTAB: { - SymbolTableCommand symbolTableCommand = (SymbolTableCommand) loadCommand; - if ( symbolTableCommand.getSymbolOffset() > 0 ) { - long newOffset = Conv.intToLong( symbolTableCommand.getSymbolOffset() ) - offsetAdjustment; - changeMap.put( symbolTableCommand.getStartIndex() + 0x8 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( symbolTableCommand.getStringTableOffset() > 0 ) { - long newOffset = Conv.intToLong( symbolTableCommand.getStringTableOffset() ) - offsetAdjustment; - changeMap.put( symbolTableCommand.getStartIndex() + 0x10 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - break; - } - case LoadCommandTypes.LC_DYSYMTAB: { - DynamicSymbolTableCommand dynamicSymbolTableCommand = (DynamicSymbolTableCommand) loadCommand; - if ( dynamicSymbolTableCommand.getTableOfContentsOffset() > 0 ) { - long newOffset = Conv.intToLong( dynamicSymbolTableCommand.getTableOfContentsOffset() ) - offsetAdjustment; - changeMap.put( dynamicSymbolTableCommand.getStartIndex() + 0x20 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dynamicSymbolTableCommand.getModuleTableOffset() > 0 ) { - long newOffset = Conv.intToLong( dynamicSymbolTableCommand.getModuleTableOffset() ) - offsetAdjustment; - changeMap.put( dynamicSymbolTableCommand.getStartIndex() + 0x28 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dynamicSymbolTableCommand.getReferencedSymbolTableOffset() > 0 ) { - long newOffset = Conv.intToLong( dynamicSymbolTableCommand.getReferencedSymbolTableOffset() ) - offsetAdjustment; - changeMap.put( dynamicSymbolTableCommand.getStartIndex() + 0x30 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dynamicSymbolTableCommand.getIndirectSymbolTableOffset() > 0 ) { - long newOffset = Conv.intToLong( dynamicSymbolTableCommand.getIndirectSymbolTableOffset() ) - offsetAdjustment; - changeMap.put( dynamicSymbolTableCommand.getStartIndex() + 0x38 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dynamicSymbolTableCommand.getExternalRelocationOffset() > 0 ) { - long newOffset = Conv.intToLong( dynamicSymbolTableCommand.getExternalRelocationOffset() ) - offsetAdjustment; - changeMap.put( dynamicSymbolTableCommand.getStartIndex() + 0x40 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dynamicSymbolTableCommand.getLocalRelocationOffset() > 0 ) { - long newOffset = Conv.intToLong( dynamicSymbolTableCommand.getLocalRelocationOffset() ) - offsetAdjustment; - changeMap.put( dynamicSymbolTableCommand.getStartIndex() + 0x48 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - break; - } - case LoadCommandTypes.LC_DYLD_INFO: - case LoadCommandTypes.LC_DYLD_INFO_ONLY: { - DyldInfoCommand dyldInfoCommand = (DyldInfoCommand) loadCommand; - if ( dyldInfoCommand.getRebaseOffset() > 0 ) { - long newOffset = Conv.intToLong( dyldInfoCommand.getRebaseOffset() ) - offsetAdjustment; - changeMap.put( dyldInfoCommand.getStartIndex() + 0x8 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dyldInfoCommand.getBindOffset() > 0 ) { - long newOffset = Conv.intToLong( dyldInfoCommand.getBindOffset() ) - offsetAdjustment; - changeMap.put( dyldInfoCommand.getStartIndex() + 0x10 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dyldInfoCommand.getWeakBindOffset() > 0 ) { - long newOffset = Conv.intToLong( dyldInfoCommand.getWeakBindOffset() ) - offsetAdjustment; - changeMap.put( dyldInfoCommand.getStartIndex() + 0x18 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dyldInfoCommand.getLazyBindOffset() > 0 ) { - long newOffset = Conv.intToLong( dyldInfoCommand.getLazyBindOffset() ) - offsetAdjustment; - changeMap.put( dyldInfoCommand.getStartIndex() + 0x20 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - if ( dyldInfoCommand.getExportOffset() > 0 ) { - long newOffset = Conv.intToLong(dyldInfoCommand.getExportOffset() ) - offsetAdjustment; - changeMap.put( dyldInfoCommand.getStartIndex() + 0x28 - offsetAdjustment, converter.getBytes( (int)newOffset ) ); - } - break; - } - } - } - - List indexList = new ArrayList( changeMap.keySet() ); - Collections.sort( indexList ); - - ByteArrayOutputStream tempOut = new ByteArrayOutputStream(); - try { - long tempIndex = offsetAdjustment; - while ( !monitor.isCancelled() ) { - final int length = 0x10000; - byte [] buffer = provider.readBytes( tempIndex, length ); - - for ( Long index : indexList ) { - if ( index + offsetAdjustment >= tempIndex && index + offsetAdjustment < tempIndex + length ) { - byte [] changedBytes = changeMap.get( index ); - System.arraycopy( changedBytes, 0, buffer, index.intValue(), changedBytes.length ); - } - } - - tempOut.write( buffer ); - tempIndex += buffer.length; - monitor.setMessage( "0x" + Long.toHexString( tempIndex ) ); - if ( tempIndex > provider.length() ) { - break; - } - } - } - finally { - tempOut.close(); - } - - return new ByteArrayInputStream(tempOut.toByteArray()); - } -} diff --git a/Ghidra/Features/FunctionGraph/build.gradle b/Ghidra/Features/FunctionGraph/build.gradle index 1048607047..1e952eb764 100644 --- a/Ghidra/Features/FunctionGraph/build.gradle +++ b/Ghidra/Features/FunctionGraph/build.gradle @@ -25,9 +25,9 @@ eclipse.project.name = 'Features Graph FunctionGraph' dependencies { - compile project(":Base") + api project(":Base") - testCompile "org.jmockit:jmockit:1.44" + testImplementation "org.jmockit:jmockit:1.44" helpPath project(path: ":Base", configuration: 'helpPath') } diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/build.gradle b/Ghidra/Features/FunctionGraphDecompilerExtension/build.gradle index 5b7397bead..4c66acffe2 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/build.gradle +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/build.gradle @@ -22,6 +22,6 @@ apply plugin: 'eclipse' eclipse.project.name = 'Features Graph FunctionGraphDecompilerExt' dependencies { - compile project(":FunctionGraph") - compile project(":Decompiler") + api project(":FunctionGraph") + api project(":Decompiler") } diff --git a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java index 39c2744d80..5b9facc738 100644 --- a/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java +++ b/Ghidra/Features/FunctionGraphDecompilerExtension/src/main/java/ghidra/app/plugin/core/functiongraph/graph/layout/DecompilerNestedLayout.java @@ -112,6 +112,12 @@ public class DecompilerNestedLayout extends AbstractFGLayout { return new DNLEdgeLabelRenderer<>(getCondenseFactor()); } + @Override + protected void condenseEdges(List> rows, + Map> newEdgeArticulations, double centerX, double centerY) { + // do not condense, as we route our edges at the preferred positions + } + @Override protected double getCondenseFactor() { // our layout needs more spacing because we have custom edge routing that we want to @@ -714,7 +720,7 @@ public class DecompilerNestedLayout extends AbstractFGLayout { -p2 - just past the left edge -p3 - just past the bottom of the vertex -p4 - back at the original x value - + | .___| | .-----. diff --git a/Ghidra/Features/FunctionID/build.gradle b/Ghidra/Features/FunctionID/build.gradle index a67ae99435..9df8dbda4c 100644 --- a/Ghidra/Features/FunctionID/build.gradle +++ b/Ghidra/Features/FunctionID/build.gradle @@ -24,9 +24,9 @@ eclipse.project.name = 'Features FunctionID' dependencies { - compile project(":Base") - compile project(":DB") - compile project(":SoftwareModeling") + api project(":Base") + api project(":DB") + api project(":SoftwareModeling") helpPath project(path: ":Base", configuration: 'helpPath') } diff --git a/Ghidra/Features/GhidraServer/build.gradle b/Ghidra/Features/GhidraServer/build.gradle index 484be39516..0b84adadbf 100644 --- a/Ghidra/Features/GhidraServer/build.gradle +++ b/Ghidra/Features/GhidraServer/build.gradle @@ -28,9 +28,9 @@ configurations { } dependencies { - compile project(":FileSystem") - compile project(":DB") - compile project(":Generic") + api project(":FileSystem") + api project(":DB") + api project(":Generic") runGhidraServer project } diff --git a/Ghidra/Features/GnuDemangler/build.gradle b/Ghidra/Features/GnuDemangler/build.gradle index 1a31ae95e7..c283470f12 100644 --- a/Ghidra/Features/GnuDemangler/build.gradle +++ b/Ghidra/Features/GnuDemangler/build.gradle @@ -23,5 +23,5 @@ eclipse.project.name = 'Features GnuDemangler' dependencies { - compile project(":Base") + api project(":Base") } diff --git a/Ghidra/Features/GraphFunctionCalls/build.gradle b/Ghidra/Features/GraphFunctionCalls/build.gradle index 34325a620b..8a941be103 100644 --- a/Ghidra/Features/GraphFunctionCalls/build.gradle +++ b/Ghidra/Features/GraphFunctionCalls/build.gradle @@ -25,7 +25,7 @@ eclipse.project.name = 'Features Graph FunctionCalls' // Note: this module's name is 'GraphFunctionCalls' dependencies { - compile project(":Base") + api project(":Base") helpPath project(path: ":Base", configuration: 'helpPath') @@ -35,7 +35,7 @@ dependencies { helpPath project(path: ":FunctionGraph", configuration: 'helpPath') // These have abstract test classes and stubs needed by this module - testCompile project(path: ':Project', configuration: 'testArtifacts') - testCompile project(path: ':SoftwareModeling', configuration: 'testArtifacts') + testImplementation project(path: ':Project', configuration: 'testArtifacts') + testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts') } diff --git a/Ghidra/Features/GraphServices/build.gradle b/Ghidra/Features/GraphServices/build.gradle index 65a77be0b8..9af6597eb7 100644 --- a/Ghidra/Features/GraphServices/build.gradle +++ b/Ghidra/Features/GraphServices/build.gradle @@ -24,26 +24,26 @@ eclipse.project.name = 'Features Graph Services' dependencies { - compile project(":Base") + api project(":Base") // jungrapht - exclude slf4j which produces a conflict with other uses with Ghidra - compile ("com.github.tomnelson:jungrapht-visualization:1.2") { exclude group: "org.slf4j", module: "slf4j-api" } - compile ("com.github.tomnelson:jungrapht-layout:1.2") { exclude group: "org.slf4j", module: "slf4j-api" } + api ("com.github.tomnelson:jungrapht-visualization:1.2") { exclude group: "org.slf4j", module: "slf4j-api" } + api ("com.github.tomnelson:jungrapht-layout:1.2") { exclude group: "org.slf4j", module: "slf4j-api" } - compile "org.jgrapht:jgrapht-core:1.5.0" + api "org.jgrapht:jgrapht-core:1.5.0" // not using jgrapht-io code that depends on antlr, so exclude antlr - compile ("org.jgrapht:jgrapht-io:1.5.0") { exclude group: "org.antlr", module: "antlr4-runtime" } + api ("org.jgrapht:jgrapht-io:1.5.0") { exclude group: "org.antlr", module: "antlr4-runtime" } - runtime "org.slf4j:slf4j-api:1.7.25" + runtimeOnly "org.slf4j:slf4j-api:1.7.25" // use this if you want no slf4j log messages - runtime "org.slf4j:slf4j-nop:1.7.25" + runtimeOnly "org.slf4j:slf4j-nop:1.7.25" // use this if you want slf4j log messages sent to log4j - // runtime "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" + // runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" - runtime "org.jheaps:jheaps:0.13" + runtimeOnly "org.jheaps:jheaps:0.13" helpPath project(path: ":Base", configuration: 'helpPath') diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle b/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle index 7e508f2b37..aef84ea4de 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/build.gradle @@ -22,6 +22,6 @@ apply plugin: 'eclipse' eclipse.project.name = 'Features MicrosoftCodeAnalyzer' dependencies { - compile project(":MicrosoftDmang") - compile project(":Base") + api project(":MicrosoftDmang") + api project(":Base") } diff --git a/Ghidra/Features/MicrosoftDemangler/build.gradle b/Ghidra/Features/MicrosoftDemangler/build.gradle index 0d5d134cae..6993ecfdb3 100644 --- a/Ghidra/Features/MicrosoftDemangler/build.gradle +++ b/Ghidra/Features/MicrosoftDemangler/build.gradle @@ -23,6 +23,6 @@ eclipse.project.name = 'Features MicrosoftDemangler' dependencies { - compile project(":Base") - compile project(":MicrosoftDmang") + api project(":Base") + api project(":MicrosoftDmang") } diff --git a/Ghidra/Features/MicrosoftDmang/build.gradle b/Ghidra/Features/MicrosoftDmang/build.gradle index 473ccb6bc3..f516122831 100644 --- a/Ghidra/Features/MicrosoftDmang/build.gradle +++ b/Ghidra/Features/MicrosoftDmang/build.gradle @@ -25,7 +25,7 @@ project.ext.excludeFromParallelTests = true project.ext.excludeFromParallelIntegrationTests = true dependencies { - compile project(':Base') + api project(':Base') } /* diff --git a/Ghidra/Features/PDB/build.gradle b/Ghidra/Features/PDB/build.gradle index b624b0005e..0ea23828b9 100644 --- a/Ghidra/Features/PDB/build.gradle +++ b/Ghidra/Features/PDB/build.gradle @@ -28,16 +28,16 @@ eclipse.project.name = 'Features PDB' * Build dependencies *********************************************************************************/ dependencies { - compile project(":Base") + api project(":Base") - testCompile "org.jmockit:jmockit:1.44" + testImplementation "org.jmockit:jmockit:1.44" // Demangler Analyzer needs to find MicrosoftDemangler - compile project(":MicrosoftDemangler") + api project(":MicrosoftDemangler") helpPath project(path: ':Base', configuration: 'helpPath') // this module's help has links to Base help files - testCompile project(path: ':Base', configuration: 'testArtifacts') - testCompile project(path: ':SoftwareModeling', configuration: 'testArtifacts') + testImplementation project(path: ':Base', configuration: 'testArtifacts') + testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts') } /** diff --git a/Ghidra/Features/ProgramDiff/build.gradle b/Ghidra/Features/ProgramDiff/build.gradle index 44f4a26937..f3f9f6c152 100644 --- a/Ghidra/Features/ProgramDiff/build.gradle +++ b/Ghidra/Features/ProgramDiff/build.gradle @@ -24,7 +24,7 @@ eclipse.project.name = 'Features ProgramDiff' dependencies { - compile project(":Base") + api project(":Base") helpPath project(path: ":Base", configuration: 'helpPath') } diff --git a/Ghidra/Features/ProgramGraph/build.gradle b/Ghidra/Features/ProgramGraph/build.gradle index 7f842dba2f..2e152e1431 100644 --- a/Ghidra/Features/ProgramGraph/build.gradle +++ b/Ghidra/Features/ProgramGraph/build.gradle @@ -24,7 +24,7 @@ eclipse.project.name = 'Features Graph ProgramGraph' dependencies { - compile project(":Base") + api project(":Base") helpPath project(path: ":Base", configuration: 'helpPath') helpPath project(path: ":GraphServices", configuration: 'helpPath') diff --git a/Ghidra/Features/Python/build.gradle b/Ghidra/Features/Python/build.gradle index e90207c471..f042afd3f0 100644 --- a/Ghidra/Features/Python/build.gradle +++ b/Ghidra/Features/Python/build.gradle @@ -31,10 +31,10 @@ configurations { } dependencies { - compile project(':Base') + api project(':Base') helpPath project(path: ":Base", configuration: "helpPath") jython JYTHON - compile JYTHON + api JYTHON } task jythonUnpack(type: Copy) { diff --git a/Ghidra/Features/Recognizers/build.gradle b/Ghidra/Features/Recognizers/build.gradle index d1249469ba..f09c2e8afc 100644 --- a/Ghidra/Features/Recognizers/build.gradle +++ b/Ghidra/Features/Recognizers/build.gradle @@ -22,7 +22,7 @@ apply plugin: 'eclipse' eclipse.project.name = 'Features Recognizers' dependencies { - compile project(':Base') + api project(':Base') } test { diff --git a/Ghidra/Features/SourceCodeLookup/build.gradle b/Ghidra/Features/SourceCodeLookup/build.gradle index 84fdd5747a..f361d43f7e 100644 --- a/Ghidra/Features/SourceCodeLookup/build.gradle +++ b/Ghidra/Features/SourceCodeLookup/build.gradle @@ -23,7 +23,7 @@ apply plugin: 'eclipse' eclipse.project.name = 'Features SourceCodeLookup' dependencies { - compile project(":Base") - compile project(":Decompiler") + api project(":Base") + api project(":Decompiler") helpPath project(path: ":Decompiler", configuration: 'helpPath') } diff --git a/Ghidra/Features/VersionTracking/build.gradle b/Ghidra/Features/VersionTracking/build.gradle index 69651c5307..9786f07f23 100644 --- a/Ghidra/Features/VersionTracking/build.gradle +++ b/Ghidra/Features/VersionTracking/build.gradle @@ -25,10 +25,10 @@ eclipse.project.name = 'Features VersionTracking' project.ext.excludeFromParallelIntegrationTests = true dependencies { - compile project(":Base") + api project(":Base") helpPath project(path: ":Base", configuration: "helpPath") - testCompile "org.jmockit:jmockit:1.44" - testCompile project(path: ':Project', configuration: 'testArtifacts') + testImplementation "org.jmockit:jmockit:1.44" + testImplementation project(path: ':Project', configuration: 'testArtifacts') } diff --git a/Ghidra/Framework/DB/build.gradle b/Ghidra/Framework/DB/build.gradle index 52b09a8c59..59d0575b0b 100644 --- a/Ghidra/Framework/DB/build.gradle +++ b/Ghidra/Framework/DB/build.gradle @@ -22,6 +22,6 @@ apply plugin: 'eclipse' eclipse.project.name = 'Framework DB' dependencies { - compile project(':Generic') + api project(':Generic') } diff --git a/Ghidra/Framework/Docking/build.gradle b/Ghidra/Framework/Docking/build.gradle index 8b9cb8f350..ae2a4525c1 100644 --- a/Ghidra/Framework/Docking/build.gradle +++ b/Ghidra/Framework/Docking/build.gradle @@ -22,16 +22,16 @@ apply plugin: 'eclipse' eclipse.project.name = 'Framework Docking' dependencies { - compile project(':Generic') - compile 'net.java.dev.timingframework:timingframework:1.0' + api project(':Generic') + api 'net.java.dev.timingframework:timingframework:1.0' // Only include this debug version of the jh library if necessary. - //compile name:'jh2.with.debug' - compile 'javax.help:javahelp:2.0.05' + //api name:'jh2.with.debug' + api 'javax.help:javahelp:2.0.05' // include code from src/test in Generic - testCompile project(path: ':Generic', configuration: 'testArtifacts') + testImplementation project(path: ':Generic', configuration: 'testArtifacts') compileOnly "junit:junit:4.12" } diff --git a/Ghidra/Framework/FileSystem/build.gradle b/Ghidra/Framework/FileSystem/build.gradle index 71d812923b..ad15b86330 100644 --- a/Ghidra/Framework/FileSystem/build.gradle +++ b/Ghidra/Framework/FileSystem/build.gradle @@ -22,10 +22,10 @@ apply plugin: 'eclipse' eclipse.project.name = 'Framework FileSystem' dependencies { - compile project(':Generic') - compile project(':DB') - compile project(':Docking') - compile "ch.ethz.ganymed:ganymed-ssh2:262@jar" + api project(':Generic') + api project(':DB') + api project(':Docking') + api "ch.ethz.ganymed:ganymed-ssh2:262@jar" } diff --git a/Ghidra/Framework/Generic/build.gradle b/Ghidra/Framework/Generic/build.gradle index 3d08b7b2ee..8198e578cb 100644 --- a/Ghidra/Framework/Generic/build.gradle +++ b/Ghidra/Framework/Generic/build.gradle @@ -24,17 +24,17 @@ apply plugin: 'eclipse' eclipse.project.name = 'Framework Generic' dependencies { - compile project(':Utility') - compile "cglib:cglib-nodep:2.2" - compile "com.google.guava:guava:19.0" - compile "org.jdom:jdom-legacy:1.1.3" - compile "org.apache.logging.log4j:log4j-api:2.12.1" - compile "org.apache.logging.log4j:log4j-core:2.12.1" - compile "org.apache.commons:commons-collections4:4.1" - compile "org.apache.commons:commons-lang3:3.9" - compile "org.apache.commons:commons-text:1.6" - compile "commons-io:commons-io:2.6" - compile "com.google.code.gson:gson:2.8.6" + api project(':Utility') + api "cglib:cglib-nodep:2.2" + api "com.google.guava:guava:19.0" + api "org.jdom:jdom-legacy:1.1.3" + api "org.apache.logging.log4j:log4j-api:2.12.1" + api "org.apache.logging.log4j:log4j-core:2.12.1" + api "org.apache.commons:commons-collections4:4.1" + api "org.apache.commons:commons-lang3:3.9" + api "org.apache.commons:commons-text:1.6" + api "commons-io:commons-io:2.6" + api "com.google.code.gson:gson:2.8.6" compileOnly "junit:junit:4.12" } diff --git a/Ghidra/Framework/Graph/build.gradle b/Ghidra/Framework/Graph/build.gradle index aac38c8cfc..2bb5214e88 100644 --- a/Ghidra/Framework/Graph/build.gradle +++ b/Ghidra/Framework/Graph/build.gradle @@ -22,21 +22,21 @@ apply plugin: 'eclipse' eclipse.project.name = 'Framework Graph' dependencies { - compile project(':Docking') + api project(':Docking') // these are not used by the project, but indirectly by the jars listed below - compile "net.sf.jung:jung-api:2.1.1" // used by jung-graph-impl - compile "net.sf.jung:jung-algorithms:2.1.1" // used by jung-visualization + api "net.sf.jung:jung-api:2.1.1" // used by jung-graph-impl + api "net.sf.jung:jung-algorithms:2.1.1" // used by jung-visualization // this project uses these directly - compile "net.sf.jung:jung-graph-impl:2.1.1" - compile "net.sf.jung:jung-visualization:2.1.1" + api "net.sf.jung:jung-graph-impl:2.1.1" + api "net.sf.jung:jung-visualization:2.1.1" - compile "org.jgrapht:jgrapht-core:1.5.0" + api "org.jgrapht:jgrapht-core:1.5.0" // not using jgrapht-io code that depends on antlr, so exclude antlr - compile ("org.jgrapht:jgrapht-io:1.5.0") { exclude group: "org.antlr", module: "antlr4-runtime" } + api ("org.jgrapht:jgrapht-io:1.5.0") { exclude group: "org.antlr", module: "antlr4-runtime" } // These have abstract test classes and stubs needed by this module - testCompile project(path: ':Docking', configuration: 'testArtifacts') + testImplementation project(path: ':Docking', configuration: 'testArtifacts') } diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java index afecdf2477..46ef566245 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/layout/AbstractVisualGraphLayout.java @@ -313,20 +313,27 @@ public abstract class AbstractVisualGraphLayout vertexLayoutLocations = positionVerticesInLayoutSpace(transformer, vertices, layoutLocations); - Map> edgeLayoutArticulationLocations = new HashMap<>(); - Rectangle graphBounds = - getTotalGraphSize(vertexLayoutLocations, edgeLayoutArticulationLocations, transformer); + Rectangle graphBounds = getTotalGraphSize(vertexLayoutLocations, transformer); double centerX = graphBounds.getCenterX(); double centerY = graphBounds.getCenterY(); + // + // Condense vertices before placing edges. This allows layouts to perform custom routing + // of edges around vertices *after* condensing. + // if (isCondensed) { List> rows = gridLocations.rows(); - condense(rows, vertexLayoutLocations, edgeLayoutArticulationLocations, transformer, - centerX, centerY); + condenseVertices(rows, vertexLayoutLocations, transformer, centerX, centerY); } - edgeLayoutArticulationLocations = positionEdgeArticulationsInLayoutSpace(transformer, - vertexLayoutLocations, edges, layoutLocations); + Map> edgeLayoutArticulations = positionEdgeArticulationsInLayoutSpace( + transformer, vertexLayoutLocations, edges, layoutLocations); + + if (isCondensed) { + // note: some layouts will not condense the edges, as they perform custom routing + List> rows = gridLocations.rows(); + condenseEdges(rows, edgeLayoutArticulations, centerX, centerY); + } // DEGUG triggers grid lines to be printed; useful for debugging // VisualGraphRenderer.DEBUG_ROW_COL_MAP.put(this, layoutLocations.copy()); @@ -334,8 +341,7 @@ public abstract class AbstractVisualGraphLayout positionVerticesInLayoutSpace( @@ -430,11 +436,12 @@ public abstract class AbstractVisualGraphLayout vertexLocationMap, - Map> edgeArticulations, com.google.common.base.Function vertexShapeTransformer) { + // note: do not include edges in the size of the graph at this point, as some layouts use + // custom edge routing after this method is called Set vertices = vertexLocationMap.keySet(); - Set edges = edgeArticulations.keySet(); + Set edges = Collections.emptySet(); Function vertexToBounds = v -> { @@ -452,14 +459,13 @@ public abstract class AbstractVisualGraphLayout> edgeToArticulations = e -> edgeArticulations.get(e); + Function> edgeToArticulations = e -> Collections.emptyList(); Rectangle bounds = GraphViewerUtils.getTotalGraphSizeInLayoutSpace(vertices, edges, vertexToBounds, edgeToArticulations); return bounds; } - private void condense(List> rows, Map newLocations, - Map> newEdgeArticulations, + protected void condenseVertices(List> rows, Map newLocations, VisualGraphVertexShapeTransformer transformer, double centerX, double centerY) { // @@ -480,6 +486,22 @@ public abstract class AbstractVisualGraphLayout> rows, Map> newEdgeArticulations, + double centerX, double centerY) { + + // + // Note: we move the articulations and vertices closer together on the x-axis. We do + // not move the y-axis, as that is already as close together as we would like at + // this point. + // + double condenseFactor = getCondenseFactor(); Collection> edgeArticulations = newEdgeArticulations.values(); for (List edgePoints : edgeArticulations) { for (Point2D point : edgePoints) { @@ -493,12 +515,6 @@ public abstract class AbstractVisualGraphLayout row, int moveLeftStartIndex, Map vertexLocations, VisualGraphVertexShapeTransformer transformer) { - for (int i = moveLeftStartIndex; i >= 0; i--) { + for (int i = moveLeftStartIndex; i >= row.getStartColumn(); i--) { V vertex = row.getVertex(i); V rightVertex = getRightVertex(row, i); moveLeftIfOverlaps(vertexLocations, transformer, vertex, rightVertex); @@ -537,7 +553,7 @@ public abstract class AbstractVisualGraphLayout row, int moveRightStartIndex, Map vertexLocations, VisualGraphVertexShapeTransformer transformer) { - for (int i = moveRightStartIndex; i < row.getColumnCount(); i++) { + for (int i = moveRightStartIndex; i <= row.getEndColumn(); i++) { V vertex = row.getVertex(i); V leftVertex = getLeftVertex(row, i); moveRightIfOverlaps(vertexLocations, transformer, vertex, leftVertex); diff --git a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/satellite/CachingSatelliteGraphViewer.java b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/satellite/CachingSatelliteGraphViewer.java index 59411db56a..466984369e 100644 --- a/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/satellite/CachingSatelliteGraphViewer.java +++ b/Ghidra/Framework/Graph/src/main/java/ghidra/graph/viewer/satellite/CachingSatelliteGraphViewer.java @@ -39,6 +39,8 @@ public class CachingSatelliteGraphViewer highlightRenderer = new VisualVertexSatelliteRenderer<>(); @@ -53,7 +55,10 @@ public class CachingSatelliteGraphViewer layout = masterViewer.getGraphLayout(); if (layout instanceof ObservableCachingLayout) { @@ -75,7 +80,7 @@ public class CachingSatelliteGraphViewer rc, V vertex, GraphicsDecorator g, Rectangle bounds) { - // Stub--we don't want the render to paint highlights, as we use a static, + // Stub--we don't want the render to paint highlights, as we use a static, // cached image. We will manually paint highlights in the paint routine of this // viewer. } @@ -95,16 +100,16 @@ public class CachingSatelliteGraphViewer getExternalRuntimeDependencies(Project project) { List list = new ArrayList() - list.addAll(getExternalDependencies(project.configurations.compile)); - list.addAll(getExternalDependencies(project.configurations.runtime)); + if (project.configurations.find { it.name == 'api' }) { + list.addAll(getExternalDependencies(project, project.configurations.api)); + } + if (project.configurations.find { it.name == 'implementation' }) { + list.addAll(getExternalDependencies(project, project.configurations.implementation)); + } + if (project.configurations.find { it.name == 'runtimeOnly' }) { + list.addAll(getExternalDependencies(project, project.configurations.runtimeOnly)); + } return list } -List getExternalDependencies(Configuration configuration) { +List getExternalDependencies(Project project, Configuration configuration) { List list = new ArrayList<>(); configuration.dependencies.each { dep -> @@ -344,9 +351,12 @@ List getExternalDependencies(Configuration configuration) { } } - // loop back through all the dependency files, looking for one that contains the dependency name. - String depPath = configuration.find { - it.name.contains(searchString) + // loop back through all the dependency files, looking for one that contains the dependency name. + String depPath = project.configurations + .findAll {it.isCanBeResolved()} + .collect {it.resolve()} + .flatten() + .find {it.getAbsolutePath().contains(searchString) } if (depPath == null) { println("****************DID NOT FIND DEPENDENCY: name = "+name+" version = "+version) @@ -369,7 +379,7 @@ Set getAllExternalDependencies(Project project) { Set set = new HashSet() project.getConfigurations().each { config -> - set.addAll(getExternalDependencies(config)) + set.addAll(getExternalDependencies(project, config)) } return set diff --git a/gradle/helpProject.gradle b/gradle/helpProject.gradle index a965467ed1..9751060d27 100644 --- a/gradle/helpProject.gradle +++ b/gradle/helpProject.gradle @@ -59,8 +59,8 @@ sourceSets { } dependencies { - helpIndexCompile "javax.help:javahelp:2.0.05" - helpIndexCompile project(path: ':Help', configuration: 'archives') + helpIndexImplementation "javax.help:javahelp:2.0.05" + helpIndexImplementation project(':Help') } // Task for calling the java help indexer, which creates a searchable index of the @@ -193,7 +193,8 @@ jar { } // build the help whenever this module's jar file is built -jar.dependsOn buildHelp +processResources.dependsOn buildHelp +jar.dependsOn buildHelp // make sure generated help directories exist during prepdev so that the directories are created and diff --git a/gradle/javaProject.gradle b/gradle/javaProject.gradle index fa43117602..d48192e229 100644 --- a/gradle/javaProject.gradle +++ b/gradle/javaProject.gradle @@ -31,7 +31,7 @@ import org.gradle.plugins.ide.eclipse.model.Library; * - all subs will have access to these properties. *********************************************************************************/ -apply plugin: 'java' +apply plugin: 'java-library' compileJava { options.compilerArgs << '-Xlint:none' @@ -46,6 +46,14 @@ compileTestJava { options.fork = true options.warnings = false } + +processResources { + duplicatesStrategy = 'exclude' +} + +processTestResources { + duplicatesStrategy = 'exclude' +} plugins.withId('java') { sourceCompatibility = "${rootProject.JAVA_COMPILER}" @@ -120,12 +128,12 @@ sourceSets { } configurations { - integrationTestCompile.extendsFrom testCompile - integrationTestRuntime.extendsFrom testRuntime, integrationTestCompile - pcodeTestCompile.extendsFrom compile - testArtifacts.extendsFrom testRuntime - integrationTestArtifacts.extendsFrom integrationTestRuntime - screenShotsCompile.extendsFrom integrationTestCompile + integrationTestImplementation.extendsFrom testImplementation + integrationTestRuntimeOnly.extendsFrom testRuntimeOnly, integrationTestImplementation + pcodeTestImplementation.extendsFrom implementation + testArtifacts.extendsFrom testRuntimeOnly + integrationTestArtifacts.extendsFrom integrationTestRuntimeOnly + screenShotsImplementation.extendsFrom integrationTestImplemenation } task testJar(type: Jar) { @@ -147,14 +155,14 @@ artifacts { Provide test dependencies here so each build file does not have to. */ dependencies { - integrationTestCompile "org.hamcrest:hamcrest-all:1.3" - integrationTestCompile "org.jmockit:jmockit:1.44" + integrationTestImplementation "org.hamcrest:hamcrest-all:1.3" + integrationTestImplementation "org.jmockit:jmockit:1.44" - testCompile "org.hamcrest:hamcrest-all:1.3" - testCompile "org.jmockit:jmockit:1.44" + testImplementation "org.hamcrest:hamcrest-all:1.3" + testImplementation "org.jmockit:jmockit:1.44" - testCompile "junit:junit:4.12" - pcodeTestCompile "junit:junit:4.12" + testImplementation "junit:junit:4.12" + pcodeTestImplementation "junit:junit:4.12" } // For Java 9, we must explicitly export references to the internal classes we are using. @@ -190,10 +198,12 @@ eclipse.classpath.file.whenMerged { classpath -> // Prevent Gradle 5.6 from setting the 'test' attribute on our test source folders and jars. // If we don't do this, things that we have outside of test directories that depend on test // libraries (like junit) will not compile in Eclipse. + // Also prevent Gradle 7.0 from adding jars to the modulepath instead of the classpath. classpath.entries.findAll { it.kind == 'src' || it.kind == 'lib' }.each { it.entryAttributes['test'] = 'false' + it.entryAttributes['module'] = 'false' } } diff --git a/gradle/root/distribution.gradle b/gradle/root/distribution.gradle index 3cf9fdd044..a1eebd064f 100644 --- a/gradle/root/distribution.gradle +++ b/gradle/root/distribution.gradle @@ -119,7 +119,7 @@ def getGitRev(repoPath) { *********************************************************************************/ task createJavadocs(type: Javadoc, description: 'Generate javadocs for all projects', group: 'Documentation') { - destinationDir = file(rootProject.projectDir.toString() + "/build/tmp/javadoc") + destinationDir file(rootProject.projectDir.toString() + "/build/tmp/javadoc") failOnError false @@ -177,7 +177,7 @@ task createJsondocs(type: Javadoc, description: 'Generate JSON docs for all proj String ROOT_PROJECT_DIR = rootProject.projectDir.toString() - destinationDir = file(ROOT_PROJECT_DIR + "/build/tmp/jsondoc") + destinationDir file(ROOT_PROJECT_DIR + "/build/tmp/jsondoc") failOnError false @@ -222,8 +222,8 @@ task createJsondocs(type: Javadoc, description: 'Generate JSON docs for all proj *********************************************************************************/ task zipJavadocs(type: Zip) { group 'private' - archiveName 'GhidraAPI_javadoc.zip' - destinationDir file(rootProject.projectDir.toString() + "/build/tmp") + archiveFileName = 'GhidraAPI_javadoc.zip' + destinationDirectory = file(rootProject.projectDir.toString() + "/build/tmp") from createJavadocs { into "api" @@ -233,7 +233,7 @@ task zipJavadocs(type: Zip) { into "api" } - description "Zips javadocs for Ghidra API. [gradle/root/distribution.gradle]" + description "Zips javadocs for Ghidra api. [gradle/root/distribution.gradle]" } @@ -491,12 +491,12 @@ task createInstallationZip(type: Zip) { t -> } if (project.hasProperty("allPlatforms")) { - archiveName "${ZIP_NAME_PREFIX}.zip" + archiveFileName = "${ZIP_NAME_PREFIX}.zip" } else { - archiveName "${ZIP_NAME_PREFIX}_${currentPlatform}.zip" + archiveFileName = "${ZIP_NAME_PREFIX}_${currentPlatform}.zip" } - destinationDir DISTRIBUTION_DIR + destinationDirectory = DISTRIBUTION_DIR // Make sure that we don't try to copy the same file with the same path. duplicatesStrategy 'exclude' diff --git a/gradle/support/distributionCommon.gradle b/gradle/support/distributionCommon.gradle index d04984e7c6..e355a8ce9b 100644 --- a/gradle/support/distributionCommon.gradle +++ b/gradle/support/distributionCommon.gradle @@ -26,8 +26,8 @@ plugins.withType(JavaPlugin) { // Define some metadata about the zip (name, location, version, etc....) t.group 'private' t.description "Creates the source zips for java modules" - t.archiveName project.name + "-src.zip" - t.destinationDir file(projectDir.path + "/build/tmp/src") + t.archiveFileName = project.name + "-src.zip" + t.destinationDirectory = file(projectDir.path + "/build/tmp/src") // Without this we get duplicate files but it's unclear why. It doesn't seem that this // task is being executed multiple times, and sourceSets.main.java contains the // correct elements. Whatever the cause, this fixes the problem. diff --git a/gradle/support/extensionCommon.gradle b/gradle/support/extensionCommon.gradle index 686b69aa85..0a4435391d 100644 --- a/gradle/support/extensionCommon.gradle +++ b/gradle/support/extensionCommon.gradle @@ -20,8 +20,8 @@ task zipExtensions (type: Zip) { it.group 'private' it.description "Creates a zip file for an extension module. [gradle/support/extensionCommon.gradle]" - it.archiveName "${rootProject.ext.ZIP_NAME_PREFIX}_${p.name}.zip" - it.destinationDir rootProject.ext.DISTRIBUTION_DIR + it.archiveFileName = "${rootProject.ext.ZIP_NAME_PREFIX}_${p.name}.zip" + it.destinationDirectory = rootProject.ext.DISTRIBUTION_DIR // Make sure that we don't try to copy the same file with the same path into the // zip (this can happen!)