GP-6773: Supporting "fallback" loaders

This commit is contained in:
Ryan Kurtz
2026-06-02 12:36:34 -04:00
parent e59ffe5e5d
commit 9b4488cc06
3 changed files with 56 additions and 17 deletions
@@ -215,6 +215,16 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
*/
public int getTierPriority();
/**
* {@return whether or not this is a "fallback" {@link Loader}}
* <p>
* Fallback loaders are only considered during the import process if no other non-fallback
* loaders were compatible with the thing being imported.
*/
public default boolean isFallback() {
return false;
}
/**
* The preferred file name to use when loading.
* <p>
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import ghidra.app.util.bin.ByteProvider;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import util.CollectionUtils;
/**
* Factory and utility methods for working with {@link Loader}s.
@@ -41,26 +42,50 @@ public class LoaderService {
public static LoaderMap getSupportedLoadSpecs(ByteProvider provider,
Predicate<Loader> loaderFilter) {
LoaderMap loaderMap = new LoaderMap();
List<Loader> fallback = new ArrayList<>();
for (Loader loader : getAllLoaders()) {
if (loaderFilter.test(loader)) {
try {
Collection<LoadSpec> loadSpecs = loader.findSupportedLoadSpecs(provider);
if (loadSpecs != null && !loadSpecs.isEmpty()) { // shouldn't be null, but protect against rogue loaders
loaderMap.put(loader, loadSpecs);
}
if (!loader.isFallback()) {
tryLoadSpecs(loader, provider, loaderMap);
}
catch (IOException e) {
// file not applicable for loader
}
catch (RuntimeException e) {
Msg.error(LoaderService.class,
"Unexpected Loader exception from " + loader.getName(), e);
else {
fallback.add(loader);
}
}
}
if (loaderMap.size() <= 1) { // BinaryLoader is always there
for (Loader loader : fallback) {
tryLoadSpecs(loader, provider, loaderMap);
}
}
return loaderMap;
}
/**
* Attempts to find and register supported {@link LoadSpec}s for the given {@link Loader}
*
* @param loader The {@link Loader} to query
* @param provider The {@link ByteProvider} to load from
* @param loaderMap The {@link LoaderMap} to populate with discovered {@link LoadSpec}s
*/
private static void tryLoadSpecs(Loader loader, ByteProvider provider, LoaderMap loaderMap) {
try {
Collection<LoadSpec> loadSpecs = loader.findSupportedLoadSpecs(provider);
if (!CollectionUtils.isBlank(loadSpecs)) {
loaderMap.put(loader, loadSpecs);
}
}
catch (IOException e) {
// file not applicable for loader
}
catch (RuntimeException e) {
Msg.error(LoaderService.class, "Unexpected Loader exception from " + loader.getName(),
e);
}
}
/**
* Gets all supported {@link LoadSpec}s for loading the given {@link ByteProvider}.
*
@@ -4,9 +4,9 @@
* 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.
@@ -15,9 +15,13 @@
*/
package ghidra.app.util.opinion;
/**
* When sorting, {@link Loader}s are first {@link Loader#compareTo(Loader) sorted} by tier, and then
* by {@link Loader#getTierPriority()} to break ties
*/
public enum LoaderTier {
SPECIALIZED_TARGET_LOADER,
GENERIC_TARGET_LOADER,
AMBIGUOUS_TARGET_LOADER,
UNTARGETED_LOADER
SPECIALIZED_TARGET_LOADER,
GENERIC_TARGET_LOADER,
AMBIGUOUS_TARGET_LOADER,
UNTARGETED_LOADER
}