mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
Merge remote-tracking branch 'origin/GP-4145_dev747368_external_symbol_resolver--SQUASHED'
This commit is contained in:
commit
9c31aa48d8
3 changed files with 427 additions and 235 deletions
|
@ -15,21 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.analysis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.app.util.opinion.MachoLoader;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Library;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ExternalSymbolResolver;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
|
@ -61,7 +56,7 @@ public class ExternalSymbolResolverAnalyzer extends AbstractAnalyzer {
|
|||
if (program.getDomainFile().getParent() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||
String format = options.getString("Executable Format", null);
|
||||
return ElfLoader.ELF_NAME.equals(format) || MachoLoader.MACH_O_NAME.equals(format);
|
||||
|
@ -71,65 +66,16 @@ public class ExternalSymbolResolverAnalyzer extends AbstractAnalyzer {
|
|||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException {
|
||||
|
||||
Object consumer = new Object();
|
||||
log = new MessageLog(); // For now, we don't want the analysis log spammed
|
||||
ProjectData projectData = program.getDomainFile().getParent().getProjectData();
|
||||
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
|
||||
|
||||
// Add program to list
|
||||
loadedPrograms.add(new Loaded<>(program, program.getName(),
|
||||
program.getDomainFile().getParent().getPathname()));
|
||||
|
||||
// Add external libraries to list
|
||||
for (Library extLibrary : ExternalSymbolResolver.getLibrarySearchList(program)) {
|
||||
monitor.checkCancelled();
|
||||
String libPath = extLibrary.getAssociatedProgramPath();
|
||||
if (libPath == null) {
|
||||
continue;
|
||||
try (ExternalSymbolResolver esr = new ExternalSymbolResolver(
|
||||
program.getDomainFile().getParent().getProjectData(), monitor)) {
|
||||
esr.addProgramToFixup(program);
|
||||
esr.fixUnresolvedExternalSymbols();
|
||||
esr.logInfo(s -> Msg.info(this, s), false);
|
||||
if (esr.hasProblemLibraries()) {
|
||||
// causes a popup message at end of analysis session
|
||||
esr.logInfo(log::appendMsg, true);
|
||||
}
|
||||
|
||||
DomainFile libDomainFile = projectData.getFile(libPath);
|
||||
if (libDomainFile == null) {
|
||||
log.appendMsg("Referenced external program not found: " + libPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
DomainObject libDomainObject =
|
||||
libDomainFile.getDomainObject(consumer, false, false, monitor);
|
||||
if (libDomainObject instanceof Program p) {
|
||||
loadedPrograms.add(new Loaded<>(p, libDomainFile.getName(),
|
||||
libDomainFile.getParent().getPathname()));
|
||||
}
|
||||
else {
|
||||
libDomainObject.release(consumer);
|
||||
log.appendMsg("Referenced external program is not a program: " + libPath);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg("Failed to open library dependency project file: " +
|
||||
libDomainFile.getPathname());
|
||||
}
|
||||
catch (VersionException e) {
|
||||
log.appendMsg(
|
||||
"Referenced external program requires updgrade, unable to consider symbols: " +
|
||||
libPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve symbols
|
||||
try {
|
||||
ExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedPrograms, false, log,
|
||||
monitor);
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
for (int i = 1; i < loadedPrograms.size(); i++) {
|
||||
loadedPrograms.get(i).release(consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,8 +158,16 @@ public class ElfLoader extends AbstractLibrarySupportLoader {
|
|||
throws CancelledException, IOException {
|
||||
super.postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
|
||||
|
||||
ExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedPrograms, true, messageLog,
|
||||
monitor);
|
||||
try (ExternalSymbolResolver esr =
|
||||
new ExternalSymbolResolver(project.getProjectData(), monitor)) {
|
||||
for (Loaded<Program> loadedProgram : loadedPrograms) {
|
||||
esr.addProgramToFixup(
|
||||
loadedProgram.getProjectFolderPath() + loadedProgram.getName(),
|
||||
loadedProgram.getDomainObject());
|
||||
}
|
||||
esr.fixUnresolvedExternalSymbols();
|
||||
esr.logInfo(messageLog::appendMsg, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
package ghidra.program.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.Loaded;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
@ -30,8 +30,15 @@ import ghidra.util.StringUtilities;
|
|||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class ExternalSymbolResolver {
|
||||
|
||||
/**
|
||||
* Moves dangling external function symbols found in the {@link Library#UNKNOWN EXTERNAL/UNKNOWN}
|
||||
* namespace into the namespace of the external library that publishes a matching symbol.
|
||||
* <p>
|
||||
* This uses an ordered list of external library names that was attached to the program during
|
||||
* import by the Elf or Macho loader (see {@link #REQUIRED_LIBRARY_PROPERTY_PREFIX}).
|
||||
*
|
||||
*/
|
||||
public class ExternalSymbolResolver implements Closeable {
|
||||
private final static String REQUIRED_LIBRARY_PROPERTY_PREFIX = "Required Library [";
|
||||
|
||||
/**
|
||||
|
@ -45,127 +52,415 @@ public class ExternalSymbolResolver {
|
|||
StringUtilities.pad("" + libraryIndex, ' ', 4));
|
||||
}
|
||||
|
||||
private final ProjectData projectData;
|
||||
private final TaskMonitor monitor;
|
||||
private final List<ProgramSymbolResolver> programsToFix = new ArrayList<>();
|
||||
private final Map<String, Program> loadedPrograms = new HashMap<>();
|
||||
private final Map<String, Throwable> problemLibraries = new HashMap<>();
|
||||
|
||||
public ExternalSymbolResolver(ProjectData projectData, TaskMonitor monitor) {
|
||||
this.projectData = projectData;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links unresolved symbols to the first symbol found in the (ordered) linked
|
||||
* libraries (saved in the program's properties as {@value #REQUIRED_LIBRARY_PROPERTY_PREFIX}).
|
||||
* Queues a program into this session that will be fixed when {@link #fixUnresolvedExternalSymbols()}
|
||||
* is called.
|
||||
* <p>
|
||||
* The ordering and precedence logic is loader specific though no particular binary formats
|
||||
* are parsed or required.
|
||||
* <p>
|
||||
* The program's external libraries need to already be populated with paths to
|
||||
* already existing / imported libraries.
|
||||
*
|
||||
* @param loadedPrograms The {@link Loaded} {@link Program}s to fix. The first entry is the
|
||||
* "primary" {@link Loaded} {@link Program}.
|
||||
* @param fixAll True if all of the {@link Loaded} {@link Program}s should be fixed;
|
||||
* false if just the "primary" {@link Loaded} {@link Program} should be fixed.
|
||||
* @param messageLog {@link MessageLog} to write info message to.
|
||||
* @param monitor {@link TaskMonitor} to watch for cancel and update with progress.
|
||||
* @throws CancelledException if user cancels
|
||||
* @throws IOException if error reading
|
||||
* The program should be fully persisted to the project if using this method, otherwise use
|
||||
* {@link #addProgramToFixup(String, Program)} and specify the pathname the program will
|
||||
* be saved to.
|
||||
*
|
||||
* @param program {@link Program} to fix
|
||||
*/
|
||||
public static void fixUnresolvedExternalSymbols(List<Loaded<Program>> loadedPrograms,
|
||||
boolean fixAll, MessageLog messageLog, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
Map<String, Loaded<Program>> loadedByPath = loadedPrograms.stream()
|
||||
.collect(Collectors.toMap(
|
||||
loaded -> loaded.getProjectFolderPath() + loaded.getName(), loaded -> loaded));
|
||||
public void addProgramToFixup(Program program) {
|
||||
addProgramToFixup(program.getDomainFile().getPathname(), program);
|
||||
}
|
||||
|
||||
List<Loaded<Program>> fixupList =
|
||||
loadedPrograms.subList(0, fixAll ? loadedPrograms.size() : 1);
|
||||
/**
|
||||
* Queues a program into this session that will be fixed when {@link #fixUnresolvedExternalSymbols()}
|
||||
* is called.
|
||||
*
|
||||
* @param programPath string project path to the program
|
||||
* @param program {@link Program} to fix
|
||||
*/
|
||||
public void addProgramToFixup(String programPath, Program program) {
|
||||
programsToFix.add(new ProgramSymbolResolver(program, programPath));
|
||||
addLoadedProgram(programPath, program);
|
||||
}
|
||||
|
||||
monitor.initialize(fixupList.size());
|
||||
for (Loaded<Program> loadedProgram : fixupList) {
|
||||
Program program = loadedProgram.getDomainObject();
|
||||
/**
|
||||
* Adds an already opened program to this session, allowing it to be used as an external
|
||||
* library without needing to look it up in the current project.
|
||||
*
|
||||
* @param programPath project path to already opened program
|
||||
* @param program {@link Program}
|
||||
*/
|
||||
public void addLoadedProgram(String programPath, Program program) {
|
||||
if (loadedPrograms.put(programPath, program) == null) {
|
||||
program.addConsumer(this);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<Long> unresolvedExternalFunctionIds =
|
||||
getUnresolvedExternalFunctionIds(program);
|
||||
if (unresolvedExternalFunctionIds.size() == 0) {
|
||||
continue;
|
||||
/**
|
||||
* Returns true if there was an error encountered when trying to open an external library.
|
||||
*
|
||||
* @return boolean flag, true if there was a problem opening an external library
|
||||
*/
|
||||
public boolean hasProblemLibraries() {
|
||||
return !problemLibraries.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
for (Program prog : loadedPrograms.values()) {
|
||||
prog.release(this);
|
||||
}
|
||||
programsToFix.clear();
|
||||
loadedPrograms.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves any unresolved external symbols in each program that has been queued up via
|
||||
* {@link #addProgramToFixup(String, Program)}.
|
||||
*
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public void fixUnresolvedExternalSymbols() throws CancelledException {
|
||||
for (ProgramSymbolResolver psr : programsToFix) {
|
||||
psr.resolveExternalSymbols();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs information about the libraries and symbols that were found during the fixup.
|
||||
*
|
||||
* @param logger consumer that will log a string
|
||||
* @param shortSummary boolean flag, if true individual symbol names will be omitted
|
||||
*/
|
||||
public void logInfo(Consumer<String> logger, boolean shortSummary) {
|
||||
for (ProgramSymbolResolver psr : programsToFix) {
|
||||
psr.log(logger, shortSummary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a program from a cache of Program instances. If the requested program
|
||||
* isn't currently in the cache, it will be opened (if possible).
|
||||
* <p>
|
||||
* This cache of programs are pinned by registering a consumer on the program, and will be
|
||||
* released during {@link #close()} of this ExternalSymbolServer instance.
|
||||
* <p>
|
||||
* This cache is shared between all ProgramSymbolResolver instances (that were created
|
||||
* by calling {@link #addProgramToFixup(String, Program)}).
|
||||
*
|
||||
* @param libPath project path to a library program
|
||||
* @return {@link Program}, or null if not found or other error during opening
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
protected Program getLibraryProgram(String libPath) throws CancelledException {
|
||||
Program result = loadedPrograms.get(libPath);
|
||||
if (result == null && !problemLibraries.containsKey(libPath)) {
|
||||
result = openLibraryFile(projectData.getFile(libPath), libPath);
|
||||
|
||||
if (result != null) {
|
||||
loadedPrograms.put(libPath, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Library> libSearchList = getLibrarySearchList(program);
|
||||
if (libSearchList.isEmpty()) {
|
||||
continue;
|
||||
/**
|
||||
* Opens a library binary.
|
||||
*
|
||||
* @param libDf optional, reference to a the DomainFile that was found in a project. If null
|
||||
* (meaning a lookup in the project failed to find a matching file), libPath will be used when
|
||||
* creating error strings that reference the problematic file
|
||||
* @param libPath project path for the DomainFile
|
||||
* @return a opened {@link Program}
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
protected Program openLibraryFile(DomainFile libDf, String libPath) throws CancelledException {
|
||||
try {
|
||||
if (libDf == null) {
|
||||
throw new IOException("Dangling external path: " + libPath);
|
||||
}
|
||||
DomainObject libDo = libDf.getDomainObject(this, false, false, monitor);
|
||||
if (libDo instanceof Program p) {
|
||||
return p;
|
||||
}
|
||||
libDo.release(this);
|
||||
throw new IOException("Referenced external program is not a program: " + libPath);
|
||||
}
|
||||
catch (IOException | VersionException e) {
|
||||
problemLibraries.put(libPath, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try (Transaction tx = program.openTransaction("Resolve External Symbols")) {
|
||||
|
||||
messageLog.appendMsg("----- [" + program.getName() + "] Resolve " +
|
||||
unresolvedExternalFunctionIds.size() + " external symbols -----");
|
||||
|
||||
for (Library extLibrary : libSearchList) {
|
||||
monitor.checkCancelled();
|
||||
String libName = extLibrary.getName();
|
||||
String libPath = extLibrary.getAssociatedProgramPath();
|
||||
if (libPath == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Loaded<Program> loadedLib = loadedByPath.get(libPath);
|
||||
if (loadedLib == null) {
|
||||
messageLog.appendMsg("Referenced external program not found: " + libName);
|
||||
continue;
|
||||
}
|
||||
|
||||
Program libProgram = loadedLib.getDomainObject();
|
||||
monitor.setMessage("Resolving symbols published by library " + libName);
|
||||
resolveSymbolsToLibrary(program, unresolvedExternalFunctionIds, extLibrary,
|
||||
libProgram, messageLog, monitor);
|
||||
/**
|
||||
* Represents a program that needs its external symbols to be fixed.
|
||||
*/
|
||||
private class ProgramSymbolResolver {
|
||||
record ExtLibInfo(String name, Library lib, String programPath, Program program,
|
||||
List<String> resolvedSymbols, Throwable problem) {
|
||||
String getProblemMessage() {
|
||||
if (problem instanceof VersionException ve) {
|
||||
return getVersionError(ve);
|
||||
}
|
||||
return problem != null ? problem.getMessage() : "";
|
||||
}
|
||||
|
||||
String getLibPath() {
|
||||
return programPath != null ? programPath : "missing";
|
||||
}
|
||||
|
||||
String getVersionError(VersionException ve) {
|
||||
String versionType = switch (ve.getVersionIndicator()) {
|
||||
case VersionException.NEWER_VERSION -> " newer";
|
||||
case VersionException.OLDER_VERSION -> "n older";
|
||||
default -> "n unknown";
|
||||
};
|
||||
|
||||
String upgradeMsg = ve.isUpgradable() ? " (upgrade is possible)" : "";
|
||||
|
||||
return "skipped: file was created with a%s version of Ghidra%s"
|
||||
.formatted(versionType, upgradeMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Program program;
|
||||
String programPath;
|
||||
int externalSymbolCount;
|
||||
List<Long> unresolvedExternalFunctionIds;
|
||||
List<ExtLibInfo> extLibs = new ArrayList<>();
|
||||
|
||||
private ProgramSymbolResolver(Program program, String programPath) {
|
||||
this.program = program;
|
||||
this.programPath = programPath;
|
||||
}
|
||||
|
||||
private int getResolvedSymbolCount() {
|
||||
return externalSymbolCount - unresolvedExternalFunctionIds.size();
|
||||
}
|
||||
|
||||
private void log(Consumer<String> logger, boolean shortSummary) {
|
||||
boolean changed = unresolvedExternalFunctionIds.size() != externalSymbolCount;
|
||||
if (extLibs.isEmpty() && externalSymbolCount == 0) {
|
||||
return;
|
||||
}
|
||||
else if (!changed && !hasSomeLibrariesConfigured()) {
|
||||
logger.accept(
|
||||
"Resolving External Symbols of [%s] - %d unresolved symbols, no external libraries configured - skipping"
|
||||
.formatted(programPath, externalSymbolCount));
|
||||
return;
|
||||
}
|
||||
|
||||
logger.accept("Resolving External Symbols of [%s]%s".formatted(programPath,
|
||||
shortSummary ? " - Summary" : ""));
|
||||
logger.accept("\t%d external symbols resolved, %d remain unresolved"
|
||||
.formatted(getResolvedSymbolCount(), unresolvedExternalFunctionIds.size()));
|
||||
for (ExtLibInfo extLib : extLibs) {
|
||||
if (extLib.problem != null) {
|
||||
logger.accept("\t[%s] -> %s, %s".formatted(extLib.name, extLib.getLibPath(),
|
||||
extLib.getProblemMessage()));
|
||||
}
|
||||
else if (extLib.programPath != null) {
|
||||
logger.accept("\t[%s] -> %s, %d new symbols resolved".formatted(extLib.name,
|
||||
extLib.getLibPath(), extLib.resolvedSymbols.size()));
|
||||
}
|
||||
else {
|
||||
logger.accept("\t[%s] -> %s".formatted(extLib.name, extLib.getLibPath()));
|
||||
}
|
||||
if (!shortSummary) {
|
||||
for (String symbolName : extLib.resolvedSymbols) {
|
||||
logger.accept("\t\t[%s]".formatted(symbolName));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!shortSummary && changed) {
|
||||
if (!unresolvedExternalFunctionIds.isEmpty()) {
|
||||
logger.accept("\tUnresolved remaining %d:"
|
||||
.formatted(unresolvedExternalFunctionIds.size()));
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
for (Long symId : unresolvedExternalFunctionIds) {
|
||||
Symbol s = symbolTable.getSymbol(symId);
|
||||
logger.accept("\t\t[%s]".formatted(s.getName()));
|
||||
}
|
||||
}
|
||||
messageLog.appendMsg("Unresolved external symbols which remain: " +
|
||||
unresolvedExternalFunctionIds.size());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasSomeLibrariesConfigured() {
|
||||
for (ExtLibInfo extLib : extLibs) {
|
||||
if (extLib.program != null || extLib.problem != null ||
|
||||
extLib.programPath != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resolveExternalSymbols() throws CancelledException {
|
||||
unresolvedExternalFunctionIds = getUnresolvedExternalFunctionIds();
|
||||
externalSymbolCount = unresolvedExternalFunctionIds.size();
|
||||
|
||||
if (unresolvedExternalFunctionIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
extLibs = getLibsToSearch();
|
||||
|
||||
if (!extLibs.isEmpty()) {
|
||||
try (Transaction tx = program.openTransaction("Resolve External Symbols")) {
|
||||
for (ExtLibInfo extLib : extLibs) {
|
||||
monitor.checkCancelled();
|
||||
resolveSymbolsToLibrary(extLib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ordered list of external libraries that need to be searched.
|
||||
*
|
||||
* @return list of ExtLibInfo elements, each representing an external library dependency
|
||||
* found in the {@link #program}
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
private List<ExtLibInfo> getLibsToSearch() throws CancelledException {
|
||||
List<ExtLibInfo> result = new ArrayList<>();
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
for (String libName : getOrderedRequiredLibraryNames()) {
|
||||
Library lib = externalManager.getExternalLibrary(libName);
|
||||
String libPath = lib != null ? lib.getAssociatedProgramPath() : null;
|
||||
Program libProg = libPath != null ? getLibraryProgram(libPath) : null;
|
||||
Throwable problem =
|
||||
libProg == null && libPath != null ? problemLibraries.get(libPath) : null;
|
||||
|
||||
result.add(
|
||||
new ExtLibInfo(libName, lib, libPath, libProg, new ArrayList<>(), problem));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves unresolved functions from the EXTERNAL/UNKNOWN namespace to the namespace of the
|
||||
* external library if the extLib publishes a symbol with a matching name.
|
||||
*
|
||||
* @param extLib {@link ExtLibInfo} representing an external library
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
private void resolveSymbolsToLibrary(ExtLibInfo extLib) throws CancelledException {
|
||||
if (extLib.program == null) {
|
||||
// can't do anything if the external library doesn't have a valid program associated
|
||||
return;
|
||||
}
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
|
||||
for (Iterator<Long> idIterator = unresolvedExternalFunctionIds.iterator(); idIterator
|
||||
.hasNext();) {
|
||||
monitor.checkCancelled();
|
||||
Symbol s = symbolTable.getSymbol(idIterator.next());
|
||||
if (s == null || !s.isExternal() || s.getSymbolType() != SymbolType.FUNCTION) {
|
||||
Msg.error(ExternalSymbolResolver.class,
|
||||
"Concurrent modification of symbol table while resolving external symbols");
|
||||
idIterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
ExternalLocation extLoc = externalManager.getExternalLocation(s);
|
||||
String extLocName =
|
||||
Objects.requireNonNullElse(extLoc.getOriginalImportedName(), extLoc.getLabel());
|
||||
if (isExportedSymbol(extLib.program, extLocName)) {
|
||||
try {
|
||||
s.setNamespace(extLib.lib);
|
||||
idIterator.remove();
|
||||
extLib.resolvedSymbols.add(s.getName());
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException
|
||||
| CircularDependencyException e) {
|
||||
Msg.error(ExternalSymbolResolver.class,
|
||||
"Error setting external symbol namespace for " + extLoc.getLabel(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all external functions under the EXTERNAL/UNKNOWN library.
|
||||
*
|
||||
* @return list of func ids that need to be fixed
|
||||
*/
|
||||
private List<Long> getUnresolvedExternalFunctionIds() {
|
||||
List<Long> symbolIds = new ArrayList<>();
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
Library library = externalManager.getExternalLibrary(Library.UNKNOWN);
|
||||
if (library != null) {
|
||||
for (Symbol s : program.getSymbolTable().getSymbols(library)) {
|
||||
if (s.getSymbolType() == SymbolType.FUNCTION &&
|
||||
s.getSource() != SourceType.DEFAULT) {
|
||||
symbolIds.add(s.getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
return symbolIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ordered list of library names, as specified by the logic/rules of the original
|
||||
* operating system's loader (eg. Elf / MachO dynamic library loading / symbol resolving
|
||||
* rules)
|
||||
*
|
||||
* @return list of library names, in original order
|
||||
*/
|
||||
private Collection<String> getOrderedRequiredLibraryNames() {
|
||||
TreeMap<Integer, String> orderLibraryMap = new TreeMap<>();
|
||||
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||
for (String optionName : options.getOptionNames()) {
|
||||
|
||||
// Legacy programs may have the old "ELF Required Library [" program property, so
|
||||
// we should not assume that the option name starts exactly with
|
||||
// REQUIRED_LIBRARY_PROPERTY_PREFIX. We must deal with a potential substring at the
|
||||
// start of the option name.
|
||||
int prefixIndex = optionName.indexOf(REQUIRED_LIBRARY_PROPERTY_PREFIX);
|
||||
if (prefixIndex == -1 || !optionName.endsWith("]")) {
|
||||
continue;
|
||||
}
|
||||
String libName = options.getString(optionName, null);
|
||||
if (libName == null) {
|
||||
continue;
|
||||
}
|
||||
String indexStr = optionName
|
||||
.substring(prefixIndex + REQUIRED_LIBRARY_PROPERTY_PREFIX.length(),
|
||||
optionName.length() - 1)
|
||||
.trim();
|
||||
try {
|
||||
orderLibraryMap.put(Integer.parseInt(indexStr), libName.trim());
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
Msg.error(ExternalSymbolResolver.class,
|
||||
"Program contains invalid property: " + optionName);
|
||||
}
|
||||
}
|
||||
return orderLibraryMap.values();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void resolveSymbolsToLibrary(Program program,
|
||||
Collection<Long> unresolvedExternalFunctionIds, Library extLibrary, Program libProgram,
|
||||
MessageLog messageLog, TaskMonitor monitor) throws CancelledException {
|
||||
int libResolvedCount = 0;
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
/**
|
||||
* Returns true if the specified program publishes a symbol with the specified name.
|
||||
*
|
||||
* @param program {@link Program}
|
||||
* @param name symbol name
|
||||
* @return true if program publishes a symbol the specified name
|
||||
*/
|
||||
private static boolean isExportedSymbol(Program program, String name) {
|
||||
|
||||
Iterator<Long> idIterator = unresolvedExternalFunctionIds.iterator();
|
||||
while (idIterator.hasNext()) {
|
||||
monitor.checkCancelled();
|
||||
Symbol s = symbolTable.getSymbol(idIterator.next());
|
||||
if (s == null || !s.isExternal() || s.getSymbolType() != SymbolType.FUNCTION) {
|
||||
Msg.error(ExternalSymbolResolver.class,
|
||||
"Concurrent modification of symbol table while resolving external symbols");
|
||||
idIterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
ExternalLocation extLoc = externalManager.getExternalLocation(s);
|
||||
if (s.getSource() == SourceType.DEFAULT ||
|
||||
!isLocationContainedInLibrary(libProgram, extLoc)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
s.setNamespace(extLibrary);
|
||||
idIterator.remove();
|
||||
libResolvedCount++;
|
||||
Msg.debug(ExternalSymbolResolver.class, "External symbol " + extLoc.getLabel() +
|
||||
" resolved to " + extLibrary.getName());
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) {
|
||||
Msg.error(ExternalSymbolResolver.class,
|
||||
"Error setting external symbol namespace for " + extLoc.getLabel(), e);
|
||||
}
|
||||
}
|
||||
messageLog.appendMsg(
|
||||
"Resolved " + libResolvedCount + " symbols to library " + extLibrary.getName());
|
||||
}
|
||||
|
||||
private static boolean isLocationContainedInLibrary(Program libProgram,
|
||||
ExternalLocation extLoc) {
|
||||
|
||||
String name = extLoc.getOriginalImportedName();
|
||||
if (name == null) {
|
||||
name = extLoc.getLabel();
|
||||
}
|
||||
for (Symbol s : libProgram.getSymbolTable().getLabelOrFunctionSymbols(name, null)) {
|
||||
for (Symbol s : program.getSymbolTable().getLabelOrFunctionSymbols(name, null)) {
|
||||
if (s.isExternalEntryPoint()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -173,61 +468,4 @@ public class ExternalSymbolResolver {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static Collection<Long> getUnresolvedExternalFunctionIds(Program program) {
|
||||
List<Long> symbolIds = new ArrayList<>();
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
Library library = externalManager.getExternalLibrary(Library.UNKNOWN);
|
||||
if (library != null) {
|
||||
for (Symbol s : program.getSymbolTable().getSymbols(library)) {
|
||||
if (s.getSymbolType() == SymbolType.FUNCTION) {
|
||||
symbolIds.add(s.getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
return symbolIds;
|
||||
}
|
||||
|
||||
private static Collection<String> getOrderedLibraryNamesNeeded(Program program) {
|
||||
TreeMap<Integer, String> orderLibraryMap = new TreeMap<>();
|
||||
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||
for (String optionName : options.getOptionNames()) {
|
||||
|
||||
// Legacy programs may have the old "ELF Required Library [" program property, so
|
||||
// we should not assume that the option name starts exactly with
|
||||
// REQUIRED_LIBRARY_PROPERTY_PREFIX. We must deal with a potential substring at the
|
||||
// start of the option name.
|
||||
int prefixIndex = optionName.indexOf(REQUIRED_LIBRARY_PROPERTY_PREFIX);
|
||||
if (prefixIndex == -1 || !optionName.endsWith("]")) {
|
||||
continue;
|
||||
}
|
||||
String libName = options.getString(optionName, null);
|
||||
if (libName == null) {
|
||||
continue;
|
||||
}
|
||||
String indexStr = optionName
|
||||
.substring(prefixIndex + REQUIRED_LIBRARY_PROPERTY_PREFIX.length(),
|
||||
optionName.length() - 1)
|
||||
.trim();
|
||||
try {
|
||||
orderLibraryMap.put(Integer.parseInt(indexStr), libName.trim());
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
Msg.error(ExternalSymbolResolver.class,
|
||||
"Program contains invalid property: " + optionName);
|
||||
}
|
||||
}
|
||||
return orderLibraryMap.values();
|
||||
}
|
||||
|
||||
public static List<Library> getLibrarySearchList(Program program) {
|
||||
List<Library> result = new ArrayList<>();
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
for (String libName : getOrderedLibraryNamesNeeded(program)) {
|
||||
Library lib = externalManager.getExternalLibrary(libName);
|
||||
if (lib != null) {
|
||||
result.add(lib);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue