Merge remote-tracking branch

'origin/GP-2277_ryanmkurtz_lib-fs--SQUASHED' (Closes #4162)
This commit is contained in:
Ryan Kurtz 2023-07-26 06:06:37 -04:00
commit e4e6a28caf
9 changed files with 301 additions and 182 deletions

View File

@ -24,8 +24,6 @@ import ghidra.framework.Platform;
* and avoiding duplicate directories.
*/
public class LibrarySearchPathManager {
public final static String CURRENT_DIRECTORY = ".";
private static List<String> pathList = createPathList();
private static boolean hasBeenRestored;
@ -37,8 +35,6 @@ public class LibrarySearchPathManager {
}
private static void loadJavaLibraryPath() {
addPath(CURRENT_DIRECTORY);//add current directory
List<String> paths = Platform.CURRENT_PLATFORM.getAdditionalLibraryPaths();
for (String path : paths) {
addPath(path);
@ -79,7 +75,6 @@ public class LibrarySearchPathManager {
public static void setLibraryPaths(String[] paths) {
pathList.clear();
addPath(CURRENT_DIRECTORY);//add current directory
for (String path : paths) {
addPath(path);
}
@ -120,6 +115,7 @@ public class LibrarySearchPathManager {
/**
* Adds the path at the specified index in path search list.
* @param index The index
* @param path the path to add
* @return true if the path was appended, false if the path was a duplicate
*/
@ -137,9 +133,6 @@ public class LibrarySearchPathManager {
* @return true if the path was removed, false if the path did not exist
*/
public static boolean removePath(String path) {
if (path.equals(CURRENT_DIRECTORY)) {
return false;
}
return pathList.remove(path);
}

View File

@ -15,22 +15,21 @@
*/
package ghidra.app.util.opinion;
import java.io.File;
import java.io.IOException;
import java.nio.file.AccessMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ObjectUtils;
import ghidra.app.util.Option;
import ghidra.app.util.OptionUtils;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.FileByteProvider;
import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.app.util.importer.MessageLog;
import ghidra.formats.gfilesystem.FSRL;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.formats.gfilesystem.*;
import ghidra.framework.model.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.lang.*;
@ -41,7 +40,6 @@ import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities;
/**
* An abstract {@link Loader} that provides a framework to conveniently load {@link Program}s with
@ -389,19 +387,20 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
}
/**
* Creates a {@link ByteProvider} for the given library file
* Creates a {@link ByteProvider} for the given library {@link FSRL}
*
* @param libFile The library file to get a {@link ByteProvider} for
* @param libFsrl The library {@link FSRL} to get a {@link ByteProvider} for
* @param loadSpec An optional {@link LoadSpec} the {@link ByteProvider} should conform to
* @param log The log
* @return A {@link ByteProvider} for the given library file, or null if one could not be
* created that matches the given {@link LoadSpec}
* @param monitor A cancellable monitor
* @return A {@link ByteProvider} for the given library {@link FSRL}, or null if one could not
* be created that matches the given {@link LoadSpec}
* @throws IOException If there was an IO-related issue
* @throws CancelledException If the user cancelled the operation
*/
protected ByteProvider createLibraryByteProvider(File libFile, LoadSpec loadSpec,
MessageLog log) throws IOException {
return new FileByteProvider(libFile, FileSystemService.getInstance().getLocalFSRL(libFile),
AccessMode.READ);
protected ByteProvider createLibraryByteProvider(FSRL libFsrl, LoadSpec loadSpec,
MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
return FileSystemService.getInstance().getByteProvider(libFsrl, true, monitor);
}
/**
@ -410,17 +409,17 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* It may be appropriate to not load a specific library after examining its bytes.
*
* @param libraryName The name of the library
* @param libraryFile The library {@link File}
* @param libraryFsrl The library {@link FSRL}
* @param provider The library bytes
* @param desiredLoadSpec The desired {@link LoadSpec}
* @param log The log
* @return True if the given library should be loaded; otherwise, false
* @throws IOException If an IO-related error occurred
*/
protected boolean shouldLoadLibrary(String libraryName, File libraryFile, ByteProvider provider,
LoadSpec desiredLoadSpec, MessageLog log) throws IOException {
protected boolean shouldLoadLibrary(String libraryName, FSRL libraryFsrl,
ByteProvider provider, LoadSpec desiredLoadSpec, MessageLog log) throws IOException {
if (matchSupportedLoadSpec(desiredLoadSpec, provider) == null) {
log.appendMsg("Skipping library which is the wrong architecture: " + libraryFile);
log.appendMsg("Skipping library which is the wrong architecture: " + libraryFsrl);
return false;
}
return true;
@ -431,7 +430,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
*
* @param library The loaded library {@link Program}
* @param libraryName The name of the library
* @param libraryFile The library {@link File}
* @param libraryFsrl The library {@link FSRL}
* @param provider The library bytes
* @param loadSpec The {@link LoadSpec} used for the load
* @param options The options
@ -440,7 +439,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* @throws IOException If an IO-related error occurred
* @throws CancelledException If the user cancelled the action
*/
protected void processLibrary(Program library, String libraryName, File libraryFile,
protected void processLibrary(Program library, String libraryName, FSRL libraryFsrl,
ByteProvider provider, LoadSpec loadSpec, List<Option> options, MessageLog log,
TaskMonitor monitor) throws IOException, CancelledException {
// Default behavior is to do nothing
@ -475,8 +474,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
createUnprocessedQueue(libraryNameList, getLibraryLoadDepth(options));
boolean loadLocalLibraries = isLoadLocalLibraries(options);
boolean loadSystemLibraries = isLoadSystemLibraries(options);
List<String> localSearchPaths = getLocalLibrarySearchPaths(provider, options);
List<String> systemSearchPaths = getSystemLibrarySearchPaths(provider, options);
List<FileSystemSearchPath> localSearchPaths =
getLocalLibrarySearchPaths(provider, options, log, monitor);
List<FileSystemSearchPath> systemSearchPaths =
getSystemLibrarySearchPaths(options, log, monitor);
DomainFolder linkSearchFolder = getLinkSearchFolder(project, projectFolderPath, options);
String libraryDestFolderPath =
getLibraryDestinationFolderPath(project, projectFolderPath, options);
@ -544,6 +545,16 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
if (!success) {
release(loadedPrograms, consumer);
}
for (FileSystemSearchPath fsSearchPath : localSearchPaths) {
if (!fsSearchPath.fsRef().isClosed()) {
fsSearchPath.fsRef().close();
}
}
for (FileSystemSearchPath fsSearchPath : systemSearchPaths) {
if (!fsSearchPath.fsRef().isClosed()) {
fsSearchPath.fsRef().close();
}
}
}
}
@ -553,7 +564,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
*
* @param libraryName The name of the library to load
* @param provider The {@link ByteProvider} of the program being loaded
* @param searchPaths The {@link List} of search paths
* @param fsSearchPaths A {@link List} of {@link FileSystemSearchPath}s that will be searched
* @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
* determined.
@ -570,22 +581,22 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* @throws CancelledException if the user cancelled the load
*/
private Loaded<Program> loadLibraryFromSearchPaths(String libraryName,
ByteProvider provider, List<String> searchPaths, String libraryDestFolderPath,
Queue<UnprocessedLibrary> unprocessed, int depth, LoadSpec desiredLoadSpec,
List<Option> options, MessageLog log, Object consumer, TaskMonitor monitor)
throws CancelledException, IOException {
ByteProvider provider, List<FileSystemSearchPath> fsSearchPaths,
String libraryDestFolderPath, Queue<UnprocessedLibrary> unprocessed, int depth,
LoadSpec desiredLoadSpec, List<Option> options, MessageLog log, Object consumer,
TaskMonitor monitor) throws CancelledException, IOException {
Program library = null;
String simpleLibraryName = FilenameUtils.getName(libraryName);
List<File> candidateLibraryFiles =
findLibrary(FilenameUtils.separatorsToUnix(libraryName), searchPaths);
List<FSRL> candidateLibraryFsrls =
findLibrary(Path.of(libraryName), fsSearchPaths, log, monitor);
boolean success = false;
try {
for (File candidateLibraryFile : candidateLibraryFiles) {
for (FSRL candidateLibraryFsrl : candidateLibraryFsrls) {
monitor.checkCancelled();
List<String> newLibraryList = new ArrayList<>();
library = loadLibrary(simpleLibraryName, candidateLibraryFile,
library = loadLibrary(simpleLibraryName, candidateLibraryFsrl,
desiredLoadSpec, newLibraryList, options, consumer, log, monitor);
for (String newLibraryName : newLibraryList) {
unprocessed.add(new UnprocessedLibrary(newLibraryName, depth - 1));
@ -593,8 +604,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
if (library == null) {
continue;
}
log.appendMsg("Library " + libraryName + ": Examining " + candidateLibraryFile);
processLibrary(library, libraryName, candidateLibraryFile, provider,
log.appendMsg("Library " + libraryName + ": Examining " + candidateLibraryFsrl);
processLibrary(library, libraryName, candidateLibraryFsrl, provider,
desiredLoadSpec, options, log, monitor);
success = true;
return new Loaded<Program>(library, simpleLibraryName, libraryDestFolderPath);
@ -667,7 +678,8 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
}
/**
* Find the library on the filesystem, returning a {@link List} of possible candidate files.
* Find the library in a {@link GFileSystem}, returning a {@link List} of possible candidate
* {@link GFile files}.
* <p>
* Each search path directory will be searched for the library file in order.
* <p>
@ -678,50 +690,70 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* If the library specifies an absolute path, its native path is searched on the local
* filesystem.
* <p>
* @param libraryPath Either a path_and_filename, or just a filename of a library
* that should be searched for
* @param searchPaths A {@link List} of filesystem paths on the local filesystem that will be
* searched
* @return A {@link List} of files that match the requested library path
* @param libraryPath The library {@link Path}. This will be either an absolute path, a
* relative path, or just a filename.
* @param fsSearchPaths A {@link List} of {@link FileSystemSearchPath}s that will be searched
* @param log The log
* @param monitor A cancelable task monitor
* @return A {@link List} of {@link GFile files} that match the requested library path
* @throws CancelledException if the user cancelled the operation
*/
private List<File> findLibrary(String libraryPath, List<String> searchPaths) {
private List<FSRL> findLibrary(Path libraryPath, List<FileSystemSearchPath> fsSearchPaths,
MessageLog log, TaskMonitor monitor) throws CancelledException {
List<FSRL> results = new ArrayList<>();
FileSystemService fsService = FileSystemService.getInstance();
String libraryName = FilenameUtils.getName(libraryPath);
List<File> results = new ArrayList<>();
Path libraryParentPath = libraryPath.getParent();
String libraryName = libraryPath.getFileName().toString();
for (String searchPath : searchPaths) {
for (FileSystemSearchPath fsSearchPath : fsSearchPaths) {
monitor.checkCancelled();
// ignore garbage entries: relative, non-existent, not directory
searchPath = FilenameUtils.normalizeNoEndSeparator(searchPath);
if (searchPath == null || searchPath.isEmpty()) {
try {
// 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);
continue;
}
File searchDir = new File(searchPath);
if (!searchDir.isAbsolute() || !searchDir.isDirectory()) {
continue;
}
// 1) Try as possible subpath under the search path
String candidatePath =
FilenameUtils.separatorsToSystem(concatenatePaths(searchPath, libraryPath));
File f = resolveLibraryFile(new File(candidatePath));
if (f == null || !f.isFile()) {
// 2) Fall back to looking for the library in the user specified search path, sans
// any subpath built into the library string
f = resolveLibraryFile(new File(searchDir, libraryName));
}
if (f != null && f.isFile() && !results.contains(f)) {
results.add(f);
}
}
if (FilenameUtils.getPrefixLength(libraryPath) > 0) {
// 3) Search the local filesystem (as if the importPath list contained "/")
// if the specified library string specifies an absolute path
File f = resolveLibraryFile(new File(libraryPath));
if (f != null && f.isAbsolute() && f.isFile() && !results.contains(f)) {
results.add(f);
}
}
return results;
@ -732,7 +764,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* a {@link ByteProvider} available.
*
* @param libraryName The name of the library to load
* @param libraryFile The library file to load
* @param libraryFsrl The library {@link FSRL} to load
* @param desiredLoadSpec The desired {@link LoadSpec}
* @param libraryNameList A {@link List} to be populated with the given library's dependent
* library names
@ -744,18 +776,19 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* @throws CancelledException if the user cancelled the load operation
* @throws IOException if there was an IO-related error during the load
*/
private Program loadLibrary(String libraryName, File libraryFile, LoadSpec desiredLoadSpec,
private Program loadLibrary(String libraryName, FSRL libraryFsrl, LoadSpec desiredLoadSpec,
List<String> libraryNameList, List<Option> options, Object consumer, MessageLog log,
TaskMonitor monitor) throws CancelledException, IOException {
try (ByteProvider provider = createLibraryByteProvider(libraryFile, desiredLoadSpec, log)) {
if (!shouldLoadLibrary(libraryName, libraryFile, provider, desiredLoadSpec, log)) {
try (ByteProvider provider =
createLibraryByteProvider(libraryFsrl, desiredLoadSpec, log, monitor)) {
if (!shouldLoadLibrary(libraryName, libraryFsrl, provider, desiredLoadSpec, log)) {
return null;
}
LoadSpec libLoadSpec = matchSupportedLoadSpec(desiredLoadSpec, provider);
if (libLoadSpec == null) {
log.appendMsg("Skipping library which is the wrong architecture: " + libraryFile);
log.appendMsg("Skipping library which is the wrong architecture: " + libraryFsrl);
return null;
}
@ -763,7 +796,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
consumer, log, monitor);
if (library == null) {
log.appendMsg("Library " + libraryFile + " failed to load for some reason");
log.appendMsg("Library " + libraryFsrl + " failed to load for some reason");
return null;
}
@ -972,36 +1005,81 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
}
/**
* Gets a {@link List} of priority-ordered local paths used to search for libraries
* A file system search path
*
* @param fsRef A {@link FileSystemRef}
* @param fsPath A {@link Path} relative to the root of the file system, or null for the root
*/
private record FileSystemSearchPath(FileSystemRef fsRef, Path fsPath) {}
/**
* Gets a {@link List} of priority-ordered local {@link FileSystemSearchPath}s used to search
* for libraries
*
* @param provider The {@link ByteProvider} of the program being loaded
* @param options The options
* @return A {@link List} of priority-ordered local paths used to search for libraries
* @param log The log
* @param monitor A cancelable task monitor
* @return A {@link List} of priority-ordered local {@link FileSystemSearchPath}s used to s
* search for libraries
*/
private List<String> getLocalLibrarySearchPaths(ByteProvider provider, List<Option> options) {
List<String> paths = new ArrayList<>();
private List<FileSystemSearchPath> getLocalLibrarySearchPaths(ByteProvider provider,
List<Option> options, MessageLog log, TaskMonitor monitor) {
List<FileSystemSearchPath> result = new ArrayList<>();
FileSystemService fsService = FileSystemService.getInstance();
if (isLoadLocalLibraries(options) || shouldSearchAllPaths(options)) {
String parent = getProviderFilePath(provider);
if (parent != null) {
paths.add(parent);
FSRL providerFsrl = provider.getFSRL();
if (providerFsrl != null) {
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
GFile parentFile = fileRef.file.getParentFile();
result.add(
new FileSystemSearchPath(fileRef.fsRef, Path.of(parentFile.getPath())));
}
catch (IOException | CancelledException e) {
log.appendException(e);
}
}
}
return paths;
return result;
}
/**
* Gets a {@link List} of priority-ordered system paths used to search for libraries
* Gets a {@link List} of priority-ordered system {@link FileSystemSearchPath}s used to search
* for libraries
*
* @param provider The {@link ByteProvider} of the program being loaded
* @param options The options
* @return A {@link List} of priority-ordered system paths used to search for libraries
* @param log The log
* @param monitor A cancelable task monitor
* @return A {@link List} of priority-ordered system {@link FileSystemSearchPath}s used to
* search for libraries
*/
private List<String> getSystemLibrarySearchPaths(ByteProvider provider, List<Option> options) {
List<String> paths = new ArrayList<>();
private List<FileSystemSearchPath> getSystemLibrarySearchPaths(List<Option> options,
MessageLog log, TaskMonitor monitor) {
List<FileSystemSearchPath> result = new ArrayList<>();
FileSystemService fsService = FileSystemService.getInstance();
if (isLoadSystemLibraries(options) || shouldSearchAllPaths(options)) {
paths.addAll(LibrarySearchPathManager.getLibraryPathsList());
List<Path> searchPaths = LibrarySearchPathManager.getLibraryPathsList()
.stream()
.map(s -> Path.of(s).normalize())
.filter(Path::isAbsolute)
.filter(Files::exists)
.toList();
for (Path searchPath : searchPaths) {
try {
FSRL searchFSRL = fsService.getLocalFSRL(searchPath.toFile());
FileSystemRef fsRef =
fsService.probeFileForFilesystem(searchFSRL, monitor, null);
if (fsRef != null) {
result.add(new FileSystemSearchPath(fsRef, null));
}
}
catch (IOException | CancelledException e) {
log.appendException(e);
}
}
}
return paths;
return result;
}
/**
@ -1050,56 +1128,41 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
}
/**
* Resolves the given library path to an existing {@link File} on disk. Some {@link Loader}s
* Resolves the given library path to an existing {@link FSRL}. Some {@link Loader}s
* have relaxed requirements on what counts as a valid library filename match. For example,
* case-insensitive lookup may be allowed, and filename extensions may be optional.
*
* @param libraryFile The library file to resolve
* @return The library file resolved to an existing {@link File} on disk, or null if it did not
* resolve
* @param fs The {@link GFileSystem file system} to resolve in
* @param libraryParentPath The {@link Path} of the libraries parent directory, relative to the
* given file system (could be null)
* @param libraryName The library name
* @return The library resolved to an existing {@link FSRL}, or null if it did not resolve
* @throws IOException If an IO-related problem occurred
*/
private File resolveLibraryFile(File libraryFile) {
File ret = libraryFile;
if (isCaseInsensitiveLibraryFilenames()) {
ret = FileUtilities.resolveFileCaseInsensitive(libraryFile);
}
if (ret.exists()) {
return ret;
}
if (isOptionalLibraryFilenameExtensions() &&
FilenameUtils.getExtension(libraryFile.toString()).equals("")) {
File[] files = libraryFile.getParentFile().listFiles();
if (files != null) {
Comparator<String> libNameComparator = getLibraryNameComparator();
for (File file : files) {
String baseName = FilenameUtils.getBaseName(file.toString());
if (libNameComparator.compare(libraryFile.getName(), baseName) == 0) {
return file;
}
protected FSRL resolveLibraryFile(GFileSystem fs, Path libraryParentPath, String libraryName)
throws IOException {
GFile libraryParentDir =
fs.lookup(libraryParentPath != null ? libraryParentPath.toString() : null);
boolean compareWithoutExtension = isOptionalLibraryFilenameExtensions() &&
FilenameUtils.getExtension(libraryName).equals("");
if (libraryParentDir != null) {
Comparator<String> libNameComparator = getLibraryNameComparator();
for (GFile file : fs.getListing(libraryParentDir)) {
if (file.isDirectory()) {
continue;
}
String compareName = file.getName();
if (compareWithoutExtension) {
compareName = FilenameUtils.getBaseName(compareName);
}
if (libNameComparator.compare(libraryName, compareName) == 0) {
return file.getFSRL();
}
}
}
return null;
}
/**
* Returns the path the loaded {@link ByteProvider} is located in.
* <p>
* Special case when the ByteProvider specifies a {@link FSRL}, try to get the 'real'
* path on the local filesystem, otherwise return null.
*
* @param provider The {@link ByteProvider}.
* @return The path the loaded {@link ByteProvider} is located in.
*/
private String getProviderFilePath(ByteProvider provider) {
FSRL fsrl = provider.getFSRL();
if ((fsrl != null) && !fsrl.getFS().hasContainer()) {
return FilenameUtils.getFullPathNoEndSeparator(fsrl.getPath());
}
File f = provider.getFile();
return (f != null) ? f.getParent() : null;
}
/**
* Gets a {@link Comparator} for comparing library filenames
*

View File

@ -24,6 +24,7 @@ import ghidra.app.util.Option;
import ghidra.app.util.OptionUtils;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.formats.gfilesystem.*;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.Project;
import ghidra.framework.options.Options;
@ -75,17 +76,19 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
}
@Override
protected boolean shouldLoadLibrary(String libName, File libFile,
ByteProvider provider, LoadSpec loadSpec, MessageLog log) throws IOException {
protected boolean shouldLoadLibrary(String libName, FSRL libFsrl, ByteProvider provider,
LoadSpec loadSpec, MessageLog log) throws IOException {
if (!super.shouldLoadLibrary(libName, libFile, provider, loadSpec, log)) {
if (!super.shouldLoadLibrary(libName, libFsrl, provider, loadSpec, log)) {
return false;
}
int size = loadSpec.getLanguageCompilerSpec().getLanguageDescription().getSize();
File localLibFile = getLocalFile(libFsrl);
if (!LibraryLookupTable.hasFileAndPathAndTimeStampMatch(libFile, size) &&
LibraryLookupTable.libraryLookupTableFileExists(libName, size)) {
if (localLibFile == null ||
!LibraryLookupTable.hasFileAndPathAndTimeStampMatch(localLibFile, size) &&
LibraryLookupTable.libraryLookupTableFileExists(libName, size)) {
log.appendMsg("WARNING! Using existing exports file for " + libName +
" which may not be an exact match");
}
@ -94,26 +97,46 @@ public abstract class AbstractOrdinalSupportLoader extends AbstractLibrarySuppor
}
@Override
protected void processLibrary(Program lib, String libName, File libFile,
ByteProvider provider, LoadSpec loadSpec, List<Option> options, MessageLog log,
TaskMonitor monitor) throws IOException, CancelledException {
protected void processLibrary(Program lib, String libName, FSRL libFsrl, ByteProvider provider,
LoadSpec loadSpec, List<Option> options, MessageLog log, TaskMonitor monitor)
throws IOException, CancelledException {
int size = loadSpec.getLanguageCompilerSpec().getLanguageDescription().getSize();
File localLibFile = getLocalFile(libFsrl);
// Create exports file
if (!LibraryLookupTable.libraryLookupTableFileExists(libName, size) ||
!LibraryLookupTable.hasFileAndPathAndTimeStampMatch(libFile, size)) {
if (localLibFile == null ||
!LibraryLookupTable.libraryLookupTableFileExists(libName, size) ||
!LibraryLookupTable.hasFileAndPathAndTimeStampMatch(localLibFile, size)) {
try {
// Need to write correct library exports file (LibrarySymbolTable)
// for use with related imports
LibraryLookupTable.createFile(lib, true, monitor);
}
catch (IOException e) {
log.appendMsg("Unable to create exports file for " + libFile);
Msg.error(this, "Unable to create exports file for " + libFile, e);
log.appendMsg("Unable to create exports file for " + libFsrl);
Msg.error(this, "Unable to create exports file for " + libFsrl, e);
}
}
}
/**
* If the given {@link FSRL} is from a {@link LocalFileSystem}, its corresponding local
* {@link File} is returned
*
* @param fsrl A {@link FSRL}
* @return The given {@link FSRL}'s corresponding local {@link File}, or null if it doesn't
* have one
*/
private File getLocalFile(FSRL fsrl) {
try {
return FileSystemService.getInstance().getLocalFS().getLocalFile(fsrl);
}
catch (IOException e) {
// fall thru
}
return null;
}
@Override
protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Project project,
List<Option> options, MessageLog messageLog, TaskMonitor monitor)

View File

@ -44,8 +44,8 @@ public class LibraryPathsDialog extends DialogComponentProvider {
tablePanel = new PathnameTablePanel(libraryPaths, false, true, () -> reset());
// false=> not editable, true=> add new paths to top of the table
tablePanel.setFileChooserProperties("Select Directory", "LibrarySearchDirectory",
GhidraFileChooserMode.DIRECTORIES_ONLY, false, null);
tablePanel.setFileChooserProperties("Select Directory or Filesystem",
"LibrarySearchDirectory", GhidraFileChooserMode.FILES_AND_DIRECTORIES, false, null);
return tablePanel;
}

View File

@ -15,18 +15,18 @@
*/
package ghidra.app.util.opinion;
import java.io.File;
import java.io.IOException;
import java.nio.file.AccessMode;
import java.nio.file.Path;
import java.util.*;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.Option;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.ByteProviderWrapper;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.ubi.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.formats.gfilesystem.*;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.listing.Program;
import ghidra.util.LittleEndianDataConverter;
@ -115,24 +115,22 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
* found that is successful (meaning it matches the correct architecture). Only one file
* in the UBI will ever be imported. If the provided file is NOT a UBI, default
* import method will be invoked.
* <hr>
* {@inheritDoc}
*/
@Override
protected ByteProvider createLibraryByteProvider(File libFile, LoadSpec loadSpec, MessageLog log)
throws IOException {
protected ByteProvider createLibraryByteProvider(FSRL libFsrl, LoadSpec loadSpec,
MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
if (!libFile.isFile()) {
return null;
}
ByteProvider provider = new FileByteProvider(libFile,
FileSystemService.getInstance().getLocalFSRL(libFile), AccessMode.READ);
ByteProvider provider = super.createLibraryByteProvider(libFsrl, loadSpec, log, monitor);
try {
FatHeader header = new FatHeader(provider);
List<FatArch> architectures = header.getArchitectures();
if (architectures.isEmpty()) {
log.appendMsg("WARNING! No archives found in the UBI: " + libFile);
log.appendMsg("WARNING! No archives found in the UBI: " + libFsrl);
return null;
}
@ -158,4 +156,45 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
return provider;
}
/**
* Special Mach-O library file resolver to account for a "Versions" subdirectory being inserted
* in the library lookup path. For example, a reference to:
* <p>
* {@code /System/Library/Frameworks/Foundation.framework/Foundation}
* <p>
* might be found at:
* <p>
* {@code /System/Library/Frameworks/Foundation.framework//Versions/C/Foundation}
* <hr>
* {@inheritDoc}
*/
@Override
protected FSRL resolveLibraryFile(GFileSystem fs, Path libraryParentPath, String libraryName)
throws IOException {
GFile libraryParentDir =
fs.lookup(libraryParentPath != null ? libraryParentPath.toString() : null);
if (libraryParentDir != null) {
for (GFile file : fs.getListing(libraryParentDir)) {
if (file.isDirectory() && file.getName().equals("Versions")) {
Path versionsPath = libraryParentPath.resolve(file.getName());
List<GFile> versionListion = fs.getListing(file);
if (!versionListion.isEmpty()) {
GFile specificVersionDir = versionListion.get(0);
if (specificVersionDir.isDirectory()) {
return resolveLibraryFile(fs,
versionsPath.resolve(specificVersionDir.getName()), libraryName);
}
}
}
else if (file.isDirectory()) {
continue;
}
if (file.getName().equals(libraryName)) {
return file.getFSRL();
}
}
}
return null;
}
}

View File

@ -1136,16 +1136,13 @@ public class MachoProgramBuilder {
}
if (libraryPath != null) {
// For now, strip off the full path and just the use the filename. We will utilize
// the full path one day when we started looking in dyld_shared_cache files for
// libraries.
int index = libraryPath.lastIndexOf("/");
String libraryName = index != -1 ? libraryPath.substring(index + 1) : libraryPath;
if (!libraryName.equals(program.getName())) {
addLibrary(libraryName);
addLibrary(libraryPath);
props.setString(
ExternalSymbolResolver.getRequiredLibraryProperty(libraryIndex++),
libraryName);
libraryPath);
}
}
}

View File

@ -15,9 +15,8 @@
*/
package ghidra.formats.gfilesystem;
import java.util.List;
import java.io.*;
import java.util.List;
import ghidra.app.util.bin.*;
import ghidra.formats.gfilesystem.FileCache.FileCacheEntry;
@ -211,11 +210,11 @@ public class FileSystemService {
}
/**
* Returns a direct reference to a filesystem that represents the local filesystem.
* Returns a direct reference to the {@link LocalFileSystem local filesystem}.
*
* @return {@link GFileSystem} that represents the local filesystem.
* @return A direct reference to the {@link LocalFileSystem local filesystem}.
*/
public GFileSystem getLocalFS() {
public LocalFileSystem getLocalFS() {
return localFS;
}

View File

@ -67,9 +67,9 @@ public class ExternalSymbolResolver {
public static void fixUnresolvedExternalSymbols(List<Loaded<Program>> loadedPrograms,
boolean fixAll, MessageLog messageLog, TaskMonitor monitor)
throws CancelledException, IOException {
Map<String, Loaded<Program>> loadedByName = loadedPrograms.stream()
.collect(
Collectors.toMap(loaded -> loaded.getName(), loaded -> loaded));
Map<String, Loaded<Program>> loadedByPath = loadedPrograms.stream()
.collect(Collectors.toMap(
loaded -> loaded.getProjectFolderPath() + loaded.getName(), loaded -> loaded));
List<Loaded<Program>> fixupList =
loadedPrograms.subList(0, fixAll ? loadedPrograms.size() : 1);
@ -101,8 +101,8 @@ public class ExternalSymbolResolver {
if (libPath == null) {
continue;
}
Loaded<Program> loadedLib = loadedByName.get(libName);
Loaded<Program> loadedLib = loadedByPath.get(libPath);
if (loadedLib == null) {
messageLog.appendMsg("Referenced external program not found: " + libName);
continue;

View File

@ -210,6 +210,11 @@ public enum Platform {
paths.add("/usr/X11R6/bin");
paths.add("/usr/X11R6/lib");
}
else if (operatingSystem == OperatingSystem.MAC_OS_X) {
paths.add("/System/Library/dyld/dyld_shared_cache_arm64e");
paths.add(
"/System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_arm64e");
}
else if (CURRENT_PLATFORM == WIN_X86_64) {
String windir = System.getenv("SystemRoot");
if (windir != null) {