GT-3376: Design improvements

This commit is contained in:
Ryan Kurtz 2019-12-26 14:06:48 -05:00
parent a46e788706
commit 73b018fd84
13 changed files with 149 additions and 170 deletions

View file

@ -17,7 +17,8 @@ package ghidra.app.util.importer;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import generic.stl.Pair;
@ -202,8 +203,7 @@ public final class AutoImporter {
private static LoadSpec getLoadSpec(Predicate<Loader> loaderFilter,
LoadSpecChooser loadSpecChooser, ByteProvider provider) {
Map<Loader, Collection<LoadSpec>> loadMap =
LoaderService.getSupportedLoadSpecs(provider, loaderFilter);
LoadMap loadMap = LoaderService.getSupportedLoadSpecs(provider, loaderFilter);
LoadSpec loadSpec = loadSpecChooser.choose(loadMap);
if (loadSpec != null) {

View file

@ -0,0 +1,57 @@
/* ###
* 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.app.util.importer;
import ghidra.app.util.opinion.*;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.lang.CompilerSpecID;
/**
* Chooses a {@link LoadSpec} for a {@link Loader} to use based on a provided {@link CompilerSpec}.
*/
public class CsHintLoadSpecChooser implements LoadSpecChooser {
private final CompilerSpecID compilerSpecID;
/**
* Creates a new {@link CsHintLoadSpecChooser}
*
* @param compilerSpecID The {@link CompilerSpecID} to use (should not be null)
*/
public CsHintLoadSpecChooser(CompilerSpecID compilerSpecID) {
this.compilerSpecID = compilerSpecID;
}
/**
* Creates a new {@link CsHintLoadSpecChooser}
*
* @param compilerSpecID The {@link CompilerSpecID} to use (should not be null)
*/
public CsHintLoadSpecChooser(String compilerSpecID) {
this(new CompilerSpecID(compilerSpecID));
}
@Override
public LoadSpec choose(LoadMap loadMap) {
return loadMap.values()
.stream()
.flatMap(loadSpec -> loadSpec.stream())
.filter(loadSpec -> loadSpec.getLanguageCompilerSpec().compilerSpecID
.equals(compilerSpecID))
.findFirst()
.orElse(null);
}
}

View file

@ -16,7 +16,6 @@
package ghidra.app.util.importer;
import java.util.Collection;
import java.util.Map;
import ghidra.app.util.opinion.*;
import ghidra.program.model.lang.*;
@ -46,22 +45,11 @@ public class LcsHintLoadSpecChooser implements LoadSpecChooser {
}
@Override
public LoadSpec choose(Map<Loader, Collection<LoadSpec>> loadMap) {
public LoadSpec choose(LoadMap loadMap) {
// We need to reduce the set of matching loaders down to one. The only requirement is that
// if we do reduce, make sure we don't reduce it to the BinaryLoader.
Loader loader;
if (loadMap.size() > 1) {
loader = loadMap.keySet()
.stream()
.filter(e -> e.getTier() != LoaderTier.UNTARGETED_LOADER)
.findFirst()
.orElse(null);
}
else if (loadMap.size() == 1) {
loader = loadMap.keySet().iterator().next();
}
else {
// Use the highest priority loader (it will be the first one)
Loader loader = loadMap.keySet().stream().findFirst().orElse(null);
if (loader == null) {
return null;
}

View file

@ -15,11 +15,7 @@
*/
package ghidra.app.util.importer;
import java.util.Collection;
import java.util.Map;
import ghidra.app.util.opinion.LoadSpec;
import ghidra.app.util.opinion.Loader;
import ghidra.app.util.opinion.*;
/**
* Chooses a {@link LoadSpec} for a {@link Loader} to use based on some criteria
@ -30,10 +26,10 @@ public interface LoadSpecChooser {
/**
* Chooses a {@link LoadSpec} for a {@link Loader} to use based on some criteria
*
* @param loadMap A {@link Map} of {@link Loader}s to their respective {@link LoadSpec}s
* @return The chosen {@link LoadSpec}
* @param loadMap A {@link LoadMap}
* @return The chosen {@link LoadSpec}, or null if one could not be found
*/
public LoadSpec choose(Map<Loader, Collection<LoadSpec>> loadMap);
public LoadSpec choose(LoadMap loadMap);
/**
* Chooses the first "preferred" {@link LoadSpec}
@ -43,8 +39,8 @@ public interface LoadSpecChooser {
public static final LoadSpecChooser CHOOSE_THE_FIRST_PREFERRED = loadMap -> {
return loadMap.values()
.stream()
.flatMap(e -> e.stream())
.filter(e -> e != null && e.isPreferred())
.flatMap(loadSpecs -> loadSpecs.stream())
.filter(loadSpec -> loadSpec != null && loadSpec.isPreferred())
.findFirst()
.orElse(null);
};

View file

@ -0,0 +1,44 @@
/* ###
* 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.app.util.opinion;
import java.util.*;
/**
* A {@link Map} of {@link Loader}s to their respective {@link LoadSpec}s.
* <p>
* The {@link Loader} keys are sorted according to their {@link Loader#compareTo(Loader) natural
* ordering}.
*/
public class LoadMap extends TreeMap<Loader, Collection<LoadSpec>> {
/**
* Creates a new, empty {@link LoadMap}
*/
public LoadMap() {
super((loader1, loader2) -> loader1.compareTo(loader2));
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Loader loader : keySet()) {
Collection<LoadSpec> loadSpecs = get(loader);
sb.append(loader.getName() + " - " + loadSpecs.size() + " load specs\n");
}
return sb.toString();
}
}

View file

@ -36,13 +36,11 @@ public class LoaderService {
*
* @param provider The {@link ByteProvider} to load.
* @param loaderFilter A {@link Predicate} that will filter out undesired {@link Loader}s.
* @return A {@link Map} of {@link Loader}s to their respective {@link LoadSpec}s. It is safe
* to assume that every {@link Loader} in the {@link Map} will have at least one
* {@link LoadSpec}.
* @return All supported {@link LoadSpec}s in the form of a {@link LoadMap}.
*/
public static Map<Loader, Collection<LoadSpec>> getSupportedLoadSpecs(ByteProvider provider,
public static LoadMap getSupportedLoadSpecs(ByteProvider provider,
Predicate<Loader> loaderFilter) {
Map<Loader, Collection<LoadSpec>> loadMap = new LinkedHashMap<>(); // maintain loader order
LoadMap loadMap = new LoadMap();
for (Loader loader : getAllLoaders()) {
if (loaderFilter.test(loader)) {
try {
@ -67,27 +65,25 @@ public class LoaderService {
* Gets all supported {@link LoadSpec}s for loading the given {@link ByteProvider}.
*
* @param provider The {@link ByteProvider} to load.
* @return A {@link Map} of {@link Loader}s to their respective {@link LoadSpec}s. It is safe
* to assume that every {@link Loader} in the {@link Map} will have at least one
* {@link LoadSpec}.
* @return All supported {@link LoadSpec}s in the form of a {@link LoadMap}.
*/
public static Map<Loader, Collection<LoadSpec>> getAllSupportedLoadSpecs(
ByteProvider provider) {
public static LoadMap getAllSupportedLoadSpecs(ByteProvider provider) {
return getSupportedLoadSpecs(provider, ACCEPT_ALL);
}
/**
* Gets all known {@link Loader}s' names.
*
* @return All known {@link Loader}s' names.
* @return All known {@link Loader}s' names. The {@link Loader} names are sorted
* according to their corresponding {@link Loader}s {@link Loader#compareTo(Loader) natural
* ordering}.
*/
public static Collection<String> getAllLoaderNames() {
//@formatter:off
return getAllLoaders()
.stream()
.sorted()
.map(loader -> loader.getName())
.collect(Collectors.toList());
//@formatter:on
}
/**
@ -99,20 +95,19 @@ public class LoaderService {
* name.
*/
public static Class<? extends Loader> getLoaderClassByName(String name) {
//@formatter:off
return getAllLoaders()
.stream()
.filter(loader -> loader.getClass().getSimpleName().equals(name))
.findFirst()
.map(loader -> loader.getClass())
.orElse(null);
//@formatter:on
}
/**
* Gets an instance of every known {@link Loader}.
*
* @return An instance of every known {@link Loader}.
* @return An instance of every known {@link Loader}. The {@link Loader} instances are sorted
* according to their {@link Loader#compareTo(Loader) natural ordering}.
*/
private synchronized static Collection<Loader> getAllLoaders() {
List<Loader> loaders = new ArrayList<>(ClassSearcher.getInstances(Loader.class));

View file

@ -15,12 +15,11 @@
*/
package ghidra.plugin.importer;
import java.util.*;
import java.util.List;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.opinion.LoadSpec;
import ghidra.app.util.opinion.Loader;
import ghidra.app.util.opinion.*;
import ghidra.formats.gfilesystem.FSRL;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
@ -47,9 +46,8 @@ public class AddToProgramDialog extends ImporterDialog {
* @param byteProvider the ByteProvider from which the bytes from the source can be read.
* @param addToProgram the program to which the newly imported data will be added
*/
protected AddToProgramDialog(PluginTool tool, FSRL fsrl,
Map<Loader, Collection<LoadSpec>> loadMap, ByteProvider byteProvider,
Program addToProgram) {
protected AddToProgramDialog(PluginTool tool, FSRL fsrl, LoadMap loadMap,
ByteProvider byteProvider, Program addToProgram) {
super("Add To Program: " + fsrl.getPath(), tool, loadMap, byteProvider, null);
this.addToProgram = addToProgram;
folderNameTextField.setText(getFolderName(addToProgram));

View file

@ -68,7 +68,7 @@ public class ImporterDialog extends DialogComponentProvider {
private ProgramManager programManager;
protected FSRL fsrl;
protected List<Option> options;
private Map<Loader, Collection<LoadSpec>> loadMap;
private LoadMap loadMap;
protected LanguageCompilerSpecPair selectedLanguage;
private DomainFolder destinationFolder;
private boolean languageNeeded;
@ -96,17 +96,15 @@ public class ImporterDialog extends DialogComponentProvider {
* option which requires the DomainFolder to already exist). The two destination paths work together
* to specify the final Ghidra project folder where the imported binary is placed.
*/
public ImporterDialog(PluginTool tool, ProgramManager programManager,
Map<Loader, Collection<LoadSpec>> loadMap, ByteProvider byteProvider,
String suggestedDestinationPath) {
public ImporterDialog(PluginTool tool, ProgramManager programManager, LoadMap loadMap,
ByteProvider byteProvider, String suggestedDestinationPath) {
this("Import " + byteProvider.getFSRL().getPath(), tool, loadMap, byteProvider,
suggestedDestinationPath);
this.programManager = programManager;
}
protected ImporterDialog(String title, PluginTool tool,
Map<Loader, Collection<LoadSpec>> loadMap, ByteProvider byteProvider,
String suggestedDestinationPath) {
protected ImporterDialog(String title, PluginTool tool, LoadMap loadMap,
ByteProvider byteProvider, String suggestedDestinationPath) {
super(title);
this.tool = tool;
this.programManager = tool.getService(ProgramManager.class);

View file

@ -271,8 +271,8 @@ public class ImporterUtilities {
return;
}
Map<Loader, Collection<LoadSpec>> loadMap = LoaderService.getSupportedLoadSpecs(
provider, loader -> loader.supportsLoadIntoProgram());
LoadMap loadMap = LoaderService.getSupportedLoadSpecs(provider,
loader -> loader.supportsLoadIntoProgram());
SystemUtilities.runSwingLater(() -> {
AddToProgramDialog dialog =
@ -309,8 +309,7 @@ public class ImporterUtilities {
try {
ByteProvider provider = FileSystemService.getInstance().getByteProvider(fsrl, monitor);
Map<Loader, Collection<LoadSpec>> loadMap =
LoaderService.getAllSupportedLoadSpecs(provider);
LoadMap loadMap = LoaderService.getAllSupportedLoadSpecs(provider);
SystemUtilities.runSwingLater(() -> {
ImporterDialog importerDialog =

View file

@ -357,8 +357,7 @@ public class BatchInfo {
try (ByteProvider provider =
FileSystemService.getInstance().getByteProvider(fsrl, monitor)) {
Map<Loader, Collection<LoadSpec>> loadMap =
pollLoadersForLoadSpecs(provider, fsrl, monitor);
LoadMap loadMap = pollLoadersForLoadSpecs(provider, fsrl, monitor);
for (Loader loader : loadMap.keySet()) {
Collection<LoadSpec> loadSpecs = loadMap.get(loader);
BatchSegregatingCriteria bsc =
@ -380,8 +379,7 @@ public class BatchInfo {
}
}
private Map<Loader, Collection<LoadSpec>> pollLoadersForLoadSpecs(ByteProvider provider,
FSRL fsrl, TaskMonitor monitor) {
private LoadMap pollLoadersForLoadSpecs(ByteProvider provider, FSRL fsrl, TaskMonitor monitor) {
monitor.setMessage(fsrl.getName());
return LoaderService.getSupportedLoadSpecs(provider,
loader -> !(loader instanceof BinaryLoader));

View file

@ -19,7 +19,6 @@ import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.*;
@ -27,53 +26,20 @@ import ghidra.app.util.bin.format.coff.*;
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
import ghidra.app.util.importer.*;
import ghidra.app.util.opinion.*;
import ghidra.app.util.opinion.Loader;
import ghidra.app.util.opinion.MSCoffLoader;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.store.local.LocalFileSystem;
import ghidra.program.model.lang.*;
import ghidra.program.model.lang.LanguageDescription;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.*;
import ghidra.util.task.CancelOnlyWrappingTaskMonitor;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
public class ImportMSLibs extends GhidraScript {
final static Predicate<Loader> LOADER_FILTER = new SingleLoaderFilter(MSCoffLoader.class);
final static LoadSpecChooser LOADSPEC_CHOOSER = loadMap -> {
Stream<LoadSpec> loadSpecStream = loadMap.values().stream().flatMap(e -> e.stream());
Iterable<LoadSpec> loadSpecs = CollectionUtils.asIterable(loadSpecStream.iterator());
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
if (lcsp.compilerSpecID.getIdAsString().equals("windows")) {
return loadSpec;
}
}
// XXX short circuit; for now only process valid windows opinions, which means x86
if (Math.min(1, 2) == 1) {
return null;
}
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
try {
if (lcsp.getLanguageDescription().getProcessor() == Processor.toProcessor(
"ARM") && lcsp.getLanguageDescription().getEndian() == Endian.LITTLE &&
lcsp.getLanguageDescription().getVariant().contains("v7")) {
return loadSpec;
}
}
catch (LanguageNotFoundException e) {
// ignore...not sure why this happened
}
}
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
if (lcsp.compilerSpecID.getIdAsString().equals("gcc")) {
return loadSpec;
}
}
return null;
};
final static LoadSpecChooser LOADSPEC_CHOOSER = new CsHintLoadSpecChooser("windows");
@Override
protected void run() throws Exception {

View file

@ -54,7 +54,6 @@ import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
@ -65,49 +64,20 @@ import ghidra.app.util.bin.format.coff.*;
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
import ghidra.app.util.importer.*;
import ghidra.app.util.opinion.*;
import ghidra.app.util.opinion.Loader;
import ghidra.app.util.opinion.MSCoffLoader;
import ghidra.framework.model.DomainFile;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.store.local.LocalFileSystem;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
import utilities.util.FileUtilities;
public class MSLibBatchImportWorker extends GhidraScript {
final static Predicate<Loader> LOADER_FILTER = new SingleLoaderFilter(MSCoffLoader.class);
final static LoadSpecChooser LOADSPEC_CHOOSER = loadMap -> {
Stream<LoadSpec> loadSpecStream = loadMap.values().stream().flatMap(e -> e.stream());
Iterable<LoadSpec> loadSpecs = CollectionUtils.asIterable(loadSpecStream.iterator());
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
if (lcsp.compilerSpecID.getIdAsString().equals("windows")) {
return loadSpec;
}
}
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
try {
if (lcsp.getLanguageDescription().getEndian() == Endian.LITTLE &&
lcsp.getLanguageDescription().getVariant().contains("v7")) {
return loadSpec;
}
}
catch (LanguageNotFoundException e) {
// ignore...not sure why this happened
}
}
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
if (lcsp.compilerSpecID.getIdAsString().equals("gcc")) {
return loadSpec;
}
}
return null;
};
final static LoadSpecChooser LOADSPEC_CHOOSER = new CsHintLoadSpecChooser("windows");
private static String getProcessId(String fallback) {
// something like '<pid>@<hostname>', at least in SUN / Oracle JVMs

View file

@ -17,7 +17,6 @@ import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
import generic.stl.Pair;
import ghidra.app.script.GhidraScript;
@ -26,47 +25,18 @@ import ghidra.app.util.bin.format.coff.*;
import ghidra.app.util.bin.format.coff.archive.CoffArchiveHeader;
import ghidra.app.util.bin.format.coff.archive.CoffArchiveMemberHeader;
import ghidra.app.util.importer.*;
import ghidra.app.util.opinion.*;
import ghidra.app.util.opinion.Loader;
import ghidra.app.util.opinion.MSCoffLoader;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.store.local.LocalFileSystem;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import util.CollectionUtils;
public class RecursiveRecursiveMSLibImport extends GhidraScript {
final static Predicate<Loader> LOADER_FILTER = new SingleLoaderFilter(MSCoffLoader.class);
final static LoadSpecChooser LOADSPEC_CHOOSER = loadMap -> {
Stream<LoadSpec> loadSpecStream = loadMap.values().stream().flatMap(e -> e.stream());
Iterable<LoadSpec> loadSpecs = CollectionUtils.asIterable(loadSpecStream.iterator());
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
if (lcsp.compilerSpecID.getIdAsString().equals("windows")) {
return loadSpec;
}
}
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
try {
if (lcsp.getLanguageDescription().getEndian() == Endian.LITTLE &&
lcsp.getLanguageDescription().getVariant().contains("v7")) {
return loadSpec;
}
}
catch (LanguageNotFoundException e) {
// ignore...not sure why this happened
}
}
for (LoadSpec loadSpec : loadSpecs) {
LanguageCompilerSpecPair lcsp = loadSpec.getLanguageCompilerSpec();
if (lcsp.compilerSpecID.getIdAsString().equals("gcc")) {
return loadSpec;
}
}
return null;
};
final static LoadSpecChooser LOADSPEC_CHOOSER = new CsHintLoadSpecChooser("windows");
@Override
protected void run() throws Exception {