GP-615: Put all symbols, including duplicates and dynamics, in assembler.

This commit is contained in:
Dan 2022-04-04 15:22:26 -04:00
parent 0bfb9ae84a
commit 228d622ef7
3 changed files with 103 additions and 61 deletions

View file

@ -25,6 +25,8 @@ import ghidra.app.plugin.assembler.sleigh.sem.*;
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNumericSymbols;
import ghidra.app.plugin.assembler.sleigh.util.DbgTimer;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectListener;
import ghidra.program.disassemble.Disassembler;
import ghidra.program.disassemble.DisassemblerMessageListener;
import ghidra.program.model.address.*;
@ -33,6 +35,7 @@ import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.util.ChangeManager;
import ghidra.util.task.TaskMonitor;
/**
@ -45,6 +48,22 @@ import ghidra.util.task.TaskMonitor;
public class SleighAssembler implements Assembler {
protected static final DbgTimer dbg = DbgTimer.INACTIVE;
protected class ListenerForSymbolsRefresh implements DomainObjectListener {
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (ev.containsEvent(ChangeManager.DOCR_SYMBOL_ADDED) ||
ev.containsEvent(ChangeManager.DOCR_SYMBOL_ADDRESS_CHANGED) ||
ev.containsEvent(ChangeManager.DOCR_SYMBOL_REMOVED) ||
ev.containsEvent(ChangeManager.DOCR_SYMBOL_RENAMED)) {
synchronized (lock) {
symbols = null;
}
}
}
}
protected final Object lock = new Object();
protected AssemblySelector selector;
protected Program program;
protected Listing listing;
@ -54,6 +73,8 @@ public class SleighAssembler implements Assembler {
protected AssemblyContextGraph ctxGraph;
protected SleighLanguage lang;
protected AssemblyNumericSymbols symbols;
/**
* Construct a SleighAssembler.
*
@ -240,10 +261,18 @@ public class SleighAssembler implements Assembler {
* @return the map
*/
protected AssemblyNumericSymbols getNumericSymbols() {
if (program != null) {
return AssemblyNumericSymbols.fromProgram(program);
synchronized (lock) {
if (symbols != null) {
return symbols;
}
if (program == null) {
symbols = AssemblyNumericSymbols.fromLanguage(lang);
}
else {
symbols = AssemblyNumericSymbols.fromProgram(program);
}
return symbols;
}
return AssemblyNumericSymbols.fromLanguage(lang);
}
@Override

View file

@ -16,13 +16,13 @@
package ghidra.app.plugin.assembler.sleigh.symbol;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*;
@ -42,12 +42,12 @@ public final class AssemblyNumericSymbols {
* @param labels the destination map
* @param language the language
*/
private static void collectLanguageLabels(Map<String, Address> labels, Language language) {
private static void collectLanguageLabels(Map<String, Set<Address>> labels, Language language) {
for (Register reg : language.getRegisters()) {
// TODO/HACK: There ought to be a better mechanism describing suitable symbolic
// substitutions for a given operand.
if (!reg.getAddressSpace().isRegisterSpace()) {
labels.put(reg.getName(), reg.getAddress());
labels.computeIfAbsent(reg.getName(), n -> new HashSet<>()).add(reg.getAddress());
}
}
}
@ -58,18 +58,33 @@ public final class AssemblyNumericSymbols {
* @param labels the destination map
* @param program the source program
*/
private static void collectProgramLabels(Map<String, Address> labels, Program program) {
final SymbolIterator it = program.getSymbolTable().getAllSymbols(false);
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();
if (sym.isExternal()) {
continue; // skip externals - will generally be referenced indirectly not directly
}
SymbolType symbolType = sym.getSymbolType();
if (symbolType != SymbolType.LABEL && symbolType != SymbolType.FUNCTION) {
if (symbolType == SymbolType.LABEL) {
if (sym.isExternal()) {
continue;
}
labels.put(sym.getName(), sym.getAddress());
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
}
}
@ -79,12 +94,12 @@ public final class AssemblyNumericSymbols {
* @param equates the destination map
* @param programthe source program
*/
private static void collectProgramEquates(Map<String, Long> equates, Program program) {
private static void collectProgramEquates(Map<String, Set<Long>> equates, Program program) {
final Iterator<Equate> it = program.getEquateTable().getEquates();
while (it.hasNext()) {
Equate eq = it.next();
// Thought is: If that's what the user sees, then that's what the user will type!
equates.put(eq.getDisplayName(), eq.getValue());
equates.computeIfAbsent(eq.getDisplayName(), n -> new HashSet<>()).add(eq.getValue());
}
}
@ -95,7 +110,7 @@ public final class AssemblyNumericSymbols {
* @return the symbols
*/
public static AssemblyNumericSymbols fromLanguage(Language language) {
Map<String, Address> labels = new HashMap<>();
Map<String, Set<Address>> labels = new HashMap<>();
collectLanguageLabels(labels, language);
return forMaps(Map.of(), labels);
}
@ -111,8 +126,8 @@ public final class AssemblyNumericSymbols {
* @return the symbols
*/
public static AssemblyNumericSymbols fromProgram(Program program) {
Map<String, Long> equates = new HashMap<>();
Map<String, Address> labels = new HashMap<>();
Map<String, Set<Long>> equates = new HashMap<>();
Map<String, Set<Address>> labels = new HashMap<>();
collectLanguageLabels(labels, program.getLanguage());
collectProgramLabels(labels, program);
collectProgramEquates(equates, program);
@ -126,27 +141,32 @@ public final class AssemblyNumericSymbols {
* @param labels the labels
* @return the symbols
*/
public static AssemblyNumericSymbols forMaps(Map<String, Long> equates,
Map<String, Address> labels) {
public static AssemblyNumericSymbols forMaps(Map<String, Set<Long>> equates,
Map<String, Set<Address>> labels) {
return new AssemblyNumericSymbols(Map.copyOf(equates), Map.copyOf(labels),
groupBySpace(labels));
}
private static Map<AddressSpace, Map<String, Address>> groupBySpace(
Map<String, Address> labels) {
return Collections.unmodifiableMap(labels.entrySet()
.stream()
.collect(Collectors.groupingBy(ent -> ent.getValue().getAddressSpace(),
Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue))));
private static Map<AddressSpace, Map<String, Set<Address>>> groupBySpace(
Map<String, Set<Address>> labels) {
Map<AddressSpace, Map<String, Set<Address>>> result = new HashMap<>();
for (Map.Entry<String, Set<Address>> entry : labels.entrySet()) {
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<>();
public final Map<String, Long> equates;
public final Map<String, Address> labels;
public final Map<AddressSpace, Map<String, Address>> labelsBySpace;
public final Map<String, Set<Long>> equates;
public final Map<String, Set<Address>> labels;
public final Map<AddressSpace, Map<String, Set<Address>>> labelsBySpace;
private AssemblyNumericSymbols(Map<String, Long> equates, Map<String, Address> labels,
Map<AddressSpace, Map<String, Address>> labelsBySpace) {
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;
@ -164,16 +184,13 @@ public final class AssemblyNumericSymbols {
* @param name the name
* @return the value, or null
*/
public Long chooseAny(String name) {
Long eq = equates.get(name);
if (eq != null) {
return eq;
public Set<Long> chooseAll(String name) {
Set<Long> result = new TreeSet<>();
result.addAll(equates.getOrDefault(name, Set.of()));
for (Address address : labels.getOrDefault(name, Set.of())) {
result.add(address.getAddressableWordOffset());
}
Address addr = labels.get(name);
if (addr != null) {
return addr.getAddressableWordOffset();
}
return null;
return result;
}
/**
@ -183,16 +200,12 @@ public final class AssemblyNumericSymbols {
* @param space the address space
* @return the addressable word offset of the found label, or null
*/
public Long chooseBySpace(String name, AddressSpace space) {
Map<String, Address> forSpace = labelsBySpace.get(space);
if (forSpace == null) {
return null;
}
Address addr = forSpace.get(name);
if (addr == null) {
return null;
}
return addr.getAddressableWordOffset();
public Set<Long> chooseBySpace(String name, AddressSpace space) {
return labelsBySpace.getOrDefault(space, Map.of())
.getOrDefault(name, Set.of())
.stream()
.map(a -> a.getAddressableWordOffset())
.collect(Collectors.toSet());
}
/**
@ -200,7 +213,7 @@ public final class AssemblyNumericSymbols {
*
* <p>
* If a space is not given, or if that space is the constant space, then this will choose from
* all symbols, via {@link #chooseAny(String)}. If a space is given, and it is not the constant
* all symbols, via {@link #chooseAll(String)}. If a space is given, and it is not the constant
* space, then this will choose from symbols in the given space, via
* {@link #chooseBySpace(String, AddressSpace)}.
*
@ -208,9 +221,9 @@ public final class AssemblyNumericSymbols {
* @param space the address space, or null
* @return the equate value, or label addressable word offset, or null
*/
public Long choose(String name, AddressSpace space) {
public Set<Long> choose(String name, AddressSpace space) {
if (space == null || space.isConstantSpace()) {
return chooseAny(name);
return chooseAll(name);
}
return chooseBySpace(name, space);
}
@ -254,7 +267,7 @@ public final class AssemblyNumericSymbols {
* @return the collection of symbol names
*/
public Collection<String> suggestBySpace(String got, AddressSpace space, int max) {
Map<String, Address> forSpace = labelsBySpace.get(space);
Map<String, Set<Address>> forSpace = labelsBySpace.get(space);
if (forSpace == null) {
return Set.of();
}
@ -266,7 +279,7 @@ public final class AssemblyNumericSymbols {
* Suggest up to max symbols having the given prefix, using space as a hint
*
* <p>
* As in {@link #chooseAny(String)}, if space is null or the constant space, then this will
* As in {@link #chooseAll(String)}, if space is null or the constant space, then this will
* suggest from all symbols, via {@link #suggestAny(String, int)}. If space is given, and it is
* not the constant space, then this will suggest from symbols in the given space, via
* {@link #suggestBySpace(String, AddressSpace, int)}.

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.assembler.sleigh.symbol;
import java.util.*;
import java.util.stream.Collectors;
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseNumericToken;
@ -138,11 +139,10 @@ public class AssemblyNumericTerminal extends AssemblyTerminal {
break;
}
String lab = buffer.substring(s, b);
Long val = symbols.choose(lab, space);
if (val == null) {
return Collections.emptySet();
}
return Collections.singleton(new AssemblyParseNumericToken(grammar, this, lab, val));
return symbols.choose(lab, space)
.stream()
.map(val -> new AssemblyParseNumericToken(grammar, this, lab, val))
.collect(Collectors.toList());
}
/**