mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
Merge remote-tracking branch 'origin/GP-894_jpleasu_version_OSGi_extra_packages--SQUASHED'
This commit is contained in:
commit
9c46bb69f1
@ -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<GhidraBundle, List<BundleRequirement>> requirementMap = new HashMap<>();
|
||||
for (GhidraBundle bundle : bundles) {
|
||||
try {
|
||||
requirementMap.put(bundle, bundle.getAllRequirements());
|
||||
List<BundleRequirement> 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);
|
||||
|
@ -129,6 +129,10 @@ public class OSGiUtils {
|
||||
static List<BundleRequirement> parseImportPackage(String importPackageString)
|
||||
throws BundleException {
|
||||
Map<String, Object> 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<BundleCapability> parseExportPackage(String exportPackageString)
|
||||
throws BundleException {
|
||||
Map<String, Object> 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<String> 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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user