mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-31 18:38:11 +08:00
GP-2089: Fix gdb manager and model to use new memory flags for gdb-12.1
This commit is contained in:
+53
-17
@@ -40,12 +40,22 @@ import ghidra.util.Msg;
|
|||||||
* The implementation of {@link GdbInferior}
|
* The implementation of {@link GdbInferior}
|
||||||
*/
|
*/
|
||||||
public class GdbInferiorImpl implements GdbInferior {
|
public class GdbInferiorImpl implements GdbInferior {
|
||||||
protected static final Pattern MEMORY_MAPPING_LINE_PATTERN = Pattern.compile("\\s*" + //
|
protected static final Pattern MEMORY_MAPPING_WOUT_FLAGS_LINE_PATTERN =
|
||||||
"0x(?<start>[0-9,A-F,a-f]+)\\s+" + //
|
Pattern.compile("\\s*" +
|
||||||
"0x(?<end>[0-9,A-F,a-f]+)\\s+" + //
|
"0x(?<start>[0-9,A-F,a-f]+)\\s+" +
|
||||||
"0x(?<size>[0-9,A-F,a-f]+)\\s+" + //
|
"0x(?<end>[0-9,A-F,a-f]+)\\s+" +
|
||||||
"0x(?<offset>[0-9,A-F,a-f]+)\\s*" + //
|
"0x(?<size>[0-9,A-F,a-f]+)\\s+" +
|
||||||
"(?<file>\\S*)\\s*");
|
"0x(?<offset>[0-9,A-F,a-f]+)\\s+" +
|
||||||
|
"(?<file>\\S*)\\s*");
|
||||||
|
|
||||||
|
protected static final Pattern MEMORY_MAPPING_LINE_PATTERN =
|
||||||
|
Pattern.compile("\\s*" + //
|
||||||
|
"0x(?<start>[0-9,A-F,a-f]+)\\s+" +
|
||||||
|
"0x(?<end>[0-9,A-F,a-f]+)\\s+" +
|
||||||
|
"0x(?<size>[0-9,A-F,a-f]+)\\s+" +
|
||||||
|
"0x(?<offset>[0-9,A-F,a-f]+)\\s+" +
|
||||||
|
"(?<flags>[rwsxp\\-]+)\\s+" +
|
||||||
|
"(?<file>\\S*)\\s*");
|
||||||
|
|
||||||
private final GdbManagerImpl manager;
|
private final GdbManagerImpl manager;
|
||||||
private final int id;
|
private final int id;
|
||||||
@@ -367,21 +377,47 @@ public class GdbInferiorImpl implements GdbInferior {
|
|||||||
.thenApply(this::parseMappings);
|
.thenApply(this::parseMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected GdbMemoryMapping parseMappingLine(String line) throws NumberFormatException {
|
||||||
|
Matcher mappingMatcher = MEMORY_MAPPING_LINE_PATTERN.matcher(line);
|
||||||
|
if (!mappingMatcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BigInteger start = new BigInteger(mappingMatcher.group("start"), 16);
|
||||||
|
BigInteger end = new BigInteger(mappingMatcher.group("end"), 16);
|
||||||
|
BigInteger size = new BigInteger(mappingMatcher.group("size"), 16);
|
||||||
|
BigInteger offset = new BigInteger(mappingMatcher.group("offset"), 16);
|
||||||
|
String flags = mappingMatcher.group("flags");
|
||||||
|
String objfile = mappingMatcher.group("file");
|
||||||
|
return new GdbMemoryMapping(start, end, size, offset, flags, objfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GdbMemoryMapping parseMappingsLineWOutFlags(String line)
|
||||||
|
throws NumberFormatException {
|
||||||
|
Matcher mappingMatcher = MEMORY_MAPPING_WOUT_FLAGS_LINE_PATTERN.matcher(line);
|
||||||
|
if (!mappingMatcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BigInteger start = new BigInteger(mappingMatcher.group("start"), 16);
|
||||||
|
BigInteger end = new BigInteger(mappingMatcher.group("end"), 16);
|
||||||
|
BigInteger size = new BigInteger(mappingMatcher.group("size"), 16);
|
||||||
|
BigInteger offset = new BigInteger(mappingMatcher.group("offset"), 16);
|
||||||
|
String objfile = mappingMatcher.group("file");
|
||||||
|
return new GdbMemoryMapping(start, end, size, offset, "rwx", objfile);
|
||||||
|
}
|
||||||
|
|
||||||
protected Map<BigInteger, GdbMemoryMapping> parseMappings(String out) {
|
protected Map<BigInteger, GdbMemoryMapping> parseMappings(String out) {
|
||||||
Set<BigInteger> startsSeen = new TreeSet<>();
|
Set<BigInteger> startsSeen = new TreeSet<>();
|
||||||
for (String line : out.split("\n")) {
|
for (String line : out.split("\n")) {
|
||||||
Matcher mappingMatcher = MEMORY_MAPPING_LINE_PATTERN.matcher(line);
|
|
||||||
if (!mappingMatcher.matches()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
BigInteger start = new BigInteger(mappingMatcher.group("start"), 16);
|
GdbMemoryMapping mapping = parseMappingLine(line);
|
||||||
BigInteger end = new BigInteger(mappingMatcher.group("end"), 16);
|
if (mapping == null) {
|
||||||
BigInteger size = new BigInteger(mappingMatcher.group("size"), 16);
|
mapping = parseMappingsLineWOutFlags(line);
|
||||||
BigInteger offset = new BigInteger(mappingMatcher.group("offset"), 16);
|
}
|
||||||
String objfile = mappingMatcher.group("file");
|
if (mapping == null) { // still, so it matches neither pattern
|
||||||
startsSeen.add(start);
|
continue; // It's just a throw-away line, or the format changed again.
|
||||||
mappings.put(start, new GdbMemoryMapping(start, end, size, offset, objfile));
|
}
|
||||||
|
startsSeen.add(mapping.getStart());
|
||||||
|
mappings.put(mapping.getStart(), mapping);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
Msg.error(this, "Could not parse mapping entry: " + line, e);
|
Msg.error(this, "Could not parse mapping entry: " + line, e);
|
||||||
|
|||||||
+57
-1
@@ -18,40 +18,96 @@ package agent.gdb.manager.impl;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The abstraction for a line of output from {@code info proc mappings}
|
||||||
|
*/
|
||||||
public class GdbMemoryMapping {
|
public class GdbMemoryMapping {
|
||||||
private final BigInteger start;
|
private final BigInteger start;
|
||||||
private final BigInteger end;
|
private final BigInteger end;
|
||||||
private final BigInteger size;
|
private final BigInteger size;
|
||||||
private final BigInteger offset;
|
private final BigInteger offset;
|
||||||
|
private final String flags;
|
||||||
private final String objfile;
|
private final String objfile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a memory mapping
|
||||||
|
*
|
||||||
|
* @param start the start offset
|
||||||
|
* @param end the end offset
|
||||||
|
* @param size the size (must be end - start)
|
||||||
|
* @param offset if backed by a file, the offset into that file
|
||||||
|
* @param flags the flags: rwxsp for read, write, execute, shared, private. If not known, the
|
||||||
|
* default "rwx" should be used.
|
||||||
|
* @param objfile if backed by a file, the name of that file
|
||||||
|
*/
|
||||||
public GdbMemoryMapping(BigInteger start, BigInteger end, BigInteger size, BigInteger offset,
|
public GdbMemoryMapping(BigInteger start, BigInteger end, BigInteger size, BigInteger offset,
|
||||||
String objfile) {
|
String flags, String objfile) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.size = size;
|
this.size = size;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
|
this.flags = flags;
|
||||||
this.objfile = objfile;
|
this.objfile = objfile;
|
||||||
|
|
||||||
assert Objects.equals(start.add(size), end);
|
assert Objects.equals(start.add(size), end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start offset
|
||||||
|
*
|
||||||
|
* @return the offset
|
||||||
|
*/
|
||||||
public BigInteger getStart() {
|
public BigInteger getStart() {
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end offset
|
||||||
|
*
|
||||||
|
* @return the end
|
||||||
|
*/
|
||||||
public BigInteger getEnd() {
|
public BigInteger getEnd() {
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size
|
||||||
|
*
|
||||||
|
* @return the size
|
||||||
|
*/
|
||||||
public BigInteger getSize() {
|
public BigInteger getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If backed by a file, the offset into that file
|
||||||
|
*
|
||||||
|
* @return the offset
|
||||||
|
*/
|
||||||
public BigInteger getOffset() {
|
public BigInteger getOffset() {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flags
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* As of gdb-12.1, this is a four-character string, e.g., r--p, where the first three indicate
|
||||||
|
* <b>r</b>ead, <b>w</b>rite, and e<b>x</b>ecute. Each position is either the character
|
||||||
|
* indicating the flag is present, or a dash indicating the flag is absent. The final position
|
||||||
|
* is either {@code s} or {@code p} to indicate shared or private.
|
||||||
|
*
|
||||||
|
* @return the flags
|
||||||
|
*/
|
||||||
|
public String getFlags() {
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If backed by a file, the name of that file
|
||||||
|
*
|
||||||
|
* @return the file
|
||||||
|
*/
|
||||||
public String getObjfile() {
|
public String getObjfile() {
|
||||||
return objfile;
|
return objfile;
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -59,6 +59,7 @@ public class GdbModelTargetMemoryRegion
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected AddressRangeImpl range;
|
protected AddressRangeImpl range;
|
||||||
|
protected final String flags;
|
||||||
protected final String objfile;
|
protected final String objfile;
|
||||||
protected final long offset;
|
protected final long offset;
|
||||||
protected final String display;
|
protected final String display;
|
||||||
@@ -76,6 +77,7 @@ public class GdbModelTargetMemoryRegion
|
|||||||
catch (AddressFormatException | AddressOverflowException e) {
|
catch (AddressFormatException | AddressOverflowException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
this.flags = mapping.getFlags();
|
||||||
changeAttributes(List.of(), Map.of( //
|
changeAttributes(List.of(), Map.of( //
|
||||||
MEMORY_ATTRIBUTE_NAME, memory, //
|
MEMORY_ATTRIBUTE_NAME, memory, //
|
||||||
RANGE_ATTRIBUTE_NAME, range, //
|
RANGE_ATTRIBUTE_NAME, range, //
|
||||||
@@ -111,19 +113,20 @@ public class GdbModelTargetMemoryRegion
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isReadable() {
|
public boolean isReadable() {
|
||||||
// It can be done if debugging locally on Linux, by reading /proc/[PID]/maps
|
// This was added at or near gdb-12.1
|
||||||
// The sections listing will give the initial protections.
|
return flags.contains("r");
|
||||||
return true; // TODO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWritable() {
|
public boolean isWritable() {
|
||||||
return true; // TODO
|
// This was added at or near gdb-12.1
|
||||||
|
return flags.contains("w");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isExecutable() {
|
public boolean isExecutable() {
|
||||||
return true; // TODO
|
// This was added at or near gdb-12.1
|
||||||
|
return flags.contains("x");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetAttributeType(
|
@TargetAttributeType(
|
||||||
|
|||||||
+4
-4
@@ -76,16 +76,16 @@ public class GdbModelTargetProcessMemory
|
|||||||
if (end.longValue() < 0) {
|
if (end.longValue() < 0) {
|
||||||
BigInteger split = BigInteger.valueOf(Long.MAX_VALUE);
|
BigInteger split = BigInteger.valueOf(Long.MAX_VALUE);
|
||||||
GdbMemoryMapping lmem = new GdbMemoryMapping(start, split,
|
GdbMemoryMapping lmem = new GdbMemoryMapping(start, split,
|
||||||
split.subtract(start), start.subtract(start), "defaultLow");
|
split.subtract(start), start.subtract(start), "rwx", "defaultLow");
|
||||||
defaultMap.put(start, lmem);
|
defaultMap.put(start, lmem);
|
||||||
split = split.add(BigInteger.valueOf(1));
|
split = split.add(BigInteger.valueOf(1));
|
||||||
GdbMemoryMapping hmem = new GdbMemoryMapping(split, end,
|
GdbMemoryMapping hmem = new GdbMemoryMapping(split, end,
|
||||||
end.subtract(split), split.subtract(split), "defaultHigh");
|
end.subtract(split), split.subtract(split), "rwx", "defaultHigh");
|
||||||
defaultMap.put(split, hmem);
|
defaultMap.put(split, hmem);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GdbMemoryMapping mem = new GdbMemoryMapping(start, end,
|
GdbMemoryMapping mem = new GdbMemoryMapping(start, end,
|
||||||
end.subtract(start), start.subtract(start), "default");
|
end.subtract(start), start.subtract(start), "rwx", "default");
|
||||||
defaultMap.put(start, mem);
|
defaultMap.put(start, mem);
|
||||||
}
|
}
|
||||||
regions =
|
regions =
|
||||||
@@ -104,7 +104,7 @@ public class GdbModelTargetProcessMemory
|
|||||||
// Can't use refresh getKnownMappings is only populated by listMappings
|
// Can't use refresh getKnownMappings is only populated by listMappings
|
||||||
return doRefresh();
|
return doRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CompletableFuture<Void> doRefresh() {
|
protected CompletableFuture<Void> doRefresh() {
|
||||||
if (inferior.getPid() == null) {
|
if (inferior.getPid() == null) {
|
||||||
setElements(List.of(), "Refreshed (while no process)");
|
setElements(List.of(), "Refreshed (while no process)");
|
||||||
|
|||||||
+2
@@ -37,6 +37,8 @@ import ghidra.util.Msg;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractDebuggerModelScenarioMemoryTest extends AbstractDebuggerModelTest {
|
public abstract class AbstractDebuggerModelScenarioMemoryTest extends AbstractDebuggerModelTest {
|
||||||
|
|
||||||
|
// TODO: Something for expected flags, permissions?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This specimen must perform some observable action, which can be affected by a memory write
|
* This specimen must perform some observable action, which can be affected by a memory write
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user