GP-3351: Purge TraceFunctionSymbol and related interfaces, impls, tables, etc.

This commit is contained in:
Dan 2023-08-02 10:51:39 -04:00
parent bece81176e
commit eeaa3486b8
50 changed files with 520 additions and 7068 deletions

View file

@ -404,11 +404,6 @@ public class DebuggerListingProvider extends CodeViewerProvider {
return !mode.canEdit(current);
}
@Override
public boolean isDynamicListing() {
return true;
}
@Override
public String getWindowGroup() {
//TODO: Overriding this to align disconnected providers

View file

@ -102,9 +102,9 @@ public enum DebuggerPcodeUtils {
* TODO: This may break things that check for the absence of a symbol
*
* I don't think it'll affect expressions, but it could later affect user Sleigh
* libraries than an expression might like to use. The better approach would be
* to incorporate a better error message into the Sleigh compiler, but it won't
* always know the use case for a clear message.
* libraries than an expression might like to use. The better approach would be to
* incorporate a better error message into the Sleigh compiler, but it won't always
* know the use case for a clear message.
*/
throw new SleighException("Unknown register or label: '" + nm + "'");
}
@ -114,9 +114,7 @@ public enum DebuggerPcodeUtils {
protected SleighSymbol findUserSymbol(String nm) {
Trace trace = coordinates.getTrace();
long snap = coordinates.getSnap();
for (TraceSymbol symbol : trace.getSymbolManager()
.labelsAndFunctions()
.getNamed(nm)) {
for (TraceSymbol symbol : trace.getSymbolManager().labels().getNamed(nm)) {
if (symbol instanceof TraceSymbolWithLifespan lifeSym &&
!lifeSym.getLifespan().contains(snap)) {
continue;

View file

@ -224,11 +224,9 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
@Override
default Symbol[] getSymbols() {
try (LockHold hold = getTrace().lockRead()) {
Collection<? extends TraceSymbol> at =
getTrace().getSymbolManager()
.labelsAndFunctions()
.getAt(getStartSnap(), getThread(),
getAddress(), true);
Collection<? extends TraceSymbol> at = getTrace().getSymbolManager()
.labels()
.getAt(getStartSnap(), getThread(), getAddress(), true);
return at.toArray(new TraceSymbol[at.size()]);
}
}
@ -236,11 +234,9 @@ public interface DBTraceCodeUnitAdapter extends TraceCodeUnit, MemBufferMixin {
@Override
default Symbol getPrimarySymbol() {
try (LockHold hold = getTrace().lockRead()) {
Collection<? extends TraceSymbol> at =
getTrace().getSymbolManager()
.labelsAndFunctions()
.getAt(getStartSnap(), getThread(),
getAddress(), true);
Collection<? extends TraceSymbol> at = getTrace().getSymbolManager()
.labels()
.getAt(getStartSnap(), getThread(), getAddress(), true);
if (at.isEmpty()) {
return null;
}

View file

@ -37,7 +37,6 @@ import ghidra.trace.database.DBTrace;
import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.listing.UndefinedDBTraceData;
import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.symbol.DBTraceFunctionSymbol;
import ghidra.trace.database.thread.DBTraceThread;
import ghidra.trace.model.*;
import ghidra.trace.model.listing.*;
@ -45,7 +44,6 @@ import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.program.TraceProgramViewListing;
import ghidra.trace.model.property.TracePropertyMapOperations;
import ghidra.trace.model.symbol.TraceFunctionSymbol;
import ghidra.trace.util.*;
import ghidra.util.*;
import ghidra.util.AddressIteratorAdapter;
@ -867,13 +865,13 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
}
@Override
public TraceFunctionSymbol createFunction(String name, Address entryPoint, AddressSetView body,
public Function createFunction(String name, Address entryPoint, AddressSetView body,
SourceType source) throws InvalidInputException, OverlappingFunctionException {
return program.functionManager.createFunction(name, entryPoint, body, source);
}
@Override
public TraceFunctionSymbol createFunction(String name, Namespace nameSpace, Address entryPoint,
public Function createFunction(String name, Namespace nameSpace, Address entryPoint,
AddressSetView body, SourceType source)
throws InvalidInputException, OverlappingFunctionException {
return program.functionManager.createFunction(name, nameSpace, entryPoint, body, source);
@ -891,22 +889,12 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public List<Function> getGlobalFunctions(String name) {
return new ArrayList<>(program.trace.getSymbolManager().functions().getGlobalsNamed(name));
return List.of();
}
@Override
public List<Function> getFunctions(String namespace, String name) {
// NOTE: This implementation allows namespaces to contain the separator symbol
List<Function> result = new ArrayList<>();
for (DBTraceFunctionSymbol func : program.trace.getSymbolManager()
.functions()
.getNamed(
name)) {
if (namespace.equals(func.getParentNamespace().getName(true))) {
result.add(func);
}
}
return result;
return List.of();
}
@Override
@ -916,7 +904,7 @@ public abstract class AbstractDBTraceProgramViewListing implements TraceProgramV
@Override
public FunctionIterator getExternalFunctions() {
return program.functionManager.getExternalFunctions();
return EmptyFunctionIterator.INSTANCE;
}
@Override

View file

@ -47,10 +47,10 @@ import ghidra.trace.database.*;
import ghidra.trace.database.listing.DBTraceCodeSpace;
import ghidra.trace.database.listing.DBTraceDefinedUnitsView;
import ghidra.trace.database.memory.DBTraceMemorySpace;
import ghidra.trace.database.symbol.DBTraceFunctionSymbolView;
import ghidra.trace.model.*;
import ghidra.trace.model.Trace.*;
import ghidra.trace.model.TraceTimeViewport.*;
import ghidra.trace.model.TraceTimeViewport.Occlusion;
import ghidra.trace.model.TraceTimeViewport.RangeQueryOcclusion;
import ghidra.trace.model.bookmark.TraceBookmark;
import ghidra.trace.model.bookmark.TraceBookmarkType;
import ghidra.trace.model.data.TraceBasedDataTypeManager;
@ -129,22 +129,6 @@ public class DBTraceProgramView implements TraceProgramView {
listenFor(TraceDataTypeChangeType.RENAMED, this::dataTypeRenamed);
listenFor(TraceDataTypeChangeType.DELETED, this::dataTypeDeleted);
listenFor(TraceFunctionChangeType.CHANGED, this::functionChanged);
listenFor(TraceFunctionChangeType.CHANGED_PURGE, this::functionChangedPurge);
listenFor(TraceFunctionChangeType.CHANGED_INLINE, this::functionChangedInline);
listenFor(TraceFunctionChangeType.CHANGED_NORETURN, this::functionChangedNoReturn);
listenFor(TraceFunctionChangeType.CHANGED_CALL_FIXUP, this::functionChangedCallFixup);
listenFor(TraceFunctionChangeType.CHANGED_RETURN, this::functionChangedReturn);
listenFor(TraceFunctionChangeType.CHANGED_PARAMETERS, this::functionChangedParameters);
listenFor(TraceFunctionChangeType.CHANGED_THUNK, this::functionChangedThunk);
listenFor(TraceFunctionChangeType.CHANGED_BODY, this::functionChangedBody);
listenFor(TraceFunctionChangeType.TAG_APPLIED, this::functionChangedTagApplied);
listenFor(TraceFunctionChangeType.TAG_REMOVED, this::functionChangedTagRemoved);
listenFor(TraceFunctionTagChangeType.ADDED, this::functionTagAdded);
listenFor(TraceFunctionTagChangeType.CHANGED, this::functionTagChanged);
listenFor(TraceFunctionTagChangeType.DELETED, this::functionTagDeleted);
listenFor(TraceInstructionChangeType.FLOW_OVERRIDE_CHANGED,
this::instructionFlowOverrideChanged);
listenFor(TraceInstructionChangeType.FALL_THROUGH_OVERRIDE_CHANGED,
@ -465,107 +449,6 @@ public class DBTraceProgramView implements TraceProgramView {
null, null, oldPath, newIsNull));
}
private void gatherThunksTo(Collection<TraceFunctionSymbol> into,
TraceFunctionSymbol function) {
into.add(function);
for (Address address : function.getFunctionThunkAddresses()) {
TraceFunctionSymbol thunkTo = functionManager.getFunctionAt(address);
if (thunkTo != null) {
gatherThunksTo(into, thunkTo);
}
}
}
private Collection<TraceFunctionSymbol> gatherThunksTo(TraceFunctionSymbol function) {
List<TraceFunctionSymbol> result = new ArrayList<>();
gatherThunksTo(result, function);
return result;
}
private void functionChangedGeneric(TraceAddressSpace space, TraceFunctionSymbol function,
int type, int subType) {
DomainObjectEventQueues queues = isFunctionVisible(space, function);
if (queues == null) {
return;
}
for (TraceFunctionSymbol f : gatherThunksTo(function)) {
queues.fireEvent(new ProgramChangeRecord(type, subType, f.getEntryPoint(),
f.getEntryPoint(), f, null, null));
}
}
private void functionChanged(TraceAddressSpace space, TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED, 0);
}
private void functionChangedPurge(TraceAddressSpace space, TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED,
ChangeManager.FUNCTION_CHANGED_PURGE);
}
private void functionChangedInline(TraceAddressSpace space, TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED,
ChangeManager.FUNCTION_CHANGED_INLINE);
}
private void functionChangedNoReturn(TraceAddressSpace space,
TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED,
ChangeManager.FUNCTION_CHANGED_NORETURN);
}
private void functionChangedCallFixup(TraceAddressSpace space,
TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED,
ChangeManager.FUNCTION_CHANGED_CALL_FIXUP);
}
private void functionChangedReturn(TraceAddressSpace space, TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED,
ChangeManager.FUNCTION_CHANGED_RETURN);
}
private void functionChangedParameters(TraceAddressSpace space,
TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED,
ChangeManager.FUNCTION_CHANGED_PARAMETERS);
}
private void functionChangedThunk(TraceAddressSpace space, TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_CHANGED,
ChangeManager.FUNCTION_CHANGED_THUNK);
}
private void functionChangedBody(TraceAddressSpace space, TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_FUNCTION_BODY_CHANGED, 0);
}
private void functionChangedTagApplied(TraceAddressSpace space,
TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_TAG_ADDED_TO_FUNCTION, 0);
}
private void functionChangedTagRemoved(TraceAddressSpace space,
TraceFunctionSymbol function) {
functionChangedGeneric(space, function, ChangeManager.DOCR_TAG_REMOVED_FROM_FUNCTION,
0);
}
private void functionTagAdded(FunctionTag tag) {
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_CREATED, null,
null, tag, null, null));
}
private void functionTagChanged(FunctionTag tag) {
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_CHANGED, null,
null, tag, null, null));
}
private void functionTagDeleted(FunctionTag tag) {
fireEventAllViews(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_TAG_DELETED, null,
null, tag, null, null));
}
private void instructionFlowOverrideChanged(TraceAddressSpace space,
TraceInstruction instruction, FlowOverride oldOverride, FlowOverride newOverride) {
DomainObjectEventQueues queues = isCodeVisible(space, instruction);
@ -667,39 +550,12 @@ public class DBTraceProgramView implements TraceProgramView {
null, null, id, null, null));
}
private void checkVariableFunctionChanged(TraceAddressSpace space, TraceSymbol symbol) {
if (!(symbol instanceof TraceVariableSymbol)) {
return;
}
TraceFunctionSymbol function = ((TraceVariableSymbol) symbol).getFunction();
if (function == null) {
return;
}
int subType = symbol instanceof TraceParameterSymbol ? //
ChangeManager.FUNCTION_CHANGED_PARAMETERS : 0;
for (TraceFunctionSymbol f : gatherThunksTo(function)) {
// NOTE: Should probably not see functions in register views anyway...
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
continue;
}
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_CHANGED,
subType, f.getEntryPoint(), f.getEntryPoint(), f, null, null));
}
}
private void symbolAdded(TraceAddressSpace space, TraceSymbol symbol) {
DomainObjectEventQueues queues = isSymbolVisible(space, symbol);
if (queues == null) {
return;
}
fireSymbolAdded(queues, symbol);
if (symbol instanceof TraceFunctionSymbol) {
TraceFunctionSymbol function = (TraceFunctionSymbol) symbol;
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_ADDED,
function.getEntryPoint(), function.getEntryPoint(), function, null, null));
}
checkVariableFunctionChanged(space, symbol);
}
public void fireSymbolAdded(DomainObjectEventQueues queues, TraceSymbol symbol) {
@ -714,7 +570,6 @@ public class DBTraceProgramView implements TraceProgramView {
}
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_SOURCE_CHANGED,
symbol.getAddress(), symbol.getAddress(), symbol, null, null));
checkVariableFunctionChanged(space, symbol);
}
private void symbolSetAsPrimary(TraceAddressSpace space, TraceSymbol symbol,
@ -731,7 +586,6 @@ public class DBTraceProgramView implements TraceProgramView {
assert oldQueues == newQueues || oldQueues == null;
newQueues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_SET_AS_PRIMARY,
symbol.getAddress(), symbol.getAddress(), null, oldPrimary, newPrimary));
checkVariableFunctionChanged(space, symbol);
}
private void symbolRenamed(TraceAddressSpace space, TraceSymbol symbol, String oldName,
@ -742,7 +596,6 @@ public class DBTraceProgramView implements TraceProgramView {
}
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_RENAMED,
symbol.getAddress(), symbol.getAddress(), symbol, oldName, newName));
checkVariableFunctionChanged(space, symbol);
}
private void symbolParentChanged(TraceAddressSpace space, TraceSymbol symbol,
@ -753,7 +606,6 @@ public class DBTraceProgramView implements TraceProgramView {
}
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_SCOPE_CHANGED,
symbol.getAddress(), symbol.getAddress(), symbol, oldParent, newParent));
checkVariableFunctionChanged(space, symbol);
}
private void symbolAssociationAdded(TraceAddressSpace space, TraceSymbol symbol,
@ -786,7 +638,6 @@ public class DBTraceProgramView implements TraceProgramView {
}
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_SYMBOL_ADDRESS_CHANGED,
oldAddress, oldAddress, symbol, oldAddress, newAddress));
checkVariableFunctionChanged(space, symbol);
}
private void symbolLifespanChanged(TraceAddressSpace space, TraceSymbolWithLifespan symbol,
@ -799,20 +650,9 @@ public class DBTraceProgramView implements TraceProgramView {
boolean inNew = isSymbolWithLifespanVisible(symbol, newSpan);
if (inOld && !inNew) {
fireSymbolRemoved(queues, symbol);
if (symbol instanceof TraceFunctionSymbol) {
TraceFunctionSymbol function = (TraceFunctionSymbol) symbol;
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_REMOVED,
function.getEntryPoint(), function.getEntryPoint(), function,
function.getBody(), null));
}
}
if (!inOld && inNew) {
fireSymbolAdded(queues, symbol);
if (symbol instanceof TraceFunctionSymbol) {
TraceFunctionSymbol function = (TraceFunctionSymbol) symbol;
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_ADDED,
function.getEntryPoint(), function.getEntryPoint(), function, null, null));
}
}
}
@ -822,13 +662,6 @@ public class DBTraceProgramView implements TraceProgramView {
return;
}
fireSymbolRemoved(queues, symbol);
if (symbol instanceof TraceFunctionSymbol) {
TraceFunctionSymbol function = (TraceFunctionSymbol) symbol;
queues.fireEvent(new ProgramChangeRecord(ChangeManager.DOCR_FUNCTION_REMOVED,
function.getEntryPoint(), function.getEntryPoint(), function,
function.getBody(), null));
}
checkVariableFunctionChanged(space, symbol);
}
protected void fireSymbolRemoved(DomainObjectEventQueues queues, TraceSymbol symbol) {
@ -1775,51 +1608,8 @@ public class DBTraceProgramView implements TraceProgramView {
return getEventQueues(space);
}
protected Occlusion<TraceFunctionSymbol> getFunctionOcclusion(TraceFunctionSymbol func) {
return new QueryOcclusion<>() {
DBTraceFunctionSymbolView functions = trace.getSymbolManager().functions();
AddressSetView body = func.getBody();
@Override
public Iterable<? extends TraceFunctionSymbol> query(AddressRange range,
Lifespan span) {
// NB. No functions in register space!
return functions.getIntersecting(Lifespan.at(span.lmax()), null, range, false);
}
public boolean itemOccludes(AddressRange range, TraceFunctionSymbol f) {
return body.intersects(f.getBody());
}
@Override
public void removeItem(AddressSet remains, TraceFunctionSymbol t) {
remains.delete(t.getBody());
}
};
}
protected boolean isFunctionVisible(TraceFunctionSymbol function, Lifespan lifespan) {
AddressSetView body = function.getBody();
AddressRange bodySpan = new AddressRangeImpl(body.getMinAddress(), body.getMaxAddress());
return viewport.isCompletelyVisible(bodySpan, function.getLifespan(), function,
getFunctionOcclusion(function));
}
protected DomainObjectEventQueues isFunctionVisible(TraceAddressSpace space,
TraceFunctionSymbol function) {
DomainObjectEventQueues queues = getEventQueues(space);
if (queues == null) {
return null;
}
return isFunctionVisible(function, function.getLifespan()) ? queues : null;
}
protected boolean isSymbolWithLifespanVisible(TraceSymbolWithLifespan symbol,
Lifespan lifespan) {
if (symbol instanceof TraceFunctionSymbol) {
TraceFunctionSymbol func = (TraceFunctionSymbol) symbol;
return isFunctionVisible(func, lifespan);
}
if (!viewport.containsAnyUpper(lifespan)) {
return false;
}
@ -1832,14 +1622,6 @@ public class DBTraceProgramView implements TraceProgramView {
if (queues == null) {
return null;
}
if (symbol instanceof TraceVariableSymbol) {
TraceVariableSymbol var = (TraceVariableSymbol) symbol;
TraceFunctionSymbol func = var.getFunction();
if (func == null) {
return queues;
}
return isFunctionVisible(space, func);
}
if (!(symbol instanceof TraceSymbolWithLifespan)) {
return queues;
}

View file

@ -15,28 +15,20 @@
*/
package ghidra.trace.database.program;
import static ghidra.lifecycle.Unfinished.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.*;
import java.util.stream.Stream;
import generic.NestedIterator;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.database.symbol.*;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.listing.TraceData;
import ghidra.trace.model.symbol.TraceFunctionSymbol;
import ghidra.trace.database.symbol.DBTraceNamespaceSymbol;
import ghidra.trace.util.EmptyFunctionIterator;
import ghidra.trace.util.WrappingFunctionIterator;
import ghidra.util.LockHold;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
@ -44,12 +36,10 @@ import ghidra.util.task.TaskMonitor;
public class DBTraceProgramViewFunctionManager implements FunctionManager {
protected final DBTraceProgramView program;
protected final DBTraceFunctionSymbolView functions;
protected final DBTraceNamespaceSymbol global;
public DBTraceProgramViewFunctionManager(DBTraceProgramView program) {
this.program = program;
this.functions = program.trace.getSymbolManager().functions();
this.global = program.trace.getSymbolManager().getGlobalNamespace();
}
@ -60,135 +50,69 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public FunctionTagManager getFunctionTagManager() {
return TODO();
throw new UnsupportedOperationException();
}
@Override
public Collection<String> getCallingConventionNames() {
return functions.getCallingConventionNames();
return Stream.of(program.trace.getBaseCompilerSpec().getCallingConventions())
.map(PrototypeModel::getName)
.toList();
}
@Override
public PrototypeModel getDefaultCallingConvention() {
return functions.getDefaultCallingConvention();
return program.trace.getBaseCompilerSpec().getDefaultCallingConvention();
}
@Override
public PrototypeModel getCallingConvention(String name) {
return functions.getCallingConvention(name);
return program.trace.getBaseCompilerSpec().getCallingConvention(name);
}
@Override
public TraceFunctionSymbol createFunction(String name, Address entryPoint, AddressSetView body,
public Function createFunction(String name, Address entryPoint, AddressSetView body,
SourceType source) throws InvalidInputException, OverlappingFunctionException {
return functions.create(program.snap, entryPoint, body, name, null, global, source);
}
protected static DBTraceNamespaceSymbol validateParent(Namespace nameSpace) {
if (!(nameSpace instanceof DBTraceNamespaceSymbol)) {
throw new IllegalArgumentException("Given namespace is not part of this trace");
}
return (DBTraceNamespaceSymbol) nameSpace;
}
protected static DBTraceFunctionSymbol validateThunked(Function thunked) {
if (!(thunked instanceof DBTraceFunctionSymbol)) {
throw new IllegalArgumentException("Given thunked function is not part of this trace");
}
return (DBTraceFunctionSymbol) thunked;
throw new UnsupportedOperationException();
}
@Override
public TraceFunctionSymbol createFunction(String name, Namespace nameSpace, Address entryPoint,
public Function createFunction(String name, Namespace nameSpace, Address entryPoint,
AddressSetView body, SourceType source)
throws InvalidInputException, OverlappingFunctionException {
return functions.create(program.snap, entryPoint, body, name, null,
validateParent(nameSpace), source);
throw new UnsupportedOperationException();
}
@Override
public TraceFunctionSymbol createThunkFunction(String name, Namespace nameSpace,
Address entryPoint, AddressSetView body, Function thunkedFunction, SourceType source)
public Function createThunkFunction(String name, Namespace nameSpace, Address entryPoint,
AddressSetView body, Function thunkedFunction, SourceType source)
throws OverlappingFunctionException {
try {
return functions.create(program.snap, entryPoint, body, name,
validateThunked(thunkedFunction), validateParent(nameSpace), source);
}
catch (InvalidInputException e) {
// TODO: Why not just declare this as thrown???
throw new RuntimeException("Unexpected for default named function", e);
}
throw new UnsupportedOperationException();
}
@Override
public int getFunctionCount() {
return functions.size(false); // NOTE: May include those not at this snap
return 0;
}
@Override
public boolean removeFunction(Address entryPoint) {
try (LockHold hold = program.trace.lockWrite()) {
TraceFunctionSymbol at = getFunctionAt(entryPoint);
if (at == null) {
return false;
}
at.delete();
return true;
}
return false;
}
@Override
public TraceFunctionSymbol getFunctionAt(Address entryPoint) {
if (!entryPoint.getAddressSpace().isMemorySpace()) {
return null;
}
for (long s : program.viewport.getOrderedSnaps()) {
// NOTE: There ought only to be one, since no overlaps allowed.
for (TraceFunctionSymbol at : functions.getAt(s, null, entryPoint, false)) {
if (entryPoint.equals(at.getEntryPoint())) {
return at;
}
else {
return null; // Anything below is occluded by the found function
}
}
}
public Function getFunctionAt(Address entryPoint) {
return null;
}
@Override
public TraceFunctionSymbol getReferencedFunction(Address address) {
if (!address.getAddressSpace().isMemorySpace()) {
return null;
}
TraceFunctionSymbol found = getFunctionAt(address);
if (found != null) {
return found;
}
TraceData data =
program.getTopCode(address, (space, s) -> space.data().getContaining(s, address));
if (data == null) {
return null;
}
DBTraceReference ref = program.trace.getReferenceManager()
.getPrimaryReferenceFrom(data.getStartSnap(), address, 0);
return ref == null ? null : getFunctionAt(ref.getToAddress());
}
@Override
public TraceFunctionSymbol getFunctionContaining(Address addr) {
// NOTE: There ought only to be one, since no overlaps allowed.
for (TraceFunctionSymbol at : functions.getAt(program.snap, null, addr, false)) {
return at;
}
public Function getReferencedFunction(Address address) {
return null;
}
protected Iterator<? extends DBTraceFunctionSymbol> getFunctionsInRange(AddressRange range,
boolean forward) {
return functions.getIntersecting(Lifespan.at(program.snap), null, range, false,
forward).iterator();
@Override
public Function getFunctionContaining(Address addr) {
return null;
}
@Override
@ -198,52 +122,27 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public FunctionIterator getFunctions(Address start, boolean forward) {
return getFunctions(DBTraceUtils.getAddressSet(program.getAddressFactory(), start, forward),
forward);
return EmptyFunctionIterator.INSTANCE;
}
@Override
public FunctionIterator getFunctions(AddressSetView asv, boolean forward) {
return new WrappingFunctionIterator(
NestedIterator.start(asv.iterator(forward), rng -> getFunctionsInRange(rng, forward)),
f -> {
if (!asv.contains(f.getEntryPoint())) {
return false;
}
return true;
});
return EmptyFunctionIterator.INSTANCE;
}
@Override
public FunctionIterator getFunctionsNoStubs(boolean forward) {
return getFunctionsNoStubs(program.getAddressFactory().getAddressSet(), forward);
return EmptyFunctionIterator.INSTANCE;
}
@Override
public FunctionIterator getFunctionsNoStubs(Address start, boolean forward) {
return getFunctionsNoStubs(
DBTraceUtils.getAddressSet(program.getAddressFactory(), start, forward), forward);
return EmptyFunctionIterator.INSTANCE;
}
@Override
public FunctionIterator getFunctionsNoStubs(AddressSetView asv, boolean forward) {
return new WrappingFunctionIterator(
NestedIterator.start(asv.iterator(forward), rng -> getFunctionsInRange(rng, forward)),
f -> {
if (f.isThunk()) {
return false;
}
if (!asv.contains(f.getEntryPoint())) {
return false;
}
if (program.trace.getCodeManager()
.instructions()
.getAt(program.snap,
f.getEntryPoint()) == null) {
return false;
}
return true;
});
return EmptyFunctionIterator.INSTANCE;
}
@Override
@ -253,8 +152,7 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public boolean isInFunction(Address addr) {
// TODO: Could use idMap directly to avoid loading the function
return getFunctionContaining(addr) != null;
return false;
}
@Override
@ -266,12 +164,7 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException {
Iterator<? extends DBTraceFunctionSymbol> it =
getFunctionsInRange(new AddressRangeImpl(startAddr, endAddr), true);
while (it.hasNext()) {
monitor.checkCancelled();
it.next().delete();
}
// Do nothing
}
@Override
@ -287,28 +180,22 @@ public class DBTraceProgramViewFunctionManager implements FunctionManager {
@Override
public void invalidateCache(boolean all) {
throw new UnsupportedOperationException();
// Do nothing
}
@Override
public Iterator<Function> getFunctionsOverlapping(AddressSetView set) {
return new WrappingFunctionIterator(
NestedIterator.start(set.iterator(true), rng -> getFunctionsInRange(rng, true)));
return Collections.emptyIterator();
}
@Override
public Variable getReferencedVariable(Address instrAddr, Address storageAddr, int size,
boolean isRead) {
TraceFunctionSymbol function = getFunctionContaining(instrAddr);
if (function == null) {
return null;
}
return DBTraceFunctionSymbolView.getReferencedVariable(function, instrAddr, storageAddr,
size, isRead, program.language);
return null;
}
@Override
public TraceFunctionSymbol getFunction(long key) {
return functions.getByKey(key);
public Function getFunction(long key) {
return null;
}
}

View file

@ -144,14 +144,9 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
}
protected <T extends TraceSymbol> T requireVisible(T sym) {
if (!(sym instanceof TraceSymbolWithLifespan)) {
if (!(sym instanceof TraceSymbolWithLifespan wl)) {
return sym;
}
if (sym instanceof TraceFunctionSymbol) {
TraceFunctionSymbol function = (TraceFunctionSymbol) sym;
return program.isFunctionVisible(function, function.getLifespan()) ? sym : null;
}
TraceSymbolWithLifespan wl = (TraceSymbolWithLifespan) sym;
if (program.viewport.containsAnyUpper(wl.getLifespan())) {
return sym;
}
@ -210,9 +205,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
TraceNamespaceSymbol parent = assertTraceNamespace(namespace);
try (LockHold hold = program.trace.lockRead()) {
List<Symbol> result = new ArrayList<>();
for (TraceSymbol sym : symbolManager.labelsAndFunctions()
.getChildrenNamed(name,
parent)) {
for (TraceSymbol sym : symbolManager.labels().getChildrenNamed(name, parent)) {
if (requireVisible(sym) != null) {
result.add(sym);
}
@ -239,17 +232,17 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
@Override
public Symbol getParameterSymbol(String name, Namespace namespace) {
return symbolManager.parameters().getChildNamed(name, assertTraceNamespace(namespace));
return null;
}
@Override
public Symbol getLocalVariableSymbol(String name, Namespace namespace) {
return symbolManager.localVariables().getChildNamed(name, assertTraceNamespace(namespace));
return null;
}
@Override
public Symbol getVariableSymbol(String name, Function function) {
return symbolManager.allVariables().getChildNamed(name, assertTraceNamespace(function));
return null;
}
@Override
@ -279,7 +272,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
public Symbol getPrimarySymbol(Address addr) {
try (LockHold hold = program.trace.lockRead()) {
Collection<? extends TraceSymbol> at =
symbolManager.labelsAndFunctions().getAt(program.snap, null, addr, true);
symbolManager.labels().getAt(program.snap, null, addr, true);
if (at.isEmpty()) {
return null;
}
@ -291,7 +284,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
public Symbol[] getSymbols(Address addr) {
try (LockHold hold = program.trace.lockRead()) {
Collection<? extends TraceSymbol> at =
symbolManager.labelsAndFunctions().getAt(program.snap, null, addr, true);
symbolManager.labels().getAt(program.snap, null, addr, true);
return at.toArray(new Symbol[at.size()]);
}
}
@ -307,7 +300,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
public Symbol[] getUserSymbols(Address addr) {
try (LockHold hold = program.trace.lockRead()) {
Collection<? extends TraceSymbol> at =
symbolManager.labelsAndFunctions().getAt(program.snap, null, addr, false);
symbolManager.labels().getAt(program.snap, null, addr, false);
return at.toArray(new Symbol[at.size()]);
}
}
@ -331,10 +324,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
@Override
public boolean hasSymbol(Address addr) {
if (addr.isMemoryAddress()) {
return symbolManager.labelsAndFunctions().hasAt(program.snap, null, addr, true);
}
if (addr.getAddressSpace().isRegisterSpace() || addr.isStackAddress()) {
return symbolManager.allVariables().hasAt(addr, true);
return symbolManager.labels().hasAt(program.snap, null, addr, true);
}
return false;
}
@ -360,23 +350,6 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
.getIntersecting(Lifespan.at(program.snap), null, range, true, forward)
.iterator();
}
if (type == SymbolType.FUNCTION) {
return symbolManager.functions()
.getIntersecting(Lifespan.at(program.snap), null, range, true, forward)
.iterator();
}
}
if (range.getAddressSpace().isRegisterSpace() ||
range.getAddressSpace().isStackSpace()) {
if (type == SymbolType.PARAMETER) {
return symbolManager.parameters().getIntersecting(range, true).iterator();
}
if (type == SymbolType.LOCAL_VAR) {
return symbolManager.localVariables().getIntersecting(range, true).iterator();
}
if (type == SymbolType.GLOBAL_VAR) {
return symbolManager.globalVariables().getIntersecting(range, true).iterator();
}
}
return Collections.emptyIterator();
}));
@ -437,8 +410,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
@Override
public SymbolIterator getSymbolIterator(Address startAddr, boolean forward) {
return new SymbolIteratorAdapter(getSymbolIteratorAtMySnap(
symbolManager.labelsAndFunctions(),
return new SymbolIteratorAdapter(getSymbolIteratorAtMySnap(symbolManager.labels(),
DBTraceUtils.getAddressSet(program.language.getAddressFactory(), startAddr, forward),
true, forward));
}
@ -456,7 +428,7 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
@Override
public SymbolIterator getPrimarySymbolIterator(AddressSetView asv, boolean forward) {
return new PrimarySymbolIterator(NestedIterator.start(asv.iterator(forward),
range -> symbolManager.labelsAndFunctions()
range -> symbolManager.labels()
.getIntersecting(Lifespan.at(program.snap), null, range, true, forward)
.iterator()));
}
@ -501,20 +473,13 @@ public class DBTraceProgramViewSymbolTable implements SymbolTable {
// NOTE: Currently, traces do not allow namespaces to have arbitrary bodies.
// Instead, their bodies are the union of addresses of their descendants.
if (addr.isMemoryAddress()) {
for (TraceSymbol sym : symbolManager.labelsAndFunctions()
.getAt(program.snap, null,
addr, true)) {
for (TraceSymbol sym : symbolManager.labels().getAt(program.snap, null, addr, true)) {
if (sym instanceof TraceNamespaceSymbol /* Function */) {
return (TraceNamespaceSymbol) sym;
}
return sym.getParentNamespace();
}
}
if (addr.getAddressSpace().isRegisterSpace() || addr.isStackAddress()) {
for (TraceSymbol sym : symbolManager.allVariables().getAt(addr, true)) {
return sym.getParentNamespace();
}
}
return symbolManager.getGlobalNamespace();
}

View file

@ -1,396 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import db.DBRecord;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.AbstractFloatDataType;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.trace.database.symbol.DBTraceSymbolManager.DBTraceVariableStorageEntry;
import ghidra.trace.model.Trace.TraceSymbolChangeType;
import ghidra.trace.model.symbol.TraceVariableSymbol;
import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.DBObjectColumn;
import ghidra.util.database.annot.DBAnnotatedColumn;
import ghidra.util.database.annot.DBAnnotatedField;
import ghidra.util.exception.InvalidInputException;
/**
* TODO: Document me
*
* TODO: Somehow, this is supposed to generate {@link TraceSymbolChangeType#ADDRESS_CHANGED}. Find
* out how and be sure to implement it.
*/
public abstract class AbstractDBTraceVariableSymbol extends AbstractDBTraceSymbol
implements TraceVariableSymbol {
static final String DATATYPE_COLUMN_NAME = "DataType";
static final String STORAGE_COLUMN_NAME = "Storage";
static final String COMMENT_COLUMN_NAME = "Comment";
@DBAnnotatedColumn(DATATYPE_COLUMN_NAME)
static DBObjectColumn DATATYPE_COLUMN;
@DBAnnotatedColumn(STORAGE_COLUMN_NAME)
static DBObjectColumn STORAGE_COLUMN;
@DBAnnotatedColumn(COMMENT_COLUMN_NAME)
static DBObjectColumn COMMENT_COLUMN;
@DBAnnotatedField(column = DATATYPE_COLUMN_NAME)
private long dataTypeID;
@DBAnnotatedField(column = STORAGE_COLUMN_NAME)
private int storageID;
@DBAnnotatedField(column = COMMENT_COLUMN_NAME)
private String comment;
protected DataType dataType;
protected VariableStorage storage;
protected Address address;
public AbstractDBTraceVariableSymbol(DBTraceSymbolManager manager, DBCachedObjectStore<?> store,
DBRecord record) {
super(manager, store, record);
}
@Override
protected void fresh(boolean created) throws IOException {
super.fresh(created);
if (created) {
return;
}
dataType = manager.dataTypeManager.getDataType(dataTypeID);
DBTraceVariableStorageEntry storageEntry = manager.storageStore.getObjectAt(storageID);
if (storageEntry == null) {
throw new IOException(
"Database is corrupt. Cannot find VariableStorage entry " + storageID);
}
storage = storageEntry.getStorage();
address = AddressSpace.VARIABLE_SPACE.getAddress(storageID);
}
protected void set(String name, DBTraceNamespaceSymbol parent, DataType dt,
VariableStorage storage, SourceType source) {
super.set(name, parent, source);
this.dataTypeID = manager.dataTypeManager.getResolvedID(dt);
this.dataType = manager.dataTypeManager.getDataType(dataTypeID);
this.storageID = manager.findOrRecordVariableStorage(storage);
update(DATATYPE_COLUMN, STORAGE_COLUMN);
this.storage = storage;
this.address = AddressSpace.VARIABLE_SPACE.getAddress(storageID);
}
protected VariableStorage adjustStorage(VariableStorage s) {
return s;
}
@Override
public String toString() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return String.format("[%s %s@%s]", getDataType().getName(), getName(),
getVariableStorage());
}
}
@Override
public Address getAddress() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return address;
}
}
@Override
public Object getObject() {
return this;
}
@Override
public Symbol getSymbol() {
return this;
}
@Override
public int getLength() {
return getDataType().getLength();
}
@Override
public boolean isValid() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
VariableStorage s = getVariableStorage(); // Overridden by DBTraceParameterSymbol
if (!s.isValid()) {
return false;
}
if (dataType instanceof AbstractFloatDataType) {
return true;
}
// NOTE: Use getDataType(), since storage may force indirection (pointer)
return s.size() == getDataType().getLength();
}
}
protected void doSetDataType(DataType dt) {
this.dataTypeID = manager.dataTypeManager.getResolvedID(dt);
this.dataType = manager.dataTypeManager.getDataType(dataTypeID);
update(DATATYPE_COLUMN);
}
protected void doSetStorage(VariableStorage s) {
this.storage = s;
update(STORAGE_COLUMN);
}
// NOTE: Must have the write lock
protected void doSetStorageAndDataType(VariableStorage s, DataType dt) {
doSetDataType(dt);
doSetStorage(adjustStorage(s));
}
protected void doUpdatesAfterSetDataType() {
// Extension point
}
@Override
public void setDataType(DataType dt, VariableStorage s, boolean force, SourceType source)
throws InvalidInputException {
s = adjustStorage(s);
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
dt = VariableUtilities.checkDataType(dt, false, getLength(), getProgram());
// NOTE: UNASSIGNED passes through
DBTraceFunctionSymbol function = getFunction();
s = VariableUtilities.checkStorage(function, s, dt, force);
VariableUtilities.checkVariableConflict(function, this, s, force);
doSetStorageAndDataType(s, dt);
doUpdatesAfterSetDataType();
}
}
protected VariableStorage doDeriveStorageForSetDataType(DataType dt, boolean alignStack,
boolean force) throws InvalidInputException {
try {
VariableStorage s = VariableUtilities.resizeStorage(getVariableStorage(), dt,
alignStack, getFunction());
VariableUtilities.checkStorage(s, dt, force);
VariableUtilities.checkVariableConflict(getFunction(), this, s, force);
return s;
}
catch (InvalidInputException e) {
if (!force) {
throw e;
}
return VariableStorage.UNASSIGNED_STORAGE;
}
}
@Override
public void setDataType(DataType dt, boolean alignStack, boolean force, SourceType source)
throws InvalidInputException {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
dt = VariableUtilities.checkDataType(dt, false, getLength(), getProgram());
VariableStorage s = doDeriveStorageForSetDataType(dt, alignStack, force);
doSetStorageAndDataType(s, dt);
doUpdatesAfterSetDataType();
}
}
@Override
public void setDataType(DataType dt, SourceType source) throws InvalidInputException {
setDataType(dt, true, false, source);
}
@Override
public DataType getDataType() {
return dataType;
}
@Override
public abstract DBTraceFunctionSymbol getFunction();
@Override
public void setComment(String comment) {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
this.comment = comment;
update(COMMENT_COLUMN);
}
}
@Override
public String getComment() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return comment;
}
}
@Override
public VariableStorage getVariableStorage() {
return storage;
}
@Override
public Varnode getFirstStorageVarnode() {
VariableStorage s = getVariableStorage();
return s == null ? null : s.getFirstVarnode();
}
@Override
public Varnode getLastStorageVarnode() {
VariableStorage s = getVariableStorage();
return s == null ? null : s.getLastVarnode();
}
@Override
public boolean isStackVariable() {
VariableStorage s = getVariableStorage();
return s == null ? false : s.isStackStorage();
}
@Override
public boolean hasStackStorage() {
VariableStorage s = getVariableStorage();
return s == null ? false : s.hasStackStorage();
}
@Override
public boolean isRegisterVariable() {
VariableStorage s = getVariableStorage();
return s == null ? false : s.isRegisterStorage();
}
@Override
public Register getRegister() {
VariableStorage s = getVariableStorage();
return s == null ? null : s.getRegister();
}
@Override
public List<Register> getRegisters() {
VariableStorage s = getVariableStorage();
return s == null ? null : s.getRegisters();
}
@Override
public Address getMinAddress() {
VariableStorage s = getVariableStorage();
return s == null ? null : s.getMinAddress();
}
@Override
public int getStackOffset() {
VariableStorage s = getVariableStorage();
if (s == null) {
throw new UnsupportedOperationException("Variable has no storage");
}
return s.getStackOffset();
}
@Override
public boolean isMemoryVariable() {
VariableStorage s = getVariableStorage();
return s == null ? false : s.isMemoryStorage();
}
@Override
public boolean isUniqueVariable() {
VariableStorage s = getVariableStorage();
return s == null ? false : s.isHashStorage();
}
@Override
public boolean isCompoundVariable() {
VariableStorage s = getVariableStorage();
return s == null ? false : s.isCompoundStorage();
}
@Override
public boolean hasAssignedStorage() {
VariableStorage s = getVariableStorage();
return s == null ? false : !s.isUnassignedStorage();
}
protected static boolean doHasCustomStorage(Variable v) {
Function f = v.getFunction();
return f == null || f.hasCustomVariableStorage();
}
public static boolean areEquivalent(Variable v1, Variable v2) {
if (v1 == null && v2 == null) {
return true;
}
if (v1 == null || v2 == null) {
return false;
}
if (v1 == v2) {
return true;
}
if ((v1 instanceof Parameter) != (v2 instanceof Parameter)) {
return false;
}
if (v1 instanceof Parameter) {
Parameter p1 = (Parameter) v1;
Parameter p2 = (Parameter) v2;
if (p1.getOrdinal() != p2.getOrdinal()) {
return false;
}
}
if (v1.getFirstUseOffset() != v2.getFirstUseOffset()) {
return false;
}
boolean eitherCustom = doHasCustomStorage(v1) || doHasCustomStorage(v2);
if (eitherCustom && !Objects.equals(v1.getVariableStorage(), v2.getVariableStorage())) {
return false;
}
if (!DataTypeUtilities.isSameOrEquivalentDataType(v1.getDataType(), v2.getDataType())) {
return false;
}
return true;
}
@Override
public boolean isEquivalent(Variable variable) {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return areEquivalent(this, variable);
}
}
@Override
public int compareTo(Variable that) {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return VariableUtilities.compare(this, that);
}
}
@Override
public boolean delete() {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
DBTraceFunctionSymbol function = getFunction();
if (function != null) {
function.doDeleteVariable(this);
}
return super.delete();
}
}
}

View file

@ -1,329 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import static ghidra.lifecycle.Unfinished.TODO;
import java.util.ArrayList;
import java.util.Arrays;
import ghidra.lifecycle.Unfinished;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.LockHold;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
public class DBTraceFunctionStackFrame implements StackFrame, Unfinished {
protected final DBTraceFunctionSymbol function;
protected Variable[] variables;
protected boolean growsNegative;
protected boolean valid;
public DBTraceFunctionStackFrame(DBTraceFunctionSymbol function) {
this.function = function;
this.growsNegative = function.getTrace().getBaseCompilerSpec().stackGrowsNegative();
this.valid = false;
}
protected synchronized boolean checkIsValid() {
if (!function.isDeleted()) {
if (!valid) {
growsNegative = function.getTrace().getBaseCompilerSpec().stackGrowsNegative();
variables = function.getVariables(VariableFilter.COMPOUND_STACK_VARIABLE_FILTER);
Arrays.sort(variables, StackVariableComparator.get());
valid = true;
}
return true;
}
return false;
}
@Override
public DBTraceFunctionSymbol getFunction() {
return function;
}
@Override
public int getFrameSize() {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
return getParameterSize() + getLocalSize();
}
}
@Override
public int getLocalSize() {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
checkIsValid();
Integer base = VariableUtilities.getBaseStackParamOffset(function);
int baseOffset = base == null ? 0 : base.intValue();
if (growsNegative) {
if (variables.length == 0) {
return baseOffset;
}
Variable v = variables[0];
if (!(v instanceof LocalVariable)) {
return baseOffset;
}
int offset = Math.max(0, (int) v.getLastStorageVarnode().getOffset());
return baseOffset - offset;
}
// else Positive growth
if (variables.length == 0) {
return -baseOffset;
}
Variable v = variables[variables.length - 1];
if (!(v instanceof LocalVariable)) {
return -baseOffset;
}
Varnode stackVarnode = v.getLastStorageVarnode();
int len = stackVarnode.getSize();
int offset = Math.max(0, (int) stackVarnode.getOffset());
return offset - baseOffset + len;
}
}
@Override
public int getParameterSize() {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
checkIsValid();
Integer base = VariableUtilities.getBaseStackParamOffset(function);
int baseOffset = base == null ? 0 : base.intValue();
if (growsNegative) {
if (variables.length == 0) {
return 0;
}
Variable v = variables[variables.length - 1];
if (!(v instanceof Parameter)) {
return 0;
}
Varnode stackVarnode = v.getLastStorageVarnode();
int len = stackVarnode.getSize();
int offset = (int) stackVarnode.getOffset();
return offset - baseOffset + len;
}
// else Positive growth
if (variables.length == 0) {
return 0;
}
Variable v = variables[0];
if (!(v instanceof Parameter)) {
return 0;
}
int offset = (int) v.getLastStorageVarnode().getOffset();
return baseOffset - offset;
}
}
@Override
public int getParameterOffset() {
Integer base = VariableUtilities.getBaseStackParamOffset(function);
return base == null ? UNKNOWN_PARAM_OFFSET : base.intValue();
}
@Override
public boolean isParameterOffset(int offset) {
Integer base = VariableUtilities.getBaseStackParamOffset(function);
if (base == null) {
return false;
}
if (growsNegative) {
return offset >= base.intValue();
}
return offset < base.intValue();
}
@Override
public void setLocalSize(int size) {
// TODO: Is this ever called?
TODO();
}
@Override
public void setReturnAddressOffset(int offset) {
function.setReturnAddressOffset(offset);
}
@Override
public int getReturnAddressOffset() {
return function.getReturnAddressOffset();
}
@Override
public Variable getVariableContaining(int offset) {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
int index = Arrays.binarySearch(variables, offset, StackVariableComparator.get());
if (index >= 0) {
return variables[index];
}
/**
* index is -insertionPoint - 1 insertionPoint is index of first element greater than
* the key, i.e., where it would be inserted. We want the last element less than they
* key
*/
index = -index - 2;
if (index < 0) {
return null;
}
// We have the preceding variable. See if it contains the offset.
Variable var = variables[index];
Varnode stackVarnode = var.getLastStorageVarnode();
int varOffset = (int) stackVarnode.getOffset();
if (offset < varOffset + stackVarnode.getSize()) {
return var;
}
return null;
}
}
@Override
public AbstractDBTraceVariableSymbol createVariable(String name, int offset, DataType dataType,
SourceType source) throws DuplicateNameException, InvalidInputException {
try (LockHold hold = LockHold.lock(function.manager.lock.writeLock())) {
checkIsValid();
if (dataType != null) {
dataType = dataType.clone(function.getTrace().getDataTypeManager());
}
Variable var = new LocalVariableImpl(name, dataType, offset, function.getProgram());
AbstractDBTraceVariableSymbol result;
if (isParameterOffset(offset)) {
// Compute ordinal
int ordinal = function.getParameterCount();
Parameter[] params = getParameters();
if (growsNegative) {
for (int i = params.length - 1; i >= 0; i--) {
Parameter p = params[i];
if (offset <= p.getLastStorageVarnode().getOffset()) {
ordinal = p.getOrdinal();
}
}
}
else {
for (int i = 0; i < params.length; i++) {
Parameter p = params[i];
if (offset <= p.getLastStorageVarnode().getOffset()) {
ordinal = p.getOrdinal();
}
}
}
result = function.insertParameter(ordinal, var, source);
}
else {
result = function.addLocalVariable(var, source);
}
/**
* TODO: The original implementation tries to implicitly enable custom storage if:
*
* 1) We're inserting a parameter,
*
* 2) Custom storage is not already enabled, and
*
* 3) The variable would not be stored where specified without custom storage
*
* Unless it is required or jarring to the user, I'm going to forego this rule. Let the
* user manually enable custom storage if precise parameter placement via this method is
* desired.
*
* The issue in the original implementation is: It detects (3) by inserting the variable
* and then checking where it was assigned storage. By this time, the function will have
* already re-assigned the other variables' storage. Despite that, the method re-assigns
* the new variable to the desired storage. This may leave the others in an unexpected
* or bad state.
*
* Perhaps it can be fixed: Instead of re-assigning the storage, deleting the new
* parameter, enable custom storage, and then re-insert the desired parameter.
*/
return result;
}
}
@Override
public void clearVariable(int offset) {
try (LockHold hold = LockHold.lock(function.manager.lock.writeLock())) {
checkIsValid();
Variable var = getVariableContaining(offset);
/**
* TODO: The original implementation implicitly enables custom storage if:
*
* 1) A variable exists at the given offset, and
*
* 2) Custom storage is not already enabled
*
* Unless it is required or jarring to the user, I'm going to forego this rule. Let the
* user manually enable custom storage if precise parameter placement via this method is
* desired.
*/
if (var != null) {
function.removeVariable(var);
}
}
}
@Override
public Variable[] getStackVariables() {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
checkIsValid();
return Arrays.copyOf(variables, variables.length);
}
}
@Override
public Parameter[] getParameters() {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
checkIsValid();
ArrayList<Parameter> list = new ArrayList<Parameter>();
for (Variable v : variables) {
if (v instanceof Parameter) {
list.add((Parameter) v);
}
}
return list.toArray(new Parameter[list.size()]);
}
}
@Override
public LocalVariable[] getLocals() {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
checkIsValid();
ArrayList<LocalVariable> list = new ArrayList<LocalVariable>();
for (Variable v : variables) {
if (v instanceof LocalVariable) {
list.add((LocalVariable) v);
}
}
return list.toArray(new LocalVariable[list.size()]);
}
}
@Override
public boolean growsNegative() {
try (LockHold hold = LockHold.lock(function.manager.lock.readLock())) {
return growsNegative;
}
}
protected synchronized void invalidate() {
valid = false;
}
}

View file

@ -1,260 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import java.util.*;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.database.symbol.OverlappingNamespaceException;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.trace.database.program.DBTraceProgramView;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace.TraceSymbolChangeType;
import ghidra.trace.model.symbol.*;
import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.LockHold;
import ghidra.util.exception.InvalidInputException;
public class DBTraceFunctionSymbolView
extends AbstractDBTraceSymbolSingleTypeWithLocationView<DBTraceFunctionSymbol>
implements TraceFunctionSymbolView {
protected static final PrototypeModel[] EMPTY_MODEL_LIST = new PrototypeModel[] {};
protected static void assertProperSpace(AddressSpace expected, AddressSetView body) {
if (!expected.isMemorySpace()) {
throw new IllegalArgumentException("Function must be in memory space");
}
for (AddressRange rng : body) {
if (rng.getAddressSpace() != expected) {
throw new IllegalArgumentException(
"Function body must be in same space as entry point");
}
}
}
public DBTraceFunctionSymbolView(DBTraceSymbolManager manager) {
super(manager, SymbolType.FUNCTION.getID(), manager.functionStore);
}
protected DBTraceNamespaceSymbol doValidateParentAndEntry(DBTraceNamespaceSymbol proposed,
Address entryPoint) {
if (proposed == null) {
return manager.globalNamespace;
}
DBTraceProgramView program = manager.trace.getProgramView();
if (!SymbolType.FUNCTION.isValidAddress(program, entryPoint)) {
throw new IllegalArgumentException("Invalid function entry point: " + entryPoint);
}
if (!SymbolType.FUNCTION.isValidParent(program, proposed, entryPoint, false)) {
throw new IllegalArgumentException("Invalid function namespace: " + proposed);
}
return proposed;
}
protected SourceType doValidateSource(SourceType proposed, String name, Address entryPoint) {
if (!SymbolType.FUNCTION.isValidSourceType(proposed, entryPoint)) {
throw new IllegalArgumentException("Invalid function source type: " + proposed);
}
return proposed;
}
protected String doValidateName(String proposed, Address entryPoint, SourceType source)
throws InvalidInputException {
if (source == SourceType.DEFAULT) {
return "";
}
// TODO: Do entryPoint and source no longer matter? (see commit 898da2b)
SymbolUtilities.validateName(proposed);
return proposed;
}
protected void assertNotOverlapping(DBTraceFunctionSymbol exclude, Address entryPoint,
Lifespan span, AddressSetView proposedBody) throws OverlappingFunctionException {
for (AddressRange rng : proposedBody) {
for (DBTraceFunctionSymbol overlap : manager.functions.getIntersecting(span, null, rng,
false, true)) {
if (overlap != exclude) {
throw new OverlappingFunctionException(entryPoint,
new OverlappingNamespaceException(rng.getMinAddress(),
rng.getMaxAddress()));
}
}
}
}
@Override
public DBTraceFunctionSymbol add(Lifespan lifespan, Address entryPoint, AddressSetView body,
String name, TraceFunctionSymbol thunked, TraceNamespaceSymbol parent,
SourceType source) throws InvalidInputException, OverlappingFunctionException {
if (name == null || name.length() == 0 || SymbolUtilities.isReservedDynamicLabelName(name,
manager.trace.getBaseAddressFactory())) {
source = SourceType.DEFAULT;
name = "";
}
else {
DBTraceSymbolManager.assertValidName(name);
}
if (!"".equals(name) && source == SourceType.DEFAULT) {
throw new IllegalArgumentException(
"Cannot create DEFAULT function with non-default name");
}
if (!body.contains(entryPoint)) {
throw new IllegalArgumentException("Function body must contain the entry point");
}
assertProperSpace(entryPoint.getAddressSpace(), body);
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
DBTraceNamespaceSymbol dbnsParent =
parent == null ? null : manager.assertIsMine((Namespace) parent);
manager.assertValidThreadAddress(null, entryPoint);
DBTraceFunctionSymbol dbThunked =
thunked == null ? null : manager.assertIsMine((Function) thunked);
if (manager.trace.getCodeManager()
.definedData()
.getAt(lifespan.lmin(), entryPoint) != null) {
throw new IllegalArgumentException(
"Function entry point cannot be at defined data");
}
if (dbThunked != null && name.equals(dbThunked.getName())) {
source = SourceType.DEFAULT;
name = "";
}
assertNotOverlapping(null, entryPoint, lifespan, body);
dbnsParent = doValidateParentAndEntry(dbnsParent, entryPoint);
source = doValidateSource(source, name, entryPoint);
name = doValidateName(name, entryPoint, source);
DBTraceLabelSymbol toPromote = manager.labels.getChildWithNameAt(name,
lifespan.lmin(), null, entryPoint, dbnsParent);
if (toPromote != null && toPromote.getLifespan().equals(lifespan)) {
toPromote.delete();
}
DBTraceFunctionSymbol function = store.create();
function.set(lifespan, entryPoint, name, dbThunked, dbnsParent, source);
function.doCreateReturnParameter();
for (AddressRange rng : body) {
manager.putID(lifespan, null, rng, function.getID());
}
cacheForAt.notifyNewEntries(lifespan, body, function);
manager.trace.setChanged(
new TraceChangeRecord<>(TraceSymbolChangeType.ADDED, null, function));
return function;
}
}
@Override
public Collection<String> getCallingConventionNames() {
return manager.dataTypeManager.getDefinedCallingConventionNames();
}
@Override
public PrototypeModel getDefaultCallingConvention() {
return manager.dataTypeManager.getDefaultCallingConvention();
}
@Override
public PrototypeModel getCallingConvention(String name) {
return manager.dataTypeManager.getCallingConvention(name);
}
// TODO: Move this into a FunctionUtilities class?
public static Variable getReferencedVariable(Function function, Address instrAddr,
Address storageAddr, int size, boolean isRead, Language language) {
Variable variables[] = function.getAllVariables();
Parameter paramCandidate = null;
List<Variable> localCandidates = null;
Variable firstCandidate = null;
size = Math.min(1, size);
Register register = language.getRegister(storageAddr, size);
for (Variable var : variables) {
VariableStorage varStorage = var.getVariableStorage();
// TODO: It seems this check will miss intersection if storageAddr precedes the
// variable, but size is large enough to intersect.
if ((register != null && varStorage.intersects(register)) ||
(register == null && varStorage.contains(storageAddr))) {
if (var instanceof Parameter) {
paramCandidate = (Parameter) var;
}
else if (firstCandidate != null) {
if (localCandidates == null) {
localCandidates = new ArrayList<>();
localCandidates.add(firstCandidate);
}
localCandidates.add(var);
}
else {
firstCandidate = var;
}
}
}
int useOffset = (int) instrAddr.subtract(function.getEntryPoint());
if (isRead) {
if (useOffset == 0) {
return paramCandidate;
}
--useOffset;
}
if (useOffset < 0) {
// A bit of a hack to deal with negative offsets (from function entry)
useOffset = Integer.MAX_VALUE - useOffset;
}
if (localCandidates == null) {
if (firstCandidate != null) {
int varFirstUse = firstCandidate.getFirstUseOffset();
if (varFirstUse < 0) {
varFirstUse = Integer.MAX_VALUE - varFirstUse;
}
if (varFirstUse <= useOffset) {
return firstCandidate;
}
}
return null;
}
Variable bestVar = null;
int bestFirstUse = 0;
for (Variable var : localCandidates) {
int varFirstUse = var.getFirstUseOffset();
if (varFirstUse < 0) {
varFirstUse = Integer.MAX_VALUE - varFirstUse;
}
if (varFirstUse <= useOffset && (bestVar == null || bestFirstUse < varFirstUse)) {
bestVar = var;
bestFirstUse = varFirstUse;
}
}
if (bestVar == null) {
bestVar = paramCandidate;
}
return bestVar;
}
}

View file

@ -1,66 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import db.DBRecord;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.SymbolType;
import ghidra.trace.model.symbol.TraceGlobalVariableSymbol;
import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.annot.DBAnnotatedObjectInfo;
@DBAnnotatedObjectInfo(version = 0)
public class DBTraceGlobalVariableSymbol extends AbstractDBTraceVariableSymbol
implements TraceGlobalVariableSymbol {
static final String TABLE_NAME = "GlobalVars";
public DBTraceGlobalVariableSymbol(DBTraceSymbolManager manager, DBCachedObjectStore<?> store,
DBRecord record) {
super(manager, store, record);
}
@Override
public SymbolType getSymbolType() {
return SymbolType.GLOBAL_VAR;
}
@Override
public DBTraceFunctionSymbol getFunction() {
return null;
}
@Override
public Address getAddress() {
// TODO: Reference implementation in Program is not complete. If ever, make this similar.
return getVariableStorage().getRegister().getAddress();
}
@Override
public boolean setPrimary() {
return false;
}
@Override
public boolean isPrimary() {
return true;
}
@Override
public int getFirstUseOffset() {
// TODO: Reference implementation in Program is not complete. If ever, make this similar.
return 0;
}
}

View file

@ -1,28 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.trace.model.symbol.TraceGlobalVariableSymbolView;
public class DBTraceGlobalVariableSymbolView
extends AbstractDBTraceSymbolSingleTypeWithAddressView<DBTraceGlobalVariableSymbol>
implements TraceGlobalVariableSymbolView {
public DBTraceGlobalVariableSymbolView(DBTraceSymbolManager manager) {
super(manager, SymbolType.GLOBAL_VAR.getID(), manager.globalVarStore);
}
}

View file

@ -240,11 +240,6 @@ public class DBTraceLabelSymbol extends AbstractDBTraceSymbol
public boolean setPrimary() {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
AddressRangeImpl range = new AddressRangeImpl(address, address);
if (!manager.functions.getIntersecting(lifespan, thread, range, false,
true).isEmpty()) {
// Labels cannot supersede a function
return false;
}
boolean result = doSetPrimary(true);
if (!result) {
return false;

View file

@ -1,123 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import db.DBRecord;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolType;
import ghidra.trace.model.Trace.TraceFunctionChangeType;
import ghidra.trace.model.symbol.TraceLocalVariableSymbol;
import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.DBObjectColumn;
import ghidra.util.database.annot.*;
@DBAnnotatedObjectInfo(version = 0)
public class DBTraceLocalVariableSymbol extends AbstractDBTraceVariableSymbol
implements TraceLocalVariableSymbol {
static final String TABLE_NAME = "LocalVars";
static final String FIRST_USE_COLUMN_NAME = "FirstUse";
@DBAnnotatedColumn(FIRST_USE_COLUMN_NAME)
static DBObjectColumn FIRST_USE_COLUMN;
@DBAnnotatedField(column = FIRST_USE_COLUMN_NAME)
int firstUseOffset;
public DBTraceLocalVariableSymbol(DBTraceSymbolManager manager, DBCachedObjectStore<?> store,
DBRecord record) {
super(manager, store, record);
}
protected void set(String name, DBTraceFunctionSymbol function, DataType dt,
VariableStorage storage, int firstUseOffset, SourceType source) {
super.set(name, function, dt, storage, source);
this.firstUseOffset = firstUseOffset;
update(FIRST_USE_COLUMN);
}
@Override
public SymbolType getSymbolType() {
return SymbolType.LOCAL_VAR;
}
@Override
public DBTraceFunctionSymbol getParentNamespace() {
return (DBTraceFunctionSymbol) super.getParentNamespace();
}
@Override
public DBTraceFunctionSymbol getParentSymbol() {
return (DBTraceFunctionSymbol) super.getParentSymbol();
}
@Override
public DBTraceFunctionSymbol getFunction() {
return getParentSymbol();
}
@Override
public boolean setPrimary() {
return false;
}
@Override
public boolean isPrimary() {
return false;
}
@Override
public boolean setFirstUseOffset(int firstUseOffset) {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
if (this.firstUseOffset == firstUseOffset) {
return true; // ineffective, but successful
}
this.firstUseOffset = firstUseOffset;
update(FIRST_USE_COLUMN);
}
manager.trace.setChanged(
new TraceChangeRecord<>(TraceFunctionChangeType.CHANGED, getSpace(), getFunction()));
return true;
}
@Override
public int getFirstUseOffset() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return firstUseOffset;
}
}
@Override
protected void doUpdatesAfterSetDataType() {
super.doUpdatesAfterSetDataType();
manager.trace.setChanged(
new TraceChangeRecord<>(TraceFunctionChangeType.CHANGED, getSpace(), getFunction()));
}
@Override
public boolean delete() {
if (super.delete()) {
manager.trace.setChanged(new TraceChangeRecord<>(TraceFunctionChangeType.CHANGED,
getSpace(), getFunction()));
return true;
}
return false;
}
}

View file

@ -1,28 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.trace.model.symbol.TraceLocalVariableSymbolView;
public class DBTraceLocalVariableSymbolView
extends AbstractDBTraceSymbolSingleTypeWithAddressView<DBTraceLocalVariableSymbol>
implements TraceLocalVariableSymbolView {
public DBTraceLocalVariableSymbolView(DBTraceSymbolManager manager) {
super(manager, SymbolType.LOCAL_VAR.getID(), manager.localVarStore);
}
}

View file

@ -1,220 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import db.DBRecord;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.trace.model.Trace.TraceFunctionChangeType;
import ghidra.trace.model.symbol.TraceParameterSymbol;
import ghidra.trace.util.TraceChangeRecord;
import ghidra.util.LockHold;
import ghidra.util.database.DBCachedObjectStore;
import ghidra.util.database.DBObjectColumn;
import ghidra.util.database.annot.*;
import ghidra.util.exception.InvalidInputException;
@DBAnnotatedObjectInfo(version = 0)
public class DBTraceParameterSymbol extends AbstractDBTraceVariableSymbol
implements TraceParameterSymbol {
static final String TABLE_NAME = "Parameters";
static final String ORDINAL_COLUMN_NAME = "Ordinal";
@DBAnnotatedColumn(ORDINAL_COLUMN_NAME)
static DBObjectColumn ORDINAL_COLUMN;
@DBAnnotatedField(column = ORDINAL_COLUMN_NAME)
int ordinal;
// This is transient, when the function does not use custom parameter storage.
// It is unused if the function uses custom storage.
protected VariableStorage dynamicStorage = VariableStorage.UNASSIGNED_STORAGE;
public DBTraceParameterSymbol(DBTraceSymbolManager manager, DBCachedObjectStore<?> store,
DBRecord record) {
super(manager, store, record);
}
protected void set(String name, DBTraceFunctionSymbol function, DataType dt,
VariableStorage storage, int ordinal, SourceType source) {
super.set(name, function, dt, storage, source);
this.ordinal = ordinal;
update(ORDINAL_COLUMN);
}
@Override
protected VariableStorage adjustStorage(VariableStorage s) {
if (!getFunction().hasCustomVariableStorage()) {
return VariableStorage.UNASSIGNED_STORAGE;
}
return super.adjustStorage(s);
}
@Override
public SymbolType getSymbolType() {
return SymbolType.PARAMETER;
}
@Override
protected Pair<String, SourceType> validateNameAndSource(String newName, SourceType newSource)
throws InvalidInputException {
if (newSource == SourceType.DEFAULT || newName == null || "".equals(newName) ||
SymbolUtilities.isDefaultParameterName(newName)) {
return new ImmutablePair<>("", SourceType.DEFAULT);
}
return new ImmutablePair<>(newName, newSource);
}
@Override
public String getName() {
if (getSource() == SourceType.DEFAULT && ordinal != -1) {
return SymbolUtilities.getDefaultParamName(ordinal);
}
return super.getName();
}
@Override
public DBTraceFunctionSymbol getParentNamespace() {
return (DBTraceFunctionSymbol) super.getParentNamespace();
}
@Override
public DBTraceFunctionSymbol getParentSymbol() {
return (DBTraceFunctionSymbol) super.getParentSymbol();
}
@Override
public DBTraceFunctionSymbol getFunction() {
return getParentSymbol();
}
@Override
public boolean setPrimary() {
return false;
}
@Override
public boolean isPrimary() {
return false;
}
@Override
public VariableStorage getVariableStorage() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
if (!getFunction().hasCustomVariableStorage()) {
return dynamicStorage;
}
return super.getVariableStorage();
}
}
// Internal
public void setOrdinal(int ordinal) {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
this.ordinal = ordinal;
update(ORDINAL_COLUMN);
}
}
@Override
public int getOrdinal() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return ordinal;
}
}
@Override
public boolean isAutoParameter() {
return false;
}
@Override
public AutoParameterType getAutoParameterType() {
return null;
}
@Override
public boolean isForcedIndirect() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
VariableStorage s = getVariableStorage();
return s == null ? false : s.isForcedIndirect();
}
}
@Override
public DataType getDataType() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return manager.checkIndirection(getVariableStorage(), getFormalDataType());
}
}
@Override
public DataType getFormalDataType() {
return super.getDataType();
}
@Override
public int getFirstUseOffset() {
return 0;
}
protected void doSetDynamicStorage(VariableStorage s) {
assert !getFunction().hasCustomVariableStorage();
this.dynamicStorage = s;
}
@Override
protected void doUpdatesAfterSetDataType() {
super.doUpdatesAfterSetDataType();
DBTraceFunctionSymbol function = getFunction();
if (!function.hasCustomVariableStorage()) {
function.doUpdateParametersAndReturn();
}
function.doUpdateSignatureSourceAfterVariableChange(getSource(), getDataType());
if (ordinal == Parameter.RETURN_ORIDINAL) {
manager.trace.setChanged(new TraceChangeRecord<>(TraceFunctionChangeType.CHANGED_RETURN,
getSpace(), getFunction()));
}
else {
manager.trace.setChanged(new TraceChangeRecord<>(
TraceFunctionChangeType.CHANGED_PARAMETERS, getSpace(), getFunction()));
}
}
@Override
protected VariableStorage doDeriveStorageForSetDataType(DataType dt, boolean alignStack,
boolean force) throws InvalidInputException {
if (!getFunction().hasCustomVariableStorage()) {
return VariableStorage.UNASSIGNED_STORAGE;
}
return super.doDeriveStorageForSetDataType(dt, alignStack, force);
}
@Override
public boolean delete() {
if (super.delete()) {
manager.trace.setChanged(new TraceChangeRecord<>(
TraceFunctionChangeType.CHANGED_PARAMETERS, getSpace(), getFunction()));
return true;
}
return false;
}
}

