Merge remote-tracking branch

'origin/GP-3015_Dan_symbolsByNameAssemblerFix--REBASED-1' (Closes #2630)
This commit is contained in:
Ryan Kurtz 2023-01-26 10:58:30 -05:00
commit aa7a93f0a3
17 changed files with 801 additions and 466 deletions

View file

@ -380,6 +380,11 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
})); }));
} }
@Override
public SymbolIterator scanSymbolsByName(String startName) {
return new SymbolIteratorAdapter(symbolManager.allSymbols().scanByName(startName));
}
@Override @Override
public int getNumSymbols() { public int getNumSymbols() {
return symbolManager.allSymbols().size(true); return symbolManager.allSymbols().size(true);

View file

@ -88,6 +88,10 @@ public abstract class AbstractDBTraceSymbolSingleTypeView<T extends AbstractDBTr
return Collections2.filter(view, s -> predicate.test(s.name)); return Collections2.filter(view, s -> predicate.test(s.name));
} }
public Iterator<? extends T> scanByName(String startName) {
return symbolsByName.tail(startName, true).values().iterator();
}
public T getByKey(long key) { public T getByKey(long key) {
return store.getObjectAt(key); return store.getObjectAt(key);
} }

View file

@ -15,13 +15,14 @@
*/ */
package ghidra.trace.database.symbol; package ghidra.trace.database.symbol;
import java.util.Arrays; import java.util.*;
import java.util.Collection; import java.util.stream.Collectors;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import generic.CatenatedCollection; import generic.CatenatedCollection;
import ghidra.trace.model.symbol.*; import ghidra.trace.model.symbol.*;
import ghidra.util.MergeSortingIterator;
public class DBTraceSymbolMultipleTypesView<T extends AbstractDBTraceSymbol> public class DBTraceSymbolMultipleTypesView<T extends AbstractDBTraceSymbol>
implements TraceSymbolView<T> { implements TraceSymbolView<T> {
@ -73,4 +74,11 @@ public class DBTraceSymbolMultipleTypesView<T extends AbstractDBTraceSymbol>
return new CatenatedCollection<>( return new CatenatedCollection<>(
Collections2.transform(parts, p -> p.getWithMatchingName(glob, caseSensitive))); Collections2.transform(parts, p -> p.getWithMatchingName(glob, caseSensitive)));
} }
@Override
public Iterator<? extends T> scanByName(String startName) {
List<Iterator<? extends T>> iterators =
parts.stream().map(p -> p.scanByName(startName)).collect(Collectors.toList());
return new MergeSortingIterator<>(iterators, Comparator.comparing(s -> s.getName()));
}
} }

View file

@ -16,6 +16,7 @@
package ghidra.trace.model.symbol; package ghidra.trace.model.symbol;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
public interface TraceSymbolView<T extends TraceSymbol> { public interface TraceSymbolView<T extends TraceSymbol> {
@ -55,4 +56,6 @@ public interface TraceSymbolView<T extends TraceSymbol> {
* @return the collection of matching symbols * @return the collection of matching symbols
*/ */
Collection<? extends T> getWithMatchingName(String glob, boolean caseSensitive); Collection<? extends T> getWithMatchingName(String glob, boolean caseSensitive);
Iterator<? extends T> scanByName(String startName);
} }

View file

@ -722,7 +722,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
} }
} }
else if (toAddr.isMemoryAddress() && symProvider.isShowingDynamicSymbols()) { else if (toAddr.isMemoryAddress() && symProvider.isShowingDynamicSymbols()) {
long dynamicSymbolId = symbolTable.getDynamicSymbolID(reference.getToAddress()); long dynamicSymbolId = symbolTable.getDynamicSymbolID(toAddr);
symProvider.symbolRemoved(dynamicSymbolId); symProvider.symbolRemoved(dynamicSymbolId);
refProvider.symbolRemoved(dynamicSymbolId); refProvider.symbolRemoved(dynamicSymbolId);
} }

View file

@ -1265,6 +1265,7 @@ public class SymbolManagerTest extends AbstractGhidraHeadedIntegrationTest {
createExternalFunction("7"); createExternalFunction("7");
createExternalLabel("8"); createExternalLabel("8");
// test restricted address range
AddressSet set = new AddressSet(addr(0), addr(50)); AddressSet set = new AddressSet(addr(0), addr(50));
set.addRange(addr(300), addr(350)); set.addRange(addr(300), addr(350));
set.addRange(addr(500), addr(1000)); set.addRange(addr(500), addr(1000));
@ -1275,22 +1276,48 @@ public class SymbolManagerTest extends AbstractGhidraHeadedIntegrationTest {
// External space before memory space // External space before memory space
Symbol s = it.next(); Symbol s = it.next();
assertNotNull(s); assertNotNull(s);
assertEquals("7", s.getName()); assertEquals("Test::7", s.getName(true));
assertEquals(extAddr(1), s.getAddress()); assertEquals(extAddr(1), s.getAddress());
s = it.next(); s = it.next();
assertNotNull(s); assertNotNull(s);
assertEquals("8", s.getName()); assertEquals("Test::8", s.getName(true));
assertEquals(extAddr(2), s.getAddress()); assertEquals(extAddr(2), s.getAddress());
s = it.next(); s = it.next();
assertNotNull(s); assertNotNull(s);
assertEquals("3", s.getName(true));
assertEquals(addr(300), s.getAddress()); assertEquals(addr(300), s.getAddress());
s = it.next(); s = it.next();
assertNotNull(s); assertNotNull(s);
assertEquals("5", s.getName(true));
assertEquals(addr(500), s.getAddress()); assertEquals(addr(500), s.getAddress());
assertTrue(!it.hasNext()); assertTrue(!it.hasNext());
assertNull(it.next()); assertNull(it.next());
// test all memory/external
it = st.getPrimarySymbolIterator((AddressSetView) null, true);
assertTrue(it.hasNext());
s = it.next();
assertNotNull(s);
assertEquals("Test::7", s.getName(true));
assertTrue(it.hasNext());
s = it.next();
assertNotNull(s);
assertEquals("Test::8", s.getName(true));
for (int i = 1; i <= 6; i++) {
assertTrue(it.hasNext());
s = it.next();
assertNotNull(s);
assertEquals(Integer.toString(i), s.getName(true));
}
assertTrue(!it.hasNext());
assertNull(it.next());
} }
@Test @Test
@ -1433,12 +1460,31 @@ public class SymbolManagerTest extends AbstractGhidraHeadedIntegrationTest {
createLabel(addr(100), "1"); createLabel(addr(100), "1");
createLabel(addr(200), "2"); createLabel(addr(200), "2");
createLabel(addr(300), "3"); createLabel(addr(300), "3");
Function extFunc = createExternalFunction("X");
createExternalLabel("Y");
Function f1 = createFunction("A", addr(150)); Function f1 = createFunction("A", addr(150));
Function f2 = createFunction("B", addr(250)); Function f2 = createFunction("B", addr(250));
SymbolIterator it = // test over constrained address set
st.getSymbols(new AddressSet(addr(0), addr(5000)), SymbolType.FUNCTION, true); AddressSet set = new AddressSet(addr(0), addr(200));
set.addRange(AddressSpace.EXTERNAL_SPACE.getMinAddress(),
AddressSpace.EXTERNAL_SPACE.getMaxAddress());
SymbolIterator it = st.getSymbols(set, SymbolType.FUNCTION, true);
assertTrue(it.hasNext());
assertEquals(extFunc.getSymbol(), it.next());
assertTrue(it.hasNext());
assertEquals(f1.getSymbol(), it.next());
assertFalse(it.hasNext());
it = st.getSymbols(null, SymbolType.FUNCTION, true);
assertTrue(it.hasNext());
assertEquals(extFunc.getSymbol(), it.next());
assertTrue(it.hasNext()); assertTrue(it.hasNext());
assertEquals(f1.getSymbol(), it.next()); assertEquals(f1.getSymbol(), it.next());
@ -2440,11 +2486,11 @@ public class SymbolManagerTest extends AbstractGhidraHeadedIntegrationTest {
.getSymbol(); .getSymbol();
} }
private Symbol createExternalFunction(String name) private Function createExternalFunction(String name)
throws InvalidInputException, DuplicateNameException { throws InvalidInputException, DuplicateNameException {
ExternalManager externalManager = program.getExternalManager(); ExternalManager externalManager = program.getExternalManager();
return externalManager.addExtFunction("Test", name, null, SourceType.USER_DEFINED) return externalManager.addExtFunction("Test", name, null, SourceType.USER_DEFINED)
.getSymbol(); .getFunction();
} }
} }

View file

@ -16,7 +16,9 @@
package ghidra.app.plugin.assembler.sleigh.symbol; package ghidra.app.plugin.assembler.sleigh.symbol;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.Map.Entry;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace; import ghidra.program.model.address.AddressSpace;
@ -30,8 +32,7 @@ import ghidra.program.model.symbol.*;
* A context to hold various symbols offered to the assembler, usable where numbers are expected. * A context to hold various symbols offered to the assembler, usable where numbers are expected.
*/ */
public final class AssemblyNumericSymbols { public final class AssemblyNumericSymbols {
public static final AssemblyNumericSymbols EMPTY = public static final AssemblyNumericSymbols EMPTY = new AssemblyNumericSymbols();
new AssemblyNumericSymbols(Map.of(), Map.of(), Map.of());
/** /**
* Collect labels derived from memory-mapped registers in a language * Collect labels derived from memory-mapped registers in a language
@ -42,7 +43,8 @@ public final class AssemblyNumericSymbols {
* @param labels the destination map * @param labels the destination map
* @param language the language * @param language the language
*/ */
private static void collectLanguageLabels(Map<String, Set<Address>> labels, Language language) { private static NavigableMap<String, Set<Address>> collectLanguageLabels(Language language) {
NavigableMap<String, Set<Address>> labels = new TreeMap<>();
for (Register reg : language.getRegisters()) { for (Register reg : language.getRegisters()) {
// TODO/HACK: There ought to be a better mechanism describing suitable symbolic // TODO/HACK: There ought to be a better mechanism describing suitable symbolic
// substitutions for a given operand. // substitutions for a given operand.
@ -50,42 +52,25 @@ public final class AssemblyNumericSymbols {
labels.computeIfAbsent(reg.getName(), n -> new HashSet<>()).add(reg.getAddress()); labels.computeIfAbsent(reg.getName(), n -> new HashSet<>()).add(reg.getAddress());
} }
} }
return labels;
} }
/** private static Stream<Address> streamAddresses(Symbol sym) {
* Collect labels from the program's database SymbolType symbolType = sym.getSymbolType();
* if (symbolType == SymbolType.LABEL) {
* @param labels the destination map return Stream.of(sym.getAddress());
* @param program the source program
*/
private static void collectProgramLabels(Map<String, Set<Address>> labels, Program program) {
final SymbolIterator it = program.getSymbolTable().getAllSymbols(true);
while (it.hasNext()) {
Symbol sym = it.next();
SymbolType symbolType = sym.getSymbolType();
if (symbolType == SymbolType.LABEL) {
if (sym.isExternal()) {
continue;
}
labels.computeIfAbsent(sym.getName(), n -> new HashSet<>()).add(sym.getAddress());
}
else if (symbolType == SymbolType.FUNCTION) {
if (!sym.getAddress().isExternalAddress()) {
labels.computeIfAbsent(sym.getName(), n -> new HashSet<>())
.add(sym.getAddress());
}
Function function = (Function) sym.getObject();
Address[] thunks = function.getFunctionThunkAddresses(true);
if (thunks != null) {
for (Address t : thunks) {
if (!t.isExternalAddress()) {
labels.computeIfAbsent(sym.getName(), n -> new HashSet<>()).add(t);
}
}
}
}
// Ignore other symbol types
} }
if (symbolType == SymbolType.FUNCTION) {
Function function = (Function) sym.getObject();
Address[] thunks = function.getFunctionThunkAddresses(true);
return thunks == null ? Stream.of(sym.getAddress())
: Stream.concat(Stream.of(sym.getAddress()), Stream.of(thunks));
}
return Stream.of();
}
private static Stream<Address> streamNonExternalAddresses(Symbol sym) {
return streamAddresses(sym).filter(a -> !a.isExternalAddress());
} }
/** /**
@ -94,13 +79,15 @@ public final class AssemblyNumericSymbols {
* @param equates the destination map * @param equates the destination map
* @param programthe source program * @param programthe source program
*/ */
private static void collectProgramEquates(Map<String, Set<Long>> equates, Program program) { private static NavigableMap<String, Set<Long>> collectProgramEquates(Program program) {
NavigableMap<String, Set<Long>> equates = new TreeMap<>();
final Iterator<Equate> it = program.getEquateTable().getEquates(); final Iterator<Equate> it = program.getEquateTable().getEquates();
while (it.hasNext()) { while (it.hasNext()) {
Equate eq = it.next(); Equate eq = it.next();
// Thought is: If that's what the user sees, then that's what the user will type! // Thought is: If that's what the user sees, then that's what the user will type!
equates.computeIfAbsent(eq.getDisplayName(), n -> new HashSet<>()).add(eq.getValue()); equates.computeIfAbsent(eq.getDisplayName(), n -> new HashSet<>()).add(eq.getValue());
} }
return equates;
} }
/** /**
@ -110,84 +97,60 @@ public final class AssemblyNumericSymbols {
* @return the symbols * @return the symbols
*/ */
public static AssemblyNumericSymbols fromLanguage(Language language) { public static AssemblyNumericSymbols fromLanguage(Language language) {
Map<String, Set<Address>> labels = new HashMap<>(); return new AssemblyNumericSymbols(language);
collectLanguageLabels(labels, language);
return forMaps(Map.of(), labels);
} }
/** /**
* Get symbols from a program (and its language) * Get symbols from a program (and its language)
* *
* <p>
* TODO: It might be nice to cache these and use a listener to keep the maps up to date. Will
* depend on interactive performance.
*
* @param program the program * @param program the program
* @return the symbols * @return the symbols
*/ */
public static AssemblyNumericSymbols fromProgram(Program program) { public static AssemblyNumericSymbols fromProgram(Program program) {
Map<String, Set<Long>> equates = new HashMap<>(); return new AssemblyNumericSymbols(program);
Map<String, Set<Address>> labels = new HashMap<>();
collectLanguageLabels(labels, program.getLanguage());
collectProgramLabels(labels, program);
collectProgramEquates(equates, program);
return forMaps(equates, labels);
} }
/** public final NavigableMap<String, Set<Long>> programEquates;
* Get symbols for the given equate and label maps public final NavigableMap<String, Set<Address>> languageLabels;
* private final Program program;
* @param equates the equates
* @param labels the labels private AssemblyNumericSymbols() {
* @return the symbols this.program = null;
*/ this.programEquates = new TreeMap<>();
public static AssemblyNumericSymbols forMaps(Map<String, Set<Long>> equates, this.languageLabels = new TreeMap<>();
Map<String, Set<Address>> labels) {
return new AssemblyNumericSymbols(Map.copyOf(equates), Map.copyOf(labels),
groupBySpace(labels));
} }
private static Map<AddressSpace, Map<String, Set<Address>>> groupBySpace( private AssemblyNumericSymbols(Language language) {
Map<String, Set<Address>> labels) { this.program = null;
Map<AddressSpace, Map<String, Set<Address>>> result = new HashMap<>(); this.programEquates = new TreeMap<>();
for (Map.Entry<String, Set<Address>> entry : labels.entrySet()) { this.languageLabels = collectLanguageLabels(language);
for (Address addr : entry.getValue()) {
result.computeIfAbsent(addr.getAddressSpace(), as -> new HashMap<>())
.computeIfAbsent(entry.getKey(), k -> new TreeSet<>())
.add(addr);
}
}
return Collections.unmodifiableMap(result);
} }
private final NavigableSet<String> all = new TreeSet<>(); private AssemblyNumericSymbols(Program program) {
public final Map<String, Set<Long>> equates; this.program = program;
public final Map<String, Set<Address>> labels; this.programEquates = collectProgramEquates(program);
public final Map<AddressSpace, Map<String, Set<Address>>> labelsBySpace; this.languageLabels = collectLanguageLabels(program.getLanguage());
private AssemblyNumericSymbols(Map<String, Set<Long>> equates, Map<String, Set<Address>> labels,
Map<AddressSpace, Map<String, Set<Address>>> labelsBySpace) {
this.equates = equates;
this.labels = labels;
this.labelsBySpace = labelsBySpace;
all.addAll(equates.keySet());
all.addAll(labels.keySet());
} }
/** /**
* Choose any symbol with the given name * Choose any symbol with the given name
* *
* <p> * <p>
* This will check equates first, then labels. If an equate is found, its value is returned. If * This will order equates first, then program labels, then language labels. For addresses, the
* a label is found, its addressable word offset is returned. * value is its addressable word offset.
* *
* @param name the name * @param name the name
* @return the value, or null * @return the value, or null
*/ */
public Set<Long> chooseAll(String name) { public Set<Long> chooseAll(String name) {
Set<Long> result = new TreeSet<>(); Set<Long> result = new TreeSet<>();
result.addAll(equates.getOrDefault(name, Set.of())); result.addAll(programEquates.getOrDefault(name, Set.of()));
for (Address address : labels.getOrDefault(name, Set.of())) { if (program != null) {
StreamSupport.stream(program.getSymbolTable().getSymbols(name).spliterator(), false)
.flatMap(sym -> streamNonExternalAddresses(sym))
.forEach(a -> result.add(a.getAddressableWordOffset()));
}
for (Address address : languageLabels.getOrDefault(name, Set.of())) {
result.add(address.getAddressableWordOffset()); result.add(address.getAddressableWordOffset());
} }
return result; return result;
@ -201,11 +164,20 @@ public final class AssemblyNumericSymbols {
* @return the addressable word offset of the found label, or null * @return the addressable word offset of the found label, or null
*/ */
public Set<Long> chooseBySpace(String name, AddressSpace space) { public Set<Long> chooseBySpace(String name, AddressSpace space) {
return labelsBySpace.getOrDefault(space, Map.of()) Set<Long> result = new TreeSet<>();
.getOrDefault(name, Set.of()) if (program != null) {
.stream() StreamSupport.stream(program.getSymbolTable().getSymbols(name).spliterator(), false)
.map(a -> a.getAddressableWordOffset()) .flatMap(sym -> streamAddresses(sym))
.collect(Collectors.toSet()); .filter(a -> a.getAddressSpace() == space)
.forEach(a -> result.add(a.getAddressableWordOffset()));
}
for (Address address : languageLabels.getOrDefault(name, Set.of())) {
if (address.getAddressSpace() != space) {
continue;
}
result.add(address.getAddressableWordOffset());
}
return result;
} }
/** /**
@ -228,23 +200,59 @@ public final class AssemblyNumericSymbols {
return chooseBySpace(name, space); return chooseBySpace(name, space);
} }
private Collection<String> suggestFrom(String got, Collection<String> keys, int max, private void suggestFrom(List<String> result, String got, NavigableSet<String> keys, int max) {
boolean sorted) {
Set<String> result = new HashSet<>();
int count = 0; int count = 0;
for (String label : keys) { for (String k : keys.tailSet(got)) {
if (count >= max) { if (count >= max || !k.startsWith(got)) {
break; return;
}
if (label.startsWith(got)) {
result.add(label);
count++;
}
else if (sorted) {
break;
} }
result.add(k);
count++;
}
}
private void suggestFromBySpace(List<String> result, String got,
NavigableMap<String, Set<Address>> labels, int max, AddressSpace space) {
int count = 0;
for (Entry<String, Set<Address>> ent : labels.entrySet()) {
if (count >= max || !ent.getKey().startsWith(got)) {
return;
}
if (!ent.getValue().stream().anyMatch(a -> a.getAddressSpace() == space)) {
continue;
}
result.add(ent.getKey());
count++;
}
}
private void suggestFromProgramAny(List<String> result, String got, int max) {
int count = 0;
for (Symbol s : program.getSymbolTable().scanSymbolsByName(got)) {
if (count >= max || !s.getName().startsWith(got)) {
return;
}
if (streamNonExternalAddresses(s).findAny().isEmpty()) {
continue;
}
result.add(s.getName());
count++;
}
}
private void suggestFromProgramBySpace(List<String> result, String got, int max,
AddressSpace space) {
int count = 0;
for (Symbol s : program.getSymbolTable().scanSymbolsByName(got)) {
if (count >= max || !s.getName().startsWith(got)) {
return;
}
if (!streamAddresses(s).anyMatch(a -> a.getAddressSpace() == space)) {
continue;
}
result.add(s.getName());
count++;
} }
return result;
} }
/** /**
@ -255,7 +263,18 @@ public final class AssemblyNumericSymbols {
* @return the collection of symbol names * @return the collection of symbol names
*/ */
public Collection<String> suggestAny(String got, int max) { public Collection<String> suggestAny(String got, int max) {
return suggestFrom(got, all.tailSet(got), max, true); List<String> result = new ArrayList<>();
suggestFrom(result, got, languageLabels.navigableKeySet(), max);
if (program == null) {
return result;
}
suggestFrom(result, got, programEquates.navigableKeySet(), max);
suggestFromProgramAny(result, got, max);
Collections.sort(result);
if (result.size() > max) {
return result.subList(0, max);
}
return result;
} }
/** /**
@ -267,12 +286,17 @@ public final class AssemblyNumericSymbols {
* @return the collection of symbol names * @return the collection of symbol names
*/ */
public Collection<String> suggestBySpace(String got, AddressSpace space, int max) { public Collection<String> suggestBySpace(String got, AddressSpace space, int max) {
Map<String, Set<Address>> forSpace = labelsBySpace.get(space); List<String> result = new ArrayList<>();
if (forSpace == null) { suggestFromBySpace(result, got, languageLabels, max, space);
return Set.of(); if (program == null) {
return result;
} }
// TODO: Should I sort these, perhaps lazily, to speed search? suggestFromProgramBySpace(result, got, max, space);
return suggestFrom(got, forSpace.keySet(), max, false); Collections.sort(result);
if (result.size() > max) {
return result.subList(0, max);
}
return result;
} }
/** /**

View file

@ -1572,8 +1572,9 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
* Create a memory reference to the given address to mark it as * Create a memory reference to the given address to mark it as
* an external entry point. * an external entry point.
* @param toAddr the address at which to make an external entry point * @param toAddr the address at which to make an external entry point
* @throws IllegalArgumentException if a non-memory address is specified
*/ */
public void addExternalEntryPointRef(Address toAddr) { public void addExternalEntryPointRef(Address toAddr) throws IllegalArgumentException {
if (!toAddr.isMemoryAddress()) { if (!toAddr.isMemoryAddress()) {
throw new IllegalArgumentException("Entry point address must be memory address"); throw new IllegalArgumentException("Entry point address must be memory address");
} }

View file

@ -366,6 +366,16 @@ abstract class SymbolDatabaseAdapter {
*/ */
abstract RecordIterator getSymbolsByName(String name) throws IOException; abstract RecordIterator getSymbolsByName(String name) throws IOException;
/**
* Scan symbols lexicographically by name starting from the given name
* <p>
* This only includes memory-based stored symbols.
*
* @param startName the starting name to search
* @throws IOException if a database io error occurs
*/
abstract RecordIterator scanSymbolsByName(String startName) throws IOException;
/** /**
* Get all symbols contained in the given {@link Namespace} that have the given name * Get all symbols contained in the given {@link Namespace} that have the given name
* @param name the symbol name * @param name the symbol name

View file

@ -255,6 +255,13 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
symbolTable.indexIterator(V0_SYMBOL_NAME_COL, val, val, true)); symbolTable.indexIterator(V0_SYMBOL_NAME_COL, val, val, true));
} }
@Override
RecordIterator scanSymbolsByName(String startName) throws IOException {
StringField val = new StringField(startName);
return new V0ConvertedRecordIterator(
symbolTable.indexIterator(V0_SYMBOL_NAME_COL, val, null, true));
}
private class V0ConvertedRecordIterator implements RecordIterator { private class V0ConvertedRecordIterator implements RecordIterator {
private RecordIterator symIter; private RecordIterator symIter;

View file

@ -254,6 +254,13 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
symbolTable.indexIterator(V1_SYMBOL_NAME_COL, field, field, true)); symbolTable.indexIterator(V1_SYMBOL_NAME_COL, field, field, true));
} }
@Override
RecordIterator scanSymbolsByName(String startName) throws IOException {
StringField val = new StringField(startName);
return new V1ConvertedRecordIterator(
symbolTable.indexIterator(V1_SYMBOL_NAME_COL, val, null, true));
}
private class V1ConvertedRecordIterator extends ConvertedRecordIterator { private class V1ConvertedRecordIterator extends ConvertedRecordIterator {
V1ConvertedRecordIterator(RecordIterator originalIterator) { V1ConvertedRecordIterator(RecordIterator originalIterator) {

View file

@ -229,6 +229,13 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
return new V2ConvertedRecordIterator(it); return new V2ConvertedRecordIterator(it);
} }
@Override
RecordIterator scanSymbolsByName(String startName) throws IOException {
StringField field = new StringField(startName);
RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, field, null, true);
return new V2ConvertedRecordIterator(it);
}
@Override @Override
Address getMaxSymbolAddress(AddressSpace space) throws IOException { Address getMaxSymbolAddress(AddressSpace space) throws IOException {
if (space.isMemorySpace()) { if (space.isMemorySpace()) {

View file

@ -264,6 +264,12 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
return symbolTable.indexIterator(SYMBOL_NAME_COL, field, field, true); return symbolTable.indexIterator(SYMBOL_NAME_COL, field, field, true);
} }
@Override
RecordIterator scanSymbolsByName(String startName) throws IOException {
StringField field = new StringField(startName);
return symbolTable.indexIterator(SYMBOL_NAME_COL, field, null, true);
}
@Override @Override
RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException { RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException {
// create a range of hash fields for all symbols with this name and namespace id over all // create a range of hash fields for all symbols with this name and namespace id over all

View file

@ -77,6 +77,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Creates a new Symbol manager. * Creates a new Symbol manager.
*
* @param handle the database handler * @param handle the database handler
* @param addrMap the address map. * @param addrMap the address map.
* @param openMode the open mode. * @param openMode the open mode.
@ -125,6 +126,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Find previously defined variable storage address * Find previously defined variable storage address
*
* @param storage variable storage * @param storage variable storage
* @return previously defined variable storage address or null if not found * @return previously defined variable storage address or null if not found
* @throws IOException if there is database exception * @throws IOException if there is database exception
@ -136,7 +138,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override @Override
public void setProgram(ProgramDB program) { public void setProgram(ProgramDB program) {
this.program = program; this.program = program;
refManager = (ReferenceDBManager) program.getReferenceManager(); refManager = program.getReferenceManager();
namespaceMgr = program.getNamespaceManager(); namespaceMgr = program.getNamespaceManager();
variableStorageMgr.setProgram(program); variableStorageMgr.setProgram(program);
} }
@ -177,17 +179,18 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Check for and upgrade old namespace symbol addresses which included a namespace ID. * Check for and upgrade old namespace symbol addresses which included a namespace ID.
* Start at end since Namespace-0 will not result in an OldGenericNamespaceAddress. * <p>
* Namespace-0 external symbols do not need to be upgraded since this is effectively * Start at end since Namespace-0 will not result in an OldGenericNamespaceAddress. Namespace-0
* where all the moved external addresses will be placed. * external symbols do not need to be upgraded since this is effectively where all the moved
* The triggering of this upgrade relies on the addition of the VariableManager which * external addresses will be placed. The triggering of this upgrade relies on the addition of
* trigger an upgrade. * the VariableManager which trigger an upgrade.
*
* @param monitor the task monitor * @param monitor the task monitor
*/ */
private boolean upgradeOldNamespaceAddresses(TaskMonitor monitor) private boolean upgradeOldNamespaceAddresses(TaskMonitor monitor)
throws IOException, CancelledException { throws IOException, CancelledException {
ReferenceDBManager refMgr = (ReferenceDBManager) program.getReferenceManager(); ReferenceDBManager refMgr = program.getReferenceManager();
Address nextExtAddr = getNextExternalSymbolAddress(); Address nextExtAddr = getNextExternalSymbolAddress();
@ -236,7 +239,9 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Upgrade old stack and register variable symbol address to variable addresses. * Upgrade old stack and register variable symbol address to variable addresses.
* <p>
* Also force associated references to be updated to new variable addresses. * Also force associated references to be updated to new variable addresses.
*
* @param monitor the task monitor * @param monitor the task monitor
* @throws IOException if there is database exception * @throws IOException if there is database exception
* @throws CancelledException if the operation is cancelled * @throws CancelledException if the operation is cancelled
@ -292,8 +297,10 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* No more sharing the same variable address for multiple variable symbols. * No more sharing the same variable address for multiple variable symbols.
* Must split these up. Only reference to variable addresses should be the * <p>
* symbol address - reference refer to physical/stack addresses, and symbolIDs. * Must split these up. Only reference to variable addresses should be the symbol address -
* reference refer to physical/stack addresses, and symbolIDs.
*
* @param monitor the task monitor * @param monitor the task monitor
* @throws CancelledException if the operation is cancelled * @throws CancelledException if the operation is cancelled
*/ */
@ -394,6 +401,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Add old local symbols * Add old local symbols
*
* @throws IOException if there is database exception * @throws IOException if there is database exception
* @throws CancelledException if the operation is cancelled * @throws CancelledException if the operation is cancelled
*/ */
@ -442,6 +450,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Save off old local symbols whose upgrade needs to be deferred until after function manager * Save off old local symbols whose upgrade needs to be deferred until after function manager
* upgrade has been completed. * upgrade has been completed.
*
* @param tmpHandle scratch pad database handle * @param tmpHandle scratch pad database handle
* @param symbolID local symbol ID * @param symbolID local symbol ID
* @param oldAddr old address value from symbol table * @param oldAddr old address value from symbol table
@ -628,7 +637,6 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return false; return false;
} }
} }
//refManager.symbolRemoved(sym);
return sym.delete(); return sym.delete();
} }
finally { finally {
@ -661,6 +669,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Removes the symbol directly * Removes the symbol directly
*
* @param sym the symbol to remove. * @param sym the symbol to remove.
* @return true if the symbol was removed, false otherwise. * @return true if the symbol was removed, false otherwise.
*/ */
@ -1144,6 +1153,21 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return null; return null;
} }
@Override
public SymbolIterator scanSymbolsByName(String startName) {
lock.acquire();
try {
return new SymbolNameScanningIterator(startName);
}
catch (IOException e) {
program.dbError(e);
}
finally {
lock.release();
}
return null;
}
@Override @Override
public Symbol getPrimarySymbol(Address addr) { public Symbol getPrimarySymbol(Address addr) {
if (!addr.isMemoryAddress() && !addr.isExternalAddress()) { if (!addr.isMemoryAddress() && !addr.isExternalAddress()) {
@ -1201,6 +1225,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Returns the maximum symbol address within the specified address space. * Returns the maximum symbol address within the specified address space.
*
* @param space address space * @param space address space
* @return maximum symbol address within space or null if none are found. * @return maximum symbol address within space or null if none are found.
*/ */
@ -1216,6 +1241,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Returns the next available external symbol address * Returns the next available external symbol address
*
* @return the address * @return the address
*/ */
public Address getNextExternalSymbolAddress() { public Address getNextExternalSymbolAddress() {
@ -1228,14 +1254,18 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
@Override @Override
public SymbolIterator getPrimarySymbolIterator(Address startAddr, boolean forward) { public SymbolIterator getPrimarySymbolIterator(Address startAddr, boolean forward)
throws IllegalArgumentException {
if (!startAddr.isMemoryAddress()) {
throw new IllegalArgumentException("Invalid memory address: " + startAddr);
}
return getPrimarySymbolIterator( return getPrimarySymbolIterator(
program.getAddressFactory().getAddressSet(startAddr, program.getMaxAddress()), forward); program.getAddressFactory().getAddressSet(startAddr, program.getMaxAddress()), forward);
} }
@Override @Override
public SymbolIterator getPrimarySymbolIterator(AddressSetView set, boolean forward) { public SymbolIterator getPrimarySymbolIterator(AddressSetView set, boolean forward) {
if (set.isEmpty()) { if (set != null && set.isEmpty()) {
return SymbolIterator.EMPTY_ITERATOR; return SymbolIterator.EMPTY_ITERATOR;
} }
try { try {
@ -1250,7 +1280,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override @Override
public SymbolIterator getSymbols(AddressSetView set, SymbolType type, boolean forward) { public SymbolIterator getSymbols(AddressSetView set, SymbolType type, boolean forward) {
if (set.isEmpty()) { if (set != null && set.isEmpty()) {
return SymbolIterator.EMPTY_ITERATOR; return SymbolIterator.EMPTY_ITERATOR;
} }
Query query = Query query =
@ -1264,7 +1294,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
@Override @Override
public SymbolIterator getSymbolIterator(Address startAddr, boolean forward) { public SymbolIterator getSymbolIterator(Address startAddr, boolean forward)
throws IllegalArgumentException {
if (!startAddr.isMemoryAddress()) {
throw new IllegalArgumentException("Invalid memory address: " + startAddr);
}
RecordIterator it; RecordIterator it;
try { try {
it = adapter.getSymbolsByAddress(startAddr, forward); it = adapter.getSymbolsByAddress(startAddr, forward);
@ -1315,7 +1349,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
@Override @Override
public void addExternalEntryPoint(Address addr) { public void addExternalEntryPoint(Address addr) throws IllegalArgumentException {
refManager.addExternalEntryPointRef(addr); refManager.addExternalEntryPointRef(addr);
} }
@ -1394,10 +1428,12 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
/** /**
* Move symbol. Only symbol address is changed. * Move symbol.
* References must be moved separately. * <p>
* @param oldAddr the old symbol address * Only symbol address is changed. References must be moved separately.
* @param newAddr the new symbol address *
* @param oldAddr the old symbol memory address
* @param newAddr the new symbol memory address
*/ */
public void moveSymbolsAt(Address oldAddr, Address newAddr) { public void moveSymbolsAt(Address oldAddr, Address newAddr) {
lock.acquire(); lock.acquire();
@ -1422,6 +1458,9 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// Unique dynamic symbol ID produced from a dynamic symbol address map which has a // Unique dynamic symbol ID produced from a dynamic symbol address map which has a
// high-order bit set to avoid potential conflict with stored symbol ID's which are // high-order bit set to avoid potential conflict with stored symbol ID's which are
// assigned starting at 0. // assigned starting at 0.
if (!addr.isMemoryAddress()) {
throw new IllegalArgumentException("Invalid memory address: " + addr);
}
return dynamicSymbolAddressMap.getKey(addr); return dynamicSymbolAddressMap.getKey(addr);
} }
@ -1450,16 +1489,17 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
FunctionManagerDB getFunctionManager() { FunctionManagerDB getFunctionManager() {
return (FunctionManagerDB) program.getFunctionManager(); return program.getFunctionManager();
} }
ExternalManagerDB getExternalManager() { ExternalManagerDB getExternalManager() {
return (ExternalManagerDB) program.getExternalManager(); return program.getExternalManager();
} }
/** /**
* Called by the NamespaceManager when a namespace is removed; remove all symbols that have the * Called by the NamespaceManager when a namespace is removed; remove all symbols that have the
* given namespace ID. * given namespace ID.
*
* @param namespaceID ID of namespace being removed * @param namespaceID ID of namespace being removed
*/ */
public void namespaceRemoved(long namespaceID) { public void namespaceRemoved(long namespaceID) {
@ -1849,11 +1889,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
} }
private class SymbolNameRecordIterator implements SymbolIterator { private abstract class AbstractSymbolNameRecordIterator implements SymbolIterator {
private RecordIterator it; private RecordIterator it;
SymbolNameRecordIterator(String name) throws IOException { AbstractSymbolNameRecordIterator(RecordIterator it) {
this.it = adapter.getSymbolsByName(name); this.it = it;
} }
@Override @Override
@ -1891,6 +1931,18 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
} }
private class SymbolNameRecordIterator extends AbstractSymbolNameRecordIterator {
SymbolNameRecordIterator(String name) throws IOException {
super(adapter.getSymbolsByName(name));
}
}
private class SymbolNameScanningIterator extends AbstractSymbolNameRecordIterator {
public SymbolNameScanningIterator(String startName) throws IOException {
super(adapter.scanSymbolsByName(startName));
}
}
private class ExternalSymbolNameRecordIterator implements SymbolIterator { private class ExternalSymbolNameRecordIterator implements SymbolIterator {
private RecordIterator it; private RecordIterator it;
@ -2302,6 +2354,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Checks to make sure there is a single valid primary symbol at each address * Checks to make sure there is a single valid primary symbol at each address
*
* @param set the set of addresses that may have to be fixed up * @param set the set of addresses that may have to be fixed up
*/ */
private void fixupPrimarySymbols(Set<Address> set) { private void fixupPrimarySymbols(Set<Address> set) {
@ -2342,9 +2395,12 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
/** /**
* Checks if the givens symbols from the same address have exactly one primary symbol amongst them * Checks if the givens symbols from the same address have exactly one primary symbol amongst
* them
*
* @param symbols the array of symbols at a an address * @param symbols the array of symbols at a an address
* @return true if there is exactly one primary symbol at the address (also true if no symbols at address) * @return true if there is exactly one primary symbol at the address (also true if no symbols
* at address)
*/ */
private boolean hasValidPrimary(Symbol[] symbols) { private boolean hasValidPrimary(Symbol[] symbols) {
if (symbols.length == 0) { if (symbols.length == 0) {
@ -2496,8 +2552,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
/** /**
* Creates variable symbols. Note this is not a method defined in the Symbol Table interface. * Creates variable symbols.
* It is intended to be used by Ghidra program internals. * <p>
* Note this is not a method defined in the Symbol Table interface. It is intended to be used by
* Ghidra program internals.
*
* @param name the name of the variable * @param name the name of the variable
* @param function the function that contains the variable. * @param function the function that contains the variable.
* @param type the type of the variable (can only be PARAMETER or LOCAL_VAR) * @param type the type of the variable (can only be PARAMETER or LOCAL_VAR)
@ -2649,6 +2708,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Create a Library symbol with the specified name and optional pathname * Create a Library symbol with the specified name and optional pathname
*
* @param name library name * @param name library name
* @param pathname project file path (may be null) * @param pathname project file path (may be null)
* @param source symbol source * @param source symbol source
@ -2664,6 +2724,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Create a Class symbol with the specified name and parent * Create a Class symbol with the specified name and parent
*
* @param name class name * @param name class name
* @param parent parent namespace (may be null for global namespace) * @param parent parent namespace (may be null for global namespace)
* @param source symbol source * @param source symbol source
@ -2680,6 +2741,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
/** /**
* Create a simple Namespace symbol with the specified name and parent * Create a simple Namespace symbol with the specified name and parent
*
* @param name class name * @param name class name
* @param parent parent namespace (may be null for global namespace) * @param parent parent namespace (may be null for global namespace)
* @param source symbol source * @param source symbol source
@ -2730,11 +2792,14 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
/** /**
* Internal method for creating label symbols. If identical memory symbol already exists * Internal method for creating label symbols.
* it will be returned. * <p>
* If identical memory symbol already exists it will be returned.
*
* @param addr the address for the new symbol (memory or external) * @param addr the address for the new symbol (memory or external)
* @param name the name of the new symbol * @param name the name of the new symbol
* @param namespace the namespace for the new symbol (null may be specified for global namespace) * @param namespace the namespace for the new symbol (null may be specified for global
* namespace)
* @param source the SourceType of the new symbol * @param source the SourceType of the new symbol
* @param stringData special use depending on the symbol type and whether or not it is external * @param stringData special use depending on the symbol type and whether or not it is external
* @return the new symbol * @return the new symbol
@ -2771,14 +2836,14 @@ public class SymbolManager implements SymbolTable, ManagerDB {
makePrimary = (primary == null); makePrimary = (primary == null);
} }
else if (addr.isExternalAddress()) { else if (addr.isExternalAddress()) {
// only one symbol per external address is allowed // TODO: remove support for external symbol creation from this method (see GP-3045)
Symbol primary = getPrimarySymbol(addr); Symbol primary = getPrimarySymbol(addr);
if (primary != null) { if (primary != null) { // only one symbol per external address is allowed
throw new IllegalArgumentException("external address already used"); throw new IllegalArgumentException("external address already used");
} }
} }
else { else {
throw new IllegalArgumentException("bad label address"); throw new IllegalArgumentException("Invalid memory address: " + addr);
} }
return doCreateSymbol(name, addr, namespace, SymbolType.LABEL, stringData, null, null, return doCreateSymbol(name, addr, namespace, SymbolType.LABEL, stringData, null, null,
@ -2794,7 +2859,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* *
* @param addr the address for the new symbol * @param addr the address for the new symbol
* @param name the name of the new symbol * @param name the name of the new symbol
* @param namespace the namespace for the new symbol (null may be specified for global namespace) * @param namespace the namespace for the new symbol (null may be specified for global
* namespace)
* @param source the SourceType of the new symbol * @param source the SourceType of the new symbol
* @param stringData special use depending on the symbol type and whether or not it is external. * @param stringData special use depending on the symbol type and whether or not it is external.
* @return the new symbol * @return the new symbol
@ -2866,9 +2932,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
/** /**
* Finds the appropriate symbol to promote when function is created. And by promote, we really * Finds the appropriate symbol to promote when function is created.
* mean find the symbol that needs to be deleted before creating the function symbol. If the * <p>
* found symbol is not dynamic, the function symbol will assume its name and namespace. * And by promote, we really mean find the symbol that needs to be deleted before creating the
* function symbol. If the found symbol is not dynamic, the function symbol will assume its name
* and namespace.
*/ */
private Symbol findSymbolToPromote(Symbol matching, Symbol primary, SourceType source) { private Symbol findSymbolToPromote(Symbol matching, Symbol primary, SourceType source) {
// if the function is default, then the primary will be promoted // if the function is default, then the primary will be promoted

View file

@ -17,6 +17,7 @@ package ghidra.program.model.symbol;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Stream;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@ -85,7 +86,7 @@ public class SymbolUtilities {
DEFAULT_DATA_PREFIX, DEFAULT_SYMBOL_PREFIX, DEFAULT_SUBROUTINE_PREFIX, DEFAULT_DATA_PREFIX, DEFAULT_SYMBOL_PREFIX, DEFAULT_SUBROUTINE_PREFIX,
DEFAULT_UNKNOWN_PREFIX, DEFAULT_EXTERNAL_ENTRY_PREFIX, DEFAULT_FUNCTION_PREFIX }; DEFAULT_UNKNOWN_PREFIX, DEFAULT_EXTERNAL_ENTRY_PREFIX, DEFAULT_FUNCTION_PREFIX };
private static List<String> DYNAMIC_DATA_TYPE_PREFIXES = getDynamicDataTypePrefixes(); private final static List<String> DYNAMIC_DATA_TYPE_PREFIXES = getDynamicDataTypePrefixes();
/** /**
* Any dynamic label will have an address with this minimum length or longer * Any dynamic label will have an address with this minimum length or longer
@ -574,7 +575,7 @@ public class SymbolUtilities {
*/ */
public static Address parseDynamicName(AddressFactory factory, String name) { public static Address parseDynamicName(AddressFactory factory, String name) {
// assume dynamic names will naver start with an underscore // assume dynamic names will never start with an underscore
if (name.startsWith(UNDERSCORE)) { if (name.startsWith(UNDERSCORE)) {
return null; return null;
} }
@ -591,7 +592,7 @@ public class SymbolUtilities {
space = factory.getDefaultAddressSpace(); space = factory.getDefaultAddressSpace();
} }
// Only consider address values which meet the meet the minimum padding behavior // Only consider address values which meet the minimum padding behavior
if (addressOffsetString.length() < MIN_LABEL_ADDRESS_DIGITS) { if (addressOffsetString.length() < MIN_LABEL_ADDRESS_DIGITS) {
return null; return null;
} }

View file

@ -172,6 +172,11 @@ public class StubSymbolTable implements SymbolTable {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public SymbolIterator scanSymbolsByName(String startName) {
throw new UnsupportedOperationException();
}
@Override @Override
public int getNumSymbols() { public int getNumSymbols() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();