mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-4144: Many fixes, esp., for dbgeng Trace RMI.
This commit is contained in:
parent
1281fb979b
commit
a6549947ab
30 changed files with 1526 additions and 725 deletions
|
@ -23,6 +23,7 @@ import ghidra.rmi.trace.TraceRmi.*;
|
|||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
class OpenTrace implements ValueDecoder {
|
||||
final DoId doId;
|
||||
|
@ -84,19 +85,47 @@ class OpenTrace implements ValueDecoder {
|
|||
|
||||
@Override
|
||||
public Address toAddress(Addr addr, boolean required) {
|
||||
/**
|
||||
* Do not clamp here, like we do for ranges. The purpose of the given address is more
|
||||
* specific here. Plus, we're not just omitting some addresses (like we would for ranges),
|
||||
* we'd be moving the address. Thus, we'd be applying some attribute to a location that was
|
||||
* never intended.
|
||||
*/
|
||||
AddressSpace space = getSpace(addr.getSpace(), required);
|
||||
return space.getAddress(addr.getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange toRange(AddrRange range, boolean required)
|
||||
throws AddressOverflowException {
|
||||
public AddressRange toRange(AddrRange range, boolean required) {
|
||||
AddressSpace space = getSpace(range.getSpace(), required);
|
||||
if (space == null) {
|
||||
return null;
|
||||
}
|
||||
Address min = space.getAddress(range.getOffset());
|
||||
Address max = space.getAddress(range.getOffset() + range.getExtend());
|
||||
/**
|
||||
* Clamp to only the valid addresses, but do at least warn.
|
||||
*/
|
||||
long minOffset = range.getOffset();
|
||||
if (Long.compareUnsigned(minOffset, space.getMinAddress().getOffset()) < 0) {
|
||||
Msg.warn(this, "Range [%s:%x+%x] partially exceeds space min. Clamping."
|
||||
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
|
||||
minOffset = space.getMinAddress().getOffset();
|
||||
}
|
||||
else if (Long.compareUnsigned(minOffset, space.getMaxAddress().getOffset()) > 0) {
|
||||
throw new AddressOutOfBoundsException("Range [%s:%x+%x] entirely exceeds space max"
|
||||
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
|
||||
}
|
||||
long maxOffset = range.getOffset() + range.getExtend(); // Use the requested offset, not adjusted
|
||||
if (Long.compareUnsigned(maxOffset, space.getMaxAddress().getOffset()) > 0) {
|
||||
Msg.warn(this, "Range [%s:%x+%x] partially exceeds space max. Clamping."
|
||||
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
|
||||
maxOffset = space.getMaxAddress().getOffset();
|
||||
}
|
||||
else if (Long.compareUnsigned(maxOffset, space.getMinAddress().getOffset()) < 0) {
|
||||
throw new AddressOutOfBoundsException("Range [%s:%x+%x] entirely exceeds space min"
|
||||
.formatted(range.getSpace(), range.getOffset(), range.getExtend()));
|
||||
}
|
||||
Address min = space.getAddress(minOffset);
|
||||
Address max = space.getAddress(maxOffset);
|
||||
return new AddressRangeImpl(min, max);
|
||||
}
|
||||
|
||||
|
|
|
@ -503,22 +503,29 @@ public class TraceRmiHandler implements TraceRmiConnection {
|
|||
}
|
||||
|
||||
default String toString(RootMessage req) {
|
||||
return switch (req.getMsgCase()) {
|
||||
case REQUEST_ACTIVATE -> "activate(%d, %d, %s)".formatted(
|
||||
req.getRequestActivate().getOid().getId(),
|
||||
req.getRequestActivate().getObject().getId(),
|
||||
req.getRequestActivate().getObject().getPath().getPath());
|
||||
case REQUEST_END_TX -> "endTx(%d)".formatted(
|
||||
req.getRequestEndTx().getTxid().getId());
|
||||
case REQUEST_START_TX -> "startTx(%d,%s)".formatted(
|
||||
req.getRequestStartTx().getTxid().getId(),
|
||||
req.getRequestStartTx().getDescription());
|
||||
case REQUEST_SET_VALUE -> "setValue(%d,%s,%s)".formatted(
|
||||
req.getRequestSetValue().getValue().getParent().getId(),
|
||||
req.getRequestSetValue().getValue().getParent().getPath().getPath(),
|
||||
req.getRequestSetValue().getValue().getKey());
|
||||
default -> null;
|
||||
};
|
||||
try {
|
||||
return switch (req.getMsgCase()) {
|
||||
case REQUEST_ACTIVATE -> "activate(%d, %d, %s)".formatted(
|
||||
req.getRequestActivate().getOid().getId(),
|
||||
req.getRequestActivate().getObject().getId(),
|
||||
req.getRequestActivate().getObject().getPath().getPath());
|
||||
case REQUEST_END_TX -> "endTx(%d)".formatted(
|
||||
req.getRequestEndTx().getTxid().getId());
|
||||
case REQUEST_START_TX -> "startTx(%d,%s)".formatted(
|
||||
req.getRequestStartTx().getTxid().getId(),
|
||||
req.getRequestStartTx().getDescription());
|
||||
case REQUEST_SET_VALUE -> "setValue(%d,%s,%s,=%s)".formatted(
|
||||
req.getRequestSetValue().getValue().getParent().getId(),
|
||||
req.getRequestSetValue().getValue().getParent().getPath().getPath(),
|
||||
req.getRequestSetValue().getValue().getKey(),
|
||||
ValueDecoder.DISPLAY
|
||||
.toValue(req.getRequestSetValue().getValue().getValue()));
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return "ERROR toStringing request: " + e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
private final Trace trace;
|
||||
|
||||
private final Matches matches = new Matches();
|
||||
private final RequestCaches requestCaches = new RequestCaches();
|
||||
private final RequestCaches requestCaches = new DorkedRequestCaches();
|
||||
private final Set<TraceBreakpointKind> supportedBreakpointKinds;
|
||||
|
||||
public TraceRmiTarget(PluginTool tool, TraceRmiConnection connection, Trace trace) {
|
||||
|
@ -760,21 +760,41 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
}
|
||||
}
|
||||
|
||||
protected static class RequestCaches {
|
||||
interface RequestCaches {
|
||||
void invalidate();
|
||||
|
||||
void invalidateMemory();
|
||||
|
||||
CompletableFuture<Void> readBlock(Address min, RemoteMethod method,
|
||||
Map<String, Object> args);
|
||||
|
||||
CompletableFuture<Void> readRegs(TraceObject obj, RemoteMethod method,
|
||||
Map<String, Object> args);
|
||||
}
|
||||
|
||||
protected static class DefaultRequestCaches implements RequestCaches {
|
||||
final Map<TraceObject, CompletableFuture<Void>> readRegs = new HashMap<>();
|
||||
final Map<Address, CompletableFuture<Void>> readBlock = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public synchronized void invalidateMemory() {
|
||||
readBlock.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void invalidate() {
|
||||
readRegs.clear();
|
||||
readBlock.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized CompletableFuture<Void> readRegs(TraceObject obj, RemoteMethod method,
|
||||
Map<String, Object> args) {
|
||||
return readRegs.computeIfAbsent(obj,
|
||||
o -> method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized CompletableFuture<Void> readBlock(Address min, RemoteMethod method,
|
||||
Map<String, Object> args) {
|
||||
return readBlock.computeIfAbsent(min,
|
||||
|
@ -782,6 +802,28 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
}
|
||||
}
|
||||
|
||||
protected static class DorkedRequestCaches implements RequestCaches {
|
||||
@Override
|
||||
public void invalidate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateMemory() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> readBlock(Address min, RemoteMethod method,
|
||||
Map<String, Object> args) {
|
||||
return method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> readRegs(TraceObject obj, RemoteMethod method,
|
||||
Map<String, Object> args) {
|
||||
return method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> activateAsync(DebuggerCoordinates prev,
|
||||
DebuggerCoordinates coords) {
|
||||
|
@ -818,6 +860,7 @@ public class TraceRmiTarget extends AbstractTarget {
|
|||
|
||||
@Override
|
||||
public CompletableFuture<Void> invalidateMemoryCachesAsync() {
|
||||
requestCaches.invalidateMemory();
|
||||
return AsyncUtils.nil();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.debug.service.tracermi;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
|
@ -22,6 +25,44 @@ import ghidra.rmi.trace.TraceRmi.*;
|
|||
|
||||
public interface ValueDecoder {
|
||||
ValueDecoder DEFAULT = new ValueDecoder() {};
|
||||
ValueDecoder DISPLAY = new ValueDecoder() {
|
||||
final Map<String, AddressSpace> spaces = new HashMap<>();
|
||||
|
||||
private AddressSpace getSpace(String space) {
|
||||
return spaces.computeIfAbsent(space, name -> {
|
||||
return new GenericAddressSpace(name, 64, AddressSpace.TYPE_RAM, 0);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address toAddress(Addr addr, boolean required) {
|
||||
AddressSpace space = getSpace(addr.getSpace());
|
||||
return space.getAddress(addr.getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange toRange(AddrRange range, boolean required) {
|
||||
AddressSpace space = getSpace(range.getSpace());
|
||||
Address min = space.getAddress(range.getOffset());
|
||||
Address max = space.getAddress(range.getOffset() + range.getExtend());
|
||||
return new AddressRangeImpl(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(ObjDesc desc, boolean required) {
|
||||
return "<Object id=%d path=%s>".formatted(desc.getId(), desc.getPath().getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(ObjSpec spec, boolean required) {
|
||||
return switch (spec.getKeyCase()) {
|
||||
case KEY_NOT_SET -> "<ERROR: No key>";
|
||||
case ID -> "<Object id=%d>".formatted(spec.getId());
|
||||
case PATH -> "<Object path=%s>".formatted(spec.getPath());
|
||||
default -> "<ERROR: default>";
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
default Address toAddress(Addr addr, boolean required) {
|
||||
if (required) {
|
||||
|
@ -30,8 +71,7 @@ public interface ValueDecoder {
|
|||
return null;
|
||||
}
|
||||
|
||||
default AddressRange toRange(AddrRange range, boolean required)
|
||||
throws AddressOverflowException {
|
||||
default AddressRange toRange(AddrRange range, boolean required) {
|
||||
if (required) {
|
||||
throw new IllegalStateException("AddressRange requires a trace for context");
|
||||
}
|
||||
|
@ -52,7 +92,7 @@ public interface ValueDecoder {
|
|||
return null;
|
||||
}
|
||||
|
||||
default Object toValue(Value value) throws AddressOverflowException {
|
||||
default Object toValue(Value value) {
|
||||
return switch (value.getValueCase()) {
|
||||
case NULL_VALUE -> null;
|
||||
case BOOL_VALUE -> value.getBoolValue();
|
||||
|
|
|
@ -61,7 +61,7 @@ class Receiver(Thread):
|
|||
result = self.client._handle_invoke_method(request)
|
||||
Client._write_value(
|
||||
reply.xreply_invoke_method.return_value, result)
|
||||
except Exception as e:
|
||||
except BaseException as e:
|
||||
reply.xreply_invoke_method.error = ''.join(
|
||||
traceback.format_exc())
|
||||
self.client._send(reply)
|
||||
|
@ -79,7 +79,7 @@ class Receiver(Thread):
|
|||
result = request.handler(
|
||||
getattr(reply, request.field_name))
|
||||
request.set_result(result)
|
||||
except Exception as e:
|
||||
except BaseException as e:
|
||||
request.set_exception(e)
|
||||
|
||||
def _recv(self, field_name, handler):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue