diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverService.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverService.java index cb9b03d6c9..e1ecfaf2ff 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverService.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueHoverService.java @@ -420,11 +420,10 @@ public class VariableValueHoverService extends AbstractConfigurableHover } public CompletableFuture fillStorage(Function function, String name, - DataType type, Program program, VariableStorage storage, - AddressSetView symbolStorage) { + DataType type, Program program, VariableStorage storage) { return executeBackground(monitor -> { UnwoundFrame frame = - VariableValueUtils.requiresFrame(program, storage, symbolStorage) + VariableValueUtils.requiresFrame(program, storage) ? eval.getStackFrame(function, warnings, monitor, true) : eval.getGlobalsFakeFrame(); return fillFrameStorage(frame, name, type, program, storage); @@ -432,15 +431,15 @@ public class VariableValueHoverService extends AbstractConfigurableHover } public CompletableFuture fillPcodeOp(Function function, String name, - DataType type, PcodeOp op, AddressSetView symbolStorage) { + DataType type, PcodeOp op) { Program program = function.getProgram(); - boolean requiresFrame = applyCopyHeuristic(program, op, symbolStorage, + boolean requiresFrame = applyCopyHeuristic(program, op, VariableValueUtils::requiresFrame, VariableValueUtils::requiresFrame); return executeBackground(monitor -> { UnwoundFrame frame = requiresFrame ? eval.getStackFrame(function, warnings, monitor, true) : eval.getGlobalsFakeFrame(); - return fillFrameOp(frame, program, name, type, op, symbolStorage); + return fillFrameOp(frame, program, name, type, op); }); } @@ -473,11 +472,11 @@ public class VariableValueHoverService extends AbstractConfigurableHover } interface CopyCase { - T evaluate(Program program, Varnode varnode, AddressSetView symbolStorage); + T evaluate(Program program, Varnode varnode); } interface DefaultCase { - T evaluate(Program program, PcodeOp op, AddressSetView symbolStorage); + T evaluate(Program program, PcodeOp op); } /** @@ -486,29 +485,25 @@ public class VariableValueHoverService extends AbstractConfigurableHover * the LHS of an assignment operator, but that could be difficult and complex.... In any * case, if it's an assignment, I'm going to evaluate the output of the output operand * instead. Trouble is, that may just traverse back over the copy, as the copy is the - * defining operator. It might only work if I can guarantee the output is part of the symbol - * storage. - * + * defining operator. */ - protected T applyCopyHeuristic(Program program, PcodeOp op, - AddressSetView symbolStorage, - CopyCase copyCase, DefaultCase defaultCase) { + protected T applyCopyHeuristic(Program program, PcodeOp op, CopyCase copyCase, + DefaultCase defaultCase) { return switch (op.getOpcode()) { - case PcodeOp.COPY -> copyCase.evaluate(program, op.getOutput(), symbolStorage); - default -> defaultCase.evaluate(program, op, symbolStorage); + case PcodeOp.COPY -> copyCase.evaluate(program, op.getOutput()); + default -> defaultCase.evaluate(program, op); }; } public VariableValueTable fillFrameOp(UnwoundFrame frame, Program program, - String name, DataType type, PcodeOp op, AddressSetView symbolStorage) { + String name, DataType type, PcodeOp op) { table.add(new NameRow(name)); if (!frame.isFake()) { table.add(new FrameRow(frame)); } table.add(new TypeRow(type)); - WatchValue value = - applyCopyHeuristic(program, op, symbolStorage, frame::evaluate, frame::evaluate); + WatchValue value = applyCopyHeuristic(program, op, frame::evaluate, frame::evaluate); // TODO: What if the type is dynamic with non-fixed size? if (type.getLength() != value.length()) { @@ -518,7 +513,7 @@ public class VariableValueHoverService extends AbstractConfigurableHover } public CompletableFuture fillHighVariable(HighVariable hVar, - String name, AddressSetView symbolStorage) { + String name) { Function function = hVar.getHighFunction().getFunction(); VariableStorage storage = VariableValueUtils.fabricateStorage(hVar); if (storage.isUniqueStorage()) { @@ -527,17 +522,14 @@ public class VariableValueHoverService extends AbstractConfigurableHover table.add(new ValueRow("(Unique)", TraceMemoryState.KNOWN)); return CompletableFuture.completedFuture(table); } - return fillStorage(function, name, hVar.getDataType(), function.getProgram(), storage, - symbolStorage); + return fillStorage(function, name, hVar.getDataType(), function.getProgram(), storage); } - public CompletableFuture fillHighVariable(HighVariable hVar, - AddressSetView symbolStorage) { - return fillHighVariable(hVar, hVar.getName(), symbolStorage); + public CompletableFuture fillHighVariable(HighVariable hVar) { + return fillHighVariable(hVar, hVar.getName()); } - public CompletableFuture fillComponent(ClangFieldToken token, - AddressSetView symbolStorage) { + public CompletableFuture fillComponent(ClangFieldToken token) { Function function = token.getClangFunction().getHighFunction().getFunction(); Program program = function.getProgram(); PcodeOp op = token.getPcodeOp(); @@ -547,13 +539,13 @@ public class VariableValueHoverService extends AbstractConfigurableHover if (hVar.getDataType().isEquivalent(new PointerDataType(type))) { op = VariableValueUtils.findDeref(program.getAddressFactory(), vn); } - return fillPcodeOp(function, token.getText(), type, op, symbolStorage); + return fillPcodeOp(function, token.getText(), type, op); } public CompletableFuture fillComposite(HighSymbol hSym, - HighVariable hVar, AddressSetView symbolStorage) { + HighVariable hVar) { return fillStorage(hVar.getHighFunction().getFunction(), hSym.getName(), - hSym.getDataType(), hSym.getProgram(), hSym.getStorage(), symbolStorage); + hSym.getDataType(), hSym.getProgram(), hSym.getStorage()); } public CompletableFuture fillToken(ClangToken token) { @@ -561,17 +553,8 @@ public class VariableValueHoverService extends AbstractConfigurableHover return null; } - /** - * I can't get just the expression tree here, except as p-code AST, which doesn't seem - * to include token info. A line should contain the full expression, though. I'll grab - * the symbols' storage from it and ensure my evaluation recurses until it hits those - * symbols. - */ - AddressSet symbolStorage = - VariableValueUtils.collectSymbolStorage(token.getLineParent()); - if (token instanceof ClangFieldToken fieldToken) { - return fillComponent(fieldToken, symbolStorage); + return fillComponent(fieldToken); } HighVariable hVar = token.getHighVariable(); @@ -594,16 +577,16 @@ public class VariableValueHoverService extends AbstractConfigurableHover Varnode representative = hVar.getRepresentative(); if (!storage.contains(representative.getAddress())) { // I'm not sure this can ever happen.... - return fillHighVariable(hVar, symbolStorage); + return fillHighVariable(hVar); } if (Arrays.asList(storage.getVarnodes()).equals(List.of(representative))) { // The var is the symbol - return fillHighVariable(hVar, symbolStorage); + return fillHighVariable(hVar); } // Presumably, there's some component path from symbol to high var - return fillComposite(hSym, hVar, symbolStorage); + return fillComposite(hSym, hVar); } public CompletableFuture fillVariable(Variable variable) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueRow.java index 5c14e1233d..a9d91d2a77 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueRow.java @@ -15,8 +15,6 @@ */ package ghidra.app.plugin.core.debug.gui.stack.vars; -import static ghidra.app.plugin.core.debug.gui.stack.vars.VariableValueRow.*; - import java.nio.ByteBuffer; import java.util.stream.Collectors; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueTable.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueTable.java index e7929ac366..05a62b832a 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueTable.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueTable.java @@ -4,9 +4,9 @@ * 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. @@ -33,7 +33,7 @@ public class VariableValueTable { * At most one of each row type can be present. Adding a row whose type already exists will * remove the old row of the same type. * - * @param row + * @param row the row to add */ public void add(VariableValueRow row) { synchronized (rows) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueUtils.java index b641dd1a8d..30c9940bb9 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueUtils.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/stack/vars/VariableValueUtils.java @@ -19,8 +19,6 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import ghidra.app.decompiler.ClangLine; -import ghidra.app.decompiler.ClangToken; import ghidra.app.plugin.core.debug.stack.*; import ghidra.app.plugin.core.debug.stack.StackUnwindWarning.CustomStackUnwindWarning; import ghidra.debug.api.tracemgr.DebuggerCoordinates; @@ -65,21 +63,6 @@ public enum VariableValueUtils { * context */ private static final class RequiresFrameEvaluator extends AbstractVarnodeEvaluator { - private final AddressSetView symbolStorage; - - private RequiresFrameEvaluator(AddressSetView symbolStorage) { - this.symbolStorage = symbolStorage; - } - - @Override - protected boolean isLeaf(Varnode vn) { - if (vn.getDef() == null && (vn.isRegister() || vn.isAddress())) { - return true; - } - return vn.isConstant() || - symbolStorage.contains(vn.getAddress(), vn.getAddress().add(vn.getSize() - 1)); - } - @Override protected Address applyBase(long offset) { throw new AssertionError(); @@ -359,13 +342,10 @@ public enum VariableValueUtils { * * @param program the program containing the variable storage * @param storage the storage to evaluate - * @param symbolStorage the leaves of evaluation, usually storage used by symbols in scope. See - * {@link #collectSymbolStorage(ClangLine)} * @return true if a frame is required, false otherwise */ - public static boolean requiresFrame(Program program, VariableStorage storage, - AddressSetView symbolStorage) { - return new RequiresFrameEvaluator(symbolStorage).evaluateStorage(program, storage); + public static boolean requiresFrame(Program program, VariableStorage storage) { + return new RequiresFrameEvaluator().evaluateStorage(program, storage); } /** @@ -373,13 +353,10 @@ public enum VariableValueUtils { * * @param program the program containing the variable storage * @param varnode the varnode to evaluate - * @param symbolStorage the leaves of evaluation, usually storage used by symbols in scope. See - * {@link #collectSymbolStorage(ClangLine)} * @return true if a frame is required, false otherwise */ - public static boolean requiresFrame(Program program, Varnode varnode, - AddressSetView symbolStorage) { - return new RequiresFrameEvaluator(symbolStorage).evaluateVarnode(program, varnode); + public static boolean requiresFrame(Program program, Varnode varnode) { + return new RequiresFrameEvaluator().evaluateVarnode(program, varnode); } /** @@ -387,12 +364,10 @@ public enum VariableValueUtils { * * @param program the program containing the variable storage * @param op the op whose output to evaluation - * @param symbolStorage the leaves of evaluation, usually storage used by symbols in scope. See - * {@link #collectSymbolStorage(ClangLine)} * @return true if a frame is required, false otherwise */ - public static boolean requiresFrame(Program program, PcodeOp op, AddressSetView symbolStorage) { - return new RequiresFrameEvaluator(symbolStorage).evaluateOp(program, op); + public static boolean requiresFrame(Program program, PcodeOp op) { + return new RequiresFrameEvaluator().evaluateOp(program, op); } /** @@ -470,8 +445,8 @@ public enum VariableValueUtils { * *

* This will prefer the stack pointer in the {@link TraceStackFrame}. If that's not available, - * it will use the value of the stack pointer register from the thread's register bank for - * frame 0. + * it will use the value of the stack pointer register from the thread's register bank for frame + * 0. * * @param platform the platform * @param thread the thread @@ -520,8 +495,8 @@ public enum VariableValueUtils { * *

* This will prefer the stack pointer in the {@link TraceStackFrame}. If that's not available, - * it will use the value of the stack pointer register from the thread's register bank for - * frame 0. + * it will use the value of the stack pointer register from the thread's register bank for frame + * 0. * * @param platform the platform * @param thread the thread @@ -607,45 +582,6 @@ public enum VariableValueUtils { return set.contains(vn.getAddress(), vn.getAddress().add(vn.getSize() - 1)); } - /** - * Collect the addresses used for storage by any symbol in the given line of decompiled C code - * - *

- * It's not the greatest, but any variable to be evaluated should only be expressed in terms of - * symbols on the same line (at least by the decompiler's definition, wrapping shouldn't count - * against us). This can be used to determine where evaluation should cease descending into - * defining p-code ops. See {@link #requiresFrame(Program, PcodeOp, AddressSetView)}, and - * {@link UnwoundFrame#evaluate(Program, PcodeOp, AddressSetView)}. - * - * @param line the line - * @return the address set - */ - public static AddressSet collectSymbolStorage(ClangLine line) { - AddressSet storage = new AddressSet(); - for (ClangToken tok : line.getAllTokens()) { - Varnode vn = tok.getVarnode(); - if (vn != null) { - storage.add(rangeFromVarnode(vn)); - } - HighVariable hVar = tok.getHighVariable(); - if (hVar == null) { - continue; - } - Varnode rep = hVar.getRepresentative(); - if (rep != null) { - storage.add(rangeFromVarnode(rep)); - } - HighSymbol hSym = hVar.getSymbol(); - if (hSym == null) { - continue; - } - for (Varnode stVn : hSym.getStorage().getVarnodes()) { - storage.add(rangeFromVarnode(stVn)); - } - } - return storage; - } - /** * Find the descendant that dereferences this given varnode * diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AbstractUnwoundFrame.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AbstractUnwoundFrame.java index 5c08c65a3d..7743f798f2 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AbstractUnwoundFrame.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AbstractUnwoundFrame.java @@ -34,7 +34,8 @@ import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason; import ghidra.pcode.opbehavior.BinaryOpBehavior; import ghidra.pcode.opbehavior.UnaryOpBehavior; import ghidra.pcode.utils.Utils; -import ghidra.program.model.address.*; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; import ghidra.program.model.lang.Language; import ghidra.program.model.lang.Register; import ghidra.program.model.listing.Program; @@ -117,31 +118,13 @@ public abstract class AbstractUnwoundFrame implements UnwoundFrame { * @param the evaluation result type */ protected abstract class FrameVarnodeEvaluator extends ArithmeticFrameVarnodeEvaluator { - private final AddressSetView symbolStorage; - /** - * Construct an evaluator with the given arithmetic and symbol storage - * - *

- * Varnodes contained completely in symbol storage are presumed to be the inputs of the - * evaluation. All other varnodes are evaluated by examining their defining p-code op. It is - * an error to include any unique space in symbol storage. + * Construct an evaluator with the given arithmetic * * @param arithmetic the arithmetic for evaluating p-code ops - * @param symbolStorage the address ranges to regard as input, i.e., the leaves of evalution */ - public FrameVarnodeEvaluator(PcodeArithmetic arithmetic, AddressSetView symbolStorage) { + public FrameVarnodeEvaluator(PcodeArithmetic arithmetic) { super(arithmetic); - this.symbolStorage = symbolStorage; - } - - @Override - protected boolean isLeaf(Varnode vn) { - if (vn.getDef() == null && (vn.isRegister() || vn.isAddress())) { - return true; - } - return vn.isConstant() || - symbolStorage.contains(vn.getAddress(), vn.getAddress().add(vn.getSize() - 1)); } } @@ -297,9 +280,9 @@ public abstract class AbstractUnwoundFrame implements UnwoundFrame { Reason.INSPECT); } - protected FrameVarnodeEvaluator newEvaluator(AddressSetView symbolStorage) { + protected FrameVarnodeEvaluator newEvaluator() { SavedRegisterMap registerMap = computeRegisterMap(); - return new FrameVarnodeEvaluator<>(state.getArithmetic(), symbolStorage) { + return new FrameVarnodeEvaluator<>(state.getArithmetic()) { @Override protected T evaluateMemory(Address address, int size) { return registerMap.getVar(state, address, size, Reason.INSPECT); @@ -308,18 +291,18 @@ public abstract class AbstractUnwoundFrame implements UnwoundFrame { } @Override - public T evaluate(Program program, VariableStorage storage, AddressSetView symbolStorage) { - return newEvaluator(symbolStorage).evaluateStorage(program, storage); + public T evaluate(Program program, VariableStorage storage) { + return newEvaluator().evaluateStorage(program, storage); } @Override - public T evaluate(Program program, Varnode varnode, AddressSetView symbolStorage) { - return newEvaluator(symbolStorage).evaluateVarnode(program, varnode); + public T evaluate(Program program, Varnode varnode) { + return newEvaluator().evaluateVarnode(program, varnode); } @Override - public T evaluate(Program program, PcodeOp op, AddressSetView symbolStorage) { - return newEvaluator(symbolStorage).evaluateOp(program, op); + public T evaluate(Program program, PcodeOp op) { + return newEvaluator().evaluateOp(program, op); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java index 66e94ed140..742b7b623f 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/AnalysisUnwoundFrame.java @@ -21,10 +21,10 @@ import java.util.stream.Collectors; import ghidra.app.plugin.core.bookmark.BookmarkNavigator; import ghidra.app.services.DebuggerControlService.StateEditor; -import ghidra.app.services.DebuggerStaticMappingService; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.framework.plugintool.PluginTool; import ghidra.pcode.exec.BytesPcodeArithmetic; +import ghidra.pcode.exec.DebuggerPcodeUtils.WatchValue; import ghidra.pcode.exec.PcodeExecutorState; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRangeImpl; @@ -47,6 +47,8 @@ import ghidra.util.task.TaskMonitor; *

* The typical pattern for invoking analysis to unwind an entire stack is to use * {@link StackUnwinder#getFrames(DebuggerCoordinates, TaskMonitor)}. + * + * @param the type of values retrievable from the unwound frame */ public class AnalysisUnwoundFrame extends AbstractUnwoundFrame { @@ -105,16 +107,13 @@ public class AnalysisUnwoundFrame extends AbstractUnwoundFrame { /** * Unwind the next frame up - * *

* Unwind the frame that would become current if the function that allocated this frame were to * return. For example, if this frame is at level 3, {@code unwindNext} will attempt to unwind * the frame at level 4. - * *

* The program counter and stack pointer for the next frame are computed using the state - * originally given in - * {@link StackUnwinder#start(DebuggerCoordinates, PcodeExecutorState, TaskMonitor)} and this + * originally given in {@link StackUnwinder#start(DebuggerCoordinates, TaskMonitor)} and this * frame's unwind information. The state is usually the watch-value state bound to the starting * coordinates. The program counter is evaluated like any other variable. The stack pointer is * computed by removing the depth of this frame. Then registers are restored and unwinding @@ -125,13 +124,12 @@ public class AnalysisUnwoundFrame extends AbstractUnwoundFrame { * @throws CancelledException if the monitor is cancelled * @throws UnwindException if unwinding fails */ - public AnalysisUnwoundFrame unwindNext(TaskMonitor monitor) + public AnalysisUnwoundFrame unwindNext(TaskMonitor monitor) throws CancelledException { if (info == null || info.ofReturn() == null) { throw new NoSuchElementException(); } - return (AnalysisUnwoundFrame) unwinder.getFrame(coordinates, state, level + 1, null, - monitor); + return unwinder.getFrame(coordinates, state, level + 1, null, monitor); } @Override @@ -192,6 +190,11 @@ public class AnalysisUnwoundFrame extends AbstractUnwoundFrame { return pcVal; } + @Override + public Address getStaticCounter() { + return staticPcVal; + } + public Address getStackPointer() { return spVal; } @@ -280,18 +283,15 @@ public class AnalysisUnwoundFrame extends AbstractUnwoundFrame { * may be placed a little after the derived stack pointer to accommodate the parameters of an * inner stack frame. The structure data type will have the category path * {@link StackUnwinder#FRAMES_PATH}. This allows follow-on analysis to identify data units - * representing unwound frames. See {@link #isFrame(TraceData)}. + * representing unwound frames. *

  • Places a comment at the start of the frame. This is meant for human consumption, so * follow-on analysis should not attempt to parse or otherwise interpret it. It will indicate * the frame level (0 being the innermost), the function name, the program counter, the stack * pointer, and the frame base pointer.
  • *
  • Places a {@link RefType#DATA} reference from the frame start to its own base address. - * This permits follow-on analysis to derive variable values stored on the stack. See - * {@link #getBase(TraceData)} and {@link #getValue(TraceData, VariableStorage)}.
  • + * This permits follow-on analysis to derive variable values stored on the stack. *
  • Places a {@link RefType#DATA} reference from the program counter to the frame start. This - * allows follow-on analysis to determine the function for the frame. See - * {@link #getProgramCounter(TraceData)} and - * {@link #getFunction(TraceData, DebuggerStaticMappingService)}.
  • + * allows follow-on analysis to determine the function for the frame. * * *

    diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/FakeUnwoundFrame.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/FakeUnwoundFrame.java index 4a416d294d..50a82c9c21 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/FakeUnwoundFrame.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/FakeUnwoundFrame.java @@ -4,9 +4,9 @@ * 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. @@ -32,6 +32,8 @@ import ghidra.program.model.listing.Function; * of a given stack frame. Based on an inspection of a variable's storage, it may not be necessary * to attempt a stack unwind to evaluate it. If that is the case, this "frame" may be used to * evaluate it where a frame interface is expected or convenient. + * + * @param the type of values retrievable from the unwound frame */ public class FakeUnwoundFrame extends AbstractUnwoundFrame { private static final SavedRegisterMap IDENTITY_MAP = new SavedRegisterMap(); @@ -71,6 +73,11 @@ public class FakeUnwoundFrame extends AbstractUnwoundFrame { return null; } + @Override + public Address getStaticCounter() { + return null; + } + @Override public Function getFunction() { return null; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/ListingUnwoundFrame.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/ListingUnwoundFrame.java index d820e3f8ad..3994d325e4 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/ListingUnwoundFrame.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/ListingUnwoundFrame.java @@ -115,6 +115,7 @@ public class ListingUnwoundFrame extends AbstractUnwoundFrame { private final int level; private final Address pcVal; + private final Address staticPcVal; private final Function function; private final Address base; @@ -139,6 +140,7 @@ public class ListingUnwoundFrame extends AbstractUnwoundFrame { this.level = loadLevel(); this.pcVal = loadProgramCounter(); this.function = loadFunction(); + this.staticPcVal = mapProgramCounter(); this.base = loadBasePointer(); } @@ -182,6 +184,15 @@ public class ListingUnwoundFrame extends AbstractUnwoundFrame { throw new UnwindException("The program counter reference is missing for the frame!"); } + private Address mapProgramCounter() { + ProgramLocation location = mappingService.getOpenMappedLocation( + new DefaultTraceLocation(trace, null, Lifespan.at(snap), pcVal)); + if (location.getProgram() != function.getProgram()) { + return null; + } + return location.getByteAddress(); + } + private Function loadFunction() { ProgramLocation staticLoc = mappingService.getOpenMappedLocation(new DefaultTraceLocation(frame.getTrace(), null, @@ -226,6 +237,11 @@ public class ListingUnwoundFrame extends AbstractUnwoundFrame { return pcVal; } + @Override + public Address getStaticCounter() { + return staticPcVal; + } + @Override public Function getFunction() { return function; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwindWarning.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwindWarning.java index 5aef0e094c..07bf9fc86b 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwindWarning.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/StackUnwindWarning.java @@ -4,9 +4,9 @@ * 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. @@ -74,6 +74,8 @@ public interface StackUnwindWarning { /** * The unwind analyzer could not find an exit path from the frame's program counter. + * + * @param pc the program counter */ public record NoReturnPathStackUnwindWarning(Address pc) implements StackUnwindWarning { @Override @@ -88,7 +90,10 @@ public interface StackUnwindWarning { } /** - * The unwind analyzer discovered at last one exit path, but none could be analyzed. + * The unwind analyzer discovered at least one exit path, but none could be analyzed. + * + * @param pc the program counter + * @param last the error from the last attempt */ public record OpaqueReturnPathStackUnwindWarning(Address pc, Exception last) implements StackUnwindWarning { @@ -107,11 +112,12 @@ public interface StackUnwindWarning { /** * While analyzing instructions, the unwind analyzer encountered a call to a function whose * effect on the stack is unknown. - * *

    * The analyzer does not descend into calls or otherwise implement inter-procedural analysis. * Instead, it relies on analysis already performed by Ghidra's other analyzers and/or the human * user. The analyzer will assume a reasonable default. + * + * @param function the target function of the encountered call */ public record UnknownPurgeStackUnwindWarning(Function function) implements StackUnwindWarning, Combinable { @@ -137,9 +143,10 @@ public interface StackUnwindWarning { /** * While analyzing instructions, the unwind analyzer encountered a call to a function whose * convention is not known. - * *

    * The analyzer will assume the default convention for the program's compiler. + * + * @param function the target function of the encountered call */ public record UnspecifiedConventionStackUnwindWarning(Function function) implements StackUnwindWarning, Combinable { @@ -165,10 +172,11 @@ public interface StackUnwindWarning { /** * While analyzing an indirect call, using the decompiler, the unwind analyzer obtained multiple * high {@link PcodeOp#CALL} or {@link PcodeOp#CALLIND} p-code ops. - * *

    * Perhaps this should be replaced by an assertion, but failing fast may not be a good approach * for this case. + * + * @param found the list of candidate call[ind] ops */ public record MultipleHighCallsStackUnwindWarning(List found) implements StackUnwindWarning { @@ -180,6 +188,8 @@ public interface StackUnwindWarning { /** * Similar to {@link MultipleHighCallsStackUnwindWarning}, except no high call p-code ops. + * + * @param op the op which had no corresponding high call[ind] after decompilation */ public record NoHighCallsStackUnwindWarning(PcodeOp op) implements StackUnwindWarning { @Override @@ -190,6 +200,8 @@ public interface StackUnwindWarning { /** * While analyzing an indirect call, the target's type was not a function pointer. + * + * @param type the actual type found */ public record UnexpectedTargetTypeStackUnwindWarning(DataType type) implements StackUnwindWarning { @@ -202,6 +214,8 @@ public interface StackUnwindWarning { /** * While analyzing an indirect call, couldn't get the function signature because its input * doesn't have a high variable. + * + * @param vn the varnode expected to identify the callee, but that had no high variable */ public record NoHighVariableFromTargetPointerTypeUnwindWarning(VarnodeAST vn) implements StackUnwindWarning { @@ -213,6 +227,8 @@ public interface StackUnwindWarning { /** * While analyzing an indirect call, the signature could not be derived from call-site context. + * + * @param op the indirect call op */ public record CouldNotRecoverSignatureStackUnwindWarning(PcodeOpAST op) implements StackUnwindWarning { @@ -225,6 +241,8 @@ public interface StackUnwindWarning { /** * A custom warning, either because a specific type is too onerous, or because the message was * deserialized and the specific type and info cannot be recovered. + * + * @param message the message */ public record CustomStackUnwindWarning(String message) implements StackUnwindWarning { @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/Sym.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/Sym.java index 80a939a483..706df5c79f 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/Sym.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/Sym.java @@ -22,14 +22,12 @@ import ghidra.program.model.lang.Register; /** * A symbolic value tailored for stack unwind analysis - * *

    * The goals of stack unwind analysis are 1) to figure the stack depth at a particular instruction, * 2) to figure the locations of saved registers on the stack, 3) to figure the location of the * return address, whether in a register or on the stack, and 4) to figure the change in stack depth * from calling the function. Not surprisingly, these are the fields of {@link UnwindInfo}. To these * ends, symbols may have only one of the following forms: - * *

      *
    • An opaque value: {@link OpaqueSym}, to represent expressions too complex.
    • *
    • A constant: {@link ConstSym}, to fold constants and use as offsets.
    • @@ -39,10 +37,8 @@ import ghidra.program.model.lang.Register; *
    • A dereference of a stack offset, i.e., *(SP + c): {@link StackDerefSym}, to detect restored * registers and return address location
    • *
    - * *

    * The rules are fairly straightforward: - * *

      *
    • a:Opaque + b:Any => Opaque()
    • *
    • a:Const + b:Const => Const(val=a.val + b.val)
    • @@ -51,7 +47,6 @@ import ghidra.program.model.lang.Register; *
    • *a:Offset => Deref(offset=a.offset)
    • *
    • *a:Register(reg==SP) => Deref(offset=0)
    • *
    - * *

    * Some minute operations are omitted for clarity. Any other operation results in Opaque(). There is * a small fault in that Register(reg=SP) and Offset(offset=0) represent the same thing, but with @@ -126,7 +121,6 @@ sealed interface Sym { /** * When this symbol is used as the offset in a given address space, translate it to the address * if possible - * *

    * The address will be used by the state to retrieve the appropriate (symbolic) value, possibly * generating a fresh symbol. If the address is {@link Address#NO_ADDRESS}, then the state will @@ -176,6 +170,9 @@ sealed interface Sym { /** * A constant symbol + * + * @param value the constant value + * @param size the size in bytes */ public record ConstSym(long value, int size) implements Sym { @Override @@ -220,6 +217,9 @@ sealed interface Sym { /** * A register symbol + * + * @param register the register + * @param mask a mask that has been applied (bitwise and) to the register */ public record RegisterSym(Register register, long mask) implements Sym { @Override @@ -266,10 +266,11 @@ sealed interface Sym { /** * A stack offset symbol - * *

    * This represents a value in the form SP + c, where SP is the stack pointer register and c is a * constant. + * + * @param offset the offset from SP (at entry) */ public record StackOffsetSym(long offset) implements Sym { @Override @@ -309,10 +310,13 @@ sealed interface Sym { /** * A stack dereference symbol - * *

    * This represents a dereferenced {@link StackOffsetSym} (or the dereferenced stack pointer * register, in which is treated as a stack offset of 0). + * + * @param offset the offset from SP (at entry) + * @param mask a mask that has been applied (bitwise and) to the stack variable + * @param size the size of the variable in bytes */ public record StackDerefSym(long offset, long mask, int size) implements Sym { @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java index cc61556d29..d29e7b653a 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/SymPcodeExecutor.java @@ -62,7 +62,6 @@ public class SymPcodeExecutor extends PcodeExecutor { * @param program the program to analyze * @param state the symbolic state * @param reason a reason to give when reading state - * @param warnings a place to emit warnings * @param monitor a monitor for analysis, usually decompilation * @return the executor */ diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/UnwoundFrame.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/UnwoundFrame.java index f8d265d609..a0683a1605 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/UnwoundFrame.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/UnwoundFrame.java @@ -18,11 +18,8 @@ package ghidra.app.plugin.core.debug.stack; import java.math.BigInteger; import java.util.concurrent.CompletableFuture; -import ghidra.app.decompiler.ClangLine; -import ghidra.app.plugin.core.debug.gui.stack.vars.VariableValueUtils; import ghidra.app.services.DebuggerControlService.StateEditor; import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSetView; import ghidra.program.model.lang.Register; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.PcodeOp; @@ -74,6 +71,16 @@ public interface UnwoundFrame { */ Address getProgramCounter(); + /** + * Get the frame's program counter in the static program + *

    + * To get the corresponding static program, use {@link #getFunction()}. + * + * @see #getProgramCounter() + * @return the frame's static program counter + */ + Address getStaticCounter(); + /** * Get the function that allocated this frame * @@ -154,8 +161,7 @@ public interface UnwoundFrame { * *

    * Each varnode's value is simply retrieved from the state, in contrast to - * {@link #evaluate(Program, VariableStorage, AddressSetView)}, which ascends to varnodes' - * defining p-code ops. + * {@link #evaluate(Program, VariableStorage)}, which ascends to varnodes' defining p-code ops. * *

    * WARNING: Never invoke this method from the Swing thread. The state could be associated @@ -195,57 +201,44 @@ public interface UnwoundFrame { T getValue(Register register); /** - * Evaluate the given storage, following defining p-code ops until symbol storage is reached - * + * Evaluate the given storage, following defining p-code ops of unique varnodes *

    * This behaves similarly to {@link #getValue(Program, VariableStorage)}, except this one will - * ascend recursively to each varnode's defining p-code op. The recursion terminates when a - * varnode is contained in the given symbol storage. The symbol storage is usually collected by - * examining the tokens on the same line, searching for ones that represent "high symbols." This - * ensures that any temporary storage used by the original program in the evaluation of, e.g., a - * field access, are not read from the current state but re-evaluated in terms of the symbols' - * current values. - * + * ascend recursively to each unique varnode's defining p-code op. *

    * WARNING: Never invoke this method from the Swing thread. The state could be associated * with a live session, and this may block to retrieve live state. * - * @see VariableValueUtils#collectSymbolStorage(ClangLine) * @param program the program containing the variable storage * @param storage the storage to evaluate - * @param symbolStorage the terminal storage, usually that of symbols * @return the value */ - T evaluate(Program program, VariableStorage storage, AddressSetView symbolStorage); + T evaluate(Program program, VariableStorage storage); /** - * Evaluate the given varnode, following defining p-code ops until symbol storage is reached - * + * Evaluate the given varnode, following defining p-code ops of unique varnodes *

    * WARNING: Never invoke this method from the Swing thread. The state could be associated * with a live session, and this may block to retrieve live state. * * @param program the program containing the varnode * @param varnode the varnode - * @param symbolStorage the terminal storage, usually that of symbols * @return the value */ - T evaluate(Program program, Varnode varnode, AddressSetView symbolStorage); + T evaluate(Program program, Varnode varnode); /** - * Evaluate the output for the given p-code op, ascending until symbol storage is reached - * + * Evaluate the output for the given p-code op, following defining p-code ops of unique varnodes *

    * WARNING: Never invoke this method from the Swing thread. The state could be associated * with a live session, and this may block to retrieve live state. * - * @see #evaluate(Program, VariableStorage, AddressSetView) + * @see #evaluate(Program, VariableStorage) * @param program the program containing the op * @param op the op - * @param symbolStorage the terminal storage, usually that of symbols * @return the value */ - T evaluate(Program program, PcodeOp op, AddressSetView symbolStorage); + T evaluate(Program program, PcodeOp op); /** * Set the value of the given storage diff --git a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/eval/AbstractVarnodeEvaluator.java b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/eval/AbstractVarnodeEvaluator.java index 011b18a475..eae54015e8 100644 --- a/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/eval/AbstractVarnodeEvaluator.java +++ b/Ghidra/Debug/ProposedUtils/src/main/java/ghidra/pcode/eval/AbstractVarnodeEvaluator.java @@ -68,7 +68,9 @@ public abstract class AbstractVarnodeEvaluator implements VarnodeEvaluator * @return true to treat the varnode as a base case, or false to ascend to its defining p-code * op */ - protected abstract boolean isLeaf(Varnode vn); + protected boolean isLeaf(Varnode vn) { + return !vn.isUnique(); + } /** * Resolve a (static) stack offset to its physical (dynamic) address in the frame @@ -163,7 +165,7 @@ public abstract class AbstractVarnodeEvaluator implements VarnodeEvaluator * @return the value */ protected T evaluateVarnode(Program program, Varnode vn, Map already) { - // computeIfAbsent does nto work because of the recursion. Will get CME. + // computeIfAbsent does not work because of the recursion. Will get CME. if (already.containsKey(vn)) { return already.get(vn); } @@ -223,7 +225,7 @@ public abstract class AbstractVarnodeEvaluator implements VarnodeEvaluator * * @param value the constant value * @param size the size of the value in bytes - * @return the value as a {@link T} + * @return the value */ protected abstract T evaluateConstant(long value, int size); @@ -281,7 +283,7 @@ public abstract class AbstractVarnodeEvaluator implements VarnodeEvaluator } /** - * Evaluate a variable whose offset is of type {@link T} + * Evaluate a variable with an abstract offset * *

    * The three parameters {@code space}, {@code offset}, and {@code size} imitate the varnode