From ae475f743bdfce9461d4f1be32ac86d7e8deca9e Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Wed, 6 Mar 2024 16:53:12 -0500 Subject: [PATCH] GP-4125 Added memory block artificial attribute flag --- .../emulation/ProgramEmulationUtils.java | 2 +- .../DBTraceProgramViewMemoryRegionBlock.java | 15 +- .../DBTraceProgramViewMemorySpaceBlock.java | 12 +- ...DBTraceProgramViewRegisterMemoryBlock.java | 12 +- .../topics/MemoryMapPlugin/Memory_Map.htm | 41 +++-- .../MemoryMapPlugin/images/AddMappedBlock.png | Bin 20933 -> 24616 bytes .../MemoryMapPlugin/images/AddMemoryBlock.png | Bin 20506 -> 23385 bytes .../MemoryMapPlugin/images/MemoryMap.png | Bin 32480 -> 33038 bytes .../cmd/memory/AbstractAddMemoryBlockCmd.java | 19 ++- .../app/merge/memory/MemoryMergeManager.java | 71 +++++---- .../core/analysis/GolangSymbolAnalyzer.java | 8 +- .../disassembler/AddressTableAnalyzer.java | 30 ++-- .../plugin/core/memory/AddBlockDialog.java | 9 ++ .../app/plugin/core/memory/AddBlockModel.java | 22 ++- .../plugin/core/memory/MemoryMapManager.java | 1 + .../plugin/core/memory/MemoryMapModel.java | 58 +++---- .../plugin/core/memory/MemoryMapProvider.java | 7 + .../plugin/core/string/StringsAnalyzer.java | 15 +- .../microsoft/ThreadEnvironmentBlock.java | 8 +- .../ghidra/app/util/opinion/CoffLoader.java | 7 +- .../app/util/opinion/ElfProgramBuilder.java | 4 + .../app/util/opinion/MachoProgramBuilder.java | 143 +++++++++--------- .../ghidra/app/util/opinion/NeLoader.java | 15 +- .../ghidra/app/util/opinion/OmfLoader.java | 5 +- .../ghidra/app/util/xml/MemoryMapXmlMgr.java | 25 +-- .../ghidra/program/util/MemoryBlockDiff.java | 91 ++++++----- .../java/ghidra/program/util/MemoryDiff.java | 131 ++++++++-------- ...ryTypeProgramLocationBasedTableColumn.java | 18 ++- .../Base/src/main/resources/PROGRAM.DTD | 1 + .../merge/memory/MemoryMergeManagerTest.java | 3 +- .../database/mem/MemoryManagerTest.java | 7 + .../core/checksums/MyTestMemoryBlock.java | 12 +- .../java/sarif/export/mm/ExtMemoryMap.java | 13 +- .../sarif/managers/MemoryMapSarifMgr.java | 73 +++++---- .../gui/validator/MemoryBlocksValidator.java | 13 +- .../generic/MemoryBlockDefinition.java | 6 +- .../program/database/mem/MemoryBlockDB.java | 85 +++++++---- .../program/database/mem/MemoryMapDB.java | 2 +- .../database/mem/MemoryMapDBAdapter.java | 28 ++-- .../database/mem/MemoryMapDBAdapterV0.java | 23 ++- .../database/mem/MemoryMapDBAdapterV1.java | 3 - .../database/mem/MemoryMapDBAdapterV2.java | 20 ++- .../database/mem/MemoryMapDBAdapterV3.java | 59 ++++---- .../ghidra/program/model/mem/MemoryBlock.java | 54 ++++++- .../program/model/mem/MemoryBlockStub.java | 12 +- .../program/database/mem/MemBlockDBTest.java | 8 +- .../relocation/MIPS_ElfRelocationContext.java | 4 + .../X86_64_ElfRelocationContext.java | 4 + 48 files changed, 734 insertions(+), 465 deletions(-) diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java index 9f9c9f4e01..aa16cb8c4d 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/ProgramEmulationUtils.java @@ -104,7 +104,7 @@ public class ProgramEmulationUtils { */ public static Set getRegionFlags(MemoryBlock block) { Set result = EnumSet.noneOf(TraceMemoryFlag.class); - int mask = block.getPermissions(); + int mask = block.getFlags(); if ((mask & MemoryBlock.READ) != 0) { result.add(TraceMemoryFlag.READ); } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemoryRegionBlock.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemoryRegionBlock.java index 4fcc08b297..69486012e9 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemoryRegionBlock.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemoryRegionBlock.java @@ -18,6 +18,8 @@ package ghidra.trace.database.program; import java.io.InputStream; import java.math.BigInteger; +import javax.help.UnsupportedOperationException; + import ghidra.framework.store.LockException; import ghidra.program.model.address.*; import ghidra.trace.database.memory.DBTraceMemorySpace; @@ -57,7 +59,7 @@ public class DBTraceProgramViewMemoryRegionBlock extends AbstractDBTraceProgramV } @Override - public int getPermissions() { + public int getFlags() { int bits = 0; for (TraceMemoryFlag flag : region.getFlags()) { bits |= flag.getBits(); @@ -145,4 +147,15 @@ public class DBTraceProgramViewMemoryRegionBlock extends AbstractDBTraceProgramV public void setVolatile(boolean v) { region.setVolatile(v); } + + @Override + public boolean isArtificial() { + // By definition, any region present on target is non-artificial + return false; + } + + @Override + public void setArtificial(boolean a) { + throw new UnsupportedOperationException(); + } } diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java index 4012121576..d38679eea6 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewMemorySpaceBlock.java @@ -50,7 +50,7 @@ public class DBTraceProgramViewMemorySpaceBlock extends AbstractDBTraceProgramVi } @Override - public int getPermissions() { + public int getFlags() { return MemoryBlock.READ | MemoryBlock.WRITE | MemoryBlock.EXECUTE; } @@ -119,6 +119,16 @@ public class DBTraceProgramViewMemorySpaceBlock extends AbstractDBTraceProgramVi throw new UnsupportedOperationException(); } + @Override + public boolean isArtificial() { + return false; + } + + @Override + public void setArtificial(boolean a) { + throw new UnsupportedOperationException(); + } + @Override public String getSourceName() { return "Trace"; // TODO: What does this method actually do? diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisterMemoryBlock.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisterMemoryBlock.java index d0e67cdff7..975509feef 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisterMemoryBlock.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/program/DBTraceProgramViewRegisterMemoryBlock.java @@ -116,7 +116,7 @@ public class DBTraceProgramViewRegisterMemoryBlock implements MemoryBlock { } @Override - public int getPermissions() { + public int getFlags() { return MemoryBlock.READ | MemoryBlock.WRITE; } @@ -220,6 +220,16 @@ public class DBTraceProgramViewRegisterMemoryBlock implements MemoryBlock { throw new UnsupportedOperationException(); } + @Override + public boolean isArtificial() { + return false; + } + + @Override + public void setArtificial(boolean a) { + throw new UnsupportedOperationException(); + } + @Override public String getSourceName() { return "Trace"; // TODO: What does this method actually do? diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm index af5d704f95..fdfb1e15c0 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/Memory_Map.htm @@ -62,26 +62,11 @@

Overlay - Each of the above memory block types may optionally be created as an Overlay block. One or more memory blocks may be defined - within the same overlay address space. - - - An overlay memory block may be created in two ways: - *

    - *
  • Specifying a {@code start} address within an existing overlay address space - * ({@code overlay} parameter is ignored), or
  • - *
  • Specifying a {@code start} address within a physical memory address space and passing - * {@code overlay=true}. This use case will force the creation of a new unique overlay - * address space.
  • - *
- - - - If this option is selected, the block is created in a new - overlay address space.  Overlay blocks can serve various - purposes where a memory range may contain different data/code or map to different areas of memory - at any given point in time or processor state.   Note that - overlay blocks are fixed and may not be moved, split, merged or expanded.  In addition, Overlays - do not relocate with image base changes and have significant limitations in conjunction with + within the same overlay address space where the Overlayed Space is reflected in the memory map table. + Overlay blocks can serve various + purposes where a memory range may contain different data/code at any given point in time + or processor state.   Note that Overlay blocks + do not relocate with image base changes and have some limitations in conjunction with decompilation and analysis.

To view the Memory Map, select Window @@ -123,6 +108,9 @@

X * - Indicates execute permission.

Volatile * - Indicates a region of volatile I/O Memory.

+ +

Artificial * - Indicates an artificial memory block which has been fabricated to + facilitate analysis.

Overlayed Space - If the block is an overlay block this column indicates the name of the overlayed physical memory space. This field will be empty for non-overlay blocks.

@@ -200,6 +188,13 @@

The volatile setting of a memory block can be changed by left-clicking on the checkbox.

+ +

Change Artificial Setting

+ +
+

The artificial setting of a memory block can be changed by left-clicking on the + checkbox.

+

Initialize Memory Block

@@ -266,7 +261,11 @@

Execute - Sets the execute permission.

-

Volatile - Marks this block as volatile I/O memory.

+

Volatile - Marks a block as volatile I/O memory.

+ +

Artificial - Marks a memory block as artificial. This may be useful when a + block is required to facilitate analysis but does not exist in the same form within a + running/loaded process state.

Overlay - Creates the block as an overlay block. An overlay memory block may be created in two ways: diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMappedBlock.png index 4d198fc7f753d664d28a880f60427a2710634202..a5fd31ff6f529cce9982f62235f115861b2c6dec 100644 GIT binary patch literal 24616 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 Bp4b2Y literal 20933 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 Kz1dc$uKfehjG|Hi diff --git a/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png b/Ghidra/Features/Base/src/main/help/help/topics/MemoryMapPlugin/images/AddMemoryBlock.png index 0812e8155070e4383a9bcdca7fbd12105aaa3313..ac077c9b3a6e9133c2e3a864e103b9fe1bb5a4f2 100644 GIT binary patch literal 23385 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

(@+JZm* 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 xA-FEFo17}LyneiT?HZr9#tCPd) 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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java index ef7d8aecef..d98b702b44 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/memory/AbstractAddMemoryBlockCmd.java @@ -28,7 +28,7 @@ import ghidra.util.exception.*; /** * Base command class for adding memory blocks. */ -abstract class AbstractAddMemoryBlockCmd implements Command { +public abstract class AbstractAddMemoryBlockCmd implements Command { protected String message; protected final String name; protected final String comment; @@ -42,6 +42,8 @@ abstract class AbstractAddMemoryBlockCmd implements Command { protected final boolean isVolatile; protected final boolean isOverlay; + private boolean isArtificial = false; + AbstractAddMemoryBlockCmd(String name, String comment, String source, Address start, long length, boolean read, boolean write, boolean execute, boolean isVolatile, boolean isOverlay) { @@ -57,6 +59,15 @@ abstract class AbstractAddMemoryBlockCmd implements Command { this.isOverlay = isOverlay; } + /** + * Prior to command execution the block's artificial attribute state may be specified + * and will be applied to the new memory block. + * @param a block artificial attribute state + */ + public void setArtificial(boolean a) { + isArtificial = a; + } + @Override public String getStatusMsg() { return message; @@ -67,9 +78,8 @@ abstract class AbstractAddMemoryBlockCmd implements Command { return "Add Memory Block"; } - protected abstract MemoryBlock createMemoryBlock(Memory memory) - throws LockException, MemoryConflictException, AddressOverflowException, - CancelledException; + protected abstract MemoryBlock createMemoryBlock(Memory memory) throws LockException, + MemoryConflictException, AddressOverflowException, CancelledException; @Override public boolean applyTo(DomainObject obj) { @@ -82,6 +92,7 @@ abstract class AbstractAddMemoryBlockCmd implements Command { block.setWrite(write); block.setExecute(execute); block.setVolatile(isVolatile); + block.setArtificial(isArtificial); block.setSourceName(source); renameFragment(program, block.getStart()); return true; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/memory/MemoryMergeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/memory/MemoryMergeManager.java index af5feab5a3..3f61909112 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/memory/MemoryMergeManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/memory/MemoryMergeManager.java @@ -196,7 +196,7 @@ public class MemoryMergeManager implements MergeResolver { if (isNameConflict(i)) { conflictList.add(new ConflictInfo(i, true, false, false)); } - if (isPermissionConflict(i)) { + if (isFlagsConflict(i)) { conflictList.add(new ConflictInfo(i, false, true, false)); } if (isCommentConflict(i)) { @@ -233,7 +233,8 @@ public class MemoryMergeManager implements MergeResolver { String myName = myBlocks[index].getName(); String origName = origBlocks[index].getName(); - if (!myName.equals(origName) && !latestName.equals(origName) && !myName.equals(latestName)) { + if (!myName.equals(origName) && !latestName.equals(origName) && + !myName.equals(latestName)) { return true; } return false; @@ -244,13 +245,12 @@ public class MemoryMergeManager implements MergeResolver { * LATEST and MY programs. * @param index block index */ - private boolean isPermissionConflict(int index) { - int latestPermissions = latestBlocks[index].getPermissions(); - int myPermissions = myBlocks[index].getPermissions(); - int origPermissions = origBlocks[index].getPermissions(); + private boolean isFlagsConflict(int index) { + int latestFlags = latestBlocks[index].getFlags(); + int myFlags = myBlocks[index].getFlags(); + int origFlags = origBlocks[index].getFlags(); - if (myPermissions != origPermissions && latestPermissions != origPermissions && - myPermissions != latestPermissions) { + if (myFlags != origFlags && latestFlags != origFlags && myFlags != latestFlags) { return true; } return false; @@ -363,27 +363,21 @@ public class MemoryMergeManager implements MergeResolver { if (info.nameConflict) { title = "Resolve Name Conflict"; - latestStr = - "Use Block name '" + latestBlocks[info.index].getName() + "' (" + - MergeConstants.LATEST_TITLE + ")"; - myStr = - "Use Block name '" + getUniqueBlockName(myBlocks[info.index].getName()) + "' (" + - MergeConstants.MY_TITLE + ")"; - origStr = - "Use Block name '" + origBlocks[info.index].getName() + "' (" + - MergeConstants.ORIGINAL_TITLE + ")"; + latestStr = "Use Block name '" + latestBlocks[info.index].getName() + "' (" + + MergeConstants.LATEST_TITLE + ")"; + myStr = "Use Block name '" + getUniqueBlockName(myBlocks[info.index].getName()) + + "' (" + MergeConstants.MY_TITLE + ")"; + origStr = "Use Block name '" + origBlocks[info.index].getName() + "' (" + + MergeConstants.ORIGINAL_TITLE + ")"; } else if (info.permissionConflict) { - title = "Resolve Permissions Conflict"; - latestStr = - "Use '" + getPermissionString(latestBlocks[info.index]) + "' (" + - MergeConstants.LATEST_TITLE + ")"; - myStr = - "Use '" + getPermissionString(myBlocks[info.index]) + "' (" + - MergeConstants.MY_TITLE + ")"; - origStr = - "Use '" + getPermissionString(origBlocks[info.index]) + "' (" + - MergeConstants.ORIGINAL_TITLE + ")"; + title = "Resolve Flags Conflict"; + latestStr = "Use '" + getFlagsString(latestBlocks[info.index]) + "' (" + + MergeConstants.LATEST_TITLE + ")"; + myStr = "Use '" + getFlagsString(myBlocks[info.index]) + "' (" + + MergeConstants.MY_TITLE + ")"; + origStr = "Use '" + getFlagsString(origBlocks[info.index]) + "' (" + + MergeConstants.ORIGINAL_TITLE + ")"; } else { // comment conflict @@ -393,7 +387,8 @@ public class MemoryMergeManager implements MergeResolver { myStr = myBlocks[info.index].getComment(); origStr = origBlocks[info.index].getComment(); } - if ((memoryDetailChoice == ASK_USER) && conflictOption == ASK_USER && mergeManager != null) { + if ((memoryDetailChoice == ASK_USER) && conflictOption == ASK_USER && + mergeManager != null) { title = title + " (Block index " + info.index + ")"; showMergePanel(panelID, title, latestStr, myStr, origStr); } @@ -428,6 +423,7 @@ public class MemoryMergeManager implements MergeResolver { resultBlocks[info.index].setWrite(sourceBlock.isWrite()); resultBlocks[info.index].setExecute(sourceBlock.isExecute()); resultBlocks[info.index].setVolatile(sourceBlock.isVolatile()); + resultBlocks[info.index].setArtificial(sourceBlock.isArtificial()); } else { resultBlocks[info.index].setComment(sourceBlock.getComment()); @@ -456,8 +452,8 @@ public class MemoryMergeManager implements MergeResolver { Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); } mergeManager.setApplyEnabled(false); - mergeManager.showComponent(mergePanel, "MemoryMerge", new HelpLocation( - HelpTopics.REPOSITORY, "MemoryConflict")); + mergeManager.showComponent(mergePanel, "MemoryMerge", + new HelpLocation(HelpTopics.REPOSITORY, "MemoryConflict")); // block until the user either cancels or hits the "Apply" button // on the merge dialog... // when the "Apply" button is hit, get the user's selection @@ -465,7 +461,7 @@ public class MemoryMergeManager implements MergeResolver { } - private String getPermissionString(MemoryBlock block) { + private String getFlagsString(MemoryBlock block) { StringBuffer sb = new StringBuffer(); sb.append("Read = "); sb.append(block.isExecute()); @@ -478,6 +474,9 @@ public class MemoryMergeManager implements MergeResolver { sb.append(", "); sb.append("Volatile = "); sb.append(block.isVolatile()); + sb.append(", "); + sb.append("Artificial = "); + sb.append(block.isArtificial()); return sb.toString(); } @@ -501,7 +500,7 @@ public class MemoryMergeManager implements MergeResolver { } } } - if (!isPermissionConflict(index)) { + if (!isFlagsConflict(index)) { boolean permission = myBlocks[index].isRead(); if (permission != origBlocks[index].isRead()) { resultBlocks[index].setRead(permission); @@ -534,6 +533,14 @@ public class MemoryMergeManager implements MergeResolver { progressUpdated = true; } } + permission = myBlocks[index].isArtificial(); + if (permission != origBlocks[index].isArtificial()) { + resultBlocks[index].setArtificial(permission); + if (!progressUpdated) { + currentMonitor.setProgress(++progressIndex); + progressUpdated = true; + } + } } if (!isCommentConflict(index)) { String myComment = myBlocks[index].getComment(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java index 03aa973861..032ae5d25c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java @@ -136,7 +136,8 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { } if (analyzerOptions.propagateRtti) { - Msg.info(this, "Golang symbol analyzer: scheduling RTTI propagation after reference analysis"); + Msg.info(this, + "Golang symbol analyzer: scheduling RTTI propagation after reference analysis"); aam.schedule(new PropagateRttiBackgroundCommand(goBinary), AnalysisPriority.REFERENCE_ANALYSIS.after().priority()); } @@ -408,6 +409,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { MemoryBlockUtils.createUninitializedBlock(program, false, "ARTIFICAL_GOLANG_CONTEXT", mbStart, len, "Artifical memory block created to hold golang context data types", null, true, true, false, null); + newMB.setArtificial(true); return newMB.getStart(); } @@ -567,8 +569,8 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { } record CallSiteInfo(Reference ref, Function callingFunc, Function calledFunc, - Register register, - java.util.function.Function returnTypeMapper) {} + Register register, java.util.function.Function returnTypeMapper) { + } private GoRttiMapper goBinary; private MarkupSession markupSession; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzer.java index 1647641938..d7fa17101c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzer.java @@ -107,10 +107,10 @@ public class AddressTableAnalyzer extends AbstractAnalyzer { processorHasLowBitCode = PseudoDisassembler.hasLowBitCodeModeInAddrValues(program); // don't align based on instruction start rules, data is important too. // if do this, will miss, runs of ptrs to data/code/data/code/.... - // ptrAlignment = program.getLanguage().getInstructionAlignment(); - // if (processorHasLowBitCode) { - // ptrAlignment = 1; - // } + // ptrAlignment = program.getLanguage().getInstructionAlignment(); + // if (processorHasLowBitCode) { + // ptrAlignment = 1; + // } return (addrSize == 32 || addrSize == 64); } @@ -164,8 +164,9 @@ public class AddressTableAnalyzer extends AbstractAnalyzer { continue; } - Bookmark bookmark = program.getBookmarkManager().getBookmark( - tableEntry.getTopAddress(), BookmarkType.ANALYSIS, "Address Table"); + Bookmark bookmark = program.getBookmarkManager() + .getBookmark(tableEntry.getTopAddress(), BookmarkType.ANALYSIS, + "Address Table"); // nothing to see here, already done. if (!ignoreBookmarks && bookmark != null) { @@ -186,9 +187,10 @@ public class AddressTableAnalyzer extends AbstractAnalyzer { // put info bookmark in if (createBookmarksEnabled) { - program.getBookmarkManager().setBookmark(tableEntry.getTopAddress(), - BookmarkType.ANALYSIS, "Address Table", - "Address table[" + tableEntry.getNumberAddressEntries() + "] created"); + program.getBookmarkManager() + .setBookmark(tableEntry.getTopAddress(), BookmarkType.ANALYSIS, + "Address Table", "Address table[" + + tableEntry.getNumberAddressEntries() + "] created"); } // if all are valid code, disassemble @@ -268,8 +270,7 @@ public class AddressTableAnalyzer extends AbstractAnalyzer { // AddressSet badBlocks = new AddressSet(); for (MemoryBlock memoryBlock : blocks) { - if (memoryBlock.isWrite() || memoryBlock.isRead() || memoryBlock.isExecute() || - memoryBlock.isVolatile()) { + if (memoryBlock.isWrite() || memoryBlock.isRead() || memoryBlock.isExecute()) { continue; } @@ -339,15 +340,16 @@ public class AddressTableAnalyzer extends AbstractAnalyzer { AddressIterator addrIter = addrSet.getAddresses(true); long maxBytes = addrSet.getNumAddresses(); - - MemoryBufferImpl buffer = new MemoryBufferImpl(memory, addrSet.getMinAddress(), (int) (maxBytes > 1024 ? 1024 : maxBytes)); + + MemoryBufferImpl buffer = new MemoryBufferImpl(memory, addrSet.getMinAddress(), + (int) (maxBytes > 1024 ? 1024 : maxBytes)); while (addrIter.hasNext()) { Address start = addrIter.next(); // skip over anything that smells like a unicode string // - int strLen = getWStrLen(buffer, start, (int)(maxBytes / 2)); + int strLen = getWStrLen(buffer, start, (int) (maxBytes / 2)); if (strLen > 4) { int numBytes = strLen * 2; addrIter = skipBytes(addrIter, addrSet, start, numBytes); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java index 78aba20b5b..b34c472ac2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockDialog.java @@ -65,6 +65,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { private JCheckBox writeCB; private JCheckBox executeCB; private JCheckBox volatileCB; + private JCheckBox artificialCB; private JCheckBox overlayCB; private RegisterField initialValueField; private JLabel initialValueLabel; @@ -108,6 +109,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { writeCB.setSelected(model.isWrite()); executeCB.setSelected(model.isExecute()); volatileCB.setSelected(model.isVolatile()); + artificialCB.setSelected(model.isArtificial()); overlayCB.setSelected(model.isOverlay()); } @@ -169,6 +171,11 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { volatileCB.setSelected(model.isVolatile()); volatileCB.addActionListener(e -> model.setVolatile(volatileCB.isSelected())); + artificialCB = new GCheckBox("Artificial"); + artificialCB.setName("Artificial"); + artificialCB.setSelected(model.isArtificial()); + artificialCB.addActionListener(e -> model.setArtificial(artificialCB.isSelected())); + overlayCB = new GCheckBox("Overlay"); overlayCB.setName("Overlay"); overlayCB.setSelected(model.isOverlay()); @@ -180,6 +187,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { panel.add(writeCB); panel.add(executeCB); panel.add(volatileCB); + panel.add(artificialCB); panel.add(overlayCB); return panel; @@ -320,6 +328,7 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener { writeCB.setSelected(model.isWrite()); executeCB.setSelected(model.isExecute()); volatileCB.setSelected(model.isVolatile()); + artificialCB.setSelected(model.isArtificial()); overlayCB.setSelected(model.isOverlay()); setOkEnabled(false); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java index 469e8f1376..ecb9be0f45 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/AddBlockModel.java @@ -18,7 +18,6 @@ package ghidra.app.plugin.core.memory; import javax.swing.event.ChangeListener; import ghidra.app.cmd.memory.*; -import ghidra.framework.cmd.Command; import ghidra.framework.plugintool.PluginTool; import ghidra.program.database.mem.ByteMappingScheme; import ghidra.program.database.mem.FileBytes; @@ -55,6 +54,7 @@ class AddBlockModel { private boolean isWrite; private boolean isExecute; private boolean isVolatile; + private boolean isArtificial; private InitializedType initializedType; private String comment; private FileBytes fileBytes; @@ -125,6 +125,7 @@ class AddBlockModel { isExecute = false; isVolatile = false; isOverlay = false; + isArtificial = false; schemeDestByteCount = blockType == MemoryBlockType.BIT_MAPPED ? 8 : 1; schemeSrcByteCount = 1; initializedType = InitializedType.UNINITIALIZED; @@ -148,6 +149,10 @@ class AddBlockModel { this.isVolatile = b; } + void setArtificial(boolean b) { + this.isArtificial = b; + } + void setOverlay(boolean b) { this.isOverlay = b; validateInfo(); @@ -226,6 +231,10 @@ class AddBlockModel { return isVolatile; } + boolean isArtificial() { + return isArtificial; + } + boolean isOverlay() { return isOverlay; } @@ -240,7 +249,8 @@ class AddBlockModel { if (!isValid) { return false; } - Command cmd = createAddBlockCommand(); + AbstractAddMemoryBlockCmd cmd = createAddBlockCommand(); + cmd.setArtificial(isArtificial); if (!tool.execute(cmd, program)) { message = cmd.getStatusMsg(); return false; @@ -248,7 +258,7 @@ class AddBlockModel { return true; } - Command createAddBlockCommand() { + AbstractAddMemoryBlockCmd createAddBlockCommand() { String source = ""; switch (blockType) { case BIT_MAPPED: @@ -267,7 +277,7 @@ class AddBlockModel { } } - private Command createNonMappedMemoryBlock(String source) { + private AbstractAddMemoryBlockCmd createNonMappedMemoryBlock(String source) { switch (initializedType) { case INITIALIZED_FROM_FILE_BYTES: return new AddFileBytesMemoryBlockCmd(blockName, comment, source, startAddr, length, @@ -293,8 +303,8 @@ class AddBlockModel { private void validateInfo() { message = ""; isValid = hasValidName() && hasValidStartAddress() && hasValidLength() && - hasNoMemoryConflicts() && hasMappedAddressIfNeeded() && - hasInitialValueIfNeeded() && hasFileBytesInfoIfNeeded() && isOverlayIfOtherSpace(); + hasNoMemoryConflicts() && hasMappedAddressIfNeeded() && hasInitialValueIfNeeded() && + hasFileBytesInfoIfNeeded() && isOverlayIfOtherSpace(); } private boolean hasFileBytesInfoIfNeeded() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java index 48f38bdf99..6d7f18c136 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapManager.java @@ -363,6 +363,7 @@ class MemoryMapManager { newBlock.setWrite(bigBlock.isWrite()); newBlock.setExecute(bigBlock.isExecute()); newBlock.setVolatile(bigBlock.isVolatile()); + newBlock.setArtificial(bigBlock.isArtificial()); newBlock.setSourceName("Resized Memory Block"); } else { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java index 4190477859..5bed0d0691 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java @@ -51,12 +51,13 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr final static byte WRITE = 5; final static byte EXECUTE = 6; final static byte VOLATILE = 7; - final static byte OVERLAY = 8; - final static byte BLOCK_TYPE = 9; - final static byte INIT = 10; - final static byte BYTE_SOURCE = 11; - final static byte SOURCE = 12; - final static byte COMMENT = 13; + final static byte ARTIFICIAL = 8; + final static byte OVERLAY = 9; + final static byte BLOCK_TYPE = 10; + final static byte INIT = 11; + final static byte BYTE_SOURCE = 12; + final static byte SOURCE = 13; + final static byte COMMENT = 14; final static String NAME_COL = "Name"; final static String START_COL = "Start"; @@ -66,6 +67,7 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr final static String WRITE_COL = "W"; final static String EXECUTE_COL = "X"; final static String VOLATILE_COL = "Volatile"; + final static String ARTIFICIAL_COL = "Artificial"; final static String OVERLAY_COL = "Overlayed Space"; final static String BLOCK_TYPE_COL = "Type"; final static String INIT_COL = "Initialized"; @@ -80,9 +82,9 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr private List memList; private MemoryMapProvider provider; - private final static String COLUMN_NAMES[] = - { NAME_COL, START_COL, END_COL, LENGTH_COL, READ_COL, WRITE_COL, EXECUTE_COL, VOLATILE_COL, - OVERLAY_COL, BLOCK_TYPE_COL, INIT_COL, BYTE_SOURCE_COL, SOURCE_COL, COMMENT_COL }; + private final static String COLUMN_NAMES[] = { NAME_COL, START_COL, END_COL, LENGTH_COL, + READ_COL, WRITE_COL, EXECUTE_COL, VOLATILE_COL, ARTIFICIAL_COL, OVERLAY_COL, BLOCK_TYPE_COL, + INIT_COL, BYTE_SOURCE_COL, SOURCE_COL, COMMENT_COL }; MemoryMapModel(MemoryMapProvider provider, Program program) { super(START); @@ -124,7 +126,7 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr @Override public boolean isSortable(int columnIndex) { if (columnIndex == READ || columnIndex == WRITE || columnIndex == EXECUTE || - columnIndex == VOLATILE || columnIndex == INIT) { + columnIndex == VOLATILE || columnIndex == ARTIFICIAL || columnIndex == INIT) { return false; } return true; @@ -163,7 +165,7 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr @Override public Class getColumnClass(int columnIndex) { if (columnIndex == READ || columnIndex == WRITE || columnIndex == EXECUTE || - columnIndex == VOLATILE || columnIndex == INIT) { + columnIndex == VOLATILE || columnIndex == ARTIFICIAL || columnIndex == INIT) { return Boolean.class; } return String.class; @@ -178,6 +180,8 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr case WRITE: case EXECUTE: case VOLATILE: + case ARTIFICIAL: + return true; case COMMENT: return true; case INIT: @@ -267,6 +271,14 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr }); break; } + case ARTIFICIAL: { + program.withTransaction("Set Artificial State", () -> { + boolean value = ((Boolean) aValue).booleanValue(); + block.setArtificial(value); + provider.setStatusText(""); + }); + break; + } case INIT: MemoryBlockType blockType = block.getType(); if (blockType == MemoryBlockType.BIT_MAPPED || @@ -428,6 +440,8 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr return block.isExecute() ? Boolean.TRUE : Boolean.FALSE; case VOLATILE: return block.isVolatile() ? Boolean.TRUE : Boolean.FALSE; + case ARTIFICIAL: + return block.isArtificial() ? Boolean.TRUE : Boolean.FALSE; case OVERLAY: return getOverlayBaseSpaceName(block); case INIT: @@ -554,29 +568,21 @@ class MemoryMapModel extends AbstractSortedTableModel implements Pr case LENGTH: return (int) (b1.getSize() - b2.getSize()); case READ: - int b1r = (b1.isRead() ? 1 : -1); - int b2r = (b2.isRead() ? 1 : -1); - return (b1r - b2r); + return Boolean.compare(b1.isRead(), b2.isRead()); case WRITE: - int b1w = (b1.isWrite() ? 1 : -1); - int b2w = (b2.isWrite() ? 1 : -1); - return (b1w - b2w); + return Boolean.compare(b1.isWrite(), b2.isWrite()); case EXECUTE: - int b1x = (b1.isExecute() ? 1 : -1); - int b2x = (b2.isExecute() ? 1 : -1); - return (b1x - b2x); + return Boolean.compare(b1.isExecute(), b2.isExecute()); case VOLATILE: - int b1v = (b1.isVolatile() ? 1 : -1); - int b2v = (b2.isVolatile() ? 1 : -1); - return (b1v - b2v); + return Boolean.compare(b1.isVolatile(), b2.isVolatile()); + case ARTIFICIAL: + return Boolean.compare(b1.isArtificial(), b2.isArtificial()); case OVERLAY: String ov1 = getOverlayBaseSpaceName(b1); String ov2 = getOverlayBaseSpaceName(b2); return ov1.compareTo(ov2); case INIT: - int b1init = (b1.isInitialized() ? 1 : -1); - int b2init = (b2.isInitialized() ? 1 : -1); - return (b1init - b2init); + return Boolean.compare(b1.isInitialized(), b2.isInitialized()); //case BYTE_SOURCE: - handled by default comparator diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java index d10b3c4dfd..6004315da1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapProvider.java @@ -480,6 +480,13 @@ class MemoryMapProvider extends ComponentProviderAdapter { column.setResizable(false); } + column = table.getColumn(MemoryMapModel.ARTIFICIAL_COL); + if (column != null) { + column.setMaxWidth(65); + column.setMinWidth(65); + column.setResizable(false); + } + column = table.getColumn(MemoryMapModel.BLOCK_TYPE_COL); if (column != null) { column.setMinWidth(25); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringsAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringsAnalyzer.java index 59f5ca18fe..e386f33e77 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringsAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringsAnalyzer.java @@ -109,6 +109,7 @@ public class StringsAnalyzer extends AbstractAnalyzer { public static enum Alignment { ALIGN_1(1), ALIGN_2(2), ALIGN_4(4); + private int alignment; Alignment(int alignment) { @@ -349,7 +350,10 @@ public class StringsAnalyzer extends AbstractAnalyzer { AddressSet addresses = new AddressSet(); for (MemoryBlock memBlock : blocks) { - if (memBlock.getPermissions() > 0) { + if (!memBlock.isLoaded()) { + continue; + } + if (memBlock.isWrite() || memBlock.isRead() || memBlock.isExecute()) { addresses = addresses.union(new AddressSet(memBlock.getStart(), memBlock.getEnd())); } } @@ -581,14 +585,15 @@ public class StringsAnalyzer extends AbstractAnalyzer { modelName = options.getString(MODELFILE_OPTION_NAME, MODEL_DEFAULT_NAME); setTrigramFileName(modelName); - minStringLength = options.getEnum(MINIMUM_STRING_LENGTH_OPTION_NAME, - MINIMUM_STRING_LENGTH_DEFAULT_VALUE).getMinLength(); + minStringLength = + options.getEnum(MINIMUM_STRING_LENGTH_OPTION_NAME, MINIMUM_STRING_LENGTH_DEFAULT_VALUE) + .getMinLength(); requireNullEnd = options.getBoolean(REQUIRE_NULL_TERMINATION_OPTION_NAME, REQUIRE_NULL_TERMINATION_DEFAULT_VALUE); - startAlignment = options.getEnum(START_ALIGNMENT_OPTION_NAME, - START_ALIGNMENT_DEFAULT_VALUE).getAlignment(); + startAlignment = options.getEnum(START_ALIGNMENT_OPTION_NAME, START_ALIGNMENT_DEFAULT_VALUE) + .getAlignment(); setStringEndAlignment( options.getInt(END_ALIGNMENT_OPTION_NAME, END_ALIGNMENT_DEFAULT_VALUE)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/ThreadEnvironmentBlock.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/ThreadEnvironmentBlock.java index 1eda299cb0..c2d97eabe4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/ThreadEnvironmentBlock.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/ThreadEnvironmentBlock.java @@ -706,9 +706,9 @@ public class ThreadEnvironmentBlock { * @throws CodeUnitInsertionException for problems laying down the structure on the block * @throws InvalidInputException for problems with the symbol name attached to the TEB */ - public void createBlockAndStructure() throws MemoryConflictException, LockException, - IllegalArgumentException, AddressOverflowException, CodeUnitInsertionException, - InvalidInputException { + public void createBlockAndStructure() + throws MemoryConflictException, LockException, IllegalArgumentException, + AddressOverflowException, CodeUnitInsertionException, InvalidInputException { Memory memory = program.getMemory(); MemoryBlock block = memory.getBlock(BLOCK_NAME); if (block != null) { @@ -722,6 +722,7 @@ public class ThreadEnvironmentBlock { block = memory.createUninitializedBlock(BLOCK_NAME, tebAddress, blockSize, false); } block.setWrite(true); + block.setArtificial(true); LayDownStructure laydown = new LayDownStructure(is64Bit); create(laydown); if (is64Bit) { @@ -806,6 +807,7 @@ public class ThreadEnvironmentBlock { null, false); } block1.setWrite(true); + block1.setArtificial(true); LayDownFlat laydown = new LayDownFlat(program, tebAddress, is64Bit); create(laydown); Data data = program.getListing().getDataAt(tebAddress.add(is64Bit ? 0x30 : 0x18)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java index d2841d8189..ea251ffcff 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/CoffLoader.java @@ -349,6 +349,9 @@ public class CoffLoader extends AbstractLibrarySupportLoader { // assume any value in external is writable. block.setWrite(true); + // Mark block as an artificial fabrication + block.setArtificial(true); + Address current = externalAddressStart; while (current.compareTo(externalAddress) < 0) { createUndefined(program.getListing(), program.getMemory(), current, @@ -745,8 +748,8 @@ public class CoffLoader extends AbstractLibrarySupportLoader { } } - private void handleRelocationError(Program program, Address address, - Short relocationType, String message, Exception causeToReport) { + private void handleRelocationError(Program program, Address address, Short relocationType, + String message, Exception causeToReport) { String bookmarkMessage = String.format("Failed to apply COFF Relocation type 0x%x: %s", relocationType, message); program.getBookmarkManager() diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java index 186a116582..ddbdd50868 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java @@ -1546,6 +1546,10 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { // assume any value in external is writable. block.setWrite(true); + + // Mark block as an artificial fabrication + block.setArtificial(true); + block.setSourceName(BLOCK_SOURCE_NAME); block.setComment( "NOTE: This block is artificial and allows ELF Relocations to work correctly"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java index 5561843494..3ea9c5c61f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java @@ -105,8 +105,8 @@ public class MachoProgramBuilder { */ public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes, MessageLog log, TaskMonitor monitor) throws Exception { - MachoProgramBuilder machoProgramBuilder = new MachoProgramBuilder(program, provider, - fileBytes, log, monitor); + MachoProgramBuilder machoProgramBuilder = + new MachoProgramBuilder(program, provider, fileBytes, log, monitor); machoProgramBuilder.build(); } @@ -154,7 +154,7 @@ public class MachoProgramBuilder { fixupProgramTree(null); // should be done last to account for new memory blocks setCompiler(); } - + /** * Sets the image base * @@ -339,8 +339,7 @@ public class MachoProgramBuilder { * @param suffix An optional suffix that will get appended to tree segment and segment nodes * @throws Exception if there was a problem fixing up the Program Tree */ - protected void fixupProgramTree(String suffix) - throws Exception { + protected void fixupProgramTree(String suffix) throws Exception { if (suffix == null) { suffix = ""; } @@ -399,7 +398,7 @@ public class MachoProgramBuilder { section.getSectionName() + suffix)); sectionFragment.move(sectionStart, sectionEnd); } - + // If the sections fully filled the segment, we can remove the now-empty segment if (segmentFragment.isEmpty()) { segmentModule.removeChild(segmentFragment.getName()); @@ -456,7 +455,7 @@ public class MachoProgramBuilder { log.appendMsg("Unable to determine entry point."); } } - + protected boolean processExports(MachHeader header) throws Exception { monitor.setMessage("Processing exports..."); @@ -501,7 +500,7 @@ public class MachoProgramBuilder { } return !exports.isEmpty(); - } + } protected void processNewExport(Address baseAddr, ExportEntry export, String name) throws AddressOutOfBoundsException, Exception { @@ -680,6 +679,10 @@ public class MachoProgramBuilder { start, undefinedSymbols.size() * machoHeader.getAddressSize(), false); // assume any value in external is writable. block.setWrite(true); + + // Mark block as an artificial fabrication + block.setArtificial(true); + block.setSourceName(BLOCK_SOURCE_NAME); block.setComment( "NOTE: This block is artificial and is used to make relocations work correctly"); @@ -792,14 +795,15 @@ public class MachoProgramBuilder { } } - private void processBindings(BindingTable bindingTable, List libraryPaths) throws Exception { + private void processBindings(BindingTable bindingTable, List libraryPaths) + throws Exception { DataConverter converter = DataConverter.getInstance(program.getLanguage().isBigEndian()); SymbolTable symbolTable = program.getSymbolTable(); List bindings = bindingTable.getBindings(); List threadedBindings = bindingTable.getThreadedBindings(); List segments = machoHeader.getAllSegments(); - + if (threadedBindings != null) { DyldChainedFixups dyldChainedFixups = new DyldChainedFixups(program, machoHeader, libraryPaths, log, monitor); @@ -834,7 +838,7 @@ public class MachoProgramBuilder { Address addr = space.getAddress(segments.get(binding.getSegmentIndex()).getVMaddress() + binding.getSegmentOffset()); - + fixupExternalLibrary(binding.getLibraryOrdinal(), symbol, libraryPaths); boolean success = false; @@ -1103,16 +1107,16 @@ public class MachoProgramBuilder { } /** - * Processes the section relocations from all {@link Section}s. - * - * @throws CancelledException if the operation was cancelled. - */ - protected void processSectionRelocations() throws CancelledException { - monitor.setMessage("Processing section relocations..."); + * Processes the section relocations from all {@link Section}s. + * + * @throws CancelledException if the operation was cancelled. + */ + protected void processSectionRelocations() throws CancelledException { + monitor.setMessage("Processing section relocations..."); LinkedHashMap relocationMap = new LinkedHashMap<>(); - for (Section section : machoHeader.getAllSections()) { - monitor.checkCancelled(); + for (Section section : machoHeader.getAllSections()) { + monitor.checkCancelled(); MemoryBlock sectionMemoryBlock = getMemoryBlock(section); if (sectionMemoryBlock == null) { @@ -1124,54 +1128,54 @@ public class MachoProgramBuilder { } for (RelocationInfo relocationInfo : section.getRelocations()) { - monitor.checkCancelled(); + monitor.checkCancelled(); Address address = sectionMemoryBlock.getStart().add(relocationInfo.getAddress()); relocationMap.put(relocationInfo, address); } } - performRelocations(relocationMap); + performRelocations(relocationMap); } /** - * Processes the external relocations from all {@link DynamicSymbolTableCommand}s. - * - * @throws CancelledException if the operation was cancelled. - */ - protected void processExternalRelocations() throws CancelledException { + * Processes the external relocations from all {@link DynamicSymbolTableCommand}s. + * + * @throws CancelledException if the operation was cancelled. + */ + protected void processExternalRelocations() throws CancelledException { monitor.setMessage("Processing external relocations..."); LinkedHashMap relocationMap = new LinkedHashMap<>(); - for (DynamicSymbolTableCommand cmd : machoHeader - .getLoadCommands(DynamicSymbolTableCommand.class)) { - monitor.checkCancelled(); - for (RelocationInfo relocationInfo : cmd.getExternalRelocations()) { - monitor.checkCancelled(); - relocationMap.put(relocationInfo, space.getAddress(relocationInfo.getAddress())); - } - } - performRelocations(relocationMap); + for (DynamicSymbolTableCommand cmd : machoHeader + .getLoadCommands(DynamicSymbolTableCommand.class)) { + monitor.checkCancelled(); + for (RelocationInfo relocationInfo : cmd.getExternalRelocations()) { + monitor.checkCancelled(); + relocationMap.put(relocationInfo, space.getAddress(relocationInfo.getAddress())); + } + } + performRelocations(relocationMap); } /** - * Processes the local relocations from all {@link DynamicSymbolTableCommand}s. - * - * @throws CancelledException if the operation was cancelled. - */ - protected void processLocalRelocations() throws CancelledException { + * Processes the local relocations from all {@link DynamicSymbolTableCommand}s. + * + * @throws CancelledException if the operation was cancelled. + */ + protected void processLocalRelocations() throws CancelledException { monitor.setMessage("Processing local relocations..."); LinkedHashMap relocationMap = new LinkedHashMap<>(); - for (DynamicSymbolTableCommand cmd : machoHeader - .getLoadCommands(DynamicSymbolTableCommand.class)) { - monitor.checkCancelled(); - for (RelocationInfo relocationInfo : cmd.getLocalRelocations()) { - monitor.checkCancelled(); - relocationMap.put(relocationInfo, space.getAddress(relocationInfo.getAddress())); - } - } - performRelocations(relocationMap); + for (DynamicSymbolTableCommand cmd : machoHeader + .getLoadCommands(DynamicSymbolTableCommand.class)) { + monitor.checkCancelled(); + for (RelocationInfo relocationInfo : cmd.getLocalRelocations()) { + monitor.checkCancelled(); + relocationMap.put(relocationInfo, space.getAddress(relocationInfo.getAddress())); + } + } + performRelocations(relocationMap); } protected List processLibraries() throws Exception { @@ -1215,7 +1219,7 @@ public class MachoProgramBuilder { if (program.getSymbolTable().getLibrarySymbol(Library.UNKNOWN) == null) { program.getSymbolTable().createExternalLibrary(Library.UNKNOWN, SourceType.IMPORTED); } - + return libraryPaths; } @@ -1257,23 +1261,23 @@ public class MachoProgramBuilder { * @throws CancelledException if the operation was cancelled. */ private void performRelocations(LinkedHashMap relocationMap) - throws CancelledException { + throws CancelledException { if (relocationMap.isEmpty()) { return; } - MachoRelocationHandler handler = MachoRelocationHandlerFactory.getHandler(machoHeader); + MachoRelocationHandler handler = MachoRelocationHandlerFactory.getHandler(machoHeader); if (handler == null) { log.appendMsg(String.format("No relocation handler for machine type 0x%x", machoHeader.getCpuType())); } - Iterator iter = relocationMap.keySet().iterator(); - while (iter.hasNext()) { - RelocationInfo relocationInfo = iter.next(); - Address address = relocationMap.get(relocationInfo); - MachoRelocation relocation = null; + Iterator iter = relocationMap.keySet().iterator(); + while (iter.hasNext()) { + RelocationInfo relocationInfo = iter.next(); + Address address = relocationMap.get(relocationInfo); + MachoRelocation relocation = null; RelocationResult result = RelocationResult.FAILURE; if (handler != null) { @@ -1281,7 +1285,7 @@ public class MachoProgramBuilder { ? new MachoRelocation(program, machoHeader, address, relocationInfo, iter.next()) : new MachoRelocation(program, machoHeader, address, relocationInfo); - try { + try { result = handler.relocate(relocation); if (result.status() == Status.UNSUPPORTED) { @@ -1291,9 +1295,9 @@ public class MachoProgramBuilder { } } catch (MemoryAccessException e) { - handleRelocationError(address, String.format( + handleRelocationError(address, String.format( "Relocation failure at address %s: error accessing memory.", address)); - } + } catch (RelocationException e) { handleRelocationError(address, String.format( "Relocation failure at address %s: %s", address, e.getMessage())); @@ -1306,18 +1310,19 @@ public class MachoProgramBuilder { msg = String.format("Relocation failure at address %s: %s", address, msg); handleRelocationError(address, msg); Msg.error(this, msg, e); - } + } } program.getRelocationTable() .add(address, result.status(), relocationInfo.getType(), - new long[] { relocationInfo.getValue(), - relocationInfo.getLength(), relocationInfo.isPcRelocated() ? 1 : 0, - relocationInfo.isExternal() ? 1 : 0, relocationInfo.isScattered() ? 1 : 0 }, + new long[] { relocationInfo.getValue(), relocationInfo.getLength(), + relocationInfo.isPcRelocated() ? 1 : 0, + relocationInfo.isExternal() ? 1 : 0, + relocationInfo.isScattered() ? 1 : 0 }, result.byteLength(), relocation != null ? relocation.getTargetDescription() : null); } } - + /** * Marks up {@link LoadCommand} dadta * @@ -1338,10 +1343,10 @@ public class MachoProgramBuilder { * @param message The error message */ private void handleRelocationError(Address address, String message) { - program.getBookmarkManager() - .setBookmark(address, BookmarkType.ERROR, "Relocations", message); - log.appendMsg(message); - } + program.getBookmarkManager() + .setBookmark(address, BookmarkType.ERROR, "Relocations", message); + log.appendMsg(message); + } private void addLibrary(String library) { library = library.replaceAll(" ", "_"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java index a0a38f7e53..1c20444cba 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/NeLoader.java @@ -256,8 +256,8 @@ public class NeLoader extends AbstractOrdinalSupportLoader { } MemoryBlock block; if (length > 0) { - block = MemoryBlockUtils.createInitializedBlock(program, false, - name, addr, fileBytes, offset, length, "", "", r, w, x, log); + block = MemoryBlockUtils.createInitializedBlock(program, false, name, addr, + fileBytes, offset, length, "", "", r, w, x, log); if (length < minalloc) { // Things actually rely on the block being padded out with real 0's, so we // must expand it @@ -354,8 +354,7 @@ public class NeLoader extends AbstractOrdinalSupportLoader { int length = resource.getFileLengthShifted(); if (length > 0) { MemoryBlockUtils.createInitializedBlock(program, false, "Rsrc" + (id++), - addr, fileBytes, offset, length, "", "", true, - false, false, log); + addr, fileBytes, offset, length, "", "", true, false, false, log); } } catch (AddressOverflowException e) { @@ -445,8 +444,12 @@ public class NeLoader extends AbstractOrdinalSupportLoader { String comment = ""; String source = ""; // This isn't a real block, just place holder addresses, so don't create an initialized block - MemoryBlockUtils.createUninitializedBlock(program, false, MemoryBlock.EXTERNAL_BLOCK_NAME, - addr, length, comment, source, true, false, false, log); + MemoryBlock block = MemoryBlockUtils.createUninitializedBlock(program, false, + MemoryBlock.EXTERNAL_BLOCK_NAME, addr, length, comment, source, true, false, false, + log); + + // Mark block as an artificial fabrication + block.setArtificial(true); for (int i = 0; i < names.length; ++i) { String moduleName = names[i].getString(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java index b5d0fb9cfb..50c6ffd37d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/OmfLoader.java @@ -204,7 +204,7 @@ public class OmfLoader extends AbstractProgramWrapperLoader { int method, index, locationType = -1; locAddress = null; - if(fixup.getDataBlock() == null) { + if (fixup.getDataBlock() == null) { continue; // If no data block don't try to fixup } try { @@ -613,6 +613,9 @@ public class OmfLoader extends AbstractProgramWrapperLoader { // assume any value in external is writable. block.setWrite(true); + // Mark block as an artificial fabrication + block.setArtificial(true); + Address current = externalAddressStart; while (current.compareTo(externalAddress) < 0) { createUndefined(program.getListing(), program.getMemory(), current, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java index d41c30ae83..eb5ea454b8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java @@ -52,8 +52,7 @@ class MemoryMapXmlMgr { /////////////////////////////////////////////////////////////////////////////////////// void read(XmlPullParser parser, boolean overwriteConflicts, TaskMonitor monitor, - String directory) - throws SAXParseException, FileNotFoundException, CancelledException { + String directory) throws SAXParseException, FileNotFoundException, CancelledException { XmlElement element = parser.next(); element = parser.next(); @@ -71,8 +70,7 @@ class MemoryMapXmlMgr { } private void processMemoryBlock(XmlElement memorySectionElement, XmlPullParser parser, - String directory, Program program, TaskMonitor monitor) - throws FileNotFoundException { + String directory, Program program, TaskMonitor monitor) throws FileNotFoundException { String name = memorySectionElement.getAttribute("NAME"); String addrStr = memorySectionElement.getAttribute("START_ADDR"); @@ -91,6 +89,9 @@ class MemoryMapXmlMgr { String volatility = memorySectionElement.getAttribute("VOLATILE"); boolean isVolatile = "y".equals(volatility); + String artificial = memorySectionElement.getAttribute("ARTIFICIAL"); + boolean isArtificial = "y".equals(artificial); + String comment = memorySectionElement.getAttribute("COMMENT"); try { @@ -124,12 +125,12 @@ class MemoryMapXmlMgr { element = parser.peek();//get next start of contents or end of section } if (overlayName != null) { - MemoryBlock block = - MemoryBlockUtils.createInitializedBlock(program, true, overlayName, addr, - new ByteArrayInputStream(bytes), - bytes.length, comment, null, r, w, x, log, monitor); + MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, true, + overlayName, addr, new ByteArrayInputStream(bytes), bytes.length, comment, + null, r, w, x, log, monitor); if (block != null) { block.setVolatile(isVolatile); + block.setArtificial(isArtificial); if (!name.equals(overlayName)) { block.setName(name); } @@ -138,8 +139,8 @@ class MemoryMapXmlMgr { else { MemoryBlock block = MemoryBlockUtils.createInitializedBlock(program, false, - name, addr, new ByteArrayInputStream(bytes), bytes.length, comment, null, - r, w, x, log, monitor); + name, addr, new ByteArrayInputStream(bytes), bytes.length, comment, null, r, + w, x, log, monitor); if (block != null) { block.setVolatile(isVolatile); } @@ -297,6 +298,10 @@ class MemoryMapXmlMgr { attrs.addAttribute("VOLATILE", true); } + if (block.isArtificial()) { + attrs.addAttribute("ARTIFICIAL", true); + } + writer.startElement("MEMORY_SECTION", attrs); if (block.getType() == MemoryBlockType.BIT_MAPPED) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryBlockDiff.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryBlockDiff.java index 3d3e925928..6a2ffaa530 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryBlockDiff.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryBlockDiff.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +22,8 @@ import ghidra.util.SystemUtilities; * MemoryBlockDiff determines the types of differences between two memory blocks. */ public class MemoryBlockDiff { - + + //@formatter:off public static final int NAME = 0x001; public static final int START_ADDRESS = 0x002; public static final int END_ADDRESS = 0x004; @@ -32,16 +32,18 @@ public class MemoryBlockDiff { public static final int WRITE = 0x020; public static final int EXECUTE = 0x040; public static final int VOLATILE = 0x080; - public static final int TYPE = 0x100; - public static final int INIT = 0x200; - public static final int SOURCE = 0x400; - public static final int COMMENT = 0x800; - public static final int ALL = 0xFFF; - + public static final int ARTIFICIAL = 0x100; + public static final int TYPE = 0x200; + public static final int INIT = 0x400; + public static final int SOURCE = 0x800; + public static final int COMMENT = 0x1000; + public static final int ALL = 0x1FFF; + //@formatter:on + private MemoryBlock block1; private MemoryBlock block2; private int diffFlags; - + /** * Constructor. MemoryBlockDiff determines the types of differences * between two memory blocks. @@ -57,139 +59,149 @@ public class MemoryBlockDiff { MemoryBlock getBlock1() { return block1; } - + MemoryBlock getBlock2() { return block2; } - + /** * Returns true if the memory block names differ. */ public boolean isNameDifferent() { return (diffFlags & NAME) != 0; } - + /** * Returns true if the start addresses of the memory blocks differ. */ public boolean isStartAddressDifferent() { return (diffFlags & START_ADDRESS) != 0; } - + /** * Returns true if the end addresses of the memory blocks differ. */ public boolean isEndAddressDifferent() { return (diffFlags & END_ADDRESS) != 0; } - + /** * Returns true if the sizes of the memory blocks differ. */ public boolean isSizeDifferent() { return (diffFlags & SIZE) != 0; } - + /** * Returns true if the memory blocks Read flags differ. */ public boolean isReadDifferent() { return (diffFlags & READ) != 0; } - + /** * Returns true if the memory blocks Write flags differ. */ public boolean isWriteDifferent() { return (diffFlags & WRITE) != 0; } - + /** * Returns true if the memory blocks Execute flags differ. */ public boolean isExecDifferent() { return (diffFlags & EXECUTE) != 0; } - + /** * Returns true if the memory blocks Volatile flags differ. */ public boolean isVolatileDifferent() { return (diffFlags & VOLATILE) != 0; } - + + /** + * Returns true if the memory blocks Artificial flags differ. + */ + public boolean isArtificialDifferent() { + return (diffFlags & ARTIFICIAL) != 0; + } + /** * Returns true if the type for the memory blocks differ. */ public boolean isTypeDifferent() { return (diffFlags & TYPE) != 0; } - + /** * Returns true if the initialization of the memory blocks isn't the same. */ public boolean isInitDifferent() { return (diffFlags & INIT) != 0; } - + /** * Returns true if the source for the memory blocks differ. */ public boolean isSourceDifferent() { return (diffFlags & SOURCE) != 0; } - + /** * Returns true if the comments on the memory blocks differ. */ public boolean isCommentDifferent() { return (diffFlags & COMMENT) != 0; } - + /** * Gets a string representation of the types of memory differences for this MemoryBlockDiff. */ public String getDifferencesAsString() { StringBuffer buf = new StringBuffer(); - if((diffFlags & NAME) != 0) { + if ((diffFlags & NAME) != 0) { buf.append("Name "); } - if((diffFlags & START_ADDRESS) != 0) { + if ((diffFlags & START_ADDRESS) != 0) { buf.append("StartAddress "); } - if((diffFlags & END_ADDRESS) != 0) { + if ((diffFlags & END_ADDRESS) != 0) { buf.append("EndAddress "); } - if((diffFlags & SIZE) != 0) { + if ((diffFlags & SIZE) != 0) { buf.append("Size "); } - if((diffFlags & READ) != 0) { + if ((diffFlags & READ) != 0) { buf.append("R "); } - if((diffFlags & WRITE) != 0) { + if ((diffFlags & WRITE) != 0) { buf.append("W "); } - if((diffFlags & EXECUTE) != 0) { + if ((diffFlags & EXECUTE) != 0) { buf.append("X "); } - if((diffFlags & VOLATILE) != 0) { + if ((diffFlags & VOLATILE) != 0) { buf.append("Volatile "); } - if((diffFlags & TYPE) != 0) { + if ((diffFlags & ARTIFICIAL) != 0) { + buf.append("Artificial "); + } + if ((diffFlags & TYPE) != 0) { buf.append("Type "); } - if((diffFlags & INIT) != 0) { + if ((diffFlags & INIT) != 0) { buf.append("Initialized "); } - if((diffFlags & SOURCE) != 0) { + if ((diffFlags & SOURCE) != 0) { buf.append("Source "); } - if((diffFlags & COMMENT) != 0) { + if ((diffFlags & COMMENT) != 0) { buf.append("Comment "); } return buf.toString(); } - + /** * Gets an integer value that has bits set as flags indicating the types of differences * that exist between the two memory blocks. @@ -207,9 +219,9 @@ public class MemoryBlockDiff { if (block2 == null) { return ALL; } - + int flags = 0; - if(!block1.getName().equals(block2.getName())) { + if (!block1.getName().equals(block2.getName())) { flags |= NAME; } if (!block1.getStart().equals(block2.getStart())) { @@ -233,6 +245,9 @@ public class MemoryBlockDiff { if (block1.isVolatile() != block2.isVolatile()) { flags |= VOLATILE; } + if (block1.isArtificial() != block2.isArtificial()) { + flags |= ARTIFICIAL; + } if (!block1.getType().equals(block2.getType())) { flags |= TYPE; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java index 99a007e792..8fab4d6b42 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/MemoryDiff.java @@ -31,14 +31,14 @@ import ghidra.util.task.TaskMonitor; * types of differences. */ public class MemoryDiff { - + private Program program1; private Program program2; private Memory memory1; private Memory memory2; private AddressRange[] ranges; private MemoryBlockDiff[] diffs; - + /** * Constructs an object for determining memory differences between two programs. * @param p1 the first program @@ -46,8 +46,7 @@ public class MemoryDiff { * @throws ProgramConflictException if the program memory can't be compared because the programs * are based on different languages. */ - public MemoryDiff(Program p1, Program p2) - throws ProgramConflictException { + public MemoryDiff(Program p1, Program p2) throws ProgramConflictException { program1 = p1; program2 = p2; memory1 = program1.getMemory(); @@ -55,7 +54,7 @@ public class MemoryDiff { computeRanges(); computeDifferences(); } - + /** * Gets the first program that is part of this MemoryDiff. * @return the first program @@ -63,7 +62,7 @@ public class MemoryDiff { public Program getProgram1() { return program1; } - + /** * Gets the second program that is part of this MemoryDiff. * @return the second program @@ -71,7 +70,7 @@ public class MemoryDiff { public Program getProgram2() { return program2; } - + /** * Determines the address ranges that the two programs memories must be broken into for * properly comparing the programs. Each of these address ranges will exist in the first @@ -82,12 +81,12 @@ public class MemoryDiff { ProgramMemoryComparator memComp = new ProgramMemoryComparator(program1, program2); ArrayList rangeList = new ArrayList(); AddressRangeIterator rangeIter = memComp.getAddressRanges(); - while(rangeIter.hasNext()) { + while (rangeIter.hasNext()) { rangeList.add(rangeIter.next()); } ranges = rangeList.toArray(new AddressRange[rangeList.size()]); } - + /** * Gets the number of address ranges that the two programs memories are broken into for * comparing the programs. @@ -96,7 +95,7 @@ public class MemoryDiff { public int getNumRanges() { return ranges.length; } - + /** * Gets the address range as indicated by index. The index is zero based. Address ranges are * in order from the minimum address to the maximum address range. @@ -106,7 +105,7 @@ public class MemoryDiff { public AddressRange getRange(int index) { return ranges[index]; } - + /** * Gets the memory difference flags for the address range as indicated by index. * @param index the index of the address range to get the difference flags for. @@ -115,7 +114,7 @@ public class MemoryDiff { public MemoryBlockDiff getDifferenceInfo(int index) { return diffs[index]; } - + /** * Determines the memory differences and sets the flags for each associated address range. */ @@ -128,7 +127,7 @@ public class MemoryDiff { diffs[i] = new MemoryBlockDiff(block1, block2); } } - + /** * Gets a string representation of the types of memory differences that exist for the memory * block that contains the indicated address. @@ -143,30 +142,30 @@ public class MemoryDiff { MemoryBlockDiff info = getDifferenceInfo(index); return info.getDifferencesAsString(); } - + /** * Gets the index of the address range containing the indicated address, * if it is contained in the list; - * otherwise, (-(insertion point) - 1). + * otherwise, (-(insertion point) - 1). * @param address the address whose range we are interested in finding. * @return the index of the address range. */ private int getAddressRangeIndex(Address address) { int low = 0; - int high = diffs.length-1; - + int high = diffs.length - 1; + while (low <= high) { - int mid = (low + high) >> 1; - AddressRange range = ranges[mid]; - if (range.contains(address)) { - return mid; - } - else if (address.compareTo(range.getMinAddress()) < 0) { - high = mid - 1; - } - else { - low = mid + 1; - } + int mid = (low + high) >> 1; + AddressRange range = ranges[mid]; + if (range.contains(address)) { + return mid; + } + else if (address.compareTo(range.getMinAddress()) < 0) { + high = mid - 1; + } + else { + low = mid + 1; + } } return -(low + 1); // not found. } @@ -187,7 +186,7 @@ public class MemoryDiff { } return rangeDiffs.toArray(new AddressRange[rangeDiffs.size()]); } - + /** * Determines whether the two memory blocks are the same. * @param block1 the first program's memory block @@ -201,7 +200,7 @@ public class MemoryDiff { else if (block2 == null) { return false; } - if(!block1.getName().equals(block2.getName())) { + if (!block1.getName().equals(block2.getName())) { return false; } if (!block1.getStart().equals(block2.getStart())) { @@ -213,7 +212,7 @@ public class MemoryDiff { if (block1.getSize() != block2.getSize()) { return false; } - if (block1.getPermissions() != block2.getPermissions()) { + if (block1.getFlags() != block2.getFlags()) { return false; } if (!block1.getType().equals(block2.getType())) { @@ -233,8 +232,7 @@ public class MemoryDiff { } return true; } - - + public boolean merge(int row, int mergeFields, TaskMonitor monitor) { if ((mergeFields & MemoryBlockDiff.ALL) == 0) { return false; @@ -246,8 +244,8 @@ public class MemoryDiff { MemoryBlock block1 = blockDiff.getBlock1(); MemoryBlock block2 = blockDiff.getBlock2(); AddressRange range = ranges[row]; - if (shouldMerge(mergeFields, MemoryBlockDiff.START_ADDRESS) - && blockDiff.isStartAddressDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.START_ADDRESS) && + blockDiff.isStartAddressDifferent()) { if (block1 == null) { // Add all or part of a block. Address start2 = block2.getStart(); @@ -271,7 +269,7 @@ public class MemoryDiff { return true; } catch (Exception e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); + Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); } return false; } @@ -295,64 +293,63 @@ public class MemoryDiff { memory1.removeBlock(blockToRemove, monitor); } return true; - } catch (LockException e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } catch (NotFoundException e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } catch (AddressOutOfBoundsException e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - } catch (MemoryBlockException e) { + } + catch (LockException e) { + Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); + } + catch (NotFoundException e) { + Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); + } + catch (AddressOutOfBoundsException e) { + Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); + } + catch (MemoryBlockException e) { Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); } return false; } } - if (shouldMerge(mergeFields, MemoryBlockDiff.END_ADDRESS) - && blockDiff.isEndAddressDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.END_ADDRESS) && + blockDiff.isEndAddressDifferent()) { // TODO } - if (shouldMerge(mergeFields, MemoryBlockDiff.SIZE) - && blockDiff.isSizeDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.SIZE) && blockDiff.isSizeDifferent()) { // TODO } - if (shouldMerge(mergeFields, MemoryBlockDiff.TYPE) - && blockDiff.isTypeDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.TYPE) && blockDiff.isTypeDifferent()) { // TODO } - if (shouldMerge(mergeFields, MemoryBlockDiff.INIT) - && blockDiff.isInitDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.INIT) && blockDiff.isInitDifferent()) { // TODO } - if (shouldMerge(mergeFields, MemoryBlockDiff.NAME) - && blockDiff.isNameDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.NAME) && blockDiff.isNameDifferent()) { try { block1.setName(block2.getName()); - } catch (LockException e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); + } + catch (LockException e) { + Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); } } - if (shouldMerge(mergeFields, MemoryBlockDiff.READ) - && blockDiff.isReadDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.READ) && blockDiff.isReadDifferent()) { block1.setRead(block2.isRead()); } - if (shouldMerge(mergeFields, MemoryBlockDiff.WRITE) - && blockDiff.isWriteDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.WRITE) && blockDiff.isWriteDifferent()) { block1.setWrite(block2.isWrite()); } - if (shouldMerge(mergeFields, MemoryBlockDiff.EXECUTE) - && blockDiff.isExecDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.EXECUTE) && blockDiff.isExecDifferent()) { block1.setExecute(block2.isExecute()); } - if (shouldMerge(mergeFields, MemoryBlockDiff.VOLATILE) - && blockDiff.isVolatileDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.VOLATILE) && blockDiff.isVolatileDifferent()) { block1.setVolatile(block2.isVolatile()); } - if (shouldMerge(mergeFields, MemoryBlockDiff.SOURCE) - && blockDiff.isSourceDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.ARTIFICIAL) && + blockDiff.isArtificialDifferent()) { + block1.setArtificial(block2.isArtificial()); + } + if (shouldMerge(mergeFields, MemoryBlockDiff.SOURCE) && blockDiff.isSourceDifferent()) { block1.setSourceName(block2.getSourceName()); } - if (shouldMerge(mergeFields, MemoryBlockDiff.COMMENT) - && blockDiff.isCommentDifferent()) { + if (shouldMerge(mergeFields, MemoryBlockDiff.COMMENT) && blockDiff.isCommentDifferent()) { block1.setComment(block2.getComment()); } return true; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java index 1aa42f8873..df76dc0fea 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/util/table/field/MemoryTypeProgramLocationBasedTableColumn.java @@ -111,6 +111,7 @@ public class MemoryTypeProgramLocationBasedTableColumn updateForWrite(block, buffy, tooltipBuffy); updateForExecute(block, buffy, tooltipBuffy); updateForVolatile(block, buffy, tooltipBuffy); + updateForArtificial(block, buffy, tooltipBuffy); } private void updateForVolatile(MemoryBlock block, StringBuilder buffy, @@ -128,6 +129,21 @@ public class MemoryTypeProgramLocationBasedTableColumn tooltipBuffy.append(HTMLUtilities.spaces(2)).append("Volatile
"); } + private void updateForArtificial(MemoryBlock block, StringBuilder buffy, + StringBuilder tooltipBuffy) { + + if (block.isArtificial()) { + buffy.append("A"); + tooltipBuffy.append(""); + } + else { + buffy.append(HTMLUtilities.colorString(disabledColor, "A")); + tooltipBuffy.append(""); + } + + tooltipBuffy.append(HTMLUtilities.spaces(2)).append("Artificial
"); + } + private void updateForExecute(MemoryBlock block, StringBuilder buffy, StringBuilder tooltipBuffy) { @@ -186,7 +202,7 @@ public class MemoryTypeProgramLocationBasedTableColumn private class MemoryTypeComparator implements Comparator { @Override public int compare(MemoryBlock o1, MemoryBlock o2) { - return o1.getPermissions() - o2.getPermissions(); + return o1.getFlags() - o2.getFlags(); } } } diff --git a/Ghidra/Features/Base/src/main/resources/PROGRAM.DTD b/Ghidra/Features/Base/src/main/resources/PROGRAM.DTD index d7410bd633..c0e72decdf 100644 --- a/Ghidra/Features/Base/src/main/resources/PROGRAM.DTD +++ b/Ghidra/Features/Base/src/main/resources/PROGRAM.DTD @@ -66,6 +66,7 @@ + diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/memory/MemoryMergeManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/memory/MemoryMergeManagerTest.java index c0ba76f89b..42546aef63 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/memory/MemoryMergeManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/memory/MemoryMergeManagerTest.java @@ -219,7 +219,7 @@ public class MemoryMergeManagerTest extends AbstractMergeTest { blocks[4].setRead(false); blocks[4].setWrite(false); blocks[4].setExecute(false); - + try { blocks[4].setName("special-debug"); } @@ -269,6 +269,7 @@ public class MemoryMergeManagerTest extends AbstractMergeTest { assertTrue(!blocks[4].isWrite()); assertTrue(!blocks[4].isExecute()); assertTrue(!blocks[4].isVolatile()); + assertTrue(!blocks[4].isArtificial()); } @Test diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java index 9c1a61dd28..7bbaa46fff 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java @@ -239,6 +239,12 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { assertEquals("Hello", block1.getComment()); assertEquals(block1, mem.getBlock(addr(5))); + block1.setArtificial(false); + assertTrue(!block1.isArtificial()); + + block1.setArtificial(true); + assertTrue(block1.isArtificial()); + block1.setVolatile(false); assertTrue(!block1.isVolatile()); @@ -473,6 +479,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest { assertNotNull(newBlock); assertEquals(block.getName() + ".copy", newBlock.getName()); assertEquals(addr(500), newBlock.getStart()); + assertEquals(block.isArtificial(), newBlock.isArtificial()); assertEquals(block.isVolatile(), newBlock.isVolatile()); assertEquals(block.isExecute(), newBlock.isExecute()); assertEquals(block.isRead(), newBlock.isRead()); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java index 787e8be331..ddccdb0656 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java @@ -32,7 +32,7 @@ class MyTestMemoryBlock implements MemoryBlock { } @Override - public int getPermissions() { + public int getFlags() { throw new UnsupportedOperationException(); } @@ -136,6 +136,16 @@ class MyTestMemoryBlock implements MemoryBlock { throw new UnsupportedOperationException(); } + @Override + public boolean isArtificial() { + throw new UnsupportedOperationException(); + } + + @Override + public void setArtificial(boolean a) { + throw new UnsupportedOperationException(); + } + @Override public String getSourceName() { throw new UnsupportedOperationException(); diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/ExtMemoryMap.java b/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/ExtMemoryMap.java index 0abb9dcc4a..99fad1e725 100644 --- a/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/ExtMemoryMap.java +++ b/Ghidra/Features/Sarif/src/main/java/sarif/export/mm/ExtMemoryMap.java @@ -30,10 +30,12 @@ public class ExtMemoryMap implements IsfObject { String kind; String comment; boolean isVolatile; + boolean isArtificial; String type; String location; - public ExtMemoryMap(AddressRange range, MemoryBlock block, MemoryMapBytesFile bf, boolean write) throws IOException { + public ExtMemoryMap(AddressRange range, MemoryBlock block, MemoryMapBytesFile bf, boolean write) + throws IOException { String permissions = ""; if (block.isRead()) { @@ -54,12 +56,17 @@ public class ExtMemoryMap implements IsfObject { if (block.isVolatile()) { isVolatile = true; } + if (block.isArtificial()) { + isArtificial = true; + } type = block.getType().name(); - if (block.getType() == MemoryBlockType.BIT_MAPPED || block.getType() == MemoryBlockType.BYTE_MAPPED) { + if (block.getType() == MemoryBlockType.BIT_MAPPED || + block.getType() == MemoryBlockType.BYTE_MAPPED) { // bit mapped blocks can only have one sub-block MemoryBlockSourceInfo info = block.getSourceInfos().get(0); location = info.getMappedRange().get().getMinAddress().toString(); - } else if (block.isInitialized() && write) { + } + else if (block.isInitialized() && write) { location = bf.getFileName() + ":" + bf.getOffset(); bf.writeBytes(range); } diff --git a/Ghidra/Features/Sarif/src/main/java/sarif/managers/MemoryMapSarifMgr.java b/Ghidra/Features/Sarif/src/main/java/sarif/managers/MemoryMapSarifMgr.java index 3d136840d9..6d2368cfe4 100644 --- a/Ghidra/Features/Sarif/src/main/java/sarif/managers/MemoryMapSarifMgr.java +++ b/Ghidra/Features/Sarif/src/main/java/sarif/managers/MemoryMapSarifMgr.java @@ -68,26 +68,27 @@ public class MemoryMapSarifMgr extends SarifMgr { //////////////////////////// @Override - public boolean read(Map result, SarifProgramOptions options, TaskMonitor monitor) - throws CancelledException { + public boolean read(Map result, SarifProgramOptions options, + TaskMonitor monitor) throws CancelledException { try { processMemoryBlock(result, programMgr.getDirectory(), program, monitor); return true; - } catch (FileNotFoundException | AddressOverflowException e) { + } + catch (FileNotFoundException | AddressOverflowException e) { log.appendException(e); } return false; } - private void processMemoryBlock(Map result, String directory, Program program, TaskMonitor monitor) - throws FileNotFoundException, AddressOverflowException { + private void processMemoryBlock(Map result, String directory, Program program, + TaskMonitor monitor) throws FileNotFoundException, AddressOverflowException { String name = (String) result.get("name"); AddressSet set = SarifUtils.getLocations(result, program, null); Address blockAddress = set.getMinAddress(); if (set.getNumAddressRanges() != 1) { - throw new RuntimeException( - "Unexpected number of ranges for block @ " + blockAddress + ": " + set.getNumAddressRanges()); + throw new RuntimeException("Unexpected number of ranges for block @ " + blockAddress + + ": " + set.getNumAddressRanges()); } int length = (int) set.getMaxAddress().subtract(blockAddress) + 1; @@ -100,6 +101,7 @@ public class MemoryMapSarifMgr extends SarifMgr { boolean x = permissions.indexOf("x") >= 0; boolean isVolatile = (boolean) result.get("isVolatile"); + boolean isArtificial = (boolean) result.get("isArtificial"); String comment = (String) result.get("comment"); String type = (String) result.get("type"); @@ -110,33 +112,41 @@ public class MemoryMapSarifMgr extends SarifMgr { MemoryBlock block = null; if (type.equals("DEFAULT")) { if (loc == null) { - block = MemoryBlockUtils.createUninitializedBlock(program, false, name, blockAddress, length, - comment, null, r, w, x, log); - } else { + block = MemoryBlockUtils.createUninitializedBlock(program, false, name, + blockAddress, length, comment, null, r, w, x, log); + } + else { String[] split = loc.split(":"); String fileName = split[0]; int fileOffset = Integer.parseInt(split[1]); byte[] bytes = setData(directory, fileName, fileOffset, length, log); - block = MemoryBlockUtils.createInitializedBlock(program, false, name, blockAddress, - new ByteArrayInputStream(bytes), bytes.length, comment, null, r, w, x, log, monitor); + block = MemoryBlockUtils.createInitializedBlock(program, false, name, + blockAddress, new ByteArrayInputStream(bytes), bytes.length, comment, null, + r, w, x, log, monitor); } - } else if (type.equals("BIT_MAPPED")) { + } + else if (type.equals("BIT_MAPPED")) { Address sourceAddr = factory.getAddress(loc); - block = MemoryBlockUtils.createBitMappedBlock(program, name, blockAddress, sourceAddr, length, comment, - comment, r, w, x, false, log); - } else if (type.equals("BYTE_MAPPED")) { + block = MemoryBlockUtils.createBitMappedBlock(program, name, blockAddress, + sourceAddr, length, comment, comment, r, w, x, false, log); + } + else if (type.equals("BYTE_MAPPED")) { Address sourceAddr = factory.getAddress(loc); - block = MemoryBlockUtils.createByteMappedBlock(program, name, blockAddress, sourceAddr, length, comment, - comment, r, w, x, false, log); - } else { + block = MemoryBlockUtils.createByteMappedBlock(program, name, blockAddress, + sourceAddr, length, comment, comment, r, w, x, false, log); + } + else { throw new RuntimeException("Unexpected type value - " + type); } if (block != null) { block.setVolatile(isVolatile); + block.setArtificial(isArtificial); } - } catch (FileNotFoundException e) { + } + catch (FileNotFoundException e) { throw e; - } catch (Exception e) { + } + catch (Exception e) { log.appendException(e); } } @@ -160,7 +170,8 @@ public class MemoryMapSarifMgr extends SarifMgr { } pos += readLen; } - } catch (IndexOutOfBoundsException e) { + } + catch (IndexOutOfBoundsException e) { log.appendMsg("Read exceeded array length " + length); } return bytes; @@ -170,8 +181,8 @@ public class MemoryMapSarifMgr extends SarifMgr { // SARIF WRITE CURRENT DTD // ///////////////////////////// - void write(JsonArray results, AddressSetView addrs, TaskMonitor monitor, boolean isWriteContents, String filePath) - throws IOException, CancelledException { + void write(JsonArray results, AddressSetView addrs, TaskMonitor monitor, + boolean isWriteContents, String filePath) throws IOException, CancelledException { monitor.setMessage("Writing MEMORY MAP ..."); List> request = new ArrayList<>(); @@ -179,7 +190,8 @@ public class MemoryMapSarifMgr extends SarifMgr { while (iter.hasNext()) { monitor.checkCancelled(); AddressRange ranges = iter.next(); - RangeBlock rb = new RangeBlock(program.getAddressFactory(), program.getMemory(), ranges); + RangeBlock rb = + new RangeBlock(program.getAddressFactory(), program.getMemory(), ranges); for (int i = 0; i < rb.getRanges().length; ++i) { AddressRange range = rb.getRanges()[i]; MemoryBlock block = rb.getBlocks()[i]; @@ -190,16 +202,19 @@ public class MemoryMapSarifMgr extends SarifMgr { try { bf = isWriteContents ? new MemoryMapBytesFile(program, filePath) : null; writeAsSARIF(request, bf, isWriteContents, results); - } finally { + } + finally { if (isWriteContents) { bf.close(); } } } - public static void writeAsSARIF(List> request, MemoryMapBytesFile bytes, - boolean isWriteContents, JsonArray results) throws IOException { - SarifMemoryMapWriter writer = new SarifMemoryMapWriter(request, null, bytes, isWriteContents); + public static void writeAsSARIF(List> request, + MemoryMapBytesFile bytes, boolean isWriteContents, JsonArray results) + throws IOException { + SarifMemoryMapWriter writer = + new SarifMemoryMapWriter(request, null, bytes, isWriteContents); new TaskLauncher(new SarifWriterTask(SUBKEY, writer, results), null); } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/validator/MemoryBlocksValidator.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/validator/MemoryBlocksValidator.java index 7b760d9d61..ed69a3200c 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/validator/MemoryBlocksValidator.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/validator/MemoryBlocksValidator.java @@ -1,6 +1,5 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,13 +68,13 @@ public class MemoryBlocksValidator extends VTPreconditionValidator { sourceProgram.getMemory().getBlock(destBlocks[i].getName()); if (matchingABlock != null) { numMatchingNames++; - int sourcePerm = matchingABlock.getPermissions(); - if (sourcePerm == destBlocks[i].getPermissions()) { + int sourceFlags = matchingABlock.getFlags(); + if (sourceFlags == destBlocks[i].getFlags()) { numMatches++; } else { warnings.append("Block " + destProgName + ":" + blockName + - " doesn't match permissions of " + sourceProgName + ":" + blockName + "\n"); + " has different flags than " + sourceProgName + ":" + blockName + "\n"); status = ConditionStatus.Warning; } } @@ -102,10 +101,12 @@ public class MemoryBlocksValidator extends VTPreconditionValidator { } } if (numMatchingNames == numBlocksNeededForPerfectMatch) { - warnings.append("\nSUMMARY: Number and names of blocks match but not all permissions match."); + warnings.append( + "\nSUMMARY: Number and names of blocks match but not all permissions match."); } else { - warnings.append("\nSUMMARY: Number, names, and permissions of blocks do not all match"); + warnings.append( + "\nSUMMARY: Number, names, and permissions of blocks do not all match"); } } return new ConditionResult(status, warnings.toString()); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java index 408335cbef..f7de363fb5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/generic/MemoryBlockDefinition.java @@ -47,7 +47,7 @@ public class MemoryBlockDefinition { private boolean readPermission = true; private boolean writePermission = true; private boolean executePermission = false; - private boolean volatilePermission = false; + private boolean isVolatile = false; /** * Construct MemoryBlockDefinition using a text-based specified. @@ -107,7 +107,7 @@ public class MemoryBlockDefinition { readPermission = mode.indexOf('r') >= 0; writePermission = mode.indexOf('w') >= 0; executePermission = mode.indexOf('x') >= 0; - volatilePermission = mode.indexOf('v') >= 0; + isVolatile = mode.indexOf('v') >= 0; } try { length = XmlUtilities.parseInt(lengthString); @@ -186,7 +186,7 @@ public class MemoryBlockDefinition { block.setRead(readPermission); block.setWrite(writePermission); block.setExecute(executePermission); - block.setVolatile(volatilePermission); + block.setVolatile(isVolatile); } @Override diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java index d3558209cc..722c4faa50 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java @@ -117,8 +117,8 @@ public class MemoryBlockDB implements MemoryBlock { } @Override - public int getPermissions() { - return record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL); + public int getFlags() { + return record.getByteValue(MemoryMapDBAdapter.FLAGS_COL); } @Override @@ -224,7 +224,7 @@ public class MemoryBlockDB implements MemoryBlock { @Override public boolean isRead() { - return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & READ) != 0; + return (record.getByteValue(MemoryMapDBAdapter.FLAGS_COL) & READ) != 0; } @Override @@ -232,8 +232,9 @@ public class MemoryBlockDB implements MemoryBlock { memMap.lock.acquire(); try { checkValid(); - setPermissionBit(READ, r); - memMap.fireBlockChanged(this); + if (setFlagBit(READ, r)) { + memMap.fireBlockChanged(this); + } } finally { memMap.lock.release(); @@ -242,7 +243,7 @@ public class MemoryBlockDB implements MemoryBlock { @Override public boolean isWrite() { - return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & WRITE) != 0; + return (record.getByteValue(MemoryMapDBAdapter.FLAGS_COL) & WRITE) != 0; } @Override @@ -250,8 +251,9 @@ public class MemoryBlockDB implements MemoryBlock { memMap.lock.acquire(); try { checkValid(); - setPermissionBit(WRITE, w); - memMap.fireBlockChanged(this); + if (setFlagBit(WRITE, w)) { + memMap.fireBlockChanged(this); + } } finally { memMap.lock.release(); @@ -260,7 +262,7 @@ public class MemoryBlockDB implements MemoryBlock { @Override public boolean isExecute() { - return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & EXECUTE) != 0; + return (record.getByteValue(MemoryMapDBAdapter.FLAGS_COL) & EXECUTE) != 0; } @Override @@ -268,9 +270,10 @@ public class MemoryBlockDB implements MemoryBlock { memMap.lock.acquire(); try { checkValid(); - setPermissionBit(EXECUTE, x); - memMap.blockExecuteChanged(this); - memMap.fireBlockChanged(this); + if (setFlagBit(EXECUTE, x)) { + memMap.blockExecuteChanged(this); + memMap.fireBlockChanged(this); + } } finally { memMap.lock.release(); @@ -282,11 +285,13 @@ public class MemoryBlockDB implements MemoryBlock { memMap.lock.acquire(); try { checkValid(); - setPermissionBit(READ, read); - setPermissionBit(WRITE, write); - setPermissionBit(EXECUTE, execute); - memMap.blockExecuteChanged(this); - memMap.fireBlockChanged(this); + boolean changed = setFlagBit(READ, read); + changed |= setFlagBit(WRITE, write); + changed |= setFlagBit(EXECUTE, execute); + if (changed) { + memMap.blockExecuteChanged(this); + memMap.fireBlockChanged(this); + } } finally { memMap.lock.release(); @@ -295,7 +300,7 @@ public class MemoryBlockDB implements MemoryBlock { @Override public boolean isVolatile() { - return (record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL) & VOLATILE) != 0; + return (record.getByteValue(MemoryMapDBAdapter.FLAGS_COL) & VOLATILE) != 0; } @Override @@ -303,8 +308,28 @@ public class MemoryBlockDB implements MemoryBlock { memMap.lock.acquire(); try { checkValid(); - setPermissionBit(VOLATILE, v); - memMap.fireBlockChanged(this); + if (setFlagBit(VOLATILE, v)) { + memMap.fireBlockChanged(this); + } + } + finally { + memMap.lock.release(); + } + } + + @Override + public boolean isArtificial() { + return (record.getByteValue(MemoryMapDBAdapter.FLAGS_COL) & ARTIFICIAL) != 0; + } + + @Override + public void setArtificial(boolean a) { + memMap.lock.acquire(); + try { + checkValid(); + if (setFlagBit(ARTIFICIAL, a)) { + memMap.fireBlockChanged(this); + } } finally { memMap.lock.release(); @@ -430,21 +455,28 @@ public class MemoryBlockDB implements MemoryBlock { } } - private void setPermissionBit(int permBitMask, boolean enable) { - byte p = record.getByteValue(MemoryMapDBAdapter.PERMISSIONS_COL); + private boolean setFlagBit(int flagBitMask, boolean enable) { + byte p = record.getByteValue(MemoryMapDBAdapter.FLAGS_COL); if (enable) { - p |= permBitMask; + if ((p & flagBitMask) == flagBitMask) { + return false; // no change + } + p |= flagBitMask; } else { - p &= ~permBitMask; + if ((p & flagBitMask) == 0) { + return false; // no change + } + p &= ~flagBitMask; } - record.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, p); + record.setByteValue(MemoryMapDBAdapter.FLAGS_COL, p); try { adapter.updateBlockRecord(record); } catch (IOException e) { memMap.dbError(e); } + return true; } @Override @@ -634,8 +666,7 @@ public class MemoryBlockDB implements MemoryBlock { splitBlocks.addAll(subList); subList.clear(); } - return adapter.createBlock(getName() + ".split", addr, newLength, getPermissions(), - splitBlocks); + return adapter.createBlock(getName() + ".split", addr, newLength, getFlags(), splitBlocks); } private int getIndexOfSubBlockToSplit(long offset) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java index eebec0a423..6003b44fc9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java @@ -901,7 +901,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener { mappedAddr = info.getMappedRange().get().getMinAddress(); } MemoryBlockDB newBlock = adapter.createBlock(block.getType(), name, start, length, - mappedAddr, block.isInitialized(), block.getPermissions(), mappingScheme); + mappedAddr, block.isInitialized(), block.getFlags(), mappingScheme); allAddrSet.add(newBlock.getStart(), newBlock.getEnd()); initializeBlocks(); fireBlockAdded(newBlock); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java index adaeabc237..9d46bfbcde 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java @@ -35,7 +35,7 @@ abstract class MemoryMapDBAdapter { static final int NAME_COL = MemoryMapDBAdapterV3.V3_NAME_COL; static final int COMMENTS_COL = MemoryMapDBAdapterV3.V3_COMMENTS_COL; static final int SOURCE_COL = MemoryMapDBAdapterV3.V3_SOURCE_COL; - static final int PERMISSIONS_COL = MemoryMapDBAdapterV3.V3_PERMISSIONS_COL; + static final int FLAGS_COL = MemoryMapDBAdapterV3.V3_FLAGS_COL; static final int START_ADDR_COL = MemoryMapDBAdapterV3.V3_START_ADDR_COL; static final int LENGTH_COL = MemoryMapDBAdapterV3.V3_LENGTH_COL; static final int SEGMENT_COL = MemoryMapDBAdapterV3.V3_SEGMENT_COL; @@ -130,7 +130,7 @@ abstract class MemoryMapDBAdapter { if (block.isInitialized()) { DBBuffer buf = block.getBuffer(); newBlock = newAdapter.createInitializedBlock(block.getName(), block.getStart(), - buf, block.getPermissions()); + buf, block.getFlags()); } else { Address mappedAddress = null; @@ -141,7 +141,7 @@ abstract class MemoryMapDBAdapter { } newBlock = newAdapter.createBlock(block.getType(), block.getName(), block.getStart(), - block.getSize(), mappedAddress, false, block.getPermissions(), 0); + block.getSize(), mappedAddress, false, block.getFlags(), 0); } newBlock.setComment(block.getComment()); newBlock.setSourceName(block.getSourceName()); @@ -185,26 +185,26 @@ abstract class MemoryMapDBAdapter { * @param startAddr the start address of the block. * @param is data source or null for zero initialization * @param length size of block - * @param permissions the new block permissions + * @param flags the new block flags * @return new memory block - * @throws IOException + * @throws IOException if a database IO error occurs. * @throws AddressOverflowException if block length is too large for the underlying space */ abstract MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is, - long length, int permissions) throws AddressOverflowException, IOException; + long length, int flags) throws AddressOverflowException, IOException; /** * Creates a new initialized block object * @param name the name of the block * @param startAddr the start address of the block. * @param buf the DBBuffer used to hold the bytes for the block. - * @param permissions the new block permissions + * @param flags the new block flags * @return new memory block * @throws IOException if a database IO error occurs. * @throws AddressOverflowException if block length is too large for the underlying space */ abstract MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf, - int permissions) throws AddressOverflowException, IOException; + int flags) throws AddressOverflowException, IOException; /** * Creates a new memory block that doesn't have associated bytes. @@ -216,14 +216,14 @@ abstract class MemoryMapDBAdapter { * the block. (used for bit/byte-mapped blocks only) * @param initializeBytes if true, creates a database buffer for storing the * bytes in the block (applies to initialized default blocks only) - * @param permissions the new block permissions + * @param flags the new block flags * @param encodedMappingScheme byte mapping scheme (used by byte-mapped blocks only) * @return new memory block * @throws IOException if a database IO error occurs. * @throws AddressOverflowException if block length is too large for the underlying space */ abstract MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address mappedAddress, boolean initializeBytes, int permissions, + long length, Address mappedAddress, boolean initializeBytes, int flags, int encodedMappingScheme) throws AddressOverflowException, IOException; /** @@ -289,13 +289,13 @@ abstract class MemoryMapDBAdapter { * @param name the name of the block * @param startAddress the start address of the block * @param length the length of the block - * @param permissions the permissions for the block + * @param flags the flags for the block * @param splitBlocks the list of subBlock objects that make up this block * @return the new MemoryBlock * @throws IOException if a database error occurs */ protected abstract MemoryBlockDB createBlock(String name, Address startAddress, long length, - int permissions, List splitBlocks) throws IOException; + int flags, List splitBlocks) throws IOException; /** * Creates a new memory block using a FileBytes @@ -304,12 +304,12 @@ abstract class MemoryMapDBAdapter { * @param length the length of the block * @param fileBytes the {@link FileBytes} object that provides the bytes for this block * @param offset the offset into the {@link FileBytes} object - * @param permissions the permissions for the block + * @param flags the flags for the block * @return the new MemoryBlock * @throws IOException if a database error occurs * @throws AddressOverflowException if block length is too large for the underlying space */ protected abstract MemoryBlockDB createFileBytesBlock(String name, Address startAddress, - long length, FileBytes fileBytes, long offset, int permissions) + long length, FileBytes fileBytes, long offset, int flags) throws IOException, AddressOverflowException; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java index a34a7133f6..e7864cc245 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV0.java @@ -106,15 +106,15 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { RecordIterator it = table.iterator(); while (it.hasNext()) { DBRecord rec = it.next(); - int permissions = 0; + int flags = 0; if (rec.getBooleanValue(V0_IS_READ_COL)) { - permissions |= MemoryBlock.READ; + flags |= MemoryBlock.READ; } if (rec.getBooleanValue(V0_IS_WRITE_COL)) { - permissions |= MemoryBlock.WRITE; + flags |= MemoryBlock.WRITE; } if (rec.getBooleanValue(V0_IS_EXECUTE_COL)) { - permissions |= MemoryBlock.EXECUTE; + flags |= MemoryBlock.EXECUTE; } Address start = addrFactory.oldGetAddressFromLong(rec.getLongValue(V0_START_ADDR_COL)); long startAddr = addrMap.getKey(start, false); @@ -131,7 +131,7 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { blockRecord.setString(NAME_COL, rec.getString(V0_NAME_COL)); blockRecord.setString(COMMENTS_COL, rec.getString(V0_COMMENTS_COL)); blockRecord.setString(SOURCE_COL, rec.getString(V0_SOURCE_NAME_COL)); - blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions); + blockRecord.setByteValue(FLAGS_COL, (byte) flags); blockRecord.setLongValue(START_ADDR_COL, startAddr); blockRecord.setLongValue(LENGTH_COL, length); blockRecord.setIntValue(SEGMENT_COL, segment); @@ -195,13 +195,13 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { @Override MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is, - long length, int permissions) throws IOException { + long length, int flags) throws IOException { throw new UnsupportedOperationException(); } @Override - MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf, - int permissions) throws IOException { + MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf, int flags) + throws IOException { throw new UnsupportedOperationException(); } @@ -231,8 +231,7 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { @Override MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address overlayAddr, boolean initializeBytes, int permissions, - int mappingScheme) + long length, Address overlayAddr, boolean initializeBytes, int flags, int mappingScheme) throws IOException { throw new UnsupportedOperationException(); } @@ -267,14 +266,14 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter { } @Override - protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions, + protected MemoryBlockDB createBlock(String name, Address addr, long length, int flags, List splitBlocks) { throw new UnsupportedOperationException(); } @Override protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length, - FileBytes fileBytes, long offset, int permissions) + FileBytes fileBytes, long offset, int flags) throws IOException, AddressOverflowException { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java index 29e9bd54e9..8e4060a8cc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV1.java @@ -45,9 +45,6 @@ class MemoryMapDBAdapterV1 extends MemoryMapDBAdapterV0 { // "Source Block ID","Segment"}); // - /** - * @param handle - */ MemoryMapDBAdapterV1(DBHandle handle, MemoryMapDB memMap) throws VersionException, IOException { super(handle, memMap, VERSION); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java index 00ce73cdab..35b751a29f 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV2.java @@ -63,7 +63,6 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { // new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address", // "Block Type", "Overlay Address", "Length", "Chain Buffer ID", "Segment" }); - protected MemoryMapDBAdapterV2(DBHandle handle, MemoryMapDB memMap) throws VersionException, IOException { this.handle = handle; @@ -86,7 +85,7 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { RecordIterator it = table.iterator(); while (it.hasNext()) { DBRecord rec = it.next(); - int permissions = rec.getByteValue(V2_PERMISSIONS_COL); + int flags = rec.getByteValue(V2_PERMISSIONS_COL); long startAddr = rec.getLongValue(V2_START_ADDR_COL); long length = rec.getLongValue(V2_LENGTH_COL); @@ -99,7 +98,7 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { blockRecord.setString(NAME_COL, rec.getString(V2_NAME_COL)); blockRecord.setString(COMMENTS_COL, rec.getString(V2_COMMENTS_COL)); blockRecord.setString(SOURCE_COL, rec.getString(V2_SOURCE_COL)); - blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions); + blockRecord.setByteValue(FLAGS_COL, (byte) flags); blockRecord.setLongValue(START_ADDR_COL, startAddr); blockRecord.setLongValue(LENGTH_COL, length); blockRecord.setIntValue(SEGMENT_COL, segment); @@ -149,22 +148,21 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { } @Override - MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf, - int permissions) throws AddressOverflowException, IOException { + MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf, int flags) + throws AddressOverflowException, IOException { throw new UnsupportedOperationException(); } @Override MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is, - long length, int permissions) throws AddressOverflowException, IOException { + long length, int flags) throws AddressOverflowException, IOException { throw new UnsupportedOperationException(); } @Override MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address mappedAddress, boolean initializeBytes, int permissions, - int mappingScheme) - throws AddressOverflowException, IOException { + long length, Address mappedAddress, boolean initializeBytes, int flags, + int mappingScheme) throws AddressOverflowException, IOException { throw new UnsupportedOperationException(); } @@ -223,14 +221,14 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter { } @Override - protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions, + protected MemoryBlockDB createBlock(String name, Address addr, long length, int flags, List splitBlocks) { throw new UnsupportedOperationException(); } @Override protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length, - FileBytes fileBytes, long offset, int permissions) + FileBytes fileBytes, long offset, int flags) throws IOException, AddressOverflowException { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java index 7cacfc57d2..c20972a878 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapterV3.java @@ -39,7 +39,7 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { static final int V3_NAME_COL = 0; static final int V3_COMMENTS_COL = 1; static final int V3_SOURCE_COL = 2; - static final int V3_PERMISSIONS_COL = 3; + static final int V3_FLAGS_COL = 3; static final int V3_START_ADDR_COL = 4; static final int V3_LENGTH_COL = 5; static final int V3_SEGMENT_COL = 6; @@ -60,7 +60,7 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { static Schema V3_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key", new Field[] { StringField.INSTANCE, StringField.INSTANCE, StringField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE }, - new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address", "Length", + new String[] { "Name", "Comments", "Source Name", "Flags", "Start Address", "Length", "Segment" }); static Schema V3_SUB_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key", @@ -116,8 +116,8 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { void refreshMemory() throws IOException { Map> subBlockMap = getSubBlockMap(); - Map blockMap = memoryBlocks.stream().collect( - Collectors.toMap(MemoryBlockDB::getID, Function.identity())); + Map blockMap = memoryBlocks.stream() + .collect(Collectors.toMap(MemoryBlockDB::getID, Function.identity())); List newBlocks = new ArrayList<>(); RecordIterator it = memBlockTable.iterator(); @@ -163,14 +163,14 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { @Override MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is, - long length, int permissions) throws AddressOverflowException, IOException { + long length, int flags) throws AddressOverflowException, IOException { // TODO verify that it is necessary to pre-define all segments in the address map updateAddressMapForAllAddresses(startAddr, length); List subBlocks = new ArrayList<>(); try { - DBRecord blockRecord = createMemoryBlockRecord(name, startAddr, length, permissions); + DBRecord blockRecord = createMemoryBlockRecord(name, startAddr, length, flags); long key = blockRecord.getKey(); int numFullBlocks = (int) (length / maxSubBlockSize); int lastSubBlockSize = (int) (length % maxSubBlockSize); @@ -201,30 +201,30 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { @Override MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr, - long length, Address mappedAddress, boolean initializeBytes, int permissions, + long length, Address mappedAddress, boolean initializeBytes, int flags, int encodedMappingScheme) throws AddressOverflowException, IOException { if (blockType == MemoryBlockType.BIT_MAPPED) { - return createBitMappedBlock(name, startAddr, length, mappedAddress, permissions); + return createBitMappedBlock(name, startAddr, length, mappedAddress, flags); } if (blockType == MemoryBlockType.BYTE_MAPPED) { - return createByteMappedBlock(name, startAddr, length, mappedAddress, permissions, + return createByteMappedBlock(name, startAddr, length, mappedAddress, flags, encodedMappingScheme); } // DEFAULT block type if (initializeBytes) { - return createInitializedBlock(name, startAddr, null, length, permissions); + return createInitializedBlock(name, startAddr, null, length, flags); } - return createUninitializedBlock(name, startAddr, length, permissions); + return createUninitializedBlock(name, startAddr, length, flags); } @Override - MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf, - int permissions) throws AddressOverflowException, IOException { + MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf, int flags) + throws AddressOverflowException, IOException { updateAddressMapForAllAddresses(startAddr, buf.length()); List subBlocks = new ArrayList<>(); - DBRecord blockRecord = createMemoryBlockRecord(name, startAddr, buf.length(), permissions); + DBRecord blockRecord = createMemoryBlockRecord(name, startAddr, buf.length(), flags); long key = blockRecord.getKey(); DBRecord subRecord = @@ -239,11 +239,11 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { } MemoryBlockDB createUninitializedBlock(String name, Address startAddress, long length, - int permissions) throws IOException, AddressOverflowException { + int flags) throws IOException, AddressOverflowException { updateAddressMapForAllAddresses(startAddress, length); List subBlocks = new ArrayList<>(); - DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions); + DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, flags); long key = blockRecord.getKey(); DBRecord subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_UNINITIALIZED, 0, 0); @@ -256,9 +256,9 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { } @Override - protected MemoryBlockDB createBlock(String name, Address startAddress, long length, - int permissions, List splitBlocks) throws IOException { - DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions); + protected MemoryBlockDB createBlock(String name, Address startAddress, long length, int flags, + List splitBlocks) throws IOException { + DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, flags); long key = blockRecord.getKey(); long startingOffset = 0; @@ -274,26 +274,26 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { } MemoryBlockDB createBitMappedBlock(String name, Address startAddress, long length, - Address mappedAddress, int permissions) throws IOException, AddressOverflowException { + Address mappedAddress, int flags) throws IOException, AddressOverflowException { return createMappedBlock(V3_SUB_TYPE_BIT_MAPPED, name, startAddress, length, mappedAddress, - permissions, 0); + flags, 0); } MemoryBlockDB createByteMappedBlock(String name, Address startAddress, long length, - Address mappedAddress, int permissions, int mappingScheme) + Address mappedAddress, int flags, int mappingScheme) throws IOException, AddressOverflowException { return createMappedBlock(V3_SUB_TYPE_BYTE_MAPPED, name, startAddress, length, mappedAddress, - permissions, mappingScheme); + flags, mappingScheme); } @Override protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length, - FileBytes fileBytes, long offset, int permissions) + FileBytes fileBytes, long offset, int flags) throws IOException, AddressOverflowException { updateAddressMapForAllAddresses(startAddress, length); List subBlocks = new ArrayList<>(); - DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions); + DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, flags); long key = blockRecord.getKey(); DBRecord subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_FILE_BYTES, @@ -307,13 +307,12 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { } private MemoryBlockDB createMappedBlock(byte type, String name, Address startAddress, - long length, Address mappedAddress, int permissions, - int mappingScheme) + long length, Address mappedAddress, int flags, int mappingScheme) throws IOException, AddressOverflowException { updateAddressMapForAllAddresses(startAddress, length); List subBlocks = new ArrayList<>(); - DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions); + DBRecord blockRecord = createMemoryBlockRecord(name, startAddress, length, flags); long key = blockRecord.getKey(); long encoded = addrMap.getKey(mappedAddress, true); @@ -377,12 +376,12 @@ public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter { } private DBRecord createMemoryBlockRecord(String name, Address startAddr, long length, - int permissions) { + int flags) { DBRecord record = V3_BLOCK_SCHEMA.createRecord(memBlockTable.getKey()); record.setString(V3_NAME_COL, name); record.setLongValue(V3_START_ADDR_COL, addrMap.getKey(startAddr, true)); record.setLongValue(V3_LENGTH_COL, length); - record.setByteValue(V3_PERMISSIONS_COL, (byte) permissions); + record.setByteValue(V3_FLAGS_COL, (byte) flags); record.setIntValue(V3_SEGMENT_COL, getSegment(startAddr)); return record; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java index 946bbd6223..86033b0f93 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java @@ -46,17 +46,22 @@ public interface MemoryBlock extends Serializable, Comparable { */ public static final String EXTERNAL_BLOCK_NAME = "EXTERNAL"; - // Memory block permission bits + // Memory block flag bits + // NOTE: These are used by stored Flags with DB (8-bits) and + // should be changed other than to add values. + public static int ARTIFICIAL = 0x10; public static int VOLATILE = 0x8; public static int READ = 0x4; public static int WRITE = 0x2; public static int EXECUTE = 0x1; /** - * Returns block permissions as a bit mask. Permission bits defined as READ, WRITE, EXECUTE and - * VOLATILE + * Returns block flags (i.e., permissions and attributes) as a bit mask. + * These bits defined as {@link #READ}, {@link #WRITE}, {@link #EXECUTE}, {@link #VOLATILE}, + * {@link #ARTIFICIAL}. + * @return block flag bits */ - public int getPermissions(); + public int getFlags(); /** * Get memory data in the form of an InputStream. Null is returned for thos memory blocks which @@ -107,6 +112,8 @@ public interface MemoryBlock extends Serializable, Comparable { /** * Get the name of this block + * + * @return block name */ public String getName(); @@ -122,6 +129,8 @@ public interface MemoryBlock extends Serializable, Comparable { /** * Get the comment associated with this block. + * + * @return block comment string */ public String getComment(); @@ -134,6 +143,8 @@ public interface MemoryBlock extends Serializable, Comparable { /** * Returns the value of the read property associated with this block + * + * @return true if enabled else false */ public boolean isRead(); @@ -146,6 +157,8 @@ public interface MemoryBlock extends Serializable, Comparable { /** * Returns the value of the write property associated with this block + * + * @return true if enabled else false */ public boolean isWrite(); @@ -158,6 +171,8 @@ public interface MemoryBlock extends Serializable, Comparable { /** * Returns the value of the execute property associated with this block + * + * @return true if enabled else false */ public boolean isExecute(); @@ -178,18 +193,41 @@ public interface MemoryBlock extends Serializable, Comparable { public void setPermissions(boolean read, boolean write, boolean execute); /** - * Returns the value of the volatile property associated with this block. This attribute is + * Returns the volatile attribute state of this block. This attribute is * generally associated with block of I/O regions of memory. + * + * @return true if enabled else false */ public boolean isVolatile(); /** - * Sets the volatile property associated with this block. + * Sets the volatile attribute state associated of this block. This attribute is + * generally associated with block of I/O regions of memory. * - * @param v the value to set the volatile property to. + * @param v the volatile attribute state. */ public void setVolatile(boolean v); + /** + * Returns the artificial attribute state of this block. This attribute is + * generally associated with blocks which have been fabricated to facilitate + * analysis but do not exist in the same form within a running/loaded process + * state. + * + * @return true if enabled else false + */ + public boolean isArtificial(); + + /** + * Sets the artificial attribute state associated with this block. This attribute is + * generally associated with blocks which have been fabricated to facilitate + * analysis but do not exist in the same form within a running/loaded process + * state. + * + * @param a the artificial attribute state. + */ + public void setArtificial(boolean a); + /** * Get the name of the source of this memory block. * @@ -208,6 +246,7 @@ public interface MemoryBlock extends Serializable, Comparable { * Returns the byte at the given address in this block. * * @param addr the address. + * @return byte value from this block and specified address * @throws MemoryAccessException if any of the requested bytes are uninitialized. * @throws IllegalArgumentException if the Address is not in this block. */ @@ -246,6 +285,7 @@ public interface MemoryBlock extends Serializable, Comparable { * Puts the given byte at the given address in this block. * * @param addr the address. + * @param b byte value * @throws MemoryAccessException if the block is uninitialized * @throws IllegalArgumentException if the Address is not in this block. */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java index 6be0695ec5..986840fc30 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java @@ -46,7 +46,7 @@ public class MemoryBlockStub implements MemoryBlock { } @Override - public int getPermissions() { + public int getFlags() { throw new UnsupportedOperationException(); } @@ -150,6 +150,16 @@ public class MemoryBlockStub implements MemoryBlock { throw new UnsupportedOperationException(); } + @Override + public boolean isArtificial() { + throw new UnsupportedOperationException(); + } + + @Override + public void setArtificial(boolean a) { + throw new UnsupportedOperationException(); + } + @Override public boolean isOverlay() { return false; diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java index f676677139..78bbaea4a9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java @@ -92,7 +92,7 @@ public class MemBlockDBTest extends AbstractGenericTest { assertEquals(false, block.isMapped()); assertNull(block.getComment()); assertNull(block.getSourceName()); - assertEquals(MemoryBlock.READ, block.getPermissions()); + assertEquals(MemoryBlock.READ, block.getFlags()); List sourceInfos = block.getSourceInfos(); @@ -192,7 +192,7 @@ public class MemBlockDBTest extends AbstractGenericTest { assertEquals(true, block.isMapped()); assertNull(block.getComment()); assertNull(block.getSourceName()); - assertEquals(MemoryBlock.READ, block.getPermissions()); + assertEquals(MemoryBlock.READ, block.getFlags()); List sourceInfos = block.getSourceInfos(); @@ -228,7 +228,7 @@ public class MemBlockDBTest extends AbstractGenericTest { assertEquals(true, block.isMapped()); assertNull(block.getComment()); assertNull(block.getSourceName()); - assertEquals(MemoryBlock.READ, block.getPermissions()); + assertEquals(MemoryBlock.READ, block.getFlags()); List sourceInfos = block.getSourceInfos(); @@ -264,7 +264,7 @@ public class MemBlockDBTest extends AbstractGenericTest { assertEquals(false, block.isMapped()); assertNull(block.getComment()); assertNull(block.getSourceName()); - assertEquals(MemoryBlock.READ, block.getPermissions()); + assertEquals(MemoryBlock.READ, block.getFlags()); List sourceInfos = block.getSourceInfos(); diff --git a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/relocation/MIPS_ElfRelocationContext.java b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/relocation/MIPS_ElfRelocationContext.java index ac88f85aa0..a7f4416720 100644 --- a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/relocation/MIPS_ElfRelocationContext.java +++ b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/util/bin/format/elf/relocation/MIPS_ElfRelocationContext.java @@ -310,6 +310,10 @@ class MIPS_ElfRelocationContext extends ElfRelocationContext