mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
allow failing scripts
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
##VERSION: 2.0
|
##VERSION: 2.0
|
||||||
##MODULE IP: BSD
|
|
||||||
##MODULE IP: MIT
|
|
||||||
##MODULE IP: Apache License 2.0
|
##MODULE IP: Apache License 2.0
|
||||||
|
##MODULE IP: BSD
|
||||||
##MODULE IP: Copyright Distribution Permitted
|
##MODULE IP: Copyright Distribution Permitted
|
||||||
##MODULE IP: FAMFAMFAM Icons - CC 2.5
|
##MODULE IP: FAMFAMFAM Icons - CC 2.5
|
||||||
##MODULE IP: LGPL 2.1
|
##MODULE IP: LGPL 2.1
|
||||||
|
##MODULE IP: MIT
|
||||||
##MODULE IP: Modified Nuvola Icons - LGPL 2.1
|
##MODULE IP: Modified Nuvola Icons - LGPL 2.1
|
||||||
##MODULE IP: Nuvola Icons - LGPL 2.1
|
##MODULE IP: Nuvola Icons - LGPL 2.1
|
||||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||||
|
|||||||
@@ -130,8 +130,11 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
bundle_host.startBundleWatcher();
|
bundle_host.startBundleWatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for bundle to be started
|
|
||||||
try {
|
try {
|
||||||
|
Bundle b = bundle_host.installExplodedPath(bi.binDir);
|
||||||
|
bi.bundleLoc = b.getLocation();
|
||||||
|
System.err.printf("new bundle loc is %s\n", bi.bundleLoc);
|
||||||
|
b.start();
|
||||||
if (!bundle_host.waitForBundleStart(bi.bundleLoc)) {
|
if (!bundle_host.waitForBundleStart(bi.bundleLoc)) {
|
||||||
Msg.error(this, "starting bundle");
|
Msg.error(this, "starting bundle");
|
||||||
return null;
|
return null;
|
||||||
@@ -187,7 +190,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
final ResourceFile sourceDir;
|
final ResourceFile sourceDir;
|
||||||
final String symbolicName;
|
final String symbolicName;
|
||||||
final Path binDir;
|
final Path binDir;
|
||||||
final String bundleLoc;
|
String bundleLoc;
|
||||||
|
|
||||||
public ScriptBundleInfo(ResourceFile sourceDir) {
|
public ScriptBundleInfo(ResourceFile sourceDir) {
|
||||||
this.sourceDir = sourceDir;
|
this.sourceDir = sourceDir;
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ public class ResourceFileJavaFileObject implements JavaFileObject {
|
|||||||
pathName = file.getAbsolutePath().substring(sourceRootPath.length() + 1);
|
pathName = file.getAbsolutePath().substring(sourceRootPath.length() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResourceFile getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI toUri() {
|
public URI toUri() {
|
||||||
return file.toURI();
|
return file.toURI();
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ghidra.app.script.osgi;
|
package ghidra.app.script.osgi;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -35,6 +34,7 @@ import aQute.bnd.osgi.*;
|
|||||||
import aQute.bnd.osgi.Clazz.QUERY;
|
import aQute.bnd.osgi.Clazz.QUERY;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.script.*;
|
import ghidra.app.script.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
public class BundleCompiler {
|
public class BundleCompiler {
|
||||||
|
|
||||||
@@ -44,10 +44,11 @@ public class BundleCompiler {
|
|||||||
this.bh = bh;
|
this.bh = bh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||||
|
|
||||||
// compile a source directory to an exploded bundle
|
// compile a source directory to an exploded bundle
|
||||||
public void compileToExplodedBundle(ResourceFile srcdir, Path bindir, Writer output)
|
public void compileToExplodedBundle(ResourceFile srcdir, Path bindir, Writer writer)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
|
||||||
List<String> options = new ArrayList<>();
|
List<String> options = new ArrayList<>();
|
||||||
options.add("-g");
|
options.add("-g");
|
||||||
options.add("-d");
|
options.add("-d");
|
||||||
@@ -60,7 +61,6 @@ public class BundleCompiler {
|
|||||||
final JavaFileManager fm0 =
|
final JavaFileManager fm0 =
|
||||||
new ResourceFileJavaFileManager(GhidraScriptUtil.getScriptSourceDirectories());
|
new ResourceFileJavaFileManager(GhidraScriptUtil.getScriptSourceDirectories());
|
||||||
BundleJavaManager fm = new BundleJavaManager(bh.getHostFramework(), fm0, options);
|
BundleJavaManager fm = new BundleJavaManager(bh.getHostFramework(), fm0, options);
|
||||||
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
|
|
||||||
|
|
||||||
final List<ResourceFileJavaFileObject> compilationUnits = new ArrayList<>();
|
final List<ResourceFileJavaFileObject> compilationUnits = new ArrayList<>();
|
||||||
|
|
||||||
@@ -76,29 +76,54 @@ public class BundleCompiler {
|
|||||||
Files.delete(dmf);
|
Files.delete(dmf);
|
||||||
}
|
}
|
||||||
|
|
||||||
JavaCompiler.CompilationTask task =
|
// try to compile, if we fail remove offenders and try again
|
||||||
compiler.getTask(output, fm, diagnostics, options, null, compilationUnits);
|
while (!compilationUnits.isEmpty()) {
|
||||||
// task.setProcessors // for annotation processing / code generation
|
DiagnosticCollector<JavaFileObject> diagnostics =
|
||||||
|
new DiagnosticCollector<JavaFileObject>();
|
||||||
|
JavaCompiler.CompilationTask task =
|
||||||
|
compiler.getTask(writer, fm, diagnostics, options, null, compilationUnits);
|
||||||
|
// task.setProcessors // for annotation processing / code generation
|
||||||
|
|
||||||
Boolean successfulCompilation = task.call();
|
Boolean successfulCompilation = task.call();
|
||||||
output.flush();
|
if (successfulCompilation) {
|
||||||
System.err.printf("%s: %s\n", successfulCompilation ? "Success" : "Fail", output);
|
break;
|
||||||
|
|
||||||
if (successfulCompilation) {
|
|
||||||
ResourceFile smf =
|
|
||||||
new ResourceFile(srcdir, "META-INF" + File.separator + "MANIFEST.MF");
|
|
||||||
if (smf.exists()) {
|
|
||||||
System.err.printf("Found manifest, not generating one\n");
|
|
||||||
Files.createFile(dmf);
|
|
||||||
Files.copy(smf.getInputStream(), dmf, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
for (Diagnostic<? extends JavaFileObject> d : diagnostics.getDiagnostics()) {
|
||||||
|
writer.write(d.getSource().toString() + ": " + d.getMessage(null) + "\n");
|
||||||
|
ResourceFileJavaFileObject sf = (ResourceFileJavaFileObject) d.getSource();
|
||||||
|
if (compilationUnits.remove(sf)) {
|
||||||
|
writer.write(String.format("skipping %s\n", sf.toString()));
|
||||||
|
ResourceFile rf = sf.getFile();
|
||||||
|
if (GhidraScriptUtil.contains(rf)) {
|
||||||
|
ScriptInfo info = GhidraScriptUtil.getScriptInfo(rf);
|
||||||
|
info.setCompileErrors(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// make sure the succeses are marked
|
||||||
|
for (ResourceFileJavaFileObject sf : compilationUnits) {
|
||||||
|
ResourceFile rf = sf.getFile();
|
||||||
|
if (GhidraScriptUtil.contains(rf)) {
|
||||||
|
ScriptInfo info = GhidraScriptUtil.getScriptInfo(rf);
|
||||||
|
info.setCompileErrors(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// no manifest, so create one with bndtools
|
ResourceFile smf = new ResourceFile(srcdir, "META-INF" + File.separator + "MANIFEST.MF");
|
||||||
Analyzer analyzer = new Analyzer();
|
if (smf.exists()) {
|
||||||
analyzer.setJar(new Jar(bindir.toFile())); // give bnd the contents
|
System.err.printf("Found manifest, not generating one\n");
|
||||||
Stream<Object> bjars = Files.list(bh.getCompiledBundlesDir()).filter(
|
Files.createFile(dmf);
|
||||||
f -> f.toString().endsWith(".jar")).map(f -> {
|
Files.copy(smf.getInputStream(), dmf, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no manifest, so create one with bndtools
|
||||||
|
Analyzer analyzer = new Analyzer();
|
||||||
|
analyzer.setJar(new Jar(bindir.toFile())); // give bnd the contents
|
||||||
|
Stream<Object> bjars =
|
||||||
|
Files.list(bh.getCompiledBundlesDir()).filter(f -> f.toString().endsWith(".jar")).map(
|
||||||
|
f -> {
|
||||||
try {
|
try {
|
||||||
return new Jar(f.toFile());
|
return new Jar(f.toFile());
|
||||||
}
|
}
|
||||||
@@ -108,91 +133,99 @@ public class BundleCompiler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
analyzer.addClasspath(bjars.collect(Collectors.toUnmodifiableList()));
|
analyzer.addClasspath(bjars.collect(Collectors.toUnmodifiableList()));
|
||||||
analyzer.setProperty("Bundle-SymbolicName",
|
analyzer.setProperty("Bundle-SymbolicName",
|
||||||
JavaScriptProvider.getSymbolicNameFromSourceDir(srcdir));
|
JavaScriptProvider.getSymbolicNameFromSourceDir(srcdir));
|
||||||
analyzer.setProperty("Bundle-Version", "1.0");
|
analyzer.setProperty("Bundle-Version", "1.0");
|
||||||
analyzer.setProperty("Import-Package", "*");
|
analyzer.setProperty("Import-Package", "*");
|
||||||
// analyzer.setBundleActivator(s);
|
// analyzer.setBundleActivator(s);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Manifest manifest = analyzer.calcManifest();
|
Manifest manifest = analyzer.calcManifest();
|
||||||
Attributes ma = manifest.getMainAttributes();
|
Attributes ma = manifest.getMainAttributes();
|
||||||
|
|
||||||
String activator_classname = null;
|
String activator_classname = null;
|
||||||
for (Clazz clazz : analyzer.getClassspace().values()) {
|
for (Clazz clazz : analyzer.getClassspace().values()) {
|
||||||
if (clazz.is(QUERY.IMPLEMENTS,
|
if (clazz.is(QUERY.IMPLEMENTS,
|
||||||
new Instruction("org.osgi.framework.BundleActivator"), analyzer)) {
|
new Instruction("org.osgi.framework.BundleActivator"), analyzer)) {
|
||||||
System.err.printf("found BundleActivator class %s\n", clazz);
|
System.err.printf("found BundleActivator class %s\n", clazz);
|
||||||
activator_classname = clazz.toString();
|
activator_classname = clazz.toString();
|
||||||
}
|
|
||||||
}
|
|
||||||
if (activator_classname == null) {
|
|
||||||
Path activator_dest = bindir.resolve("GeneratedActivator.java");
|
|
||||||
try (PrintWriter writer = new PrintWriter(
|
|
||||||
Files.newBufferedWriter(activator_dest, Charset.forName("UTF-8")))) {
|
|
||||||
writer.println("import ghidra.app.script.osgi.GhidraBundleActivator;");
|
|
||||||
writer.println("import org.osgi.framework.BundleActivator;");
|
|
||||||
writer.println("import org.osgi.framework.BundleContext;");
|
|
||||||
writer.println(
|
|
||||||
"public class GeneratedActivator extends GhidraBundleActivator {");
|
|
||||||
writer.println(" protected void start(BundleContext bc, Object api) {");
|
|
||||||
writer.println(" // TODO: stuff to do on bundle start");
|
|
||||||
writer.println(" }");
|
|
||||||
writer.println(" protected void stop(BundleContext bc, Object api) {");
|
|
||||||
writer.println(" // TODO: stuff to do on bundle stop");
|
|
||||||
writer.println(" }");
|
|
||||||
writer.println();
|
|
||||||
writer.println("}");
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
activator_classname = "GeneratedActivator";
|
|
||||||
|
|
||||||
options.clear();
|
|
||||||
options.add("-g");
|
|
||||||
options.add("-d");
|
|
||||||
options.add(bindir.toString());
|
|
||||||
options.add("-sourcepath");
|
|
||||||
options.add(bindir.toString());
|
|
||||||
options.add("-classpath");
|
|
||||||
options.add(System.getProperty("java.class.path"));
|
|
||||||
options.add("-proc:none");
|
|
||||||
|
|
||||||
StandardJavaFileManager fm02 =
|
|
||||||
compiler.getStandardFileManager(null, null, null);
|
|
||||||
fm = new BundleJavaManager(bh.getHostFramework(), fm02, options);
|
|
||||||
Iterable<? extends JavaFileObject> compilationUnits2 =
|
|
||||||
fm02.getJavaFileObjectsFromPaths(List.of(activator_dest));
|
|
||||||
|
|
||||||
JavaCompiler.CompilationTask task2 =
|
|
||||||
compiler.getTask(output, fm, diagnostics, options, null, compilationUnits2);
|
|
||||||
if (!task2.call()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// since we add the activator after bndtools built the imports, we should add its imports too
|
|
||||||
String imps = ma.getValue(Constants.IMPORT_PACKAGE);
|
|
||||||
ma.putValue(Constants.IMPORT_PACKAGE, imps + ",ghidra.app.script.osgi");
|
|
||||||
}
|
|
||||||
ma.putValue(Constants.BUNDLE_ACTIVATOR, activator_classname);
|
|
||||||
|
|
||||||
Files.createDirectories(dmf.getParent());
|
|
||||||
try (OutputStream out = Files.newOutputStream(dmf)) {
|
|
||||||
manifest.write(out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
if (activator_classname == null) {
|
||||||
e.printStackTrace();
|
activator_classname = "GeneratedActivator";
|
||||||
|
if (!createActivator(bindir, activator_classname, writer)) {
|
||||||
|
Msg.error(this, "failed to create activator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// since we add the activator after bndtools built the imports, we should add its imports too
|
||||||
|
String imps = ma.getValue(Constants.IMPORT_PACKAGE);
|
||||||
|
ma.putValue(Constants.IMPORT_PACKAGE, imps + ",ghidra.app.script.osgi");
|
||||||
|
}
|
||||||
|
ma.putValue(Constants.BUNDLE_ACTIVATOR, activator_classname);
|
||||||
|
|
||||||
|
// write the manifest
|
||||||
|
Files.createDirectories(dmf.getParent());
|
||||||
|
try (OutputStream out = Files.newOutputStream(dmf)) {
|
||||||
|
manifest.write(out);
|
||||||
}
|
}
|
||||||
analyzer.close();
|
|
||||||
}
|
}
|
||||||
else {
|
catch (Exception e) {
|
||||||
for (Diagnostic<? extends JavaFileObject> dm : diagnostics.getDiagnostics()) {
|
e.printStackTrace();
|
||||||
System.err.printf("COMPILE ERROR: %s\n", dm);
|
}
|
||||||
}
|
finally {
|
||||||
|
analyzer.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean createActivator(Path bindir, String activator_classname, Writer output)
|
||||||
|
throws IOException {
|
||||||
|
Path activator_dest = bindir.resolve(activator_classname + ".java");
|
||||||
|
|
||||||
|
try (PrintWriter writer =
|
||||||
|
new PrintWriter(Files.newBufferedWriter(activator_dest, Charset.forName("UTF-8")))) {
|
||||||
|
writer.println("import ghidra.app.script.osgi.GhidraBundleActivator;");
|
||||||
|
writer.println("import org.osgi.framework.BundleActivator;");
|
||||||
|
writer.println("import org.osgi.framework.BundleContext;");
|
||||||
|
writer.println("public class GeneratedActivator extends GhidraBundleActivator {");
|
||||||
|
writer.println(" protected void start(BundleContext bc, Object api) {");
|
||||||
|
writer.println(" // TODO: stuff to do on bundle start");
|
||||||
|
writer.println(" }");
|
||||||
|
writer.println(" protected void stop(BundleContext bc, Object api) {");
|
||||||
|
writer.println(" // TODO: stuff to do on bundle stop");
|
||||||
|
writer.println(" }");
|
||||||
|
writer.println();
|
||||||
|
writer.println("}");
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> options = new ArrayList<>();
|
||||||
|
options.add("-g");
|
||||||
|
options.add("-d");
|
||||||
|
options.add(bindir.toString());
|
||||||
|
options.add("-sourcepath");
|
||||||
|
options.add(bindir.toString());
|
||||||
|
options.add("-classpath");
|
||||||
|
options.add(System.getProperty("java.class.path"));
|
||||||
|
options.add("-proc:none");
|
||||||
|
|
||||||
|
StandardJavaFileManager fm0 = compiler.getStandardFileManager(null, null, null);
|
||||||
|
BundleJavaManager fm = new BundleJavaManager(bh.getHostFramework(), fm0, options);
|
||||||
|
Iterable<? extends JavaFileObject> compilationUnits2 =
|
||||||
|
fm0.getJavaFileObjectsFromPaths(List.of(activator_dest));
|
||||||
|
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
|
||||||
|
JavaCompiler.CompilationTask task2 =
|
||||||
|
compiler.getTask(output, fm, diagnostics, options, null, compilationUnits2);
|
||||||
|
if (!task2.call()) {
|
||||||
|
for (Diagnostic<? extends JavaFileObject> d : diagnostics.getDiagnostics()) {
|
||||||
|
output.write(d.getSource().toString() + ": " + d.getMessage(null) + "\n");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ghidra.app.script.osgi;
|
package ghidra.app.script.osgi;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.*;
|
import static java.util.stream.Collectors.*;
|
||||||
@@ -28,7 +27,6 @@ import java.util.regex.Pattern;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.felix.fileinstall.internal.FileInstall;
|
|
||||||
import org.apache.felix.framework.FrameworkFactory;
|
import org.apache.felix.framework.FrameworkFactory;
|
||||||
import org.apache.felix.framework.util.FelixConstants;
|
import org.apache.felix.framework.util.FelixConstants;
|
||||||
import org.apache.felix.main.AutoProcessor;
|
import org.apache.felix.main.AutoProcessor;
|
||||||
@@ -68,6 +66,11 @@ public class BundleHost {
|
|||||||
return bc.installBundle(location, new FileInputStream(new File(path_to_jar)));
|
return bc.installBundle(location, new FileInputStream(new File(path_to_jar)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bundle installExplodedPath(Path p) throws BundleException {
|
||||||
|
return bc.installBundle("reference:file://" + p.toAbsolutePath().normalize().toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void dumpLoadedBundles() {
|
void dumpLoadedBundles() {
|
||||||
System.err.printf("=== Bundles ===\n");
|
System.err.printf("=== Bundles ===\n");
|
||||||
for (Bundle bundle : bc.getBundles()) {
|
for (Bundle bundle : bc.getBundles()) {
|
||||||
@@ -196,8 +199,9 @@ public class BundleHost {
|
|||||||
});
|
});
|
||||||
|
|
||||||
felix.start();
|
felix.start();
|
||||||
fileinstall_bundle = installFromPath(findJarForClass(FileInstall.class));
|
// fileinstall_bundle = installFromPath(findJarForClass(FileInstall.class));
|
||||||
fileinstall_bundle.start();
|
// fileinstall_bundle.start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path getOsgiDir() {
|
private Path getOsgiDir() {
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ghidra.app.script.osgi;
|
package ghidra.app.script.osgi;
|
||||||
|
|
||||||
import org.osgi.framework.BundleActivator;
|
import org.osgi.framework.BundleActivator;
|
||||||
|
|||||||
Reference in New Issue
Block a user