diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java index e9327f9740..4e08f16528 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetProcessMemory.java @@ -62,41 +62,58 @@ public class GdbModelTargetProcessMemory this.inferior = inferior.inferior; } + protected CompletableFuture> defaultUsingAddressSize() { + return inferior.evaluate("sizeof(int*)").thenApply(sizeStr -> { + int size; + try { + size = Integer.parseInt(sizeStr); + } + catch (NumberFormatException e) { + throw new GdbCommandError("Couldn't determine address size: " + e); + } + + BigInteger start = BigInteger.ZERO; + BigInteger end = BigInteger.ONE.shiftLeft(size * 8); + if (size >= 0 && size < 8) { + GdbMemoryMapping mapping = new GdbMemoryMapping(start, end, + end.subtract(start), BigInteger.ZERO, "rwx", "default"); + return Map.of(start, mapping); + } + if (size == 8) { + // TODO: This split shouldn't be necessary. + BigInteger split = BigInteger.valueOf(Long.MAX_VALUE); + GdbMemoryMapping lowMapping = new GdbMemoryMapping(start, split, + split.subtract(start), BigInteger.ZERO, "rwx", "defaultLow"); + GdbMemoryMapping highMapping = new GdbMemoryMapping(split, end, + end.subtract(split), BigInteger.ZERO, "rwx", "defaultHigh"); + return Map.of(start, lowMapping, split, highMapping); + } + throw new GdbCommandError("Unexpected address size: " + size); + }); + } + protected void updateUsingMappings(Map byStart) { - List regions; synchronized (this) { - regions = - byStart.values().stream().map(this::getTargetRegion).collect(Collectors.toList()); - if (regions.isEmpty() && valid) { - Map defaultMap = - new HashMap(); - AddressSet addressSet = impl.getAddressFactory().getAddressSet(); - BigInteger start = addressSet.getMinAddress().getOffsetAsBigInteger(); - BigInteger end = addressSet.getMaxAddress().getOffsetAsBigInteger(); - if (end.longValue() < 0) { - BigInteger split = BigInteger.valueOf(Long.MAX_VALUE); - GdbMemoryMapping lmem = new GdbMemoryMapping(start, split, - split.subtract(start), start.subtract(start), "rwx", "defaultLow"); - defaultMap.put(start, lmem); - split = split.add(BigInteger.valueOf(1)); - GdbMemoryMapping hmem = new GdbMemoryMapping(split, end, - end.subtract(split), split.subtract(split), "rwx", "defaultHigh"); - defaultMap.put(split, hmem); - } - else { - GdbMemoryMapping mem = new GdbMemoryMapping(start, end, - end.subtract(start), start.subtract(start), "rwx", "default"); - defaultMap.put(start, mem); - } - regions = - defaultMap.values() - .stream() - .map(this::getTargetRegion) - .collect(Collectors.toList()); + if (!valid) { + setElements(List.of(), "Refreshed"); } } - - setElements(regions, "Refreshed"); + CompletableFuture> maybeDefault = + byStart.isEmpty() ? defaultUsingAddressSize() + : CompletableFuture.completedFuture(byStart); + maybeDefault.thenAccept(mappings -> { + List regions; + synchronized (this) { + regions = mappings.values() + .stream() + .map(this::getTargetRegion) + .collect(Collectors.toList()); + } + setElements(regions, "Refreshed"); + }).exceptionally(ex -> { + Msg.info(this, "Failed to update regions: " + ex); + return null; + }); } @Override diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMemoryMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMemoryMapper.java index 10b895eb51..52e54f39c3 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMemoryMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DebuggerMemoryMapper.java @@ -55,4 +55,13 @@ public interface DebuggerMemoryMapper { return new AddressRangeImpl(targetToTrace(targetRange.getMinAddress()), targetToTrace(targetRange.getMaxAddress())); } + + /** + * Map the given address range from the target process into the trace, truncating it to the + * portion intersecting the trace-side address space + * + * @param targetRange the range in the target's address space + * @return the intersection of the "same range" and the trace's address space + */ + AddressRange targetToTraceTruncated(AddressRange targetRange); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerMemoryMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerMemoryMapper.java index 3f5f58b950..d16d94ec27 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerMemoryMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/DefaultDebuggerMemoryMapper.java @@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.mapping; import ghidra.dbg.DebuggerObjectModel; import ghidra.program.model.address.*; import ghidra.program.model.lang.Language; +import ghidra.util.MathUtilities; public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper { protected final AddressFactory traceAddressFactory; @@ -50,13 +51,29 @@ public class DefaultDebuggerMemoryMapper implements DebuggerMemoryMapper { @Override public Address targetToTrace(Address targetAddr) { if (targetAddr == SpecialAddress.NO_ADDRESS) { - /** - * TODO: Allow NO_ADDRESS into the database? There will be a bit of fallout as the UI - * will have to accommodate the same possibility, esp., for go-to. - */ - return null; + return SpecialAddress.NO_ADDRESS; } assert isInFactory(targetAddr, targetAddressFactory); return toSameNamedSpace(targetAddr, traceAddressFactory); } + + @Override + public AddressRange targetToTraceTruncated(AddressRange targetRange) { + AddressSpace traceSpace = + traceAddressFactory.getAddressSpace(targetRange.getAddressSpace().getName()); + long maxTraceSpaceOffset = traceSpace.getMaxAddress().getOffset(); + Address targetMin = targetRange.getMinAddress(); + long minTargetOffset = targetMin.getOffset(); + if (Long.compareUnsigned(maxTraceSpaceOffset, minTargetOffset) < 0) { + return null; + } + Address traceMin = traceSpace.getAddress(minTargetOffset); + + Address targetMax = targetRange.getMaxAddress(); + long maxTargetOffset = targetMax.getOffset(); + Address traceMax = + traceSpace.getAddress(MathUtilities.unsignedMin(maxTraceSpaceOffset, maxTargetOffset)); + + return new AddressRangeImpl(traceMin, traceMax); + } } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/ObjectBasedDebuggerMemoryMapper.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/ObjectBasedDebuggerMemoryMapper.java index 6cefd90d21..33ff7e4cc8 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/ObjectBasedDebuggerMemoryMapper.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/mapping/ObjectBasedDebuggerMemoryMapper.java @@ -70,6 +70,12 @@ public class ObjectBasedDebuggerMemoryMapper implements DebuggerMemoryMapper { return traceSpace.getAddress(targetAddr.getOffset()); } + @Override + public AddressRange targetToTraceTruncated(AddressRange targetRange) { + // the DATA space can always accommodate all 64 bits + return targetToTrace(targetRange); + } + protected AddressSpace createSpace(String name) { try (UndoableTransaction tid = UndoableTransaction.start(trace, "Create space for mapping")) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DefaultMemoryRecorder.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DefaultMemoryRecorder.java index d433f4dcd2..3d1adc29b4 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DefaultMemoryRecorder.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DefaultMemoryRecorder.java @@ -24,8 +24,7 @@ import ghidra.app.plugin.core.debug.service.model.interfaces.ManagedMemoryRecord import ghidra.app.plugin.core.debug.service.model.record.RecorderUtils; import ghidra.dbg.target.TargetMemory; import ghidra.dbg.target.TargetMemoryRegion; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.address.*; import ghidra.trace.model.Trace; import ghidra.trace.model.memory.*; import ghidra.util.Msg; @@ -72,8 +71,16 @@ public class DefaultMemoryRecorder implements ManagedMemoryRecorder { Msg.warn(this, "Region " + path + " already recorded"); return; } - traceRegion = memoryManager.addRegion(path, Range.atLeast(snap), - recorder.getMemoryMapper().targetToTrace(region.getRange()), + AddressRange traceRange = + recorder.getMemoryMapper().targetToTraceTruncated(region.getRange()); + if (traceRange == null) { + Msg.warn(this, "Dropped unmappable region: " + region); + return; + } + if (region.getRange().getLength() != traceRange.getLength()) { + Msg.warn(this, "Truncated region: " + region); + } + traceRegion = memoryManager.addRegion(path, Range.atLeast(snap), traceRange, getTraceFlags(region)); traceRegion.setName(region.getDisplay()); } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderComposedMemory.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderComposedMemory.java index f66ae9ff08..2ec9b0c743 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderComposedMemory.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderComposedMemory.java @@ -82,7 +82,11 @@ public class RecorderComposedMemory implements AbstractRecorderMemory { if (acc == null || !acc.getAllAccessibility()) { continue; } - accessible.add(memMapper.targetToTrace(ent.getKey().getRange())); + AddressRange traceRange = memMapper.targetToTraceTruncated(ent.getKey().getRange()); + if (traceRange == null) { + continue; + } + accessible.add(traceRange); } return accessible; } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderSimpleMemory.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderSimpleMemory.java index 7824771103..65022f0a70 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderSimpleMemory.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/RecorderSimpleMemory.java @@ -105,7 +105,11 @@ public class RecorderSimpleMemory implements AbstractRecorderMemory { AddressSet accessible = new AddressSet(); if (memMapper != null) { for (Entry ent : byMin.entrySet()) { - accessible.add(memMapper.targetToTrace(ent.getValue().getRange())); + AddressRange traceRange = + memMapper.targetToTraceTruncated(ent.getValue().getRange()); + if (traceRange != null) { + accessible.add(traceRange); + } } } return accessible;