From 8836dffec27f0992d1e6cac3a4e1dce61aae435d Mon Sep 17 00:00:00 2001
From: ghidragon <106987263+ghidragon@users.noreply.github.com>
Date: Fri, 13 Feb 2026 13:59:38 -0500
Subject: [PATCH] GP-6148 Changed the byteviewer to display values that cross
memory block boundaries in a different color(gray)
---
.../memory/DebuggerMemoryBytesProvider.java | 9 +++---
.../data/byteviewer.theme.properties | 1 +
.../ByteViewerPlugin/The_Byte_Viewer.htm | 14 +++++++++
.../ByteViewerComponentProvider.java | 1 +
.../plugin/core/byteviewer/FieldFactory.java | 13 +++++++++
.../core/byteviewer/MemoryByteBlock.java | 17 +++++++++--
.../core/byteviewer/ProgramByteBlockSet.java | 29 +++++++++----------
7 files changed, 62 insertions(+), 22 deletions(-)
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java
index 4a1767f3c2..559e0d42a1 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesProvider.java
@@ -57,7 +57,6 @@ import ghidra.framework.plugintool.annotation.AutoConfigStateField;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
@@ -325,8 +324,8 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
* Override where edits are allowed and direct sets through the control service.
*/
class TargetByteBlock extends MemoryByteBlock {
- protected TargetByteBlock(Program program, Memory memory, MemoryBlock block) {
- super(program, memory, block);
+ protected TargetByteBlock(Program program, MemoryBlock block) {
+ super(program, block);
}
/**
@@ -390,8 +389,8 @@ public class DebuggerMemoryBytesProvider extends ProgramByteViewerComponentProvi
}
@Override
- protected MemoryByteBlock newMemoryByteBlock(Memory memory, MemoryBlock memBlock) {
- return new TargetByteBlock(program, memory, memBlock);
+ protected MemoryByteBlock newMemoryByteBlock(MemoryBlock memBlock) {
+ return new TargetByteBlock(program, memBlock);
}
@Override
diff --git a/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties b/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties
index 300ff23873..79df5491fb 100644
--- a/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties
+++ b/Ghidra/Features/ByteViewer/data/byteviewer.theme.properties
@@ -6,6 +6,7 @@ color.bg.byteviewer.highlight.middle.mouse = color.bg.highlight
color.fg.byteviewer.separator = color.palette.blue
color.fg.byteviewer.changed = red
+color.fg.byteviewer.boundary.crossing = gray
color.cursor.byteviewer.focused.edit = color.palette.red
color.cursor.byteviewer.unfocused.edit = color.palette.pink
diff --git a/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm b/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm
index d71cc211cd..0fe34d45ec 100644
--- a/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm
+++ b/Ghidra/Features/ByteViewer/src/main/help/help/topics/ByteViewerPlugin/The_Byte_Viewer.htm
@@ -21,6 +21,20 @@
Bytes: ... menu.
The following paragraphs describe the Byte Viewer.
+ +The Byte Viewer creates boundaries between memory blocks. Whenever a memory block + boundary is encountered, the Byte Viewer will display a line of dots to indicate the + transition to a new memory block. Since the Byte Viewer shows multiple addresses on a line, + the block may start or end at some position other than the start of the line. In this case + any value positions on a line that are not part of the current block are blank.
+One special + case is when a multi-byte value spans adjacent memory blocks, the value that spans the + blocks is shown with the block that it starts in. To indicate that the value spans blocks, + it is displayed in a different color, which by default, is gray.
+Also, note that when a block starts somewhere other than the beginning of the line, the + address shows the address of the first byte in the block, regardless of what position it + starts at.
diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java index b8c7bea390..4db6e2098f 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ByteViewerComponentProvider.java @@ -70,6 +70,7 @@ public abstract class ByteViewerComponentProvider extends ComponentProviderAdapt static final GColor BG_COLOR = new GColor("color.bg.byteviewer"); static final GColor SEPARATOR_COLOR = new GColor("color.fg.byteviewer.separator"); + static final GColor BOUNDARY_CROSSING_COLOR = new GColor("color.fg.byteviewer.boundary.crossing"); static final GColor EDITED_TEXT_COLOR = new GColor("color.fg.byteviewer.changed"); static final GColor CURSOR_COLOR_FOCUSED_EDIT = new GColor("color.cursor.byteviewer.focused.edit"); static final GColor CURSOR_COLOR_UNFOCUSED_EDIT = new GColor("color.cursor.byteviewer.unfocused.edit"); diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FieldFactory.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FieldFactory.java index d5edf925a9..ba55394938 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FieldFactory.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/FieldFactory.java @@ -119,12 +119,16 @@ class FieldFactory { // The following line is the same as the catch (ByteBlockAccessException) handler. return getByteField(readErrorStr, index); } + String str = model.getDataRepresentation(block, offset); ByteField bf = new ByteField(str, fm, startX, width, charWidth, false, fieldOffset, index, highlightFactory); if (blockSet.isChanged(block, offset, unitByteSize)) { bf.setForeground(ByteViewerComponentProvider.EDITED_TEXT_COLOR); } + else if (crossesBlockBoundary(block, offset)) { + bf.setForeground(ByteViewerComponentProvider.BOUNDARY_CROSSING_COLOR); + } return bf; } catch (ByteBlockAccessException e) { @@ -139,6 +143,15 @@ class FieldFactory { } } + private boolean crossesBlockBoundary(ByteBlock block, BigInteger offset) { + int unitSize = model.getUnitByteSize(); + if (unitSize == 1) { + return false; + } + BigInteger lastOffset = offset.add(BigInteger.valueOf(unitSize)); + return lastOffset.compareTo(block.getLength()) > 0; + } + public int getWidth() { return width; } diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java index 7ccc418aee..6e74f9421d 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/MemoryByteBlock.java @@ -41,14 +41,26 @@ public class MemoryByteBlock implements ByteBlock { * @param memory memory from a program * @param block block from memory */ - protected MemoryByteBlock(Program program, Memory memory, MemoryBlock block) { + protected MemoryByteBlock(Program program, MemoryBlock block) { this.program = program; - this.memory = memory; + this.memory = program.getMemory(); this.block = block; this.start = block.getStart(); this.bigEndian = memory.isBigEndian(); } + public Address getStart() { + return start; + } + + public Address getEnd() { + return block.getEnd(); + } + + public boolean contains(Address address) { + return block.contains(address); + } + /** * Get the location representation for the given index. * @@ -377,4 +389,5 @@ public class MemoryByteBlock implements ByteBlock { } return true; } + } diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteBlockSet.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteBlockSet.java index 13422a1bae..cb6d73cd27 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteBlockSet.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/ProgramByteBlockSet.java @@ -35,10 +35,9 @@ import ghidra.util.NumericUtilities; */ public class ProgramByteBlockSet implements ByteBlockSet { - private MemoryBlock[] memBlocks; protected final Program program; private ByteBlockChangeManager bbcm; - private ByteBlock[] blocks; + private MemoryByteBlock[] blocks; private final ProgramByteViewerComponentProvider provider; protected ProgramByteBlockSet(ProgramByteViewerComponentProvider provider, Program program, @@ -113,8 +112,8 @@ public class ProgramByteBlockSet implements ByteBlockSet { // Use entries that groups the relevant objects instead of co-indexed arrays // Though a nicety, it becomes necessary if indexing/sorting by start address for (int i = 0; i < blocks.length; i++) { - Address blockStart = memBlocks[i].getStart(); - Address blockEnd = memBlocks[i].getEnd(); + Address blockStart = blocks[i].getStart(); + Address blockEnd = blocks[i].getEnd(); AddressRange intersection = range.intersect(new AddressRangeImpl(blockStart, blockEnd)); if (intersection != null) { @@ -210,7 +209,7 @@ public class ProgramByteBlockSet implements ByteBlockSet { } try { - Address addr = memBlocks[i].getStart(); + Address addr = blocks[i].getStart(); return addr.addNoWrap(offset); } @@ -233,12 +232,12 @@ public class ProgramByteBlockSet implements ByteBlockSet { } for (int i = 0; i < blocks.length; i++) { - if (!memBlocks[i].contains(address)) { + if (!blocks[i].contains(address)) { continue; } try { - long off = address.subtract(memBlocks[i].getStart()); + long off = address.subtract(blocks[i].getStart()); BigInteger offset = NumericUtilities.unsignedLongToBigInteger(off); return new ByteBlockInfo(blocks[i], offset); } @@ -254,12 +253,12 @@ public class ProgramByteBlockSet implements ByteBlockSet { } protected Address getBlockStart(int blockNumber) { - return memBlocks[blockNumber].getStart(); + return blocks[blockNumber].getStart(); } protected int getByteBlockNumber(Address blockStartAddr) { - for (int i = 0; i < memBlocks.length; i++) { - if (memBlocks[i].getStart().compareTo(blockStartAddr) == 0) { + for (int i = 0; i < blocks.length; i++) { + if (blocks[i].getStart().compareTo(blockStartAddr) == 0) { return i; } } @@ -282,15 +281,15 @@ public class ProgramByteBlockSet implements ByteBlockSet { protected void newMemoryBlocks() { Memory memory = program.getMemory(); - memBlocks = memory.getBlocks(); - blocks = new ByteBlock[memBlocks.length]; + MemoryBlock[] memBlocks = memory.getBlocks(); + blocks = new MemoryByteBlock[memBlocks.length]; for (int i = 0; i < memBlocks.length; i++) { - blocks[i] = newMemoryByteBlock(memory, memBlocks[i]); + blocks[i] = newMemoryByteBlock(memBlocks[i]); } } - protected MemoryByteBlock newMemoryByteBlock(Memory memory, MemoryBlock memBlock) { - return new MemoryByteBlock(program, memory, memBlock); + protected MemoryByteBlock newMemoryByteBlock(MemoryBlock memBlock) { + return new MemoryByteBlock(program, memBlock); } @Override