View file

@ -1,28 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.trace.model.symbol.TraceParameterSymbolView;
public class DBTraceParameterSymbolView
extends AbstractDBTraceSymbolSingleTypeWithAddressView<DBTraceParameterSymbol>
implements TraceParameterSymbolView {
public DBTraceParameterSymbolView(DBTraceSymbolManager manager) {
super(manager, SymbolType.PARAMETER.getID(), manager.parameterStore);
}
}

View file

@ -151,25 +151,7 @@ public class DBTraceReference implements TraceReference {
return;
}
Address toAddress = getToAddress();
if (dbSym instanceof AbstractDBTraceVariableSymbol) {
AbstractDBTraceVariableSymbol varSym = (AbstractDBTraceVariableSymbol) dbSym;
// Variables' lifespans are governed by the parent function.
// Globals span all time.
DBTraceNamespaceSymbol parent = varSym.getParentNamespace();
if (parent instanceof TraceSymbolWithLifespan) {
TraceSymbolWithLifespan symWl = (TraceSymbolWithLifespan) parent;
if (!symWl.getLifespan().intersects(getLifespan())) {
throw new IllegalArgumentException(
"Associated symbol and reference must have connected lifespans");
}
}
if (!varSym.getVariableStorage().contains(toAddress)) {
throw new IllegalArgumentException(String.format(
"Variable symbol storage of '%s' must contain Reference's to address (%s)",
varSym.getName(), toAddress));
}
}
else if (!Objects.equals(symbol.getAddress(), toAddress)) {
if (!Objects.equals(symbol.getAddress(), toAddress)) {
throw new IllegalArgumentException(String.format(
"Symbol address (%s) of '%s' must match Reference's to address (%s)",
symbol.getAddress(), symbol.getName(), toAddress));

View file

@ -21,10 +21,12 @@ import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import db.*;
import ghidra.lifecycle.Internal;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.VariableStorage;
import ghidra.program.model.symbol.*;
import ghidra.trace.database.DBTrace;
import ghidra.trace.database.DBTraceManager;
@ -37,7 +39,6 @@ import ghidra.trace.database.space.DBTraceSpaceKey;
import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceFunctionTagChangeType;
import ghidra.trace.model.Trace.TraceSymbolChangeType;
import ghidra.trace.model.symbol.*;
import ghidra.trace.model.thread.TraceThread;
@ -93,164 +94,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
}
}
@DBAnnotatedObjectInfo(version = 0)
public static class DBTraceFunctionTag extends DBAnnotatedObject implements FunctionTag {
static final String TABLE_NAME = "FunctionTags";
static final String NAME_COLUMN_NAME = "Name";
static final String COMMENT_COLUMN_NAME = "Comment";
@DBAnnotatedColumn(NAME_COLUMN_NAME)
static DBObjectColumn NAME_COLUMN;
@DBAnnotatedColumn(COMMENT_COLUMN_NAME)
static DBObjectColumn COMMENT_COLUMN;
@DBAnnotatedField(column = NAME_COLUMN_NAME, indexed = true)
String name;
@DBAnnotatedField(column = COMMENT_COLUMN_NAME)
String comment;
protected final DBTraceSymbolManager manager;
public DBTraceFunctionTag(DBTraceSymbolManager manager, DBCachedObjectStore<?> store,
DBRecord record) {
super(store, record);
this.manager = manager;
}
protected void set(String name, String comment) {
this.name = name;
this.comment = comment;
update(NAME_COLUMN, COMMENT_COLUMN);
}
@Override
public int hashCode() {
return Long.hashCode(key);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof FunctionTag)) {
return false;
}
if (obj == this) {
return true;
}
FunctionTag that = (FunctionTag) obj;
if (!Objects.equals(this.getName(), that.getName())) {
return false;
}
if (!Objects.equals(this.getComment(), that.getComment())) {
return false;
}
return true;
}
@Override
public int compareTo(FunctionTag o) {
int result;
result = this.getName().compareToIgnoreCase(o.getName());
if (result != 0) {
return result;
}
result = this.getComment().compareToIgnoreCase(o.getComment());
if (result != 0) {
return result;
}
return 0;
}
@Override
public long getId() {
return key;
}
@Override
public String getName() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return name;
}
}
@Override
public String getComment() {
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
return comment;
}
}
@Override
public void setName(String name) {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
this.name = name;
update(NAME_COLUMN);
}
manager.trace.setChanged(
new TraceChangeRecord<>(TraceFunctionTagChangeType.CHANGED, null, this));
}
@Override
public void setComment(String comment) {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
this.comment = comment;
update(COMMENT_COLUMN);
}
manager.trace.setChanged(
new TraceChangeRecord<>(TraceFunctionTagChangeType.CHANGED, null, this));
}
@Override
public void delete() {
try (LockHold hold = LockHold.lock(manager.lock.writeLock())) {
for (DBTraceFunctionTagMapping mapping : manager.tagMappingsByTag.get(key)) {
manager.tagMappingStore.delete(mapping);
}
manager.tagStore.delete(this);
}
manager.trace.setChanged(
new TraceChangeRecord<>(TraceFunctionTagChangeType.DELETED, null, this));
}
}
@DBAnnotatedObjectInfo(version = 0)
public static class DBTraceFunctionTagMapping extends DBAnnotatedObject {
static final String TABLE_NAME = "FunctionTagMappings";
static final String FUNCTION_COLUMN_NAME = "Function";
static final String TAG_COLUMN_NAME = "Tag";
@DBAnnotatedColumn(FUNCTION_COLUMN_NAME)
static DBObjectColumn FUNCTION_COLUMN;
@DBAnnotatedColumn(TAG_COLUMN_NAME)
static DBObjectColumn TAG_COLUMN;
@DBAnnotatedField(column = FUNCTION_COLUMN_NAME, indexed = true)
private long functionKey;
@DBAnnotatedField(column = TAG_COLUMN_NAME, indexed = true)
private long tagKey;
public DBTraceFunctionTagMapping(DBCachedObjectStore<?> store, DBRecord record) {
super(store, record);
}
protected void set(DBTraceFunctionSymbol function, DBTraceFunctionTag tag) {
this.functionKey = function.getKey();
this.tagKey = tag.getKey();
update(FUNCTION_COLUMN, TAG_COLUMN);
}
public long getFunctionKey() {
return functionKey;
}
public long getTagKey() {
return tagKey;
}
}
public static class VariableStorageDBFieldCodec extends
AbstractDBFieldCodec<VariableStorage, DBTraceVariableStorageEntry, StringField> {
@ -351,25 +194,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
CLASS {
@Override
boolean isValidParent(DBTraceNamespaceSymbol parent) {
return isNoFunctionAncestor(parent);
}
},
FUNCTION {
@Override
boolean isValidParent(DBTraceNamespaceSymbol parent) {
return isNoFunctionAncestor(parent);
}
},
PARAMETER {
@Override
boolean isValidParent(DBTraceNamespaceSymbol parent) {
return parent instanceof Function;
}
},
LOCAL_VAR {
@Override
boolean isValidParent(DBTraceNamespaceSymbol parent) {
return parent instanceof Function;
return true;
}
},
GLOBAL_VAR {
@ -382,15 +207,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
public static final List<MySymbolTypes> VALUES = List.of(values());
abstract boolean isValidParent(DBTraceNamespaceSymbol parent);
boolean isNoFunctionAncestor(DBTraceNamespaceSymbol parent) {
for (DBTraceNamespaceSymbol p = parent; p != null; p = p.parent) {
if (p instanceof Function) {
return false;
}
}
return true;
}
}
protected final DBTrace trace;
@ -401,41 +217,23 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
protected final DBTraceAddressSnapRangePropertyMap<Long, DBTraceSymbolIDEntry> idMap;
protected final DBCachedObjectStore<DBTraceFunctionTag> tagStore;
protected final DBCachedObjectIndex<String, DBTraceFunctionTag> tagsByName;
protected final DBCachedObjectStore<DBTraceFunctionTagMapping> tagMappingStore;
protected final DBCachedObjectIndex<Long, DBTraceFunctionTagMapping> tagMappingsByFunc;
protected final DBCachedObjectIndex<Long, DBTraceFunctionTagMapping> tagMappingsByTag;
// NB. This is unused since the purging of trace function symbols
// In theory, may get used by global variables.
protected final DBCachedObjectStore<DBTraceVariableStorageEntry> storageStore;
protected final DBCachedObjectIndex<VariableStorage, DBTraceVariableStorageEntry> storageByStorage;
protected final DBCachedObjectStore<DBTraceLabelSymbol> labelStore;
protected final DBCachedObjectStore<DBTraceNamespaceSymbol> namespaceStore;
protected final DBCachedObjectStore<DBTraceClassSymbol> classStore;
protected final DBCachedObjectStore<DBTraceFunctionSymbol> functionStore;
protected final DBCachedObjectIndex<Long, DBTraceFunctionSymbol> functionsByThunked;
protected final DBCachedObjectStore<DBTraceParameterSymbol> parameterStore;
protected final DBCachedObjectStore<DBTraceLocalVariableSymbol> localVarStore;
// Seems only for "global register" variables
protected final DBCachedObjectStore<DBTraceGlobalVariableSymbol> globalVarStore;
protected final DBTraceNamespaceSymbol globalNamespace;
protected final DBTraceLabelSymbolView labels;
protected final DBTraceNamespaceSymbolView namespaces;
protected final DBTraceClassSymbolView classes;
protected final DBTraceFunctionSymbolView functions;
protected final DBTraceParameterSymbolView parameters;
protected final DBTraceLocalVariableSymbolView localVars;
protected final DBTraceGlobalVariableSymbolView globalVars;
protected final DBTraceSymbolMultipleTypesView<? extends DBTraceNamespaceSymbol> allNamespaces;
protected final DBTraceSymbolMultipleTypesNoDuplicatesView<? extends DBTraceNamespaceSymbol> uniqueNamespaces;
protected final DBTraceSymbolMultipleTypesWithAddressNoDuplicatesView<? extends AbstractDBTraceVariableSymbol> allLocals;
protected final DBTraceSymbolMultipleTypesWithAddressNoDuplicatesView<?> allVariables;
protected final DBTraceSymbolMultipleTypesWithLocationView<?> labelsAndFunctions;
protected final DBTraceSymbolMultipleTypesNoDuplicatesView<?> notLabelsNorFunctions;
protected final DBTraceSymbolMultipleTypesNoDuplicatesView<?> notLabels;
protected final DBTraceSymbolMultipleTypesView<?> allSymbols;
protected final Map<Byte, AbstractDBTraceSymbolSingleTypeView<?>> symbolViews = new HashMap<>();
@ -457,17 +255,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
baseLanguage, trace, threadManager, DBTraceSymbolIDEntry.class,
DBTraceSymbolIDEntry::new);
tagStore = factory.getOrCreateCachedStore(DBTraceFunctionTag.TABLE_NAME,
DBTraceFunctionTag.class, (s, r) -> new DBTraceFunctionTag(this, s, r), true);
tagsByName = tagStore.getIndex(String.class, DBTraceFunctionTag.NAME_COLUMN);
tagMappingStore = factory.getOrCreateCachedStore(DBTraceFunctionTagMapping.TABLE_NAME,
DBTraceFunctionTagMapping.class, DBTraceFunctionTagMapping::new, true);
tagMappingsByFunc =
tagMappingStore.getIndex(long.class, DBTraceFunctionTagMapping.FUNCTION_COLUMN);
tagMappingsByTag =
tagMappingStore.getIndex(long.class, DBTraceFunctionTagMapping.TAG_COLUMN);
storageStore = factory.getOrCreateCachedStore(DBTraceVariableStorageEntry.TABLE_NAME,
DBTraceVariableStorageEntry.class,
(s, r) -> new DBTraceVariableStorageEntry(this, s, r), true);
@ -480,18 +267,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
DBTraceNamespaceSymbol.class, (s, r) -> new DBTraceNamespaceSymbol(this, s, r), true);
classStore = factory.getOrCreateCachedStore(DBTraceClassSymbol.TABLE_NAME,
DBTraceClassSymbol.class, (s, r) -> new DBTraceClassSymbol(this, s, r), true);
functionStore = factory.getOrCreateCachedStore(DBTraceFunctionSymbol.TABLE_NAME,
DBTraceFunctionSymbol.class, (s, r) -> new DBTraceFunctionSymbol(this, s, r), true);
functionsByThunked =
functionStore.getIndex(long.class, DBTraceFunctionSymbol.THUNKED_COLUMN);
parameterStore = factory.getOrCreateCachedStore(DBTraceParameterSymbol.TABLE_NAME,
DBTraceParameterSymbol.class, (s, r) -> new DBTraceParameterSymbol(this, s, r), true);
localVarStore = factory.getOrCreateCachedStore(DBTraceLocalVariableSymbol.TABLE_NAME,
DBTraceLocalVariableSymbol.class, (s, r) -> new DBTraceLocalVariableSymbol(this, s, r),
true);
globalVarStore = factory.getOrCreateCachedStore(DBTraceGlobalVariableSymbol.TABLE_NAME,
DBTraceGlobalVariableSymbol.class,
(s, r) -> new DBTraceGlobalVariableSymbol(this, s, r), true);
globalNamespace = getOrCreateGlobalNamespace();
@ -499,25 +274,13 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
labels = putInMap(new DBTraceLabelSymbolView(this));
namespaces = putInMap(new DBTraceNamespaceSymbolView(this));
classes = putInMap(new DBTraceClassSymbolView(this));
functions = putInMap(new DBTraceFunctionSymbolView(this));
parameters = putInMap(new DBTraceParameterSymbolView(this));
localVars = putInMap(new DBTraceLocalVariableSymbolView(this));
globalVars = putInMap(new DBTraceGlobalVariableSymbolView(this));
allNamespaces = new DBTraceSymbolMultipleTypesView<>(this, namespaces, classes, functions);
allNamespaces = new DBTraceSymbolMultipleTypesView<>(this, namespaces, classes);
uniqueNamespaces =
new DBTraceSymbolMultipleTypesNoDuplicatesView<>(this, namespaces, classes);
allLocals = new DBTraceSymbolMultipleTypesWithAddressNoDuplicatesView<>(this, parameters,
localVars);
allVariables = new DBTraceSymbolMultipleTypesWithAddressNoDuplicatesView<>(this, parameters,
localVars, globalVars);
labelsAndFunctions = new DBTraceSymbolMultipleTypesWithLocationView<AbstractDBTraceSymbol>(
this, labels, functions);
notLabelsNorFunctions = new DBTraceSymbolMultipleTypesNoDuplicatesView<>(this, namespaces,
classes, parameters, localVars, globalVars);
allSymbols = new DBTraceSymbolMultipleTypesView<>(this, labels, namespaces, classes,
functions, parameters, localVars, globalVars);
notLabels =
new DBTraceSymbolMultipleTypesNoDuplicatesView<>(this, namespaces, classes);
allSymbols = new DBTraceSymbolMultipleTypesView<>(this, labels, namespaces, classes);
}
protected DataType checkIndirection(VariableStorage s, DataType formal) {
@ -595,8 +358,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
// Internal
public void replaceDataTypes(long oldID, long newID) {
// TODO Auto-generated method stub
// DataTypes of Function returns, params, locals, globalRegs
// Would apply to functions and variables, but those are not supported.
}
protected void assertValidThreadAddress(TraceThread thread, Address address) {
@ -644,26 +406,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
return classes;
}
@Override
public DBTraceFunctionSymbolView functions() {
return functions;
}
@Override
public DBTraceParameterSymbolView parameters() {
return parameters;
}
@Override
public DBTraceLocalVariableSymbolView localVariables() {
return localVars;
}
@Override
public DBTraceGlobalVariableSymbolView globalVariables() {
return globalVars;
}
@Override
public TraceSymbolView<? extends DBTraceNamespaceSymbol> allNamespaces() {
return allNamespaces;
@ -674,23 +416,8 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
}
@Override
public TraceSymbolWithAddressNoDuplicatesView<? extends AbstractDBTraceVariableSymbol> allLocals() {
return allLocals;
}
@Override
public TraceSymbolWithAddressNoDuplicatesView<? extends AbstractDBTraceSymbol> allVariables() {
return allVariables;
}
@Override
public TraceSymbolWithLocationView<? extends AbstractDBTraceSymbol> labelsAndFunctions() {
return labelsAndFunctions;
}
@Override
public TraceSymbolNoDuplicatesView<? extends AbstractDBTraceSymbol> notLabelsNorFunctions() {
return notLabelsNorFunctions;
public TraceSymbolNoDuplicatesView<? extends AbstractDBTraceSymbol> notLabels() {
return notLabels;
}
@Override
@ -700,10 +427,9 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
// Internal
public DBTraceNamespaceSymbol checkIsMine(Namespace ns) {
if (!(ns instanceof DBTraceNamespaceSymbol)) {
if (!(ns instanceof DBTraceNamespaceSymbol dbns)) {
return null;
}
DBTraceNamespaceSymbol dbns = (DBTraceNamespaceSymbol) ns;
if (dbns.manager != this) {
return null;
}
@ -713,13 +439,8 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
if (namespaceStore.contains(dbns)) {
return dbns;
}
if (dbns instanceof DBTraceClassSymbol) {
if (classStore.contains((DBTraceClassSymbol) dbns)) {
return dbns;
}
}
if (dbns instanceof DBTraceFunctionSymbol) {
if (functionStore.contains((DBTraceFunctionSymbol) dbns)) {
if (dbns instanceof DBTraceClassSymbol dbcs) {
if (classStore.contains(dbcs)) {
return dbns;
}
}
@ -728,10 +449,9 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
// Internal
public AbstractDBTraceSymbol checkIsMine(Symbol symbol) {
if (!(symbol instanceof AbstractDBTraceSymbol)) {
if (!(symbol instanceof AbstractDBTraceSymbol dbSym)) {
return null;
}
AbstractDBTraceSymbol dbSym = (AbstractDBTraceSymbol) symbol;
if (dbSym.manager != this) {
return null;
}
@ -750,25 +470,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
return dbSym;
}
// Internal
public DBTraceFunctionSymbol checkIsMine(Function function) {
if (!(function instanceof DBTraceFunctionSymbol)) {
return null;
}
DBTraceFunctionSymbol dbFunc = (DBTraceFunctionSymbol) function;
if (dbFunc.manager != this) {
return null;
}
if (dbFunc.isDeleted()) {
return null;
}
if (functionStore.contains(dbFunc)) {
return dbFunc;
}
return null;
}
// Internal
@Internal
public DBTraceNamespaceSymbol assertIsMine(Namespace ns) {
DBTraceNamespaceSymbol dbns = checkIsMine(ns);
if (dbns == null) {
@ -777,7 +479,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
return dbns;
}
// Internal
@Internal
public AbstractDBTraceSymbol assertIsMine(Symbol symbol) {
AbstractDBTraceSymbol dbSym = checkIsMine(symbol);
if (dbSym == null) {
@ -786,15 +488,6 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
return dbSym;
}
// Internal
public DBTraceFunctionSymbol assertIsMine(Function function) {
DBTraceFunctionSymbol dbFunc = checkIsMine(function);
if (dbFunc == null) {
throw new IllegalArgumentException("Given function is not in this trace");
}
return dbFunc;
}
protected static void assertValidName(String name) throws InvalidInputException {
if (name == null || name.length() == 0 || !name.matches("\\p{Graph}+")) {
throw new InvalidInputException(name);
@ -802,8 +495,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
}
/**
* Checks for duplicate names, allowing {@link SymbolType#LABEL} and
* {@link SymbolType#FUNCTION}.
* Checks for duplicate names, allowing {@link SymbolType#LABEL}
*
* @param name the proposed name
* @param parent the parent namespace
@ -811,7 +503,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
*/
protected void assertUniqueName(String name, DBTraceNamespaceSymbol parent)
throws DuplicateNameException {
for (AbstractDBTraceSymbol symbol : notLabelsNorFunctions.getChildren(parent)) {
for (AbstractDBTraceSymbol symbol : notLabels.getChildren(parent)) {
if (name.equals(symbol.name)) {
throw new DuplicateNameException(name);
}
@ -866,8 +558,8 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
TraceThread thread, Address address, String name, DBTraceNamespaceSymbol parent)
throws DuplicateNameException {
if (address.isMemoryAddress()) {
for (AbstractDBTraceSymbol duplicate : labelsAndFunctions.getIntersecting(lifespan,
thread, new AddressRangeImpl(address, address), false, true)) {
for (AbstractDBTraceSymbol duplicate : labels.getIntersecting(lifespan, thread,
new AddressRangeImpl(address, address), false, true)) {
if (duplicate == exclude) {
continue;
}
@ -885,8 +577,7 @@ public class DBTraceSymbolManager implements TraceSymbolManager, DBTraceManager
protected void assertNotDuplicate(AbstractDBTraceSymbol exclude, String name,
DBTraceNamespaceSymbol parent) throws DuplicateNameException {
for (AbstractDBTraceSymbol duplicate : notLabelsNorFunctions.getChildrenNamed(name,
parent)) {
for (AbstractDBTraceSymbol duplicate : notLabels.getChildrenNamed(name, parent)) {
if (duplicate == exclude) {
continue;
}

View file

@ -229,44 +229,6 @@ public interface Trace extends DataTypeManagerDomainObject {
new TraceDataTypeChangeType<>();
}
public static final class TraceFunctionChangeType<U>
extends DefaultTraceChangeType<TraceFunctionSymbol, U> {
// NOTE: ADDED/DELETED/LIFESPAN_CHANGED are SymbolChangeTypes
public static final TraceFunctionChangeType<Void> CHANGED = new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<Integer> CHANGED_PURGE =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<Boolean> CHANGED_INLINE =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<Boolean> CHANGED_NORETURN =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<String> CHANGED_CALL_FIXUP =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<Void> CHANGED_RETURN =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<Void> CHANGED_PARAMETERS =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<TraceFunctionSymbol> CHANGED_THUNK =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<AddressSetView> CHANGED_BODY =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<FunctionTag> TAG_APPLIED =
new TraceFunctionChangeType<>();
public static final TraceFunctionChangeType<FunctionTag> TAG_REMOVED =
new TraceFunctionChangeType<>();
// TODO: VARIABLE_REFERENCE_ADDED? Or would these be reported by ref manager?
// TODO: VARIABLE_REFERENCE_DELETED? Or would these be reported by ref manager?
}
public static final class TraceFunctionTagChangeType<U>
extends DefaultTraceChangeType<FunctionTag, U> {
public static final TraceFunctionTagChangeType<Void> ADDED =
new TraceFunctionTagChangeType<>();
public static final TraceFunctionTagChangeType<Void> CHANGED =
new TraceFunctionTagChangeType<>();
public static final TraceFunctionTagChangeType<Void> DELETED =
new TraceFunctionTagChangeType<>();
}
public static final class TraceInstructionChangeType<U>
extends DefaultTraceChangeType<TraceInstruction, U> {
public static final TraceInstructionChangeType<FlowOverride> FLOW_OVERRIDE_CHANGED =

View file

@ -17,6 +17,9 @@ package ghidra.trace.model.symbol;
import ghidra.program.model.listing.GhidraClass;
/**
* A trace class symbol
*/
public interface TraceClassSymbol extends TraceNamespaceSymbol, GhidraClass {
// Nothing to add.
}

View file

@ -19,8 +19,21 @@ import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
/**
* The class symbol view.
*/
public interface TraceClassSymbolView extends TraceSymbolNoDuplicatesView<TraceClassSymbol> {
/**
* Add a new class symbol.
*
* @param name the name of the class
* @param parent the parent namespace
* @param source the source
* @return the new class symbol
* @throws DuplicateNameException if the name is duplicated in the parent namespace
* @throws InvalidInputException if the name is not valid
* @throws IllegalArgumentException if some other argument is not valid
*/
TraceClassSymbol add(String name, TraceNamespaceSymbol parent, SourceType source)
throws DuplicateNameException, InvalidInputException, IllegalArgumentException;
}

View file

@ -1,81 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
public interface TraceFunctionSymbol
extends TraceNamespaceSymbol, TraceSymbolWithLifespan, Function {
// TODO: If possible, remove the DuplicateNameException on these three methods.
/*
@Override
void setName(String newName, SourceType source) throws InvalidInputException;
@Override
void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source)
throws InvalidInputException, CircularDependencyException;
@Override
void setNamespace(Namespace newNamespace)
throws InvalidInputException, CircularDependencyException;
*/
@Override
TraceParameterSymbol getReturn();
@Override
TraceParameterSymbol addParameter(Variable var, SourceType source)
throws DuplicateNameException, InvalidInputException;
@Override
TraceParameterSymbol insertParameter(int ordinal, Variable var, SourceType source)
throws DuplicateNameException, InvalidInputException;
@Override
Parameter getParameter(int ordinal);
@Override
Parameter moveParameter(int fromOrdinal, int toOrdinal) throws InvalidInputException;
@Override
Parameter[] getParameters();
@Override
Parameter[] getParameters(VariableFilter filter);
@Override
TraceLocalVariableSymbol[] getLocalVariables();
@Override
TraceLocalVariableSymbol[] getLocalVariables(VariableFilter filter);
@Override
Variable[] getAllVariables();
@Override
Variable[] getVariables(VariableFilter filter);
@Override
TraceLocalVariableSymbol addLocalVariable(Variable var, SourceType source)
throws DuplicateNameException, InvalidInputException;
@Override
TraceFunctionSymbol getThunkedFunction(boolean recursive);
}

View file

@ -1,66 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
import java.util.Collection;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.symbol.SourceType;
import ghidra.trace.model.Lifespan;
import ghidra.util.exception.InvalidInputException;
public interface TraceFunctionSymbolView extends TraceSymbolWithLocationView<TraceFunctionSymbol> {
TraceFunctionSymbol add(Lifespan lifespan, Address entryPoint, AddressSetView body,
String name, TraceFunctionSymbol thunked, TraceNamespaceSymbol parent,
SourceType source) throws InvalidInputException, OverlappingFunctionException;
default TraceFunctionSymbol create(long snap, Address entryPoint, AddressSetView body,
String name, TraceFunctionSymbol thunked, TraceNamespaceSymbol parent,
SourceType source) throws InvalidInputException, OverlappingFunctionException {
return add(Lifespan.nowOn(snap), entryPoint, body, name, thunked, parent, source);
}
/**
* Get the ordered unmodifiable set of defined calling convention names. The reserved names
* "unknown" and "default" are not included. The returned collection may not include all names
* referenced by various functions and function-definitions. This set is limited to
* those defined by the associated compiler specification.
*
* @return the set of defined calling convention names.
*/
Collection<String> getCallingConventionNames();
/**
* Get the default calling convention's prototype model.
*
* @return the default calling convention prototype model.
*/
PrototypeModel getDefaultCallingConvention();
/**
* Get the prototype model of the calling convention with the specified name from the
* associated compiler specification. If {@link Function#DEFAULT_CALLING_CONVENTION_STRING}
* is specified {@link #getDefaultCallingConvention()} will be returned.
*
* @param name the calling convention name
* @return the named function calling convention prototype model or null if not defined.
*/
PrototypeModel getCallingConvention(String name);
}

View file

@ -1,21 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
public interface TraceGlobalVariableSymbol extends TraceVariableSymbol /*, GlobalVariable, no? */
{
}

View file

@ -1,21 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
public interface TraceGlobalVariableSymbolView
extends TraceSymbolWithAddressNoDuplicatesView<TraceGlobalVariableSymbol> {
}

View file

@ -15,14 +15,16 @@
*/
package ghidra.trace.model.symbol;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.trace.model.listing.TraceCodeUnit;
/**
* TODO: Document me
*
* See {@link CodeSymbol}
* A trace label symbol.
*/
public interface TraceLabelSymbol extends TraceSymbolWithLifespan {
/**
* Get the code unit at this label
*
* @return the code unit
*/
TraceCodeUnit getCodeUnit();
}

View file

@ -21,11 +21,39 @@ import ghidra.trace.model.Lifespan;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.exception.InvalidInputException;
/**
* The label symbol view.
*/
public interface TraceLabelSymbolView extends TraceSymbolWithLocationView<TraceLabelSymbol> {
/**
* Add a new label symbol.
*
* @param lifespan the lifespan of the symbol
* @param thread the thread, if in register space
* @param address the address of the label
* @param name the name of the label
* @param parent the parent namespace
* @param source the source
* @return the new label symbol
* @throws InvalidInputException if the name is not valid
*/
TraceLabelSymbol add(Lifespan lifespan, TraceThread thread, Address address, String name,
TraceNamespaceSymbol parent, SourceType source) throws InvalidInputException;
/**
* A shorthand for
* {@link #add(Lifespan, TraceThread, Address, String, TraceNamespaceSymbol, SourceType)} where
* lifespan is from the given snap on.
*
* @param snap the starting snapshot key of the symbol
* @param thread the thread, if in register space
* @param address the address of the label
* @param name the name of the label
* @param parent the parent namespace
* @param source the source
* @return the new label symbol
* @throws InvalidInputException if the name is not valid
*/
default TraceLabelSymbol create(long snap, TraceThread thread, Address address, String name,
TraceNamespaceSymbol parent, SourceType source) throws InvalidInputException {
return add(Lifespan.nowOn(snap), thread, address, name, parent, source);

View file

@ -1,22 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
import ghidra.program.model.listing.LocalVariable;
public interface TraceLocalVariableSymbol extends TraceVariableSymbol, LocalVariable {
}

View file

@ -1,21 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
public interface TraceLocalVariableSymbolView
extends TraceSymbolWithAddressNoDuplicatesView<TraceLocalVariableSymbol> {
}

View file

@ -20,6 +20,9 @@ import java.util.Collection;
import ghidra.program.model.symbol.Namespace;
import ghidra.trace.model.Trace;
/**
* A trace namespace symbol.
*/
public interface TraceNamespaceSymbol extends TraceSymbol, Namespace {
@Override
Trace getTrace();
@ -37,6 +40,11 @@ public interface TraceNamespaceSymbol extends TraceSymbol, Namespace {
@Override
TraceNamespaceSymbol getParentNamespace();
/**
* Get the children of this namespace
*
* @return the children
*/
Collection<? extends TraceSymbol> getChildren();
@Override

View file

@ -19,9 +19,21 @@ import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
/**
* The namespace symbol view.
*/
public interface TraceNamespaceSymbolView
extends TraceSymbolNoDuplicatesView<TraceNamespaceSymbol> {
/**
* Add a new namespace symbol.
*
* @param name the name of the namespace
* @param parent the parent namespace
* @param source the source
* @return the new namespace symbol
* @throws DuplicateNameException if the name is duplicated in the parent namespace
* @throws InvalidInputException if the name is not valid
*/
TraceNamespaceSymbol add(String name, TraceNamespaceSymbol parent, SourceType source)
throws DuplicateNameException, InvalidInputException;
}

View file

@ -1,22 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
import ghidra.program.model.listing.Parameter;
public interface TraceParameterSymbol extends TraceSymbol, Parameter {
}

View file

@ -1,21 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
public interface TraceParameterSymbolView
extends TraceSymbolWithAddressNoDuplicatesView<TraceParameterSymbol> {
}

View file

@ -17,14 +17,37 @@ package ghidra.trace.model.symbol;
import java.util.Collection;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
import ghidra.trace.model.Trace;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.task.TaskMonitor;
/**
* A trace symbol.
*
* <p>
* This is essentially the equivalent concept of {@link Symbol} from a {@link Program}. One
* important distinction is that in the trace implementation, the symbol and the object it describes
* are the same. For example, in a {@link Program}, a {@link Namespace} and its symbol are two
* different things. To get the namespace, you would invoke {@link Symbol#getObject()}. That is
* unnecessary, though permissible, with a trace, because {@link TraceNamespaceSymbol} extends from
* both {@link Namespace} and {@link Symbol}.
*/
public interface TraceSymbol extends Symbol {
/**
* Get the trace to which this symbol belongs.
*
* @return the trace
*/
Trace getTrace();
/**
* If in register space, get the thread associated with this symbol.
*
* @return the thread
*/
TraceThread getThread();
@Override
@ -42,6 +65,11 @@ public interface TraceSymbol extends Symbol {
@Override
TraceReference[] getReferences(TaskMonitor monitor);
/**
* Get all memory references to the address of this symbol.
*
* @return the references
*/
Collection<? extends TraceReference> getReferenceCollection();
/**

View file

@ -18,19 +18,32 @@ package ghidra.trace.model.symbol;
import java.util.Collection;
import java.util.Comparator;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.Trace;
/**
* The symbol table for traces.
*
* <p>
* Currently, functions are not supported, so effectively, the only symbol types possible in a trace
* are: labels, namespaces, and classes. Global variables are partially implemented, but as they are
* not finished, even in {@link Program}, they are not available in traces, either.
*
* <p>
* This manager supports a "fluid" API syntax. The methods on this manager narrow the scope in terms
* of the symbol type. Each returns a view, the methods of which operate on that type specifically.
* For example, to get the label at a specific address:
*
* <pre>
* trace.getSymbolManager().labels().getAt(0, null, addr, false);
* </pre>
*/
public interface TraceSymbolManager {
/**
* A comparator that sorts primary symbols first.
*/
static Comparator<TraceSymbol> PRIMALITY_COMPARATOR = (a, b) -> {
boolean aFunc = a instanceof TraceFunctionSymbol;
boolean bFunc = b instanceof TraceFunctionSymbol;
if (aFunc && !bFunc) {
return -1;
}
if (!aFunc && bFunc) {
return 1;
}
boolean aPrim = a.isPrimary();
boolean bPrim = b.isPrimary();
if (aPrim && !bPrim) {
@ -42,47 +55,94 @@ public interface TraceSymbolManager {
return 0;
};
/**
* Get the trace for this manager.
*
* @return the trace
*/
Trace getTrace();
/**
* Get a symbol by its unique identifier.
*
* <p>
* The identifier is only unique within this trace.
*
* @param symbolID the id
* @return the symbol, or null
*/
TraceSymbol getSymbolByID(long symbolID);
/**
* Get the trace's global namespace.
*
* @return the global namespace
*/
TraceNamespaceSymbol getGlobalNamespace();
/**
* Get a view of the labels in the trace.
*
* @return the labels view
*/
TraceLabelSymbolView labels();
/**
* Get a view of the namespaces in the trace.
*
* @return the namespaces view
*/
TraceNamespaceSymbolView namespaces();
/**
* Get a view of the classes in the trace.
*
* @return the classes view
*/
TraceClassSymbolView classes();
TraceFunctionSymbolView functions();
TraceParameterSymbolView parameters();
TraceLocalVariableSymbolView localVariables();
TraceGlobalVariableSymbolView globalVariables();
/**
* TODO: Document me
* Get a view of all the namespaces (including classes) in the trace.
*
* Note because functions are namespaces, and duplicate function names are allowed, this
* composed view may have duplicate names.
*
* @return
* @return the all-namespaces view
*/
TraceSymbolView<? extends TraceNamespaceSymbol> allNamespaces();
TraceSymbolWithAddressNoDuplicatesView<? extends TraceVariableSymbol> allLocals();
TraceSymbolWithAddressNoDuplicatesView<? extends TraceSymbol> allVariables();
TraceSymbolWithLocationView<? extends TraceSymbol> labelsAndFunctions();
TraceSymbolNoDuplicatesView<? extends TraceSymbol> notLabelsNorFunctions();
/**
* Get a view of all the symbols except labels in the trace.
*
* <p>
* <b>NOTE:</b> This method is somewhat vestigial. At one point, functions were partially
* implemented, so this would have contained functions, variables, etc. As the manager now only
* supports labels, namespaces, and classes, this is essentially the same as
* {@link #allNamespaces()}.
*
* @return the not-labels view
*/
TraceSymbolNoDuplicatesView<? extends TraceSymbol> notLabels();
/**
* Get a view of all symbols in the trace.
*
* @return the all-symbols view
*/
TraceSymbolView<? extends TraceSymbol> allSymbols();
/**
* Get the set of unique symbol IDs that are added going from one snapshot to another.
*
* @param from the first snapshot key
* @param to the second snapshot key
* @return the set of IDs absent in the first but present in the second
*/
Collection<Long> getIDsAdded(long from, long to);
/**
* Get the set of unique symbol IDs that are removed going from one snapshot to another.
*
* @param from the first snapshot key
* @param to the second snapshot key
* @return the set of IDs present in the first but absent in the second
*/
Collection<Long> getIDsRemoved(long from, long to);
}

View file

@ -17,8 +17,20 @@ package ghidra.trace.model.symbol;
import ghidra.util.LockHold;
/**
* A symbol view where names cannot be duplicated within the same parent namespace
*
* @param <T> the type of symbols in the view
*/
public interface TraceSymbolNoDuplicatesView<T extends TraceSymbol> extends TraceSymbolView<T> {
/**
* Get the child of the given parent having the given name.
*
* @param name the name of the symbol
* @param parent the parent namespace
* @return the symbol, or null
*/
default T getChildNamed(String name, TraceNamespaceSymbol parent) {
try (LockHold hold = getManager().getTrace().lockRead()) {
for (T symbol : getChildrenNamed(name, parent)) {
@ -28,6 +40,13 @@ public interface TraceSymbolNoDuplicatesView<T extends TraceSymbol> extends Trac
}
}
/**
* A shorthand for {@link #getChildNamed(String, TraceNamespaceSymbol)} where parent is the
* global namespace.
*
* @param name the name of the symbol
* @return the symbol, or null
*/
default T getGlobalNamed(String name) {
return getChildNamed(name, getManager().getGlobalNamespace());
}

View file

@ -18,44 +18,102 @@ package ghidra.trace.model.symbol;
import java.util.Collection;
import java.util.Iterator;
/**
* A type-specific view in the trace symbol table
*
* <p>
* The sub-interfaces of this handle the nuances for symbol types with more capabilities and/or
* restrictions.
*
* @param <T> the type of symbols in the view
*/
public interface TraceSymbolView<T extends TraceSymbol> {
/**
* Get the symbol manager for the trace.
*
* @return the symbol manager
*/
TraceSymbolManager getManager();
/**
* Get the number of symbols in this view.
*
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @return the number of symbols
*/
default int size(boolean includeDynamicSymbols) {
return getAll(includeDynamicSymbols).size();
}
/**
* Get all the symbols in this view.
*
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getAll(boolean includeDynamicSymbols);
/**
* Get all children of the given parent namespace having the given name in this view.
*
* @param name the name of the symbols
* @param parent the parent namespace
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getChildrenNamed(String name, TraceNamespaceSymbol parent);
/**
* Get all children of the given parent namespace in this view.
*
* @param parent the parent namespace
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getChildren(TraceNamespaceSymbol parent);
/**
* A shorthand for {@link #getChildrenNamed(String, TraceNamespaceSymbol)} where parent is the
* global namespace.
*
* @param name the name of the symbols
* @return the symbols in this view satisfying the query
*/
default Collection<? extends T> getGlobalsNamed(String name) {
return getChildrenNamed(name, getManager().getGlobalNamespace());
}
/**
* A shorthand for {@link #getChildren(TraceNamespaceSymbol)} where parent is the global
* namespace.
*
* @return the symbols in this view satisfying the query
*/
default Collection<? extends T> getGlobals() {
return getChildren(getManager().getGlobalNamespace());
}
/**
* Get symbols with the given name, regardless of parent namespace
* Get symbols in this view with the given name, regardless of parent namespace
*
* @param name the name
* @return the collection of symbols with the given name
* @param name the name of the symbols
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getNamed(String name);
/**
* Get symbols whose names match the given glob, regardless of parent namespace
* Get symbols in this view whose names match the given glob, regardless of parent namespace
*
* @param glob the glob (* matches zero-or-more, ? matches one character)
* @param caseSensitive true to match case
* @return the collection of matching symbols
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getWithMatchingName(String glob, boolean caseSensitive);
/**
* Scan symbols in this view lexicographically by name starting at the given lower bound
*
* @param startName the starting lower bound
* @return an iterator over symbols in this view satisfying the query
*/
Iterator<? extends T> scanByName(String startName);
}

View file

@ -15,6 +15,11 @@
*/
package ghidra.trace.model.symbol;
/**
* A symbol view where names cannot be duplicated and things have an address
*
* @param <T> the type of symbols in the view
*/
public interface TraceSymbolWithAddressNoDuplicatesView<T extends TraceSymbol>
extends TraceSymbolWithAddressView<T>, TraceSymbolNoDuplicatesView<T> {
// Just combine the interfaces

View file

@ -21,22 +21,70 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
/**
* A view for symbols located in stack or register space not associated with a particular thread.
* A symbol view for things with an address in stack or register space, but not associated with a
* trace thread.
*
* @param <T> the type of symbol in the view
* <p>
* <b>NOTE:</b> This class is somewhat vestigial. It would be used to index parameters, locals, and
* global variables by their storage addresses. However, functions (and thus parameters and locals)
* are no longer supported. Furthermore, global variables are not fully implemented, yet.
*
* @implNote If this is later used for global variables, we might need to consider that the variable
* is no longer implicitly bound in time by a parent function. We might remove this and
* use {@link TraceSymbolWithLocationView} instead. Even if we brought back function
* support, being able to query by those implicit bounds would probably be useful.
*
* @param <T> the type of symbols in the view
*/
public interface TraceSymbolWithAddressView<T extends TraceSymbol> extends TraceSymbolView<T> {
/**
* Get the child of the given parent having the given name at the given address.
*
* @param name the name of the symbol
* @param address the address of the symbol
* @param parent the parent namespace
* @return the symbol, or null
*/
T getChildWithNameAt(String name, Address address, TraceNamespaceSymbol parent);
/**
* A shorthand for {@link #getChildWithNameAt(String, Address, TraceNamespaceSymbol)} where
* parent is the global namespace.
*
* @param name the name of the symbol
* @param address the address of the symbol
* @return the symbol, or null
*/
default T getGlobalWithNameAt(String name, Address address) {
return getChildWithNameAt(name, address, getManager().getGlobalNamespace());
}
/**
* Get symbols in this view intersecting the given address range.
*
* @param range the range
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getIntersecting(AddressRange range, boolean includeDynamicSymbols);
/**
* Get symbols in this view containing the given address.
*
* @param address the address of the symbol
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getAt(Address address, boolean includeDynamicSymbols);
/**
* Check if this view contains any symbols at the given address.
*
* @param address the address of the symbol
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @return true if any symbols in this view satisfy the query
*/
default boolean hasAt(Address address, boolean includeDynamicSymbols) {
return !getAt(address, includeDynamicSymbols).isEmpty();
}

View file

@ -17,12 +17,35 @@ package ghidra.trace.model.symbol;
import ghidra.trace.model.Lifespan;
/**
* A trace symbol having a lifespan.
*/
public interface TraceSymbolWithLifespan extends TraceSymbol {
/**
* Get the lifespan of the symbol
*
* @return the lifespan
*/
Lifespan getLifespan();
/**
* Get the minimum snapshot key in the lifespan
*
* @return the minimum snapshot key
*/
long getStartSnap();
/**
* Set the maximum snapshot key in the lifespan
*
* @param snap the new maximum snapshot key
*/
void setEndSnap(long snap);
/**
* Get the maximum snapshot key in the lifespan
*
* @return the maximum snapshot key
*/
long getEndSnap();
}

View file

@ -22,28 +22,70 @@ import ghidra.trace.model.Lifespan;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.LockHold;
/**
* A symbol view for things bound by an address range and lifespan.
*
* <p>
* <b>NOTE:</b> We may eventually drop the {@code thread} parameter from these methods, as we
* transition to using register-space overlays.
*
* @param <T> the type of symbols in the view
*/
public interface TraceSymbolWithLocationView<T extends TraceSymbol> extends TraceSymbolView<T> {
/**
* Get the child of the given parent having the given name at the given point.
*
* @param name the name of the symbol
* @param snap the snapshot key
* @param thread the thread, if in register space
* @param address the address of the symbol
* @param parent the parent namespace
* @return the symbol, or null
*/
T getChildWithNameAt(String name, long snap, TraceThread thread, Address address,
TraceNamespaceSymbol parent);
/**
* A shorthand for
* {@link #getChildWithNameAt(String, long, TraceThread, Address, TraceNamespaceSymbol)} where
* parent is the global namespace.
*
* @param name the name of the symbol
* @param snap the snapshot key
* @param thread the thread, if in register space
* @param address the address of the symbol
* @return the symbol, or null
*/
default T getGlobalWithNameAt(String name, long snap, TraceThread thread, Address address) {
return getChildWithNameAt(name, snap, thread, address, getManager().getGlobalNamespace());
}
/**
* Get symbols in this view intersecting the given box.
*
* @param span the time bound of the box
* @param thread the thread, if in register space
* @param range the address bound of the box
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @param forward true if the collection should be ordered forward by address, false for
* backward by address.
* @return the symbols in this view satisfying the query
*/
Collection<? extends T> getIntersecting(Lifespan span, TraceThread thread,
AddressRange range, boolean includeDynamicSymbols, boolean forward);
/**
* Get the symbols at the given snap and address, starting with the primary
* Get symbols in this view at the given point.
*
* TODO: Document me
* <p>
* The result will be ordered with the primary symbol first.
*
* @param snap
* @param thread
* @param address
* @param includeDynamicSymbols
* @return
* @param snap the snapshot key
* @param thread the thread, if in register space
* @param address the address of the symbols
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @return the symbols in this view satisfying the query
*/
default Collection<? extends T> getAt(long snap, TraceThread thread, Address address,
boolean includeDynamicSymbols) {
@ -56,6 +98,15 @@ public interface TraceSymbolWithLocationView<T extends TraceSymbol> extends Trac
}
}
/**
* Check if this view contains any symbols at the given point.
*
* @param snap the snapshot key
* @param thread the thread, if in register space
* @param address the address of the symbols
* @param includeDynamicSymbols true to include dynamically-generated symbols
* @return true if any symbols in this view satisfy the query
*/
default boolean hasAt(long snap, TraceThread thread, Address address,
boolean includeDynamicSymbols) {
try (LockHold hold = getManager().getTrace().lockRead()) {

View file

@ -1,23 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.model.symbol;
import ghidra.program.model.listing.Variable;
public interface TraceVariableSymbol extends TraceSymbol, Variable {
@Override
TraceFunctionSymbol getFunction();
}

View file

@ -1,435 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.trace.database.program;
import static ghidra.lifecycle.Unfinished.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import org.junit.*;
import db.Transaction;
import ghidra.app.cmd.function.AddStackVarCmd;
import ghidra.app.cmd.refs.AddStackRefCmd;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.util.exception.InvalidInputException;
public class DBTraceProgramViewFunctionManagerTest extends AbstractGhidraHeadlessIntegrationTest {
ToyDBTraceBuilder b;
FunctionManager functionManager;
Program program;
Transaction tx;
@Before
public void setUpFunctionManagerTest() throws IOException {
b = new ToyDBTraceBuilder("Testing", ProgramBuilder._TOY);
program = b.trace.getFixedProgramView(0);
functionManager = program.getFunctionManager();
tx = b.startTransaction();
}
@After
public void tearDownFunctionManagerTest() {
tx.close();
b.close();
}
protected Function createFunction(String name, Address entry, AddressSetView body)
throws InvalidInputException, OverlappingFunctionException {
Function created =
functionManager.createFunction(name, entry, body, SourceType.USER_DEFINED);
Function found = functionManager.getFunctionAt(entry);
assertSame(created, found);
assertEquals(entry, created.getEntryPoint());
assertEquals(body, created.getBody());
return created;
}
@Test
public void testCreateFunction() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
// Overlapping functions - not allowed
try {
functionManager.createFunction("foo1", b.addr(50), b.set(b.range(50, 100)),
SourceType.USER_DEFINED);
Assert.fail();
}
catch (OverlappingFunctionException e) {
// Expected
}
try {
functionManager.createFunction("foo2", b.addr(200), b.set(b.range(200, 250)),
SourceType.USER_DEFINED);
Assert.fail();
}
catch (OverlappingFunctionException e) {
// Expected
}
try {
functionManager.createFunction("foo3", b.addr(150), b.set(b.range(150, 250)),
SourceType.USER_DEFINED);
Assert.fail();
}
catch (OverlappingFunctionException e) {
// Expected
}
// Invalid entry address
try {
createFunction("foo4", b.addr(250), b.set(b.range(300, 350)));
Assert.fail();
}
catch (IllegalArgumentException e) {
// expected
}
createFunction("foo4", b.addr(50), b.set(b.range(50, 99)));
createFunction("foo5", b.addr(201), b.set(b.range(201, 250)));
// try duplicate name
createFunction("foo5", b.addr(500), b.set(b.range(500, 600)));
}
@Test
public void testCreateVarArgFunction() throws Exception {
Function f = createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
f.setVarArgs(true);
assertEquals(true, f.hasVarArgs());
f.setVarArgs(false);
assertEquals(false, f.hasVarArgs());
}
@Test
public void testCreateInlineFunction() throws Exception {
Function f = createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
assertEquals(false, f.isInline());
f.setInline(true);
assertEquals(true, f.isInline());
f.setInline(false);
assertEquals(false, f.isInline());
}
@Test
public void testCreateNoReturnFunction() throws Exception {
Function f = createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
assertEquals(false, f.hasNoReturn());
f.setNoReturn(true);
assertEquals(true, f.hasNoReturn());
f.setNoReturn(false);
assertEquals(false, f.hasNoReturn());
}
@Test
@Ignore("TODO, low priority")
public void testRemoveFunction() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
Function foo2 = createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
// add stack references and make sure they get removed when the
// function is deleted
int transactionIDForTest = program.startTransaction("test");
AddStackVarCmd cmd = new AddStackVarCmd(foo2.getEntryPoint(), -4, "local_var", null,
SourceType.USER_DEFINED);
assertTrue(cmd.applyTo(program));
cmd = new AddStackVarCmd(foo2.getEntryPoint(), 4, "param_1", null, SourceType.USER_DEFINED);
assertTrue(cmd.applyTo(program));
AddStackRefCmd c = new AddStackRefCmd(b.addr(210), 0, -4, SourceType.USER_DEFINED);
assertTrue(c.applyTo(program));
c = new AddStackRefCmd(b.addr(222), 1, 4, SourceType.USER_DEFINED);
assertTrue(c.applyTo(program));
program.endTransaction(transactionIDForTest, true);
Variable[] vars = foo2.getLocalVariables();
assertEquals(1, vars.length);
ReferenceManager refMgr = program.getReferenceManager();
TODO(); // TODO: Need to support variable references
Reference[] vrefs = refMgr.getReferencesTo(vars[0]);
assertEquals(1, vrefs.length);
assertEquals(b.addr(210), vrefs[0].getFromAddress());
Parameter[] params = foo2.getParameters();
assertEquals(1, params.length);
vrefs = refMgr.getReferencesTo(params[0]);
assertEquals(1, vrefs.length);
assertEquals(b.addr(222), vrefs[0].getFromAddress());
functionManager.removeFunction(b.addr(201));
vrefs = refMgr.getReferencesTo(vars[0]);
assertEquals(0, vrefs.length);
vrefs = refMgr.getReferencesTo(params[0]);
assertEquals(0, vrefs.length);
Function f = functionManager.getFunctionAt(b.addr(100));
assertEquals(b.set(b.range(100, 200)), f.getBody());
assertNull(functionManager.getFunctionAt(b.addr(201)));
f = functionManager.getFunctionAt(b.addr(250));
assertEquals(b.set(b.range(250, 350)), f.getBody());
assertTrue(program.getSymbolTable()
.getPrimarySymbol(
b.addr(201))
.getSymbolType() != SymbolType.FUNCTION);
}
@Test
public void testGetFirstFunctionContaining() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
Function f = functionManager.getFunctionContaining(b.addr(120));
assertEquals(b.set(b.range(100, 200)), f.getBody());
f = functionManager.getFunctionContaining(b.addr(240));
assertTrue(b.set(b.range(201, 249)).equals(f.getBody()));
}
@Test
public void testIsInFunction() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
assertTrue(!functionManager.isInFunction(b.addr(95)));
assertTrue(functionManager.isInFunction(b.addr(100)));
assertTrue(functionManager.isInFunction(b.addr(250)));
assertTrue(functionManager.isInFunction(b.addr(240)));
assertTrue(!functionManager.isInFunction(b.addr(500)));
}
@Test
public void testGetFunctionsContaining() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
Function function = functionManager.getFunctionContaining(b.addr(160));
assertNotNull(function);
assertEquals(b.addr(100), function.getEntryPoint());
function = functionManager.getFunctionContaining(b.addr(50));
assertNull(function);
function = functionManager.getFunctionContaining(b.addr(100));
assertNotNull(function);
assertEquals(b.addr(100), function.getEntryPoint());
}
@Test
public void testGetFunctionsOverlapping() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
AddressSet set = new AddressSet();
set.addRange(b.addr(50), b.addr(100));
set.addRange(b.addr(350), b.addr(500));
Iterator<Function> iter = functionManager.getFunctionsOverlapping(set);
assertEquals(b.addr(100), iter.next().getEntryPoint());
assertEquals(b.addr(250), iter.next().getEntryPoint());
assertTrue(!iter.hasNext());
iter = functionManager.getFunctionsOverlapping(b.set(b.range(99, 199)));
assertEquals(b.addr(100), iter.next().getEntryPoint());
assertTrue(!iter.hasNext());
}
/*
* Test for FunctionIterator getFunctions()
*/
@Test
public void testGetFunctions() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
FunctionIterator iter = functionManager.getFunctions(true);
int cnt = 0;
while (iter.hasNext()) {
++cnt;
assertNotNull(iter.next());
}
assertEquals(3, cnt);
}
@Test
public void testGetReferencedFunction() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
Function foo2 = createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
program.getMemory().setInt(b.addr(50), 201);
program.getListing().createData(b.addr(50), PointerDataType.dataType);
// TODO: It seems this is failing because of a missing inferred reference.
Reference cheating = program.getReferenceManager()
.addMemoryReference(b.addr(50),
b.addr(201), RefType.DATA, SourceType.ANALYSIS, 0);
// TODO: Remove the explicit reference above and ensure this test still passes
program.getReferenceManager().setPrimary(cheating, true);
// TODO: Also remove explit set primary
assertEquals(foo2, program.getFunctionManager().getReferencedFunction(b.addr(50)));
}
/*
* Test for FunctionIterator getFunctions(Address)
*/
@Test
public void testGetFunctionsAddress() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
FunctionIterator iter = functionManager.getFunctions(b.addr(125), true);
int cnt = 0;
while (iter.hasNext()) {
++cnt;
assertNotNull(iter.next());
}
assertEquals(2, cnt);
}
/*
* Test for FunctionIterator getFunctions(AddressSetView)
*/
@Test
public void testGetFunctionsAddressSetView() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(250), b.set(b.range(250, 350)));
createFunction("foo2", b.addr(201), b.set(b.range(201, 249)));
AddressSet asv = new AddressSet();
asv.addRange(b.addr(50), b.addr(60));
asv.addRange(b.addr(70), b.addr(90));
asv.addRange(b.addr(110), b.addr(160));
asv.addRange(b.addr(200), b.addr(249));
FunctionIterator iter = functionManager.getFunctions(asv, true);
int cnt = 0;
while (iter.hasNext()) {
++cnt;
Function f = iter.next();
assertEquals(b.set(b.range(201, 249)), f.getBody());
}
assertEquals(1, cnt);
}
@Test
public void testFunctionIteratorBackwards() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(201), b.set(b.range(201, 249)));
createFunction("foo2", b.addr(250), b.set(b.range(250, 350)));
FunctionIterator iter = functionManager.getFunctions(false);
assertTrue(iter.hasNext());
Function f = iter.next();
assertEquals("foo2", f.getName());
assertTrue(iter.hasNext());
f = iter.next();
assertEquals("foo1", f.getName());
assertTrue(iter.hasNext());
f = iter.next();
assertEquals("foo", f.getName());
assertTrue(!iter.hasNext());
}
@Test
public void testFunctionIteratorBackwards2() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(201), b.set(b.range(201, 210)));
createFunction("foo2", b.addr(250), b.set(b.range(250, 350)));
FunctionIterator iter = functionManager.getFunctions(b.addr(250), false);
assertTrue(iter.hasNext());
Function f = iter.next();
assertEquals("foo2", f.getName());
assertTrue(iter.hasNext());
f = iter.next();
assertEquals("foo1", f.getName());
assertTrue(iter.hasNext());
f = iter.next();
assertEquals("foo", f.getName());
assertTrue(!iter.hasNext());
}
@Test
public void testFunctionIteratorBackwards3() throws Exception {
createFunction("foo", b.addr(100), b.set(b.range(100, 200)));
createFunction("foo1", b.addr(201), b.set(b.range(201, 210)));
createFunction("foo2", b.addr(250), b.set(b.range(250, 350)));
FunctionIterator iter = functionManager.getFunctions(b.addr(300), false);
assertTrue(iter.hasNext());
Function f = iter.next();
assertEquals("foo2", f.getName());
assertTrue(iter.hasNext());
f = iter.next();
assertEquals("foo1", f.getName());
assertTrue(iter.hasNext());
f = iter.next();
assertEquals("foo", f.getName());
assertTrue(!iter.hasNext());
}
@Test
public void testGetDefaultCallingConvention() throws Exception {
PrototypeModel protoModel = functionManager.getDefaultCallingConvention();
assertEquals("__stdcall", protoModel.getName());
PrototypeModel defaultModel = functionManager.getCallingConvention("default");
assertEquals(defaultModel, protoModel);
}
@Test
public void testGetCallingConventionNames() throws Exception {
Collection<String> names = functionManager.getCallingConventionNames();
assertTrue(names.size() >= 1);
for (String name : names) {
assertNotNull(functionManager.getCallingConvention(name));
}
}
}

View file

@ -262,11 +262,10 @@ public class DBTraceSymbolManagerTest extends AbstractGhidraHeadlessIntegrationT
assertEquals(Set.of(nsA, clsA), new HashSet<>(manager.allNamespaces().getAll(false)));
assertEquals(2, manager.allNamespaces().size(false));
assertEquals(Set.of(lab1, lab2), new HashSet<>(manager.labelsAndFunctions().getAll(false)));
assertEquals(2, manager.labelsAndFunctions().size(false));
assertEquals(Set.of(nsA, clsA),
new HashSet<>(manager.notLabelsNorFunctions().getAll(false)));
assertEquals(2, manager.notLabelsNorFunctions().size(false));
assertEquals(Set.of(lab1, lab2), new HashSet<>(manager.labels().getAll(false)));
assertEquals(2, manager.labels().size(false));
assertEquals(Set.of(nsA, clsA), new HashSet<>(manager.notLabels().getAll(false)));
assertEquals(2, manager.notLabels().size(false));
// TODO: Remaining composites
assertEquals(Set.of(nsA, clsA, lab1, lab2),
new HashSet<>(manager.allSymbols().getAll(false)));
@ -297,8 +296,7 @@ public class DBTraceSymbolManagerTest extends AbstractGhidraHeadlessIntegrationT
assertEquals(Set.of(clsA), new HashSet<>(manager.classes().getChildrenNamed("A", nsA)));
assertEquals(clsA, manager.classes().getChildNamed("A", nsA));
assertEquals(Set.of(lab1), new HashSet<>(manager.labels().getChildrenNamed("LAB1", nsA)));
assertEquals(Set.of(lab2),
new HashSet<>(manager.labelsAndFunctions().getChildrenNamed("LAB2", clsA)));
assertEquals(Set.of(lab2), new HashSet<>(manager.labels().getChildrenNamed("LAB2", clsA)));
assertEquals(Set.of(), new HashSet<>(manager.labels().getChildrenNamed("LAB2", nsA)));
}
@ -405,9 +403,9 @@ public class DBTraceSymbolManagerTest extends AbstractGhidraHeadlessIntegrationT
SourceType.USER_DEFINED);
}
assertEquals(Set.of(lab1),
new HashSet<>(manager.labelsAndFunctions().getWithMatchingName("LAB?", true)));
new HashSet<>(manager.labels().getWithMatchingName("LAB?", true)));
assertEquals(Set.of(lab1, lab2),
new HashSet<>(manager.labelsAndFunctions().getWithMatchingName("LAB?", false)));
new HashSet<>(manager.labels().getWithMatchingName("LAB?", false)));
}
@Test
@ -440,21 +438,19 @@ public class DBTraceSymbolManagerTest extends AbstractGhidraHeadlessIntegrationT
}
assertEquals(lab1,
manager.labels().getChildWithNameAt("LAB1", 4, null, b.addr(0x4000), nsA));
assertNull(manager.functions().getChildWithNameAt("LAB1", 4, null, b.addr(0x4000), nsA));
assertNull(manager.labels().getChildWithNameAt("LAB2", 4, null, b.addr(0x4000), nsA));
assertNull(manager.labels().getChildWithNameAt("LAB1", 0, null, b.addr(0x4000), nsA));
assertNull(manager.labels()
.getChildWithNameAt("LAB1", 4, thread,
b.language.getRegister("r4").getAddress(), nsA));
assertNull(manager.labels().getChildWithNameAt("LAB1", 4, null, b.addr(0x4001), nsA));
assertNull(
manager.labelsAndFunctions().getChildWithNameAt("LAB1", 4, null, b.addr(0x4000), clsA));
assertNull(manager.labels().getChildWithNameAt("LAB1", 4, null, b.addr(0x4000), clsA));
assertEquals(lab2,
manager.labelsAndFunctions().getChildWithNameAt("lab2", 4, null, b.addr(0x4001), clsA));
manager.labels().getChildWithNameAt("lab2", 4, null, b.addr(0x4001), clsA));
assertEquals(lab3,
manager.labelsAndFunctions().getChildWithNameAt("lab3", 4, null, b.addr(0x4001), clsA));
assertEquals(lab4, manager.labelsAndFunctions()
manager.labels().getChildWithNameAt("lab3", 4, null, b.addr(0x4001), clsA));
assertEquals(lab4, manager.labels()
.getChildWithNameAt("lab4", 0, thread,
b.language.getRegister("r4").getAddress(), nsA));
}
@ -468,8 +464,6 @@ public class DBTraceSymbolManagerTest extends AbstractGhidraHeadlessIntegrationT
.create(4, null, b.addr(0x4000), "LAB1", global,
SourceType.USER_DEFINED);
}
assertEquals(lab1,
manager.labelsAndFunctions().getGlobalWithNameAt("LAB1", 4, null, b.addr(0x4000)));
assertEquals(lab1, manager.labels().getGlobalWithNameAt("LAB1", 4, null, b.addr(0x4000)));
}
@ -504,9 +498,9 @@ public class DBTraceSymbolManagerTest extends AbstractGhidraHeadlessIntegrationT
// TODO: Test that functions are properly excluded from labels()
// once I have a means of adding them.
assertEquals(Set.of(),
new HashSet<>(manager.labelsAndFunctions()
.getIntersecting(Lifespan.span(0, 0), null,
b.range(0x0000, 0x4000), false, true)));
new HashSet<>(manager.labels()
.getIntersecting(Lifespan.span(0, 0), null, b.range(0x0000, 0x4000), false,
true)));
assertEquals(Set.of(lab1, lab2, lab3),
new HashSet<>(manager.labels()
.getIntersecting(Lifespan.nowOn(0), null,
@ -549,7 +543,7 @@ public class DBTraceSymbolManagerTest extends AbstractGhidraHeadlessIntegrationT
assertEquals(Set.of(lab1),
new HashSet<>(manager.labels().getAt(4, null, b.addr(0x4000), false)));
assertEquals(Set.of(lab2, lab3),
new HashSet<>(manager.labelsAndFunctions().getAt(4, null, b.addr(0x4001), false)));
new HashSet<>(manager.labels().getAt(4, null, b.addr(0x4001), false)));
// TODO: Test ordering by setPrimary
assertFalse(manager.labels().hasAt(0, null, b.addr(0x4000), false));

View file

@ -177,13 +177,6 @@ public class CodeViewerProvider extends NavigatableComponentProviderAdapter
return !isConnected();
}
/**
* @return true if this listing is backed by a dynamic data source (e.g., debugger)
*/
public boolean isDynamicListing() {
return false;
}
/**
* TODO: Remove or rename this to something that accommodates redirecting writes, e.g., to a
* debug target process, particularly for assembly, which may involve code unit modification

View file

@ -432,7 +432,10 @@ public class FunctionPlugin extends Plugin implements DataService {
public boolean isCreateFunctionAllowed(ListingActionContext context, boolean allowExisting,
boolean createThunk) {
// Debugger traces do not support functions
if (context.getNavigatable().isDynamic()) {
return false;
}
// A program and location is needed for any create function action.
Program program = context.getProgram();
if (program == null) {

View file

@ -22,7 +22,6 @@ import ghidra.program.util.ProgramLocation;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
/**
* Interface for a symbol, which associates a string value with
@ -110,7 +109,7 @@ public interface Symbol {
/**
* Returns all memory references to the address of this symbol. If you do not have a
* {@link TaskMonitor} instance, then you can pass {@link TaskMonitorAdapter#DUMMY_MONITOR} or
* {@link TaskMonitor} instance, then you can pass {@link TaskMonitor#DUMMY} or
* <code>null</code>.
*
* @return all memory references to the address of this symbol.