Skip to content

Commit 9c31aa4

Browse files
committedFeb 8, 2024
Merge remote-tracking branch 'origin/GP-4145_dev747368_external_symbol_resolver--SQUASHED'
2 parents a9ef880 + dcc3043 commit 9c31aa4

File tree

3 files changed

+412
-220
lines changed

3 files changed

+412
-220
lines changed
 

‎Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ExternalSymbolResolverAnalyzer.java

Lines changed: 12 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,16 @@
1515
*/
1616
package ghidra.app.plugin.core.analysis;
1717

18-
import java.io.IOException;
19-
import java.util.ArrayList;
20-
import java.util.List;
21-
2218
import ghidra.app.services.*;
2319
import ghidra.app.util.importer.MessageLog;
24-
import ghidra.app.util.opinion.*;
25-
import ghidra.framework.model.*;
20+
import ghidra.app.util.opinion.ElfLoader;
21+
import ghidra.app.util.opinion.MachoLoader;
2622
import ghidra.framework.options.Options;
2723
import ghidra.program.model.address.AddressSetView;
28-
import ghidra.program.model.listing.Library;
2924
import ghidra.program.model.listing.Program;
3025
import ghidra.program.util.ExternalSymbolResolver;
26+
import ghidra.util.Msg;
3127
import ghidra.util.exception.CancelledException;
32-
import ghidra.util.exception.VersionException;
3328
import ghidra.util.task.TaskMonitor;
3429

3530
/**
@@ -61,7 +56,7 @@ public boolean canAnalyze(Program program) {
6156
if (program.getDomainFile().getParent() == null) {
6257
return false;
6358
}
64-
59+
6560
Options options = program.getOptions(Program.PROGRAM_INFO);
6661
String format = options.getString("Executable Format", null);
6762
return ElfLoader.ELF_NAME.equals(format) || MachoLoader.MACH_O_NAME.equals(format);
@@ -71,65 +66,16 @@ public boolean canAnalyze(Program program) {
7166
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
7267
throws CancelledException {
7368

74-
Object consumer = new Object();
75-
log = new MessageLog(); // For now, we don't want the analysis log spammed
76-
ProjectData projectData = program.getDomainFile().getParent().getProjectData();
77-
List<Loaded<Program>> loadedPrograms = new ArrayList<>();
78-
79-
// Add program to list
80-
loadedPrograms.add(new Loaded<>(program, program.getName(),
81-
program.getDomainFile().getParent().getPathname()));
82-
83-
// Add external libraries to list
84-
for (Library extLibrary : ExternalSymbolResolver.getLibrarySearchList(program)) {
85-
monitor.checkCancelled();
86-
String libPath = extLibrary.getAssociatedProgramPath();
87-
if (libPath == null) {
88-
continue;
89-
}
90-
91-
DomainFile libDomainFile = projectData.getFile(libPath);
92-
if (libDomainFile == null) {
93-
log.appendMsg("Referenced external program not found: " + libPath);
94-
continue;
95-
}
96-
97-
try {
98-
DomainObject libDomainObject =
99-
libDomainFile.getDomainObject(consumer, false, false, monitor);
100-
if (libDomainObject instanceof Program p) {
101-
loadedPrograms.add(new Loaded<>(p, libDomainFile.getName(),
102-
libDomainFile.getParent().getPathname()));
103-
}
104-
else {
105-
libDomainObject.release(consumer);
106-
log.appendMsg("Referenced external program is not a program: " + libPath);
107-
}
69+
try (ExternalSymbolResolver esr = new ExternalSymbolResolver(
70+
program.getDomainFile().getParent().getProjectData(), monitor)) {
71+
esr.addProgramToFixup(program);
72+
esr.fixUnresolvedExternalSymbols();
73+
esr.logInfo(s -> Msg.info(this, s), false);
74+
if (esr.hasProblemLibraries()) {
75+
// causes a popup message at end of analysis session
76+
esr.logInfo(log::appendMsg, true);
10877
}
109-
catch (IOException e) {
110-
log.appendMsg("Failed to open library dependency project file: " +
111-
libDomainFile.getPathname());
112-
}
113-
catch (VersionException e) {
114-
log.appendMsg(
115-
"Referenced external program requires updgrade, unable to consider symbols: " +
116-
libPath);
117-
}
118-
}
119-
120-
// Resolve symbols
121-
try {
122-
ExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedPrograms, false, log,
123-
monitor);
12478
return true;
12579
}
126-
catch (IOException e) {
127-
return false;
128-
}
129-
finally {
130-
for (int i = 1; i < loadedPrograms.size(); i++) {
131-
loadedPrograms.get(i).release(consumer);
132-
}
133-
}
13480
}
13581
}

‎Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfLoader.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,16 @@ protected void postLoadProgramFixups(List<Loaded<Program>> loadedPrograms, Proje
158158
throws CancelledException, IOException {
159159
super.postLoadProgramFixups(loadedPrograms, project, options, messageLog, monitor);
160160

161-
ExternalSymbolResolver.fixUnresolvedExternalSymbols(loadedPrograms, true, messageLog,
162-
monitor);
161+
try (ExternalSymbolResolver esr =
162+
new ExternalSymbolResolver(project.getProjectData(), monitor)) {
163+
for (Loaded<Program> loadedProgram : loadedPrograms) {
164+
esr.addProgramToFixup(
165+
loadedProgram.getProjectFolderPath() + loadedProgram.getName(),
166+
loadedProgram.getDomainObject());
167+
}
168+
esr.fixUnresolvedExternalSymbols();
169+
esr.logInfo(messageLog::appendMsg, true);
170+
}
163171
}
164172

165173
@Override

‎Ghidra/Features/Base/src/main/java/ghidra/program/util/ExternalSymbolResolver.java

Lines changed: 390 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
*/
1616
package ghidra.program.util;
1717

18+
import java.io.Closeable;
1819
import java.io.IOException;
1920
import java.util.*;
20-
import java.util.stream.Collectors;
21+
import java.util.function.Consumer;
2122

2223
import db.Transaction;
23-
import ghidra.app.util.importer.MessageLog;
24-
import ghidra.app.util.opinion.Loaded;
24+
import ghidra.framework.model.*;
2525
import ghidra.framework.options.Options;
2626
import ghidra.program.model.listing.*;
2727
import ghidra.program.model.symbol.*;
@@ -30,8 +30,15 @@
3030
import ghidra.util.exception.*;
3131
import ghidra.util.task.TaskMonitor;
3232

33-
public class ExternalSymbolResolver {
34-
33+
/**
34+
* Moves dangling external function symbols found in the {@link Library#UNKNOWN EXTERNAL/UNKNOWN}
35+
* namespace into the namespace of the external library that publishes a matching symbol.
36+
* <p>
37+
* This uses an ordered list of external library names that was attached to the program during
38+
* import by the Elf or Macho loader (see {@link #REQUIRED_LIBRARY_PROPERTY_PREFIX}).
39+
*
40+
*/
41+
public class ExternalSymbolResolver implements Closeable {
3542
private final static String REQUIRED_LIBRARY_PROPERTY_PREFIX = "Required Library [";
3643

3744
/**
@@ -45,189 +52,420 @@ public static String getRequiredLibraryProperty(int libraryIndex) {
4552
StringUtilities.pad("" + libraryIndex, ' ', 4));
4653
}
4754

55+
private final ProjectData projectData;
56+
private final TaskMonitor monitor;
57+
private final List<ProgramSymbolResolver> programsToFix = new ArrayList<>();
58+
private final Map<String, Program> loadedPrograms = new HashMap<>();
59+
private final Map<String, Throwable> problemLibraries = new HashMap<>();
60+
61+
public ExternalSymbolResolver(ProjectData projectData, TaskMonitor monitor) {
62+
this.projectData = projectData;
63+
this.monitor = monitor;
64+
}
65+
66+
/**
67+
* Queues a program into this session that will be fixed when {@link #fixUnresolvedExternalSymbols()}
68+
* is called.
69+
* <p>
70+
* The program should be fully persisted to the project if using this method, otherwise use
71+
* {@link #addProgramToFixup(String, Program)} and specify the pathname the program will
72+
* be saved to.
73+
*
74+
* @param program {@link Program} to fix
75+
*/
76+
public void addProgramToFixup(Program program) {
77+
addProgramToFixup(program.getDomainFile().getPathname(), program);
78+
}
79+
80+
/**
81+
* Queues a program into this session that will be fixed when {@link #fixUnresolvedExternalSymbols()}
82+
* is called.
83+
*
84+
* @param programPath string project path to the program
85+
* @param program {@link Program} to fix
86+
*/
87+
public void addProgramToFixup(String programPath, Program program) {
88+
programsToFix.add(new ProgramSymbolResolver(program, programPath));
89+
addLoadedProgram(programPath, program);
90+
}
91+
92+
/**
93+
* Adds an already opened program to this session, allowing it to be used as an external
94+
* library without needing to look it up in the current project.
95+
*
96+
* @param programPath project path to already opened program
97+
* @param program {@link Program}
98+
*/
99+
public void addLoadedProgram(String programPath, Program program) {
100+
if (loadedPrograms.put(programPath, program) == null) {
101+
program.addConsumer(this);
102+
}
103+
}
104+
105+
/**
106+
* Returns true if there was an error encountered when trying to open an external library.
107+
*
108+
* @return boolean flag, true if there was a problem opening an external library
109+
*/
110+
public boolean hasProblemLibraries() {
111+
return !problemLibraries.isEmpty();
112+
}
113+
114+
@Override
115+
public void close() {
116+
for (Program prog : loadedPrograms.values()) {
117+
prog.release(this);
118+
}
119+
programsToFix.clear();
120+
loadedPrograms.clear();
121+
}
122+
123+
/**
124+
* Resolves any unresolved external symbols in each program that has been queued up via
125+
* {@link #addProgramToFixup(String, Program)}.
126+
*
127+
* @throws CancelledException if cancelled
128+
*/
129+
public void fixUnresolvedExternalSymbols() throws CancelledException {
130+
for (ProgramSymbolResolver psr : programsToFix) {
131+
psr.resolveExternalSymbols();
132+
}
133+
}
134+
48135
/**
49-
* Links unresolved symbols to the first symbol found in the (ordered) linked
50-
* libraries (saved in the program's properties as {@value #REQUIRED_LIBRARY_PROPERTY_PREFIX}).
136+
* Logs information about the libraries and symbols that were found during the fixup.
137+
*
138+
* @param logger consumer that will log a string
139+
* @param shortSummary boolean flag, if true individual symbol names will be omitted
140+
*/
141+
public void logInfo(Consumer<String> logger, boolean shortSummary) {
142+
for (ProgramSymbolResolver psr : programsToFix) {
143+
psr.log(logger, shortSummary);
144+
}
145+
}
146+
147+
/**
148+
* Fetches a program from a cache of Program instances. If the requested program
149+
* isn't currently in the cache, it will be opened (if possible).
51150
* <p>
52-
* The ordering and precedence logic is loader specific though no particular binary formats
53-
* are parsed or required.
151+
* This cache of programs are pinned by registering a consumer on the program, and will be
152+
* released during {@link #close()} of this ExternalSymbolServer instance.
54153
* <p>
55-
* The program's external libraries need to already be populated with paths to
56-
* already existing / imported libraries.
57-
*
58-
* @param loadedPrograms The {@link Loaded} {@link Program}s to fix. The first entry is the
59-
* "primary" {@link Loaded} {@link Program}.
60-
* @param fixAll True if all of the {@link Loaded} {@link Program}s should be fixed;
61-
* false if just the "primary" {@link Loaded} {@link Program} should be fixed.
62-
* @param messageLog {@link MessageLog} to write info message to.
63-
* @param monitor {@link TaskMonitor} to watch for cancel and update with progress.
64-
* @throws CancelledException if user cancels
65-
* @throws IOException if error reading
154+
* This cache is shared between all ProgramSymbolResolver instances (that were created
155+
* by calling {@link #addProgramToFixup(String, Program)}).
156+
*
157+
* @param libPath project path to a library program
158+
* @return {@link Program}, or null if not found or other error during opening
159+
* @throws CancelledException if cancelled
66160
*/
67-
public static void fixUnresolvedExternalSymbols(List<Loaded<Program>> loadedPrograms,
68-
boolean fixAll, MessageLog messageLog, TaskMonitor monitor)
69-
throws CancelledException, IOException {
70-
Map<String, Loaded<Program>> loadedByPath = loadedPrograms.stream()
71-
.collect(Collectors.toMap(
72-
loaded -> loaded.getProjectFolderPath() + loaded.getName(), loaded -> loaded));
161+
protected Program getLibraryProgram(String libPath) throws CancelledException {
162+
Program result = loadedPrograms.get(libPath);
163+
if (result == null && !problemLibraries.containsKey(libPath)) {
164+
result = openLibraryFile(projectData.getFile(libPath), libPath);
165+
166+
if (result != null) {
167+
loadedPrograms.put(libPath, result);
168+
}
169+
}
170+
return result;
171+
}
73172

74-
List<Loaded<Program>> fixupList =
75-
loadedPrograms.subList(0, fixAll ? loadedPrograms.size() : 1);
173+
/**
174+
* Opens a library binary.
175+
*
176+
* @param libDf optional, reference to a the DomainFile that was found in a project. If null
177+
* (meaning a lookup in the project failed to find a matching file), libPath will be used when
178+
* creating error strings that reference the problematic file
179+
* @param libPath project path for the DomainFile
180+
* @return a opened {@link Program}
181+
* @throws CancelledException if cancelled
182+
*/
183+
protected Program openLibraryFile(DomainFile libDf, String libPath) throws CancelledException {
184+
try {
185+
if (libDf == null) {
186+
throw new IOException("Dangling external path: " + libPath);
187+
}
188+
DomainObject libDo = libDf.getDomainObject(this, false, false, monitor);
189+
if (libDo instanceof Program p) {
190+
return p;
191+
}
192+
libDo.release(this);
193+
throw new IOException("Referenced external program is not a program: " + libPath);
194+
}
195+
catch (IOException | VersionException e) {
196+
problemLibraries.put(libPath, e);
197+
}
198+
return null;
199+
}
76200

77-
monitor.initialize(fixupList.size());
78-
for (Loaded<Program> loadedProgram : fixupList) {
79-
Program program = loadedProgram.getDomainObject();
201+
/**
202+
* Represents a program that needs its external symbols to be fixed.
203+
*/
204+
private class ProgramSymbolResolver {
205+
record ExtLibInfo(String name, Library lib, String programPath, Program program,
206+
List<String> resolvedSymbols, Throwable problem) {
207+
String getProblemMessage() {
208+
if (problem instanceof VersionException ve) {
209+
return getVersionError(ve);
210+
}
211+
return problem != null ? problem.getMessage() : "";
212+
}
80213

81-
Collection<Long> unresolvedExternalFunctionIds =
82-
getUnresolvedExternalFunctionIds(program);
83-
if (unresolvedExternalFunctionIds.size() == 0) {
84-
continue;
214+
String getLibPath() {
215+
return programPath != null ? programPath : "missing";
85216
}
86217

87-
List<Library> libSearchList = getLibrarySearchList(program);
88-
if (libSearchList.isEmpty()) {
89-
continue;
218+
String getVersionError(VersionException ve) {
219+
String versionType = switch (ve.getVersionIndicator()) {
220+
case VersionException.NEWER_VERSION -> " newer";
221+
case VersionException.OLDER_VERSION -> "n older";
222+
default -> "n unknown";
223+
};
224+
225+
String upgradeMsg = ve.isUpgradable() ? " (upgrade is possible)" : "";
226+
227+
return "skipped: file was created with a%s version of Ghidra%s"
228+
.formatted(versionType, upgradeMsg);
90229
}
91230

92-
try (Transaction tx = program.openTransaction("Resolve External Symbols")) {
231+
}
232+
233+
Program program;
234+
String programPath;
235+
int externalSymbolCount;
236+
List<Long> unresolvedExternalFunctionIds;
237+
List<ExtLibInfo> extLibs = new ArrayList<>();
238+
239+
private ProgramSymbolResolver(Program program, String programPath) {
240+
this.program = program;
241+
this.programPath = programPath;
242+
}
93243

94-
messageLog.appendMsg("----- [" + program.getName() + "] Resolve " +
95-
unresolvedExternalFunctionIds.size() + " external symbols -----");
244+
private int getResolvedSymbolCount() {
245+
return externalSymbolCount - unresolvedExternalFunctionIds.size();
246+
}
96247

97-
for (Library extLibrary : libSearchList) {
98-
monitor.checkCancelled();
99-
String libName = extLibrary.getName();
100-
String libPath = extLibrary.getAssociatedProgramPath();
101-
if (libPath == null) {
102-
continue;
248+
private void log(Consumer<String> logger, boolean shortSummary) {
249+
boolean changed = unresolvedExternalFunctionIds.size() != externalSymbolCount;
250+
if (extLibs.isEmpty() && externalSymbolCount == 0) {
251+
return;
252+
}
253+
else if (!changed && !hasSomeLibrariesConfigured()) {
254+
logger.accept(
255+
"Resolving External Symbols of [%s] - %d unresolved symbols, no external libraries configured - skipping"
256+
.formatted(programPath, externalSymbolCount));
257+
return;
258+
}
259+
260+
logger.accept("Resolving External Symbols of [%s]%s".formatted(programPath,
261+
shortSummary ? " - Summary" : ""));
262+
logger.accept("\t%d external symbols resolved, %d remain unresolved"
263+
.formatted(getResolvedSymbolCount(), unresolvedExternalFunctionIds.size()));
264+
for (ExtLibInfo extLib : extLibs) {
265+
if (extLib.problem != null) {
266+
logger.accept("\t[%s] -> %s, %s".formatted(extLib.name, extLib.getLibPath(),
267+
extLib.getProblemMessage()));
268+
}
269+
else if (extLib.programPath != null) {
270+
logger.accept("\t[%s] -> %s, %d new symbols resolved".formatted(extLib.name,
271+
extLib.getLibPath(), extLib.resolvedSymbols.size()));
272+
}
273+
else {
274+
logger.accept("\t[%s] -> %s".formatted(extLib.name, extLib.getLibPath()));
275+
}
276+
if (!shortSummary) {
277+
for (String symbolName : extLib.resolvedSymbols) {
278+
logger.accept("\t\t[%s]".formatted(symbolName));
103279
}
104-
105-
Loaded<Program> loadedLib = loadedByPath.get(libPath);
106-
if (loadedLib == null) {
107-
messageLog.appendMsg("Referenced external program not found: " + libName);
108-
continue;
280+
}
281+
}
282+
if (!shortSummary && changed) {
283+
if (!unresolvedExternalFunctionIds.isEmpty()) {
284+
logger.accept("\tUnresolved remaining %d:"
285+
.formatted(unresolvedExternalFunctionIds.size()));
286+
SymbolTable symbolTable = program.getSymbolTable();
287+
for (Long symId : unresolvedExternalFunctionIds) {
288+
Symbol s = symbolTable.getSymbol(symId);
289+
logger.accept("\t\t[%s]".formatted(s.getName()));
109290
}
110-
111-
Program libProgram = loadedLib.getDomainObject();
112-
monitor.setMessage("Resolving symbols published by library " + libName);
113-
resolveSymbolsToLibrary(program, unresolvedExternalFunctionIds, extLibrary,
114-
libProgram, messageLog, monitor);
115291
}
116-
messageLog.appendMsg("Unresolved external symbols which remain: " +
117-
unresolvedExternalFunctionIds.size());
118292
}
119293
}
120-
}
121294

122-
private static void resolveSymbolsToLibrary(Program program,
123-
Collection<Long> unresolvedExternalFunctionIds, Library extLibrary, Program libProgram,
124-
MessageLog messageLog, TaskMonitor monitor) throws CancelledException {
125-
int libResolvedCount = 0;
126-
ExternalManager externalManager = program.getExternalManager();
127-
SymbolTable symbolTable = program.getSymbolTable();
128-
129-
Iterator<Long> idIterator = unresolvedExternalFunctionIds.iterator();
130-
while (idIterator.hasNext()) {
131-
monitor.checkCancelled();
132-
Symbol s = symbolTable.getSymbol(idIterator.next());
133-
if (s == null || !s.isExternal() || s.getSymbolType() != SymbolType.FUNCTION) {
134-
Msg.error(ExternalSymbolResolver.class,
135-
"Concurrent modification of symbol table while resolving external symbols");
136-
idIterator.remove();
137-
continue;
138-
}
139-
140-
ExternalLocation extLoc = externalManager.getExternalLocation(s);
141-
if (s.getSource() == SourceType.DEFAULT ||
142-
!isLocationContainedInLibrary(libProgram, extLoc)) {
143-
continue;
144-
}
145-
try {
146-
s.setNamespace(extLibrary);
147-
idIterator.remove();
148-
libResolvedCount++;
149-
Msg.debug(ExternalSymbolResolver.class, "External symbol " + extLoc.getLabel() +
150-
" resolved to " + extLibrary.getName());
151-
}
152-
catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) {
153-
Msg.error(ExternalSymbolResolver.class,
154-
"Error setting external symbol namespace for " + extLoc.getLabel(), e);
155-
}
156-
}
157-
messageLog.appendMsg(
158-
"Resolved " + libResolvedCount + " symbols to library " + extLibrary.getName());
159-
}
295+
private boolean hasSomeLibrariesConfigured() {
296+
for (ExtLibInfo extLib : extLibs) {
297+
if (extLib.program != null || extLib.problem != null ||
298+
extLib.programPath != null) {
299+
return true;
300+
}
301+
}
302+
return false;
303+
}
160304

161-
private static boolean isLocationContainedInLibrary(Program libProgram,
162-
ExternalLocation extLoc) {
305+
private void resolveExternalSymbols() throws CancelledException {
306+
unresolvedExternalFunctionIds = getUnresolvedExternalFunctionIds();
307+
externalSymbolCount = unresolvedExternalFunctionIds.size();
163308

164-
String name = extLoc.getOriginalImportedName();
165-
if (name == null) {
166-
name = extLoc.getLabel();
167-
}
168-
for (Symbol s : libProgram.getSymbolTable().getLabelOrFunctionSymbols(name, null)) {
169-
if (s.isExternalEntryPoint()) {
170-
return true;
309+
if (unresolvedExternalFunctionIds.isEmpty()) {
310+
return;
171311
}
172-
}
173-
return false;
174-
}
175312

176-
private static Collection<Long> getUnresolvedExternalFunctionIds(Program program) {
177-
List<Long> symbolIds = new ArrayList<>();
178-
ExternalManager externalManager = program.getExternalManager();
179-
Library library = externalManager.getExternalLibrary(Library.UNKNOWN);
180-
if (library != null) {
181-
for (Symbol s : program.getSymbolTable().getSymbols(library)) {
182-
if (s.getSymbolType() == SymbolType.FUNCTION) {
183-
symbolIds.add(s.getID());
313+
extLibs = getLibsToSearch();
314+
315+
if (!extLibs.isEmpty()) {
316+
try (Transaction tx = program.openTransaction("Resolve External Symbols")) {
317+
for (ExtLibInfo extLib : extLibs) {
318+
monitor.checkCancelled();
319+
resolveSymbolsToLibrary(extLib);
320+
}
184321
}
185322
}
323+
186324
}
187-
return symbolIds;
188-
}
189325

190-
private static Collection<String> getOrderedLibraryNamesNeeded(Program program) {
191-
TreeMap<Integer, String> orderLibraryMap = new TreeMap<>();
192-
Options options = program.getOptions(Program.PROGRAM_INFO);
193-
for (String optionName : options.getOptionNames()) {
326+
/**
327+
* Returns an ordered list of external libraries that need to be searched.
328+
*
329+
* @return list of ExtLibInfo elements, each representing an external library dependency
330+
* found in the {@link #program}
331+
* @throws CancelledException if cancelled
332+
*/
333+
private List<ExtLibInfo> getLibsToSearch() throws CancelledException {
334+
List<ExtLibInfo> result = new ArrayList<>();
335+
ExternalManager externalManager = program.getExternalManager();
336+
for (String libName : getOrderedRequiredLibraryNames()) {
337+
Library lib = externalManager.getExternalLibrary(libName);
338+
String libPath = lib != null ? lib.getAssociatedProgramPath() : null;
339+
Program libProg = libPath != null ? getLibraryProgram(libPath) : null;
340+
Throwable problem =
341+
libProg == null && libPath != null ? problemLibraries.get(libPath) : null;
194342

195-
// Legacy programs may have the old "ELF Required Library [" program property, so
196-
// we should not assume that the option name starts exactly with
197-
// REQUIRED_LIBRARY_PROPERTY_PREFIX. We must deal with a potential substring at the
198-
// start of the option name.
199-
int prefixIndex = optionName.indexOf(REQUIRED_LIBRARY_PROPERTY_PREFIX);
200-
if (prefixIndex == -1 || !optionName.endsWith("]")) {
201-
continue;
343+
result.add(
344+
new ExtLibInfo(libName, lib, libPath, libProg, new ArrayList<>(), problem));
202345
}
203-
String libName = options.getString(optionName, null);
204-
if (libName == null) {
205-
continue;
346+
return result;
347+
}
348+
349+
/**
350+
* Moves unresolved functions from the EXTERNAL/UNKNOWN namespace to the namespace of the
351+
* external library if the extLib publishes a symbol with a matching name.
352+
*
353+
* @param extLib {@link ExtLibInfo} representing an external library
354+
* @throws CancelledException if cancelled
355+
*/
356+
private void resolveSymbolsToLibrary(ExtLibInfo extLib) throws CancelledException {
357+
if (extLib.program == null) {
358+
// can't do anything if the external library doesn't have a valid program associated
359+
return;
206360
}
207-
String indexStr = optionName
208-
.substring(prefixIndex + REQUIRED_LIBRARY_PROPERTY_PREFIX.length(),
209-
optionName.length() - 1)
210-
.trim();
211-
try {
212-
orderLibraryMap.put(Integer.parseInt(indexStr), libName.trim());
361+
ExternalManager externalManager = program.getExternalManager();
362+
SymbolTable symbolTable = program.getSymbolTable();
363+
364+
for (Iterator<Long> idIterator = unresolvedExternalFunctionIds.iterator(); idIterator
365+
.hasNext();) {
366+
monitor.checkCancelled();
367+
Symbol s = symbolTable.getSymbol(idIterator.next());
368+
if (s == null || !s.isExternal() || s.getSymbolType() != SymbolType.FUNCTION) {
369+
Msg.error(ExternalSymbolResolver.class,
370+
"Concurrent modification of symbol table while resolving external symbols");
371+
idIterator.remove();
372+
continue;
373+
}
374+
375+
ExternalLocation extLoc = externalManager.getExternalLocation(s);
376+
String extLocName =
377+
Objects.requireNonNullElse(extLoc.getOriginalImportedName(), extLoc.getLabel());
378+
if (isExportedSymbol(extLib.program, extLocName)) {
379+
try {
380+
s.setNamespace(extLib.lib);
381+
idIterator.remove();
382+
extLib.resolvedSymbols.add(s.getName());
383+
}
384+
catch (DuplicateNameException | InvalidInputException
385+
| CircularDependencyException e) {
386+
Msg.error(ExternalSymbolResolver.class,
387+
"Error setting external symbol namespace for " + extLoc.getLabel(), e);
388+
}
389+
}
213390
}
214-
catch (NumberFormatException e) {
215-
Msg.error(ExternalSymbolResolver.class,
216-
"Program contains invalid property: " + optionName);
391+
}
392+
393+
/**
394+
* Returns a list of all external functions under the EXTERNAL/UNKNOWN library.
395+
*
396+
* @return list of func ids that need to be fixed
397+
*/
398+
private List<Long> getUnresolvedExternalFunctionIds() {
399+
List<Long> symbolIds = new ArrayList<>();
400+
ExternalManager externalManager = program.getExternalManager();
401+
Library library = externalManager.getExternalLibrary(Library.UNKNOWN);
402+
if (library != null) {
403+
for (Symbol s : program.getSymbolTable().getSymbols(library)) {
404+
if (s.getSymbolType() == SymbolType.FUNCTION &&
405+
s.getSource() != SourceType.DEFAULT) {
406+
symbolIds.add(s.getID());
407+
}
408+
}
409+
}
410+
return symbolIds;
411+
}
412+
413+
/**
414+
* Returns an ordered list of library names, as specified by the logic/rules of the original
415+
* operating system's loader (eg. Elf / MachO dynamic library loading / symbol resolving
416+
* rules)
417+
*
418+
* @return list of library names, in original order
419+
*/
420+
private Collection<String> getOrderedRequiredLibraryNames() {
421+
TreeMap<Integer, String> orderLibraryMap = new TreeMap<>();
422+
Options options = program.getOptions(Program.PROGRAM_INFO);
423+
for (String optionName : options.getOptionNames()) {
424+
425+
// Legacy programs may have the old "ELF Required Library [" program property, so
426+
// we should not assume that the option name starts exactly with
427+
// REQUIRED_LIBRARY_PROPERTY_PREFIX. We must deal with a potential substring at the
428+
// start of the option name.
429+
int prefixIndex = optionName.indexOf(REQUIRED_LIBRARY_PROPERTY_PREFIX);
430+
if (prefixIndex == -1 || !optionName.endsWith("]")) {
431+
continue;
432+
}
433+
String libName = options.getString(optionName, null);
434+
if (libName == null) {
435+
continue;
436+
}
437+
String indexStr = optionName
438+
.substring(prefixIndex + REQUIRED_LIBRARY_PROPERTY_PREFIX.length(),
439+
optionName.length() - 1)
440+
.trim();
441+
try {
442+
orderLibraryMap.put(Integer.parseInt(indexStr), libName.trim());
443+
}
444+
catch (NumberFormatException e) {
445+
Msg.error(ExternalSymbolResolver.class,
446+
"Program contains invalid property: " + optionName);
447+
}
217448
}
449+
return orderLibraryMap.values();
218450
}
219-
return orderLibraryMap.values();
451+
220452
}
221453

222-
public static List<Library> getLibrarySearchList(Program program) {
223-
List<Library> result = new ArrayList<>();
224-
ExternalManager externalManager = program.getExternalManager();
225-
for (String libName : getOrderedLibraryNamesNeeded(program)) {
226-
Library lib = externalManager.getExternalLibrary(libName);
227-
if (lib != null) {
228-
result.add(lib);
454+
/**
455+
* Returns true if the specified program publishes a symbol with the specified name.
456+
*
457+
* @param program {@link Program}
458+
* @param name symbol name
459+
* @return true if program publishes a symbol the specified name
460+
*/
461+
private static boolean isExportedSymbol(Program program, String name) {
462+
463+
for (Symbol s : program.getSymbolTable().getLabelOrFunctionSymbols(name, null)) {
464+
if (s.isExternalEntryPoint()) {
465+
return true;
229466
}
230467
}
231-
return result;
468+
return false;
232469
}
470+
233471
}

0 commit comments

Comments
 (0)
Please sign in to comment.