mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-5478: Cleaning up library loading code
This commit is contained in:
parent
636f28dbdc
commit
3f9f79b49f
10 changed files with 205 additions and 280 deletions
|
@ -20,7 +20,6 @@ import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
|
||||||
import ghidra.app.util.opinion.Loader;
|
import ghidra.app.util.opinion.Loader;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.framework.Platform;
|
import ghidra.framework.Platform;
|
||||||
|
@ -53,15 +52,14 @@ public class LibrarySearchPathManager {
|
||||||
/**
|
/**
|
||||||
* Returns a {@link List} of {@link FSRL}s to search for libraries
|
* Returns a {@link List} of {@link FSRL}s to search for libraries
|
||||||
*
|
*
|
||||||
* @param provider The {@link ByteProvider} of the program being loaded
|
|
||||||
* @param program The {@link Program} being loaded
|
* @param program The {@link Program} being loaded
|
||||||
* @param log The log
|
* @param log The log
|
||||||
* @param monitor A cancellable monitor
|
* @param monitor A cancellable monitor
|
||||||
* @return a {@link List} of {@link FSRL}s to search for libraries
|
* @return a {@link List} of {@link FSRL}s to search for libraries
|
||||||
* @throws CancelledException if the user cancelled the operation
|
* @throws CancelledException if the user cancelled the operation
|
||||||
*/
|
*/
|
||||||
public static synchronized List<FSRL> getLibraryFsrlList(ByteProvider provider, Program program,
|
public static synchronized List<FSRL> getLibraryFsrlList(Program program, MessageLog log,
|
||||||
MessageLog log, TaskMonitor monitor) throws CancelledException {
|
TaskMonitor monitor) throws CancelledException {
|
||||||
FileSystemService fsService = FileSystemService.getInstance();
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
List<FSRL> fsrlList = new ArrayList<>();
|
List<FSRL> fsrlList = new ArrayList<>();
|
||||||
for (String path : pathSet) {
|
for (String path : pathSet) {
|
||||||
|
@ -70,10 +68,7 @@ public class LibrarySearchPathManager {
|
||||||
FSRL fsrl = null;
|
FSRL fsrl = null;
|
||||||
try {
|
try {
|
||||||
if (path.equals(".")) {
|
if (path.equals(".")) {
|
||||||
FSRL providerFsrl = provider.getFSRL();
|
FSRL providerFsrl = FSRL.fromProgram(program);
|
||||||
if (providerFsrl == null) {
|
|
||||||
providerFsrl = FSRL.fromProgram(program);
|
|
||||||
}
|
|
||||||
if (providerFsrl != null) {
|
if (providerFsrl != null) {
|
||||||
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
|
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
|
||||||
GFile parentFile = fileRef.file.getParentFile();
|
GFile parentFile = fileRef.file.getParentFile();
|
||||||
|
|
|
@ -17,7 +17,6 @@ package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.InvalidPathException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
@ -25,7 +24,6 @@ import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
|
||||||
|
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.OptionUtils;
|
import ghidra.app.util.OptionUtils;
|
||||||
|
@ -122,9 +120,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the libraries
|
// Load the libraries
|
||||||
List<Loaded<Program>> libraries = loadLibraries(provider, program, project,
|
loadedProgramList.addAll(loadLibraries(provider, program, project, projectFolderPath,
|
||||||
projectFolderPath, loadSpec, options, log, consumer, libraryNameList, monitor);
|
loadSpec, options, log, consumer, libraryNameList, monitor));
|
||||||
loadedProgramList.addAll(libraries);
|
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
return loadedProgramList;
|
return loadedProgramList;
|
||||||
|
@ -162,7 +159,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
if (loadedPrograms.isEmpty() ||
|
if (loadedPrograms.isEmpty() ||
|
||||||
(!isLinkExistingLibraries(options) && !isLoadLibraries(options))) {
|
(!isLinkExistingLibraries(options) && !isLoadLibraries(options))) {
|
||||||
|
@ -172,6 +169,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
List<DomainFolder> searchFolders =
|
List<DomainFolder> searchFolders =
|
||||||
getLibrarySearchFolders(loadedPrograms, project, options);
|
getLibrarySearchFolders(loadedPrograms, project, options);
|
||||||
|
|
||||||
|
List<LibrarySearchPath> searchPaths = getLibrarySearchPaths(
|
||||||
|
loadedPrograms.getFirst().getDomainObject(), loadSpec, options, messageLog, monitor);
|
||||||
|
|
||||||
List<Loaded<Program>> saveablePrograms =
|
List<Loaded<Program>> saveablePrograms =
|
||||||
loadedPrograms.stream().filter(Predicate.not(Loaded::shouldDiscard)).toList();
|
loadedPrograms.stream().filter(Predicate.not(Loaded::shouldDiscard)).toList();
|
||||||
|
|
||||||
|
@ -190,8 +190,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
monitor.setMessage("Resolving..." + program.getName());
|
monitor.setMessage("Resolving..." + program.getName());
|
||||||
int id = program.startTransaction("Resolving external references");
|
int id = program.startTransaction("Resolving external references");
|
||||||
try {
|
try {
|
||||||
resolveExternalLibraries(program, saveablePrograms, searchFolders, monitor,
|
resolveExternalLibraries(program, saveablePrograms, searchFolders, searchPaths,
|
||||||
messageLog);
|
options, monitor, messageLog);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, true);
|
program.endTransaction(id, true);
|
||||||
|
@ -496,7 +496,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the given list of libraries into the given {@link DomainFolder folder}
|
* Loads the given list of libraries as {@link Loaded} {@link Program}s
|
||||||
*
|
*
|
||||||
* @param provider The {@link ByteProvider} of the program being loaded
|
* @param provider The {@link ByteProvider} of the program being loaded
|
||||||
* @param program The {@link Program} being loaded
|
* @param program The {@link Program} being loaded
|
||||||
|
@ -520,13 +520,12 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
|
|
||||||
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
|
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
|
||||||
Set<String> processed = new TreeSet<>(getLibraryNameComparator());
|
Set<String> processed = new TreeSet<>(getLibraryNameComparator());
|
||||||
Queue<UnprocessedLibrary> unprocessed =
|
Queue<UnprocessedLibrary> unprocessed = createUnprocessedQueue(libraryNameList, options);
|
||||||
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
|
|
||||||
boolean loadLibraries = isLoadLibraries(options);
|
boolean loadLibraries = isLoadLibraries(options);
|
||||||
List<FileSystemSearchPath> customSearchPaths =
|
List<LibrarySearchPath> customSearchPaths =
|
||||||
getCustomLibrarySearchPaths(provider, options, log, monitor);
|
getCustomLibrarySearchPaths(provider, options, log, monitor);
|
||||||
List<FileSystemSearchPath> searchPaths =
|
List<LibrarySearchPath> searchPaths =
|
||||||
getLibrarySearchPaths(provider, program, desiredLoadSpec, options, log, monitor);
|
getLibrarySearchPaths(program, desiredLoadSpec, options, log, monitor);
|
||||||
DomainFolder linkSearchFolder =
|
DomainFolder linkSearchFolder =
|
||||||
getLinkSearchFolder(project, program, projectFolderPath, options);
|
getLinkSearchFolder(project, program, projectFolderPath, options);
|
||||||
String libraryDestFolderPath =
|
String libraryDestFolderPath =
|
||||||
|
@ -539,60 +538,37 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
while (!unprocessed.isEmpty()) {
|
while (!unprocessed.isEmpty()) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
UnprocessedLibrary unprocessedLibrary = unprocessed.remove();
|
UnprocessedLibrary unprocessedLibrary = unprocessed.remove();
|
||||||
String libraryName = unprocessedLibrary.name();
|
String library = unprocessedLibrary.name().trim();
|
||||||
boolean discard = unprocessedLibrary.discard();
|
|
||||||
int depth = unprocessedLibrary.depth();
|
int depth = unprocessedLibrary.depth();
|
||||||
if (depth == 0 || processed.contains(libraryName)) {
|
if (depth == 0 || processed.contains(library)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
processed.add(libraryName);
|
processed.add(library);
|
||||||
if (libraryDestFolder != null &&
|
if (findLibraryInProject(library, libraryDestFolder, searchPaths, options,
|
||||||
findLibrary(libraryName, libraryDestFolder) != null) {
|
monitor) != null) {
|
||||||
log.appendMsg("Found %s in %s...".formatted(libraryName, libraryDestFolder));
|
log.appendMsg("Found %s in %s...".formatted(library, libraryDestFolder));
|
||||||
log.appendMsg("------------------------------------------------\n");
|
log.appendMsg("------------------------------------------------\n");
|
||||||
}
|
}
|
||||||
else if (linkSearchFolder != null &&
|
else if (findLibraryInProject(library, linkSearchFolder, searchPaths, options,
|
||||||
findLibrary(libraryName, linkSearchFolder) != null) {
|
monitor) != null) {
|
||||||
log.appendMsg("Found %s in %s...".formatted(libraryName, linkSearchFolder));
|
log.appendMsg("Found %s in %s...".formatted(library, linkSearchFolder));
|
||||||
log.appendMsg("------------------------------------------------\n");
|
log.appendMsg("------------------------------------------------\n");
|
||||||
}
|
}
|
||||||
else if (!customSearchPaths.isEmpty() || !searchPaths.isEmpty()) {
|
else if (isLoadLibraries(options) || shouldSearchAllPaths(program, options)) {
|
||||||
// Note that it is possible to have search paths with those
|
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(library, provider,
|
||||||
// options turned off (if shouldSearchAllPaths() is overridden to return true).
|
customSearchPaths, libraryDestFolderPath, unprocessed, depth,
|
||||||
// In this case, we still want to process those libraries, but we
|
desiredLoadSpec, options, log, consumer, monitor);
|
||||||
// do not want to save them, so they can be released.
|
if (loadedLibrary == null) {
|
||||||
boolean loaded = false;
|
loadedLibrary = loadLibraryFromSearchPaths(library, provider, searchPaths,
|
||||||
if (!customSearchPaths.isEmpty()) {
|
libraryDestFolderPath, unprocessed, depth, desiredLoadSpec, options,
|
||||||
log.appendMsg("Searching %d custom path%s for library %s...".formatted(
|
log, consumer, monitor);
|
||||||
customSearchPaths.size(), customSearchPaths.size() > 1 ? "s" : "",
|
|
||||||
libraryName));
|
|
||||||
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(libraryName,
|
|
||||||
provider, customSearchPaths, libraryDestFolderPath, unprocessed, depth,
|
|
||||||
desiredLoadSpec, options, log, consumer, monitor);
|
|
||||||
if (loadedLibrary != null) {
|
|
||||||
loaded = loadLibraries && !discard;
|
|
||||||
loadedLibrary.setDiscard(!loaded);
|
|
||||||
loadedPrograms.add(loadedLibrary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!loaded && !searchPaths.isEmpty()) {
|
if (loadedLibrary != null) {
|
||||||
log.appendMsg("Searching %d path%s for library %s...".formatted(
|
boolean discarding = !loadLibraries || unprocessedLibrary.discard();
|
||||||
searchPaths.size(), searchPaths.size() > 1 ? "s" : "", libraryName));
|
loadedLibrary.setDiscard(discarding);
|
||||||
Loaded<Program> loadedLibrary = loadLibraryFromSearchPaths(libraryName,
|
loadedPrograms.add(loadedLibrary);
|
||||||
provider, searchPaths, libraryDestFolderPath, unprocessed, depth,
|
log.appendMsg(discarding ? "Library not saved to project."
|
||||||
desiredLoadSpec, options, log, consumer, monitor);
|
: "Saving library to: " + loadedLibrary);
|
||||||
if (loadedLibrary != null) {
|
|
||||||
loaded = loadLibraries && !discard;
|
|
||||||
loadedLibrary.setDiscard(!loaded);
|
|
||||||
loadedPrograms.add(loadedLibrary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (loaded) {
|
|
||||||
log.appendMsg("Saving library to: " +
|
|
||||||
loadedPrograms.get(loadedPrograms.size() - 1).toString());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.appendMsg("Library not saved to project.");
|
|
||||||
}
|
}
|
||||||
log.appendMsg("------------------------------------------------\n");
|
log.appendMsg("------------------------------------------------\n");
|
||||||
}
|
}
|
||||||
|
@ -619,9 +595,9 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* Loads the given library into the given {@link DomainFolder folder} if it can find it in
|
* Loads the given library into the given {@link DomainFolder folder} if it can find it in
|
||||||
* the given {@link List} of search paths
|
* the given {@link List} of search paths
|
||||||
*
|
*
|
||||||
* @param libraryName The name of the library to load
|
* @param library The library to load
|
||||||
* @param provider The {@link ByteProvider} of the program being loaded
|
* @param provider The {@link ByteProvider} of the program being loaded
|
||||||
* @param fsSearchPaths A {@link List} of {@link FileSystemSearchPath}s that will be searched
|
* @param searchPaths A {@link List} of {@link LibrarySearchPath}s that will be searched
|
||||||
* @param libraryDestFolderPath The path of the project folder to load the libraries into.
|
* @param libraryDestFolderPath The path of the project folder to load the libraries into.
|
||||||
* Could be null if the specified project is null or a destination folder path could not be
|
* Could be null if the specified project is null or a destination folder path could not be
|
||||||
* determined.
|
* determined.
|
||||||
|
@ -637,29 +613,27 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @throws IOException if there was an IO-related problem loading
|
* @throws IOException if there was an IO-related problem loading
|
||||||
* @throws CancelledException if the user cancelled the load
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private Loaded<Program> loadLibraryFromSearchPaths(String libraryName,
|
private Loaded<Program> loadLibraryFromSearchPaths(String library, ByteProvider provider,
|
||||||
ByteProvider provider, List<FileSystemSearchPath> fsSearchPaths,
|
List<LibrarySearchPath> searchPaths, String libraryDestFolderPath,
|
||||||
String libraryDestFolderPath, Queue<UnprocessedLibrary> unprocessed, int depth,
|
Queue<UnprocessedLibrary> unprocessed, int depth, LoadSpec desiredLoadSpec,
|
||||||
LoadSpec desiredLoadSpec, List<Option> options, MessageLog log, Object consumer,
|
List<Option> options, MessageLog log, Object consumer, TaskMonitor monitor)
|
||||||
TaskMonitor monitor) throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
libraryName = libraryName.trim();
|
if (searchPaths.isEmpty()) {
|
||||||
Program library = null;
|
return null;
|
||||||
if (libraryDestFolderPath != null) {
|
|
||||||
String libraryPath = FilenameUtils.getPath(libraryName);
|
|
||||||
if (libraryPath != null && !libraryPath.isEmpty()) {
|
|
||||||
if (!libraryDestFolderPath.endsWith("/")) {
|
|
||||||
libraryDestFolderPath += "/";
|
|
||||||
}
|
|
||||||
libraryDestFolderPath += libraryPath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
String simpleLibraryName = FilenameUtils.getName(libraryName);
|
|
||||||
|
log.appendMsg("Searching %d path%s for library %s...".formatted(searchPaths.size(),
|
||||||
|
searchPaths.size() > 1 ? "s" : "", library));
|
||||||
|
|
||||||
|
Program libraryProgram = null;
|
||||||
|
String simpleLibraryName = FilenameUtils.getName(library);
|
||||||
|
boolean isAbsolute = new File(library).isAbsolute();
|
||||||
|
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
List<FSRL> candidateLibraryFsrls =
|
List<FSRL> candidateLibraryFsrls =
|
||||||
findLibrary(getCheckedPath(libraryName), fsSearchPaths, log, monitor);
|
findLibraryOnDisk(library, searchPaths, log, monitor);
|
||||||
if (candidateLibraryFsrls.isEmpty()) {
|
if (candidateLibraryFsrls.isEmpty()) {
|
||||||
log.appendMsg("Library not found.");
|
log.appendMsg("Library not found.");
|
||||||
return null;
|
return null;
|
||||||
|
@ -668,34 +642,37 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
for (FSRL candidateLibraryFsrl : candidateLibraryFsrls) {
|
for (FSRL candidateLibraryFsrl : candidateLibraryFsrls) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
List<String> newLibraryList = new ArrayList<>();
|
List<String> newLibraryList = new ArrayList<>();
|
||||||
library = loadLibrary(simpleLibraryName, candidateLibraryFsrl,
|
libraryProgram = loadLibrary(simpleLibraryName, candidateLibraryFsrl,
|
||||||
desiredLoadSpec, newLibraryList, options, consumer, log, monitor);
|
desiredLoadSpec, newLibraryList, options, consumer, log, monitor);
|
||||||
for (String newLibraryName : newLibraryList) {
|
for (String newLibraryName : newLibraryList) {
|
||||||
unprocessed.add(new UnprocessedLibrary(newLibraryName, depth - 1, false));
|
unprocessed.add(new UnprocessedLibrary(newLibraryName, depth - 1, false));
|
||||||
}
|
}
|
||||||
if (library == null) {
|
if (libraryProgram == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
processLibrary(library, libraryName, candidateLibraryFsrl, provider, unprocessed,
|
processLibrary(libraryProgram, library, candidateLibraryFsrl, provider, unprocessed,
|
||||||
depth, desiredLoadSpec, options, log, monitor);
|
depth, desiredLoadSpec, options, log, monitor);
|
||||||
success = true;
|
success = true;
|
||||||
return new Loaded<Program>(library, simpleLibraryName, libraryDestFolderPath);
|
String folderPath = libraryDestFolderPath;
|
||||||
|
if (folderPath != null) {
|
||||||
|
if (isAbsolute) {
|
||||||
|
folderPath = joinPaths(folderPath, FilenameUtils.getFullPath(library));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Loaded<Program>(libraryProgram, simpleLibraryName, folderPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidInputException e) {
|
|
||||||
log.appendMsg("Cannot load library with invalid name: \"" + libraryName + "\"");
|
|
||||||
}
|
|
||||||
finally {
|
finally {
|
||||||
if (!success && library != null) {
|
if (!success && libraryProgram != null) {
|
||||||
library.release(consumer);
|
libraryProgram.release(consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the library within the specified {@link DomainFolder folder}. This method will handle
|
* Find the library within the specified {@link DomainFolder root search folder}. This method
|
||||||
* relative path normalization.
|
* will handle relative path normalization.
|
||||||
* <p>
|
* <p>
|
||||||
* If the library path is a simple name without any path separators, only the given folder
|
* If the library path is a simple name without any path separators, only the given folder
|
||||||
* will be searched.
|
* will be searched.
|
||||||
|
@ -706,21 +683,27 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* If the library path has a path and it wasn't found under the given folder, the
|
* If the library path has a path and it wasn't found under the given folder, the
|
||||||
* filename part of library path will be used to search the given folder for matches.
|
* filename part of library path will be used to search the given folder for matches.
|
||||||
*
|
*
|
||||||
* @param libraryPath path with filename of the library to find
|
* @param library library to find
|
||||||
* @param folder {@link DomainFolder} within which imported libraries will be searched.
|
* @param rootSearchFolder {@link DomainFolder root folder} within which imported libraries will
|
||||||
* If null this method will return null.
|
* be searched. If null this method will return null.
|
||||||
|
* @param searchPaths A {@link List} of {@link LibrarySearchPath}s that will be searched
|
||||||
|
* @param options The load options
|
||||||
|
* @param monitor A cancelable task monitor
|
||||||
* @return The found {@link DomainFile} or null if not found
|
* @return The found {@link DomainFile} or null if not found
|
||||||
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
protected DomainFile findLibrary(String libraryPath, DomainFolder folder) {
|
protected DomainFile findLibraryInProject(String library, DomainFolder rootSearchFolder,
|
||||||
if (folder == null) {
|
List<LibrarySearchPath> searchPaths, List<Option> options, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
if (rootSearchFolder == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup by full project path
|
// Lookup by full project path
|
||||||
// NOTE: probably no need to support optional extensions and case-insensitivity for this case
|
// NOTE: probably no need to support optional extensions and case-insensitivity for this case
|
||||||
String projectPath = concatenatePaths(folder.getPathname(), libraryPath);
|
String projectPath = joinPaths(rootSearchFolder.getPathname(), library);
|
||||||
DomainFile ret =
|
ProjectData projectData = rootSearchFolder.getProjectData();
|
||||||
folder.getProjectData().getFile(FilenameUtils.separatorsToUnix(projectPath));
|
DomainFile ret = projectData.getFile(projectPath);
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -728,8 +711,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
// Quick lookup by library filename (ignoring full library path) in given folder.
|
// Quick lookup by library filename (ignoring full library path) in given folder.
|
||||||
// We try this first to hopefully avoid needing to iterate over the files in the folder
|
// We try this first to hopefully avoid needing to iterate over the files in the folder
|
||||||
// factoring in case and extensions
|
// factoring in case and extensions
|
||||||
String libraryName = FilenameUtils.getName(libraryPath);
|
String libraryName = FilenameUtils.getName(library);
|
||||||
if ((ret = folder.getFile(libraryName)) != null) {
|
if ((ret = rootSearchFolder.getFile(libraryName)) != null) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,7 +720,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
// a match
|
// a match
|
||||||
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
|
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
|
||||||
Comparator<String> comparator = getLibraryNameComparator();
|
Comparator<String> comparator = getLibraryNameComparator();
|
||||||
for (DomainFile file : folder.getFiles()) {
|
for (DomainFile file : rootSearchFolder.getFiles()) {
|
||||||
String candidateName = file.getName();
|
String candidateName = file.getName();
|
||||||
if (isOptionalLibraryFilenameExtensions() && noExtension) {
|
if (isOptionalLibraryFilenameExtensions() && noExtension) {
|
||||||
candidateName = FilenameUtils.getBaseName(candidateName);
|
candidateName = FilenameUtils.getBaseName(candidateName);
|
||||||
|
@ -760,74 +743,40 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* each search path directory that is searched, and if not found, the filename part of
|
* each search path directory that is searched, and if not found, the filename part of
|
||||||
* the library is used to search just the search path directory.
|
* the library is used to search just the search path directory.
|
||||||
* <p>
|
* <p>
|
||||||
* If the library specifies an absolute path, its native path is searched on the local
|
* If the library specifies an absolute path, its native path is also searched on the local
|
||||||
* filesystem.
|
* filesystem.
|
||||||
*
|
*
|
||||||
* @param libraryPath The library {@link Path}. This will be either an absolute path, a
|
* @param library The library. This will be either an absolute path, a relative path, or just a
|
||||||
* relative path, or just a filename.
|
* filename.
|
||||||
* @param fsSearchPaths A {@link List} of {@link FileSystemSearchPath}s that will be searched
|
* @param searchPaths A {@link List} of {@link LibrarySearchPath}s that will be searched
|
||||||
* @param log The log
|
* @param log The log
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return A {@link List} of {@link GFile files} that match the requested library path
|
* @return A {@link List} of {@link GFile files} that match the requested library path
|
||||||
* @throws CancelledException if the user cancelled the operation
|
* @throws CancelledException if the user cancelled the operation
|
||||||
*/
|
*/
|
||||||
private List<FSRL> findLibrary(Path libraryPath, List<FileSystemSearchPath> fsSearchPaths,
|
private List<FSRL> findLibraryOnDisk(String library, List<LibrarySearchPath> searchPaths,
|
||||||
MessageLog log, TaskMonitor monitor) throws CancelledException {
|
MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
||||||
List<FSRL> results = new ArrayList<>();
|
List<FSRL> results = new ArrayList<>();
|
||||||
FileSystemService fsService = FileSystemService.getInstance();
|
|
||||||
|
|
||||||
Path libraryParentPath = libraryPath.getParent();
|
try {
|
||||||
String libraryName = libraryPath.getFileName().toString();
|
for (LibrarySearchPath searchPath : searchPaths) {
|
||||||
|
monitor.checkCancelled();
|
||||||
for (FileSystemSearchPath fsSearchPath : fsSearchPaths) {
|
String fullLibraryPath = joinPaths(searchPath.relativeFsPath(), library);
|
||||||
monitor.checkCancelled();
|
GFileSystem fs = searchPath.fsRef().getFilesystem();
|
||||||
|
FSRL fsrl = resolveLibraryFile(fs, fullLibraryPath);
|
||||||
try {
|
Optional.ofNullable(fsrl).ifPresent(results::add);
|
||||||
// Handle 3 different library-lookup cases:
|
|
||||||
|
|
||||||
// 1) libraryPath is library name, relative path, or absolute path from the root
|
|
||||||
// of the searchPath. We need to join our fsSearchPath with our
|
|
||||||
// libraryParentPath
|
|
||||||
Path combinedParentPath =
|
|
||||||
ObjectUtils.allNotNull(fsSearchPath.fsPath(), libraryParentPath)
|
|
||||||
? fsSearchPath.fsPath().resolve(libraryParentPath)
|
|
||||||
: ObjectUtils.firstNonNull(fsSearchPath.fsPath(), libraryParentPath);
|
|
||||||
FSRL resolvedFsrl = resolveLibraryFile(fsSearchPath.fsRef().getFilesystem(),
|
|
||||||
combinedParentPath, libraryName);
|
|
||||||
if (resolvedFsrl != null) {
|
|
||||||
results.add(resolvedFsrl);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) libraryPath is an absolute path and should be looked up as-is on the
|
|
||||||
// LocalFileSystem. Note that the root of the LocalFileSystem should not be
|
|
||||||
// assumed to be in searchPaths for this case (otherwise case 1 would find it)
|
|
||||||
if (libraryParentPath != null && libraryParentPath.isAbsolute()) {
|
|
||||||
resolvedFsrl = resolveLibraryFile(fsService.getLocalFS(), libraryParentPath,
|
|
||||||
libraryName);
|
|
||||||
if (resolvedFsrl != null) {
|
|
||||||
results.add(resolvedFsrl);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3) libraryPath is some kind of path that we haven't found yet, so handle a
|
|
||||||
// flat-directory structure by just appending filename part of the path to the
|
|
||||||
// searchPath. Not sure if this case is still necessary but supporting for
|
|
||||||
// legacy support.
|
|
||||||
resolvedFsrl = resolveLibraryFile(fsSearchPath.fsRef().getFilesystem(),
|
|
||||||
fsSearchPath.fsPath(), libraryName);
|
|
||||||
if (resolvedFsrl != null) {
|
|
||||||
results.add(resolvedFsrl);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
|
||||||
log.appendException(e);
|
if (results.isEmpty() && new File(library).isAbsolute()) {
|
||||||
continue;
|
LocalFileSystem localFS = FileSystemService.getInstance().getLocalFS();
|
||||||
|
FSRL fsrl = resolveLibraryFile(localFS, library);
|
||||||
|
Optional.ofNullable(fsrl).ifPresent(results::add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
log.appendException(e);
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
@ -899,7 +848,6 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
Program program = createProgram(provider, programName, imageBaseAddr, getName(), language,
|
Program program = createProgram(provider, programName, imageBaseAddr, getName(), language,
|
||||||
compilerSpec, consumer);
|
compilerSpec, consumer);
|
||||||
|
|
||||||
|
|
||||||
int transactionID = program.startTransaction("Loading");
|
int transactionID = program.startTransaction("Loading");
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
|
@ -944,9 +892,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
return libraryNames;
|
return libraryNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resolveExternalLibraries(Program program,
|
private void resolveExternalLibraries(Program program, List<Loaded<Program>> loadedPrograms,
|
||||||
List<Loaded<Program>> loadedPrograms, List<DomainFolder> searchFolders,
|
List<DomainFolder> searchFolders, List<LibrarySearchPath> fsSearchPaths,
|
||||||
TaskMonitor monitor, MessageLog messageLog) throws CancelledException {
|
List<Option> options, TaskMonitor monitor, MessageLog messageLog)
|
||||||
|
throws CancelledException {
|
||||||
ExternalManager extManager = program.getExternalManager();
|
ExternalManager extManager = program.getExternalManager();
|
||||||
String[] extLibNames = extManager.getExternalLibraryNames();
|
String[] extLibNames = extManager.getExternalLibraryNames();
|
||||||
messageLog.appendMsg(
|
messageLog.appendMsg(
|
||||||
|
@ -958,17 +907,17 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
try {
|
try {
|
||||||
Loaded<Program> matchingExtProgram = findLibrary(loadedPrograms, externalLibName);
|
Loaded<Program> match = findLibraryInLoadedList(loadedPrograms, externalLibName);
|
||||||
if (matchingExtProgram != null) {
|
if (match != null) {
|
||||||
String path =
|
String path = match.getProjectFolderPath() + match.getName();
|
||||||
matchingExtProgram.getProjectFolderPath() + matchingExtProgram.getName();
|
|
||||||
extManager.setExternalPath(externalLibName, path, false);
|
extManager.setExternalPath(externalLibName, path, false);
|
||||||
messageLog.appendMsg(" [" + externalLibName + "] -> [" + path + "]");
|
messageLog.appendMsg(" [" + externalLibName + "] -> [" + path + "]");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (DomainFolder searchFolder : searchFolders) {
|
for (DomainFolder searchFolder : searchFolders) {
|
||||||
DomainFile alreadyImportedLib = findLibrary(externalLibName, searchFolder);
|
DomainFile alreadyImportedLib = findLibraryInProject(externalLibName,
|
||||||
|
searchFolder, fsSearchPaths, options, monitor);
|
||||||
if (alreadyImportedLib != null) {
|
if (alreadyImportedLib != null) {
|
||||||
extManager.setExternalPath(externalLibName,
|
extManager.setExternalPath(externalLibName,
|
||||||
alreadyImportedLib.getPathname(), false);
|
alreadyImportedLib.getPathname(), false);
|
||||||
|
@ -1005,25 +954,28 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* library names in the given list
|
* library names in the given list
|
||||||
*
|
*
|
||||||
* @param libraryNames A {@link List} of unprocessed library names
|
* @param libraryNames A {@link List} of unprocessed library names
|
||||||
* @param depth The initial load depth of each library
|
* @param options The options
|
||||||
* @return A {@link Queue} of {@link UnprocessedLibrary}s
|
* @return A {@link Queue} of {@link UnprocessedLibrary}s
|
||||||
*/
|
*/
|
||||||
private Queue<UnprocessedLibrary> createUnprocessedQueue(List<String> libraryNames, int depth) {
|
private Queue<UnprocessedLibrary> createUnprocessedQueue(List<String> libraryNames,
|
||||||
|
List<Option> options) {
|
||||||
|
int depth = getLibraryLoadDepth(options);
|
||||||
return libraryNames.stream()
|
return libraryNames.stream()
|
||||||
.map(name -> new UnprocessedLibrary(name, depth, false))
|
.map(name -> new UnprocessedLibrary(name, depth, false))
|
||||||
.collect(Collectors.toCollection(LinkedList::new));
|
.collect(Collectors.toCollection(LinkedList::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A file system search path
|
* A library search path
|
||||||
*
|
*
|
||||||
* @param fsRef A {@link FileSystemRef}
|
* @param fsRef The root {@link FileSystemRef}
|
||||||
* @param fsPath A {@link Path} relative to the root of the file system, or null for the root
|
* @param relativeFsPath A {@link Path} relative to the root of the file system, or null for the
|
||||||
|
* root
|
||||||
*/
|
*/
|
||||||
protected record FileSystemSearchPath(FileSystemRef fsRef, Path fsPath) {}
|
protected record LibrarySearchPath(FileSystemRef fsRef, String relativeFsPath) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@link List} of priority-ordered custom {@link FileSystemSearchPath}s used to search
|
* Gets a {@link List} of priority-ordered custom {@link LibrarySearchPath}s used to search
|
||||||
* for libraries. The default implementation of this method returns an empty {@link List}.
|
* for libraries. The default implementation of this method returns an empty {@link List}.
|
||||||
* Subclasses can override it as needed.
|
* Subclasses can override it as needed.
|
||||||
*
|
*
|
||||||
|
@ -1031,11 +983,11 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param options The options
|
* @param options The options
|
||||||
* @param log The log
|
* @param log The log
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return A {@link List} of priority-ordered custom {@link FileSystemSearchPath}s used to
|
* @return A {@link List} of priority-ordered custom {@link LibrarySearchPath}s used to
|
||||||
* search for libraries
|
* search for libraries
|
||||||
* @throws CancelledException if the user cancelled the load
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
protected List<FileSystemSearchPath> getCustomLibrarySearchPaths(ByteProvider provider,
|
protected List<LibrarySearchPath> getCustomLibrarySearchPaths(ByteProvider provider,
|
||||||
List<Option> options, MessageLog log, TaskMonitor monitor) throws CancelledException {
|
List<Option> options, MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
@ -1057,32 +1009,30 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@link List} of priority-ordered {@link FileSystemSearchPath}s used to search for
|
* Gets a {@link List} of priority-ordered {@link LibrarySearchPath}s used to search for
|
||||||
* libraries
|
* libraries
|
||||||
*
|
*
|
||||||
* @param provider The {@link ByteProvider} of the program being loaded
|
|
||||||
* @param program The {@link Program} being loaded
|
* @param program The {@link Program} being loaded
|
||||||
* @param loadSpec The {@link LoadSpec} to use during load
|
* @param loadSpec The {@link LoadSpec} to use during load
|
||||||
* @param options The options
|
* @param options The options
|
||||||
* @param log The log
|
* @param log The log
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return A {@link List} of priority-ordered {@link FileSystemSearchPath}s used to search for
|
* @return A {@link List} of priority-ordered {@link LibrarySearchPath}s used to search for
|
||||||
* libraries
|
* libraries
|
||||||
* @throws CancelledException if the user cancelled the load
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private List<FileSystemSearchPath> getLibrarySearchPaths(ByteProvider provider, Program program,
|
protected List<LibrarySearchPath> getLibrarySearchPaths(Program program, LoadSpec loadSpec,
|
||||||
LoadSpec loadSpec, List<Option> options, MessageLog log, TaskMonitor monitor)
|
List<Option> options, MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||||
throws CancelledException {
|
if (!isLoadLibraries(options) && !isLinkExistingLibraries(options) &&
|
||||||
if (!isLoadLibraries(options) && !shouldSearchAllPaths(program, options)) {
|
!shouldSearchAllPaths(program, options)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystemService fsService = FileSystemService.getInstance();
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
List<FileSystemSearchPath> result = new ArrayList<>();
|
List<LibrarySearchPath> result = new ArrayList<>();
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
for (FSRL fsrl : LibrarySearchPathManager.getLibraryFsrlList(provider, program, log,
|
for (FSRL fsrl : LibrarySearchPathManager.getLibraryFsrlList(program, log, monitor)) {
|
||||||
monitor)) {
|
|
||||||
|
|
||||||
if (!isValidSearchPath(fsrl, loadSpec, monitor)) {
|
if (!isValidSearchPath(fsrl, loadSpec, monitor)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1093,7 +1043,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
FileSystemRef fileRef =
|
FileSystemRef fileRef =
|
||||||
fsService.probeFileForFilesystem(fsrl, monitor, null);
|
fsService.probeFileForFilesystem(fsrl, monitor, null);
|
||||||
if (fileRef != null) {
|
if (fileRef != null) {
|
||||||
result.add(new FileSystemSearchPath(fileRef, null));
|
result.add(new LibrarySearchPath(fileRef, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
@ -1104,7 +1054,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
try (RefdFile fileRef = fsService.getRefdFile(fsrl, monitor)) {
|
try (RefdFile fileRef = fsService.getRefdFile(fsrl, monitor)) {
|
||||||
if (fileRef != null) {
|
if (fileRef != null) {
|
||||||
File f = new File(fileRef.file.getPath()); // File API will sanitize Windows-style paths
|
File f = new File(fileRef.file.getPath()); // File API will sanitize Windows-style paths
|
||||||
result.add(new FileSystemSearchPath(fileRef.fsRef.dup(), f.toPath()));
|
result.add(new LibrarySearchPath(fileRef.fsRef.dup(), f.getPath()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
|
@ -1123,14 +1073,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the library within the given {@link Map} of {@link Program}s
|
* Find the library within the given {@link List} of {@link Loaded} {@link Program}s
|
||||||
*
|
*
|
||||||
* @param loadedPrograms the list of {@link Loaded} {@link Program}s
|
* @param loadedPrograms the list of {@link Loaded} {@link Program}s
|
||||||
* @param libraryName The library name to lookup. Depending on the type of library, this could
|
* @param libraryName The library name to lookup. Depending on the type of library, this could
|
||||||
* be a simple filename or an absolute path.
|
* be a simple filename or an absolute path.
|
||||||
* @return The found {@link Loaded} {@link Program} or null if not found
|
* @return The found {@link Loaded} {@link Program} or null if not found
|
||||||
*/
|
*/
|
||||||
protected Loaded<Program> findLibrary(List<Loaded<Program>> loadedPrograms,
|
protected Loaded<Program> findLibraryInLoadedList(List<Loaded<Program>> loadedPrograms,
|
||||||
String libraryName) {
|
String libraryName) {
|
||||||
Comparator<String> comparator = getLibraryNameComparator();
|
Comparator<String> comparator = getLibraryNameComparator();
|
||||||
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
|
boolean noExtension = FilenameUtils.getExtension(libraryName).equals("");
|
||||||
|
@ -1181,27 +1131,20 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* case-insensitive lookup may be allowed, and filename extensions may be optional.
|
* case-insensitive lookup may be allowed, and filename extensions may be optional.
|
||||||
*
|
*
|
||||||
* @param fs The {@link GFileSystem file system} to resolve in
|
* @param fs The {@link GFileSystem file system} to resolve in
|
||||||
* @param libraryParentPath The {@link Path} of the library's parent directory, relative to the
|
* @param library The library. This will be either an absolute path, a relative path, or just a
|
||||||
* given file system (could be null)
|
* filename.
|
||||||
* @param libraryName The library name
|
|
||||||
* @return The library resolved to an existing {@link FSRL}, or null if it did not resolve
|
* @return The library resolved to an existing {@link FSRL}, or null if it did not resolve
|
||||||
* @throws IOException If an IO-related problem occurred
|
* @throws IOException If an IO-related problem occurred
|
||||||
*/
|
*/
|
||||||
protected FSRL resolveLibraryFile(GFileSystem fs, Path libraryParentPath, String libraryName)
|
protected FSRL resolveLibraryFile(GFileSystem fs, String library) throws IOException {
|
||||||
throws IOException {
|
|
||||||
String lpp = libraryParentPath != null
|
|
||||||
? FilenameUtils.separatorsToUnix(libraryParentPath.toString())
|
|
||||||
: null;
|
|
||||||
String targetPath = FSUtilities.appendPath(lpp, libraryName);
|
|
||||||
|
|
||||||
Comparator<String> baseNameComp = getLibraryNameComparator();
|
Comparator<String> baseNameComp = getLibraryNameComparator();
|
||||||
Comparator<String> nameComp = isOptionalLibraryFilenameExtensions() &&
|
Comparator<String> nameComp = isOptionalLibraryFilenameExtensions() &&
|
||||||
FilenameUtils.getExtension(libraryName).isEmpty()
|
FilenameUtils.getExtension(library).isEmpty()
|
||||||
? (s1, s2) -> baseNameComp.compare(FilenameUtils.getBaseName(s1),
|
? (s1, s2) -> baseNameComp.compare(FilenameUtils.getBaseName(s1),
|
||||||
FilenameUtils.getBaseName(s2))
|
FilenameUtils.getBaseName(s2))
|
||||||
: baseNameComp;
|
: baseNameComp;
|
||||||
|
|
||||||
GFile foundFile = fs.lookup(targetPath, nameComp);
|
GFile foundFile = fs.lookup(library, nameComp);
|
||||||
return foundFile != null && !foundFile.isDirectory() ? foundFile.getFSRL() : null;
|
return foundFile != null && !foundFile.isDirectory() ? foundFile.getFSRL() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1215,24 +1158,4 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
? String.CASE_INSENSITIVE_ORDER
|
? String.CASE_INSENSITIVE_ORDER
|
||||||
: (s1, s2) -> s1.compareTo(s2);
|
: (s1, s2) -> s1.compareTo(s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls {@link Path#of(String, String...)} with the given parameter, throwing a checked
|
|
||||||
* exception if a failure occurred.
|
|
||||||
* <p>
|
|
||||||
* NOTE: {@link Path#of(String, String...)} throws an unchecked exception, which has proven
|
|
||||||
* dangerous.
|
|
||||||
*
|
|
||||||
* @param str The string to convert to a {@link Path}
|
|
||||||
* @return The string converted to a {@link Path}
|
|
||||||
* @throws InvalidInputException if the given string cannot be converted to a {@link Path}
|
|
||||||
*/
|
|
||||||
private Path getCheckedPath(String str) throws InvalidInputException {
|
|
||||||
try {
|
|
||||||
return Path.of(str);
|
|
||||||
}
|
|
||||||
catch (InvalidPathException e) {
|
|
||||||
throw new InvalidInputException(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
if (shouldPerformOrdinalLookup(options)) {
|
if (shouldPerformOrdinalLookup(options)) {
|
||||||
|
@ -148,7 +148,8 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
super.postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog,
|
||||||
|
monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,12 +19,15 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
import ghidra.app.plugin.processors.generic.MemoryBlockDefinition;
|
import ghidra.app.plugin.processors.generic.MemoryBlockDefinition;
|
||||||
import ghidra.app.util.Option;
|
import ghidra.app.util.Option;
|
||||||
import ghidra.app.util.OptionUtils;
|
import ghidra.app.util.OptionUtils;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.formats.gfilesystem.FSUtilities;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.store.LockException;
|
import ghidra.framework.store.LockException;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
|
@ -138,7 +141,7 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subclasses can perform custom post-load fix-ups
|
// Subclasses can perform custom post-load fix-ups
|
||||||
postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog, monitor);
|
||||||
|
|
||||||
// Discard unneeded programs
|
// Discard unneeded programs
|
||||||
Iterator<Loaded<Program>> iter = loadedPrograms.iterator();
|
Iterator<Loaded<Program>> iter = loadedPrograms.iterator();
|
||||||
|
@ -218,6 +221,7 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
*
|
*
|
||||||
* @param loadedPrograms The {@link Loaded loaded programs} to be fixed up.
|
* @param loadedPrograms The {@link Loaded loaded programs} to be fixed up.
|
||||||
* @param project The {@link Project} to load into. Could be null if there is no project.
|
* @param project The {@link Project} to load into. Could be null if there is no project.
|
||||||
|
* @param loadSpec The {@link LoadSpec} to use during load.
|
||||||
* @param options The load options.
|
* @param options The load options.
|
||||||
* @param messageLog The message log.
|
* @param messageLog The message log.
|
||||||
* @param monitor A cancelable task monitor.
|
* @param monitor A cancelable task monitor.
|
||||||
|
@ -225,7 +229,7 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
* @throws CancelledException if the user cancelled the load.
|
* @throws CancelledException if the user cancelled the load.
|
||||||
*/
|
*/
|
||||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
// Default behavior is to do nothing
|
// Default behavior is to do nothing
|
||||||
}
|
}
|
||||||
|
@ -255,32 +259,18 @@ public abstract class AbstractProgramLoader implements Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenates the given path elements to form a single path. Empty and null path elements
|
* Joins the given path elements to form a single path. Empty and null path elements
|
||||||
* are ignored.
|
* are ignored. The returned path's separators are converted to unix-style and
|
||||||
|
* windows-specific characters like {@code :} are stripped out, making the path suitable
|
||||||
|
* to be a project path
|
||||||
*
|
*
|
||||||
* @param pathElements The path elements to append to one another
|
* @param pathElements The path elements to append to one another
|
||||||
* @return A single path consisting of the given path elements appended together
|
* @return A single path consisting of the given path elements appended together
|
||||||
|
* @see FSUtilities#appendPath(String...)
|
||||||
*/
|
*/
|
||||||
protected String concatenatePaths(String... pathElements) {
|
protected String joinPaths(String... pathElements) {
|
||||||
StringBuilder sb = new StringBuilder();
|
String str = FSUtilities.appendPath(pathElements);
|
||||||
for (String pathElement : pathElements) {
|
return str != null ? FilenameUtils.separatorsToUnix(str).replaceAll(":", "") : null;
|
||||||
if (pathElement == null || pathElement.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
boolean sbEndsWithSlash =
|
|
||||||
sb.length() > 0 && "/\\".indexOf(sb.charAt(sb.length() - 1)) != -1;
|
|
||||||
boolean elementStartsWithSlash = "/\\".indexOf(pathElement.charAt(0)) != -1;
|
|
||||||
|
|
||||||
if (!sbEndsWithSlash && !elementStartsWithSlash && sb.length() > 0) {
|
|
||||||
sb.append("/");
|
|
||||||
}
|
|
||||||
else if (elementStartsWithSlash && sbEndsWithSlash) {
|
|
||||||
pathElement = pathElement.substring(1);
|
|
||||||
}
|
|
||||||
sb.append(pathElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -153,9 +153,10 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
super.postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
super.postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog,
|
||||||
|
monitor);
|
||||||
|
|
||||||
ProjectData projectData = project != null ? project.getProjectData() : null;
|
ProjectData projectData = project != null ? project.getProjectData() : null;
|
||||||
try (ExternalSymbolResolver esr = new ExternalSymbolResolver(projectData, monitor)) {
|
try (ExternalSymbolResolver esr = new ExternalSymbolResolver(projectData, monitor)) {
|
||||||
|
|
|
@ -115,7 +115,7 @@ public class Loaded<T extends DomainObject> {
|
||||||
* project folder will be used.
|
* project folder will be used.
|
||||||
*/
|
*/
|
||||||
public void setProjectFolderPath(String projectFolderPath) {
|
public void setProjectFolderPath(String projectFolderPath) {
|
||||||
if (projectFolderPath == null) {
|
if (projectFolderPath == null || projectFolderPath.isBlank()) {
|
||||||
projectFolderPath = "/";
|
projectFolderPath = "/";
|
||||||
}
|
}
|
||||||
else if (!projectFolderPath.endsWith("/")) {
|
else if (!projectFolderPath.endsWith("/")) {
|
||||||
|
|
|
@ -16,9 +16,10 @@
|
||||||
package ghidra.app.util.opinion;
|
package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
import ghidra.app.util.*;
|
import ghidra.app.util.*;
|
||||||
import ghidra.app.util.bin.*;
|
import ghidra.app.util.bin.*;
|
||||||
import ghidra.app.util.bin.format.golang.GoConstants;
|
import ghidra.app.util.bin.format.golang.GoConstants;
|
||||||
|
@ -255,25 +256,29 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
* <p>
|
* <p>
|
||||||
* might be found at:
|
* might be found at:
|
||||||
* <p>
|
* <p>
|
||||||
* {@code /System/Library/Frameworks/Foundation.framework//Versions/C/Foundation}
|
* {@code /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation}
|
||||||
* <hr>
|
* <hr>
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected FSRL resolveLibraryFile(GFileSystem fs, Path libraryParentPath, String libraryName)
|
protected FSRL resolveLibraryFile(GFileSystem fs, String library) throws IOException {
|
||||||
throws IOException {
|
FSRL fsrl = super.resolveLibraryFile(fs, library);
|
||||||
GFile libraryParentDir =
|
if (fsrl != null) {
|
||||||
fs.lookup(libraryParentPath != null ? libraryParentPath.toString() : null);
|
return fsrl;
|
||||||
|
}
|
||||||
|
String libraryParentPath = FilenameUtils.getFullPath(library);
|
||||||
|
String libraryName = FilenameUtils.getName(library);
|
||||||
|
GFile libraryParentDir = fs.lookup(libraryParentPath);
|
||||||
if (libraryParentDir != null) {
|
if (libraryParentDir != null) {
|
||||||
for (GFile file : fs.getListing(libraryParentDir)) {
|
for (GFile file : fs.getListing(libraryParentDir)) {
|
||||||
if (file.isDirectory() && file.getName().equals("Versions")) {
|
if (file.isDirectory() && file.getName().equals("Versions")) {
|
||||||
Path versionsPath = libraryParentPath.resolve(file.getName());
|
String versionsPath = joinPaths(libraryParentPath, file.getName());
|
||||||
List<GFile> versionListion = fs.getListing(file);
|
List<GFile> versionListion = fs.getListing(file);
|
||||||
if (!versionListion.isEmpty()) {
|
if (!versionListion.isEmpty()) {
|
||||||
GFile specificVersionDir = versionListion.get(0);
|
GFile specificVersionDir = versionListion.get(0);
|
||||||
if (specificVersionDir.isDirectory()) {
|
if (specificVersionDir.isDirectory()) {
|
||||||
return resolveLibraryFile(fs,
|
return resolveLibraryFile(fs,
|
||||||
versionsPath.resolve(specificVersionDir.getName()), libraryName);
|
joinPaths(versionsPath, specificVersionDir.getName(), libraryName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,7 +382,7 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
|
|
||||||
if (shouldPerformReexports(options)) {
|
if (shouldPerformReexports(options)) {
|
||||||
|
@ -385,6 +390,10 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
List<DomainFolder> searchFolders =
|
List<DomainFolder> searchFolders =
|
||||||
getLibrarySearchFolders(loadedPrograms, project, options);
|
getLibrarySearchFolders(loadedPrograms, project, options);
|
||||||
|
|
||||||
|
List<LibrarySearchPath> searchPaths =
|
||||||
|
getLibrarySearchPaths(loadedPrograms.getFirst().getDomainObject(), loadSpec,
|
||||||
|
options, messageLog, monitor);
|
||||||
|
|
||||||
monitor.initialize(loadedPrograms.size());
|
monitor.initialize(loadedPrograms.size());
|
||||||
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||||
monitor.increment();
|
monitor.increment();
|
||||||
|
@ -392,7 +401,8 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
Program program = loadedProgram.getDomainObject();
|
Program program = loadedProgram.getDomainObject();
|
||||||
int id = program.startTransaction("Reexporting");
|
int id = program.startTransaction("Reexporting");
|
||||||
try {
|
try {
|
||||||
reexport(program, loadedPrograms, searchFolders, monitor, messageLog);
|
reexport(program, loadedPrograms, searchFolders, searchPaths, options, monitor,
|
||||||
|
messageLog);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
messageLog.appendException(e);
|
messageLog.appendException(e);
|
||||||
|
@ -403,7 +413,8 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
super.postLoadProgramFixups(loadedPrograms, project, loadSpec, options, messageLog,
|
||||||
|
monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -414,26 +425,30 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
||||||
* reexportable symbols from
|
* reexportable symbols from
|
||||||
* @param searchFolders A {@link List} of project folders that may contain already-loaded
|
* @param searchFolders A {@link List} of project folders that may contain already-loaded
|
||||||
* {@link Program}s with reexportable symbols
|
* {@link Program}s with reexportable symbols
|
||||||
|
* @param searchPaths A {@link List} of file system search paths that will be searched
|
||||||
|
* @param options The load options
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @param messageLog The log
|
* @param messageLog The log
|
||||||
* @throws CancelledException if the user cancelled the load operation
|
* @throws CancelledException if the user cancelled the load operation
|
||||||
* @throws IOException if there was an IO-related error during the load
|
* @throws IOException if there was an IO-related error during the load
|
||||||
*/
|
*/
|
||||||
private void reexport(Program program, List<Loaded<Program>> loadedPrograms,
|
private void reexport(Program program, List<Loaded<Program>> loadedPrograms,
|
||||||
List<DomainFolder> searchFolders, TaskMonitor monitor, MessageLog messageLog)
|
List<DomainFolder> searchFolders, List<LibrarySearchPath> searchPaths,
|
||||||
|
List<Option> options, TaskMonitor monitor, MessageLog messageLog)
|
||||||
throws CancelledException, Exception {
|
throws CancelledException, Exception {
|
||||||
|
|
||||||
for (String path : getReexportPaths(program)) {
|
for (String path : getReexportPaths(program)) {
|
||||||
Program programToRelease = null;
|
Program programToRelease = null;
|
||||||
try {
|
try {
|
||||||
Loaded<Program> match = findLibrary(loadedPrograms, path);
|
Loaded<Program> match = findLibraryInLoadedList(loadedPrograms, path);
|
||||||
Program lib = null;
|
Program lib = null;
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
lib = match.getDomainObject();
|
lib = match.getDomainObject();
|
||||||
}
|
}
|
||||||
if (lib == null) {
|
if (lib == null) {
|
||||||
for (DomainFolder searchFolder : searchFolders) {
|
for (DomainFolder searchFolder : searchFolders) {
|
||||||
DomainFile df = findLibrary(path, searchFolder);
|
DomainFile df =
|
||||||
|
findLibraryInProject(path, searchFolder, searchPaths, options, monitor);
|
||||||
if (df != null) {
|
if (df != null) {
|
||||||
DomainObject obj = df.getDomainObject(this, true, true, monitor);
|
DomainObject obj = df.getDomainObject(this, true, true, monitor);
|
||||||
if (obj instanceof Program p) {
|
if (obj instanceof Program p) {
|
||||||
|
|
|
@ -89,8 +89,8 @@ public class ApkLoader extends DexLoader {
|
||||||
// defer to the super class (DexLoader) to actually load the DEX file
|
// defer to the super class (DexLoader) to actually load the DEX file
|
||||||
List<Loaded<Program>> loadedPrograms =
|
List<Loaded<Program>> loadedPrograms =
|
||||||
super.loadProgram(dexProvider, classesDexFile.getName(), project,
|
super.loadProgram(dexProvider, classesDexFile.getName(), project,
|
||||||
concatenatePaths(programFolderPath, programName), loadSpec, options,
|
joinPaths(programFolderPath, programName), loadSpec, options, log,
|
||||||
log, consumer, monitor);
|
consumer, monitor);
|
||||||
|
|
||||||
allLoadedPrograms.addAll(loadedPrograms);
|
allLoadedPrograms.addAll(loadedPrograms);
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,7 @@ public class DyldCacheExtractLoader extends MachoLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ public class MachoFileSetExtractLoader extends MachoLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
|
||||||
List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
LoadSpec loadSpec, List<Option> options, MessageLog messageLog, TaskMonitor monitor)
|
||||||
throws CancelledException, IOException {
|
throws CancelledException, IOException {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue