mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-615: Put all symbols, including duplicates and dynamics, in assembler.
This commit is contained in:
parent
0bfb9ae84a
commit
228d622ef7
3 changed files with 103 additions and 61 deletions
|
@ -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
|
||||
|
|
|
@ -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)}.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue