diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDAHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDAHeader.java index 753132f237..d548c3b13e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDAHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDAHeader.java @@ -150,8 +150,8 @@ public class LSDAHeader extends GccAnalysisClass { LEB128Info uleb128 = GccAnalysisUtils.readULEB128Info(program, addr); - // this offset it based from *here*.. - ttypeOffset = uleb128.asLong() + curSize; + // this offset is based from the end of *this* field, and the base is the table's last byte + ttypeOffset = uleb128.asLong() + curSize + uleb128.getLength() - 1; createAndCommentData(program, addr, UnsignedLeb128DataType.dataType, comment, CommentType.EOL); @@ -306,7 +306,7 @@ public class LSDAHeader extends GccAnalysisClass { } /** - * The offset from the type offset field to get to the base address of the type table. + * The offset from the start of the LSDA to the base address of the type table. * @return the type table offset */ public int getTTypeOffset() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDATypeTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDATypeTable.java index a3de98912b..1b814d1f94 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDATypeTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDATypeTable.java @@ -20,7 +20,7 @@ import java.util.List; import ghidra.app.cmd.comments.SetCommentCmd; import ghidra.app.plugin.exceptionhandlers.gcc.*; -import ghidra.program.model.address.Address; +import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.CommentType; import ghidra.program.model.listing.Program; @@ -100,7 +100,8 @@ public class LSDATypeTable extends GccAnalysisClass { } } - catch (MemoryAccessException mae) { + catch (MemoryAccessException | AddressOutOfBoundsException e) { + // a pc-relative entry landing outside the address space throws the unchecked one SetCommentCmd commentCmd = new SetCommentCmd(addr, CommentType.EOL, "Unable to resolve pointer"); commentCmd.applyTo(program); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDAHeaderTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDAHeaderTest.java new file mode 100644 index 0000000000..6f589c6373 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDAHeaderTest.java @@ -0,0 +1,113 @@ +/* ### + * 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.exceptionhandlers.gcc.structures.gccexcepttable; + +import static org.junit.Assert.*; + +import org.junit.*; + +import generic.test.AbstractGenericTest; +import ghidra.app.plugin.exceptionhandlers.gcc.RegionDescriptor; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.Program; +import ghidra.util.task.TaskMonitor; + +/** + * Tests that the LSDA header locates the type table correctly however many bytes the TType + * offset ULEB128 occupies. Each LSDA below runs LPStart encoding (omitted), TType encoding + * (pcrel|sdata4), TType offset, call site encoding (uleb128) and table length, call site + * records, action table, then a single type table entry ending at the type table base. + */ +public class LSDAHeaderTest extends AbstractGenericTest { + + private ProgramBuilder builder; + private Program program; + + @Before + public void setUp() throws Exception { + builder = new ProgramBuilder("lsda", ProgramBuilder._X64); + builder.createMemory("lsda", "0x1000", 0x400); + program = builder.getProgram(); + } + + @After + public void tearDown() { + builder.dispose(); + } + + @Test + public void testSingleByteTTypeOffset() throws Exception { + builder.setBytes("0x1000", "ff 1b 0d 01 04 00 02 02 01 01 00 00 f4 02 00 00"); + + LSDATable table = createTable(); + + assertEquals(addr(0x100f), table.getHeader().getTTypeBaseAddress()); + assertEquals(addr(0x1300), table.getTypeTable().getTypeInfoAddress(1)); + } + + @Test + public void testTwoByteTTypeOffset() throws Exception { + builder.setBytes("0x1000", + "ff 1b 80 01 01 78 " + "00 02 02 01 ".repeat(30) + "01 00 80 02 00 00"); + + LSDATable table = createTable(); + + assertEquals(addr(0x1083), table.getHeader().getTTypeBaseAddress()); + assertEquals(addr(0x1300), table.getTypeTable().getTypeInfoAddress(1)); + } + + @Test + public void testThreeByteTTypeOffset() throws Exception { + builder.setBytes("0x1000", "ff 1b 80 80 01 01 00"); + + LSDAHeader header = new LSDAHeader(TaskMonitor.DUMMY, program, region()); + int id = program.startTransaction("lsda"); + try { + header.create(addr(0x1000)); + } + finally { + program.endTransaction(id, true); + } + + assertEquals(addr(0x5004), header.getTTypeBaseAddress()); + } + + private LSDATable createTable() throws Exception { + RegionDescriptor region = region(); + region.setLSDAAddress(addr(0x1000)); + + int id = program.startTransaction("lsda"); + try { + LSDATable table = new LSDATable(TaskMonitor.DUMMY, program); + table.create(addr(0x1000), region); + return table; + } + finally { + program.endTransaction(id, true); + } + } + + private RegionDescriptor region() { + RegionDescriptor region = new RegionDescriptor(program.getMemory().getBlock(addr(0x1000))); + region.setIPRange(new AddressRangeImpl(addr(0x1000), addr(0x1100))); + return region; + } + + private Address addr(long offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDATypeTableTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDATypeTableTest.java new file mode 100644 index 0000000000..39e37a9f41 --- /dev/null +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/exceptionhandlers/gcc/structures/gccexcepttable/LSDATypeTableTest.java @@ -0,0 +1,76 @@ +/* ### + * 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.exceptionhandlers.gcc.structures.gccexcepttable; + +import static org.junit.Assert.*; + +import org.junit.*; + +import generic.test.AbstractGenericTest; +import ghidra.app.plugin.exceptionhandlers.gcc.RegionDescriptor; +import ghidra.program.database.ProgramBuilder; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.Program; +import ghidra.util.task.TaskMonitor; + +/** + * Tests that an entry the type table cannot resolve costs only that entry. The type table is + * built last, so an exception escaping it would discard the call site and action tables that + * {@link LSDATable#create} has already finished. + */ +public class LSDATypeTableTest extends AbstractGenericTest { + + private ProgramBuilder builder; + private Program program; + + @Before + public void setUp() throws Exception { + builder = new ProgramBuilder("lsda", ProgramBuilder._X64); + builder.createMemory("lsda", "0x1000", 0x400); + program = builder.getProgram(); + } + + @After + public void tearDown() { + builder.dispose(); + } + + @Test + public void testUnresolvableTypeEntryKeepsCallSiteTable() throws Exception { + // the sole type table entry is a pc-relative displacement that leaves the address space + builder.setBytes("0x1000", "ff 1b 0d 01 04 00 02 02 01 01 00 00 01 00 00 80"); + + Address lsda = addr(0x1000); + RegionDescriptor region = new RegionDescriptor(program.getMemory().getBlock(lsda)); + region.setIPRange(new AddressRangeImpl(lsda, addr(0x1100))); + region.setLSDAAddress(lsda); + + LSDATable table = new LSDATable(TaskMonitor.DUMMY, program); + int id = program.startTransaction("lsda"); + try { + table.create(lsda, region); + } + finally { + program.endTransaction(id, true); + } + + assertEquals(1, table.getCallSiteTable().getCallSiteRecords().size()); + } + + private Address addr(long offset) { + return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + } +}