GP-6773: Supporting "fallback" loaders

This commit is contained in:
Ryan Kurtz
2026-06-02 12:28:06 -04:00
parent e59ffe5e5d
commit 9b4488cc06
3 changed files with 56 additions and 17 deletions

View File

@@ -215,6 +215,16 @@ public interface Loader extends ExtensionPoint, Comparable<Loader> {
*/ */
public int getTierPriority(); 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. * The preferred file name to use when loading.
* <p> * <p>

View File

@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import util.CollectionUtils;
/** /**
* Factory and utility methods for working with {@link Loader}s. * Factory and utility methods for working with {@link Loader}s.
@@ -41,26 +42,50 @@ public class LoaderService {
public static LoaderMap getSupportedLoadSpecs(ByteProvider provider, public static LoaderMap getSupportedLoadSpecs(ByteProvider provider,
Predicate<Loader> loaderFilter) { Predicate<Loader> loaderFilter) {
LoaderMap loaderMap = new LoaderMap(); LoaderMap loaderMap = new LoaderMap();
List<Loader> fallback = new ArrayList<>();
for (Loader loader : getAllLoaders()) { for (Loader loader : getAllLoaders()) {
if (loaderFilter.test(loader)) { if (loaderFilter.test(loader)) {
try { if (!loader.isFallback()) {
Collection<LoadSpec> loadSpecs = loader.findSupportedLoadSpecs(provider); tryLoadSpecs(loader, provider, loaderMap);
if (loadSpecs != null && !loadSpecs.isEmpty()) { // shouldn't be null, but protect against rogue loaders
loaderMap.put(loader, loadSpecs);
}
} }
catch (IOException e) { else {
// file not applicable for loader fallback.add(loader);
}
catch (RuntimeException e) {
Msg.error(LoaderService.class,
"Unexpected Loader exception from " + loader.getName(), e);
} }
} }
} }
if (loaderMap.size() <= 1) { // BinaryLoader is always there
for (Loader loader : fallback) {
tryLoadSpecs(loader, provider, loaderMap);
}
}
return 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}. * Gets all supported {@link LoadSpec}s for loading the given {@link ByteProvider}.
* *

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,9 +15,13 @@
*/ */
package ghidra.app.util.opinion; 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 { public enum LoaderTier {
SPECIALIZED_TARGET_LOADER, SPECIALIZED_TARGET_LOADER,
GENERIC_TARGET_LOADER, GENERIC_TARGET_LOADER,
AMBIGUOUS_TARGET_LOADER, AMBIGUOUS_TARGET_LOADER,
UNTARGETED_LOADER UNTARGETED_LOADER
} }