From 0299e66dadead914e266a42aa728abd8a2934aa6 Mon Sep 17 00:00:00 2001 From: Meow <259395@student.pwr.edu.pl> Date: Tue, 21 Jul 2026 20:38:21 +0200 Subject: [PATCH 1/3] Fix AddressTableAnalyzer to recognize all undefined data types --- .../ghidra/app/plugin/core/disassembler/AddressTable.java | 4 ++-- .../app/plugin/core/disassembler/AddressTableAnalyzer.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTable.java index 4579981de5..ca3170e47b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/AddressTable.java @@ -285,7 +285,7 @@ public class AddressTable { for (int k = 0; k < totalLen; k++) { Data data = listing.getDataContaining(currentAddress.addWrap(k)); if (data == null || - (!(data.isPointer() || data.getDataType() instanceof Undefined) && + (!(data.isPointer() || Undefined.isUndefined(data.getDataType())) && data.isDefined())) { return false; } @@ -1235,7 +1235,7 @@ public class AddressTable { } // undefined data is OK, could be a pointer - if (data.getDataType() instanceof Undefined) { + if (Undefined.isUndefined(data.getDataType())) { continue; } 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 b86c9ce367..c17e057c1f 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 @@ -322,7 +322,7 @@ public class AddressTableAnalyzer extends AbstractAnalyzer { DataIterator definedData = program.getListing().getDefinedData(addrSet, true); for (Data data : definedData) { DataType dataType = data.getDataType(); - if (dataType instanceof Undefined) { + if (Undefined.isUndefined(dataType)) { continue; } From ea5863548e1e6e8efc8adbce5612b98dfd443782 Mon Sep 17 00:00:00 2001 From: Meow <259395@student.pwr.edu.pl> Date: Tue, 21 Jul 2026 21:53:54 +0200 Subject: [PATCH 2/3] added AddressTableAnalyzerTest for undefined data and undefined array --- .../AddressTableAnalyzerTest.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java new file mode 100644 index 0000000000..5ef22d5830 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java @@ -0,0 +1,122 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ghidra.app.plugin.core.disassembler; + +import static org.junit.Assert.*; + +import org.junit.*; + +import generic.test.AbstractGenericTest; +import ghidra.app.cmd.data.CreateDataCmd; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.database.ProgramDB; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSet; +import ghidra.program.model.data.ArrayDataType; +import ghidra.program.model.data.Undefined1DataType; +import ghidra.program.model.listing.Bookmark; +import ghidra.program.model.listing.BookmarkType; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Listing; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class AddressTableAnalyzerTest extends AbstractGenericTest { + + private ProgramDB program; + private ProgramBuilder builder; + private int pointerSize; + private String[] targets; + + @Before + public void setUp() throws Exception { + targets = new String[] { "0x101000", "0x101100", "0x101200", "0x101300" }; + builder = new ProgramBuilder("AddressTableAnalyzerTest", ProgramBuilder._X64); + builder.createMemory(".text", "0x100000", 0x1000); + builder.createMemory(".data", "0x101000", 0x1000); + program = builder.getProgram(); + pointerSize = program.getDefaultPointerSize(); + } + + @After + public void tearDown() { + builder.dispose(); + } + + @Test + public void testAnalyzerCreatesTableOverUndefinedData() throws Exception { + assertAnalyzerCreatesTableOver(undefinedDataType()); + } + + @Test + public void testAnalyzerCreatesTableOverUndefinedArray() throws Exception { + assertAnalyzerCreatesTableOver(undefinedArrayDataType(targets.length * pointerSize)); + } + + private void assertAnalyzerCreatesTableOver(ArrayDataType dataType) throws Exception { + Address tableStart = builder.addr("0x101100"); + + layOutAddressTable(tableStart); + defineUndefinedArray(tableStart, dataType.getLength()); + + runAnalyzer(tableStart); + + Listing listing = program.getListing(); + for (int i = 0; i < targets.length; i++) { + Data slot = listing.getDefinedDataAt(tableStart.add(i * pointerSize)); + assertNotNull("missing pointer at table[" + i + "]", slot); + assertTrue("table[" + i + "] should be a pointer", slot.isPointer()); + } + + Bookmark bookmark = program.getBookmarkManager() + .getBookmark(tableStart, BookmarkType.ANALYSIS, "Address Table"); + assertNotNull("bookmark should have been created", bookmark); + } + + private void layOutAddressTable(Address tableStart) throws Exception { + long base = tableStart.getOffset(); + for (int i = 0; i < targets.length; i++) { + builder.putAddress(Long.toHexString(base + i * pointerSize), targets[i]); + } + } + + private ArrayDataType undefinedDataType() { + return undefinedArrayDataType(pointerSize); + } + + private ArrayDataType undefinedArrayDataType(int byteLength) { + return new ArrayDataType(new Undefined1DataType(), byteLength, 1); + } + + private void defineUndefinedArray(Address at, int byteLength) { + builder.withTransaction(() -> { + new CreateDataCmd(at, undefinedArrayDataType(byteLength)) + .applyTo(program); + }); + } + + private void runAnalyzer(Address inSet) throws CancelledException { + int tx = program.startTransaction("analyze"); + try { + new AddressTableAnalyzer().added(program, new AddressSet(inSet, inSet), + TaskMonitor.DUMMY, new MessageLog()); + } finally { + program.endTransaction(tx, true); + } + } +} From 75a3b5a028f04ef1d5f315e913475680deeb36cd Mon Sep 17 00:00:00 2001 From: emteere <47253321+emteere@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:39:39 -0400 Subject: [PATCH 3/3] GP-7090 PR and additional test case for split AdressTable from references and address table sizes --- .../AddressTableAnalyzerTest.java | 219 +++++++++++++++--- 1 file changed, 185 insertions(+), 34 deletions(-) diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java index 5ef22d5830..5019390905 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/disassembler/AddressTableAnalyzerTest.java @@ -23,34 +23,40 @@ import org.junit.*; import generic.test.AbstractGenericTest; import ghidra.app.cmd.data.CreateDataCmd; import ghidra.app.util.importer.MessageLog; +import ghidra.framework.options.Options; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSet; -import ghidra.program.model.data.ArrayDataType; -import ghidra.program.model.data.Undefined1DataType; -import ghidra.program.model.listing.Bookmark; -import ghidra.program.model.listing.BookmarkType; -import ghidra.program.model.listing.Data; -import ghidra.program.model.listing.Listing; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; public class AddressTableAnalyzerTest extends AbstractGenericTest { + private static final String BaseUndefinedAddrStr = "0x101100"; private ProgramDB program; private ProgramBuilder builder; private int pointerSize; private String[] targets; + private Address[] tableAddrs; + private AddressTableAnalyzer tableAnalyzer = new AddressTableAnalyzer(); @Before public void setUp() throws Exception { - targets = new String[] { "0x101000", "0x101100", "0x101200", "0x101300" }; + targets = new String[] { "0x101000", "0x101100", "0x101200", "0x101300", "0x0", "0x101400", "0x101500", "0x0", "0x101600" }; + builder = new ProgramBuilder("AddressTableAnalyzerTest", ProgramBuilder._X64); builder.createMemory(".text", "0x100000", 0x1000); builder.createMemory(".data", "0x101000", 0x1000); program = builder.getProgram(); pointerSize = program.getDefaultPointerSize(); + + tableAddrs = new Address[targets.length]; + for (int i = 0; i < tableAddrs.length; i++) { + tableAddrs[i] = builder.addr(BaseUndefinedAddrStr).add(i*pointerSize); + } } @After @@ -59,33 +65,172 @@ public class AddressTableAnalyzerTest extends AbstractGenericTest { } @Test - public void testAnalyzerCreatesTableOverUndefinedData() throws Exception { - assertAnalyzerCreatesTableOver(undefinedDataType()); + public void testAnalyzerCreatesTableOverDefaultData() throws Exception { + setMinTableSize(1); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(1)); + assertBookmarkAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,4,true); + assertBookmarkAt("0x101128"); + assertAnalyzerCreatesTableAt("0x101128",2,true); + assertBookmarkAt("0x101140"); + assertAnalyzerCreatesTableAt("0x101140",1,false); } + + @Test + public void testAnalyzerCreatesTableOverUndefined1Data() throws Exception { + setMinTableSize(1); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(1)); + assertBookmarkAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,4,true); + assertBookmarkAt("0x101128"); + assertAnalyzerCreatesTableAt("0x101128",2,true); + assertBookmarkAt("0x101140"); + assertAnalyzerCreatesTableAt("0x101140",1,false); + } + + @Test + public void testAnalyzerCreatesTableOverUndefined4Data() throws Exception { + setMinTableSize(1); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(4)); + assertBookmarkAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,4,true); + assertBookmarkAt("0x101128"); + assertAnalyzerCreatesTableAt("0x101128",2,true); + assertBookmarkAt("0x101140"); + assertAnalyzerCreatesTableAt("0x101140",1,false); + } + @Test - public void testAnalyzerCreatesTableOverUndefinedArray() throws Exception { - assertAnalyzerCreatesTableOver(undefinedArrayDataType(targets.length * pointerSize)); + public void testAnalyzerCreatesTableOverUndefinedPointerArray() throws Exception { + setMinTableSize(1); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(pointerSize)); + assertBookmarkAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,4,true); + assertBookmarkAt("0x101128"); + assertAnalyzerCreatesTableAt("0x101128",2,true); + assertBookmarkAt("0x101140"); + assertAnalyzerCreatesTableAt("0x101140",1,false); + } + + @Test + public void testAnalyzerCreatesTableMinSize2() throws Exception { + setMinTableSize(2); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(1)); + assertBookmarkAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,4,true); + assertNoBookmarkAt("0x101120"); + assertNoDataAt("0x101120"); + assertBookmarkAt("0x101128"); + assertAnalyzerCreatesTableAt("0x101128",2,true); + assertNoBookmarkAt("0x101140"); + assertNoDataAt("0x101140"); + } + + @Test + public void testAnalyzerCreatesTableWithReference() throws Exception { + String InternalTableRefAddrStr = "0x101108"; + // reference should split tables + builder.createMemoryReadReference("0x100000", InternalTableRefAddrStr); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(1)); + assertBookmarkAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,1,false); + assertAnalyzerCreatesTableAt(InternalTableRefAddrStr,3,true); + assertBookmarkAt(InternalTableRefAddrStr); + } + + @Test + public void testAnalyzerCreatesTableWithReferenceMinSize2() throws Exception { + String InternalTableRefAddrStr = "0x101108"; + // reference should split tables + builder.createMemoryReadReference("0x100000", InternalTableRefAddrStr); + setMinTableSize(2); + setupAndAnalyzeTables(null); + assertNoBookmarkAt(BaseUndefinedAddrStr); + assertNoDataAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(InternalTableRefAddrStr,3,true); + assertBookmarkAt(InternalTableRefAddrStr); } - private void assertAnalyzerCreatesTableOver(ArrayDataType dataType) throws Exception { - Address tableStart = builder.addr("0x101100"); + + @Test + public void testAnalyzerCreatesTableOverUndefinedArrayDataMinSize() throws Exception { + setMinTableSize(2); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(pointerSize)); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,4,true); + assertNoBookmarkAt("0x101120"); + assertNoDataAt("0x101120"); + assertBookmarkAt("0x101128"); + assertAnalyzerCreatesTableAt("0x101128",2,true); + assertNoBookmarkAt("0x101140"); + assertNoDataAt("0x101140"); + } + + + @Test + public void testAnalyzerCreatesTableWithReferenceOverUndefinedArrayData() throws Exception { + String InternalTableRefAddrStr = "0x101108"; + // reference should split tables + builder.createMemoryReadReference("0x100000", InternalTableRefAddrStr); + setMinTableSize(1); + setupAndAnalyzeTables(Undefined.getUndefinedDataType(pointerSize)); + assertBookmarkAt(BaseUndefinedAddrStr); + assertAnalyzerCreatesTableAt(BaseUndefinedAddrStr,1,false); + assertAnalyzerCreatesTableAt(InternalTableRefAddrStr,3,true); + assertBookmarkAt(InternalTableRefAddrStr); + } + + private void setupAndAnalyzeTables(DataType elemDataType) throws Exception { + Address tableStart = builder.addr(BaseUndefinedAddrStr); layOutAddressTable(tableStart); - defineUndefinedArray(tableStart, dataType.getLength()); + if (elemDataType != null) { + defineUndefinedArray(tableStart, elemDataType, (targets.length*pointerSize) / elemDataType.getLength()); + } - runAnalyzer(tableStart); + runAnalyzer(tableStart,tableStart.add(targets.length*pointerSize)); + } + + private void assertAnalyzerCreatesTableAt(String tableStartStr, int size, boolean nothingFollows) throws Exception { + Address tableStart = builder.addr(tableStartStr); Listing listing = program.getListing(); - for (int i = 0; i < targets.length; i++) { + for (int i = 0; i < size; i++) { Data slot = listing.getDefinedDataAt(tableStart.add(i * pointerSize)); assertNotNull("missing pointer at table[" + i + "]", slot); assertTrue("table[" + i + "] should be a pointer", slot.isPointer()); } + if (nothingFollows) { + Data slot = listing.getDefinedDataAt(tableStart.add(size * pointerSize)); + assertNull("Should not be a pointer defined", slot); + } + } + + private void assertNoDataAt(String addrStr) { + Address addr = builder.addr(addrStr); + + Data data = program.getListing().getDefinedDataAt(addr); + if (data == null) { + return; + } + assertTrue(Undefined.isUndefined(data.getDataType())); + } + + private void assertBookmarkAt(String tableStartStr) { + Address tableStart = builder.addr(tableStartStr); + Bookmark bookmark = program.getBookmarkManager() .getBookmark(tableStart, BookmarkType.ANALYSIS, "Address Table"); - assertNotNull("bookmark should have been created", bookmark); + assertNotNull("bookmark should have been created at " + tableStart, bookmark); + } + + private void assertNoBookmarkAt(String tableStartStr) { + Address tableStart = builder.addr(tableStartStr); + + Bookmark bookmark = program.getBookmarkManager() + .getBookmark(tableStart, BookmarkType.ANALYSIS, "Address Table"); + assertNull("bookmark should NOT have been created at " + tableStart, bookmark); } private void layOutAddressTable(Address tableStart) throws Exception { @@ -95,28 +240,34 @@ public class AddressTableAnalyzerTest extends AbstractGenericTest { } } - private ArrayDataType undefinedDataType() { - return undefinedArrayDataType(pointerSize); - } - - private ArrayDataType undefinedArrayDataType(int byteLength) { - return new ArrayDataType(new Undefined1DataType(), byteLength, 1); - } - - private void defineUndefinedArray(Address at, int byteLength) { + private void defineUndefinedArray(Address at, DataType elementType, int numElements) { builder.withTransaction(() -> { - new CreateDataCmd(at, undefinedArrayDataType(byteLength)) + new CreateDataCmd(at, new ArrayDataType(elementType,numElements)) .applyTo(program); }); } + + private void setMinTableSize(int size) { + builder.tx(() -> { + int txId = program.startTransaction("Analyze"); + Options analysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES); + + analysisOptions.setInt("Create Address Tables.Minimum Table Size", size); + + tableAnalyzer.optionsChanged(analysisOptions.getOptions("Create Address Tables"), program); + + program.endTransaction(txId, true); + }); + } - private void runAnalyzer(Address inSet) throws CancelledException { - int tx = program.startTransaction("analyze"); - try { - new AddressTableAnalyzer().added(program, new AddressSet(inSet, inSet), + private void runAnalyzer(Address start, Address end) { + builder.tx(() -> { + tableAnalyzer.added(program, new AddressSet(start, end), TaskMonitor.DUMMY, new MessageLog()); - } finally { - program.endTransaction(tx, true); - } + // need to let full analysis run, as each table found schedules + // follow on create table analysis so that effects of found table can be processed, + // then new tables found + builder.analyze(); + }); } }