From c6de9dc49301da57d3d7a572b64667bef7213139 Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Fri, 19 Mar 2021 14:12:06 -0400 Subject: [PATCH] GP-743 fix DWARF handling of empty compile units and older gcc dwarf info --- .../util/bin/format/dwarf4/DIEAggregate.java | 27 +++++++++- .../bin/format/dwarf4/DWARFCompileUnit.java | 5 +- .../next/DWARFStaticVarImporterTest.java | 54 +++++++++++++++++++ .../util/bin/format/dwarf4/DIECreator.java | 18 +++++-- .../util/bin/format/dwarf4/DWARFTestBase.java | 26 ++++++++- .../dwarf4/MockDWARFCompilationUnit.java | 10 +++- 6 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFStaticVarImporterTest.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java index 888e101975..35893f8ff8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DIEAggregate.java @@ -15,9 +15,10 @@ */ package ghidra.app.util.bin.format.dwarf4; -import java.io.IOException; import java.util.*; +import java.io.IOException; + import org.apache.commons.lang3.ArrayUtils; import ghidra.app.util.bin.BinaryReader; @@ -884,6 +885,30 @@ public class DIEAggregate { throw new IOException("Bad/unsupported DW_AT_high_pc attribute value or type"); } + /** + * Returns true if the raw lowPc and highPc values are the same. + *

+ * This indicates an empty range, in which case the caller may want to take + * special steps to avoid issues with Ghidra ranges. + *

+ * Only seen in extremely old gcc versions. Typically the low & high + * pc values are omitted if the CU is empty. + * + * @return boolean true if the LowPC and HighPC values are present and equal + */ + public boolean isLowPCEqualHighPC() { + AttrInfo low = findAttribute(DWARFAttribute.DW_AT_low_pc); + AttrInfo high = findAttribute(DWARFAttribute.DW_AT_high_pc); + if (low != null && high != null && low.form == high.form && + low.attr instanceof DWARFNumericAttribute && + high.attr instanceof DWARFNumericAttribute) { + DWARFNumericAttribute lowVal = (DWARFNumericAttribute) low.attr; + DWARFNumericAttribute highVal = (DWARFNumericAttribute) high.attr; + return lowVal.getValue() == highVal.getValue(); + } + return false; + } + /** * A simple class used by findAttribute() to return the found attribute, along with * the DIE it was found in, and the DWARFForm type of the raw attribute. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompileUnit.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompileUnit.java index 2b46c53442..3c8bd88929 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompileUnit.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFCompileUnit.java @@ -49,11 +49,14 @@ public class DWARFCompileUnit { String comp_dir = diea.getString(DWARFAttribute.DW_AT_comp_dir, null); Number high_pc = null, low_pc = null, language = null, stmt_list = null; + if (diea.hasAttribute(DWARFAttribute.DW_AT_low_pc)) { low_pc = diea.getLowPC(0); } - if (diea.hasAttribute(DWARFAttribute.DW_AT_high_pc)) { + // if lowPC and highPC values are the same, don't read the high value + // because Ghidra can't express an empty range. + if (diea.hasAttribute(DWARFAttribute.DW_AT_high_pc) && !diea.isLowPCEqualHighPC()) { high_pc = diea.getHighPC(); } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFStaticVarImporterTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFStaticVarImporterTest.java new file mode 100644 index 0000000000..331946e691 --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/bin/format/dwarf4/next/DWARFStaticVarImporterTest.java @@ -0,0 +1,54 @@ +/* ### + * 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.util.bin.format.dwarf4.next; + +import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*; +import static org.junit.Assert.*; + +import java.io.IOException; + +import org.junit.Test; + +import ghidra.app.util.bin.format.dwarf4.*; +import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag; +import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes; +import ghidra.program.model.data.IntegerDataType; +import ghidra.program.model.listing.CodeUnit; +import ghidra.program.model.listing.Data; +import ghidra.util.exception.CancelledException; + +public class DWARFStaticVarImporterTest extends DWARFTestBase { + + @Test + public void testIntStaticVar() throws CancelledException, IOException, DWARFException { + DebugInfoEntry intDIE = addInt(cu); + new DIECreator(DWARFTag.DW_TAG_variable) + .addString(DW_AT_name, "static_var1") + .addRef(DW_AT_type, intDIE) + .addBlock(DW_AT_location, DWARFExpressionOpCodes.DW_OP_addr, 0x10, 0x4, 0, 0, 0, 0, + 0, 0) + .create(cu); + + importFunctions(); + + CodeUnit cu = program.getListing().getCodeUnitAt(addr(0x410)); + assertNotNull(cu); + assertEquals("static_var1", cu.getLabel()); + assertEquals(4, cu.getLength()); + assertTrue(((Data) cu).getDataType() instanceof IntegerDataType); + } + +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreator.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreator.java index ada4b7453f..506f22a8ad 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreator.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DIECreator.java @@ -87,6 +87,16 @@ public class DIECreator { return this; } + public DIECreator addBlock(int attribute, int... intBytes) { + byte[] bytes = new byte[intBytes.length]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = (byte) intBytes[i]; + } + attributes.put(attribute, + new AttrInfo(attribute, DWARFForm.DW_FORM_block1, new DWARFBlobAttribute(bytes))); + return this; + } + DWARFAbbreviation createAbbreviation(MockDWARFCompilationUnit cu) { DWARFAttributeSpecification[] attrSpecs = new DWARFAttributeSpecification[attributes.size()]; @@ -121,13 +131,15 @@ public class DIECreator { die.addChild(childDIE); } - cu.addMockEntry(die); - + if (parent == null) { + parent = cu.getCompileUnitDIE(); + } if (parent != null) { + die.setParent(parent); Assert.assertTrue(parent.getCompilationUnit() == cu); parent.addChild(die); - die.setParent(parent); } + cu.addMockEntry(die); return die; } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java index a27508aace..6db58b90a1 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/DWARFTestBase.java @@ -32,6 +32,8 @@ import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.NullSectionProvide import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.database.data.DataTypeManagerDB; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; import ghidra.program.model.data.CategoryPath; import ghidra.program.model.data.DataTypeManager; import ghidra.test.AbstractGhidraHeadedIntegrationTest; @@ -44,7 +46,10 @@ import ghidra.util.task.TaskMonitor; */ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { + protected static final long BaseAddress = 0x400; + protected ProgramDB program; + protected AddressSpace space; protected DataTypeManagerDB dataMgr; protected DataTypeManager builtInDTM; protected int transactionID; @@ -62,10 +67,16 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { */ @Before public void setUp() throws Exception { - program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._TOY, this); + program = createDefaultProgram(testName.getMethodName(), ProgramBuilder._X64, this); + space = program.getAddressFactory().getDefaultAddressSpace(); + dataMgr = program.getDataTypeManager(); startTransaction(); + program.getMemory() + .createInitializedBlock("test", addr(BaseAddress), 500, (byte) 0, TaskMonitor.DUMMY, + false); + AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program); DataTypeManagerService dtms = mgr.getDataTypeManagerService(); builtInDTM = dtms.getBuiltInDataTypesManager(); @@ -113,6 +124,16 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { dwarfDTM.importAllDataTypes(monitor); } + protected void importFunctions() throws CancelledException, IOException, DWARFException { + dwarfProg.checkPreconditions(monitor); + dwarfDTM.importAllDataTypes(monitor); + + DWARFImportSummary importSummary = new DWARFImportSummary(); + DWARFFunctionImporter dfi = + new DWARFFunctionImporter(dwarfProg, dwarfDTM, importOptions, importSummary, monitor); + dfi.importFunctions(); + } + protected DIEAggregate getAggregate(DebugInfoEntry die) throws CancelledException, IOException, DWARFException { dwarfProg.setCurrentCompilationUnit(die.getCompilationUnit(), monitor); @@ -278,4 +299,7 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { return arrayType; } + protected Address addr(long l) { + return space.getAddress(l); + } } diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFCompilationUnit.java b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFCompilationUnit.java index b6b1c6643f..d254b9b52a 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFCompilationUnit.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/util/bin/format/dwarf4/MockDWARFCompilationUnit.java @@ -18,13 +18,14 @@ package ghidra.app.util.bin.format.dwarf4; import java.util.ArrayList; import java.util.List; -import ghidra.app.util.bin.format.dwarf4.encoding.DWARFIdentifierCase; +import ghidra.app.util.bin.format.dwarf4.encoding.*; import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram; import ghidra.util.task.TaskMonitor; public class MockDWARFCompilationUnit extends DWARFCompilationUnit { private List mockEntries = new ArrayList<>(); + private DebugInfoEntry compUnitDIE; public MockDWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset, long length, int format, short version, long abbreviationOffset, byte pointerSize, @@ -35,6 +36,9 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit { setCompileUnit( new DWARFCompileUnit("Mock Comp Unit", "Mock Comp Unit Producer", "Mock Comp Unit Dir", 0, 0, 0, 0, DWARFIdentifierCase.DW_ID_case_insensitive, false, null)); + compUnitDIE = new DIECreator(DWARFTag.DW_TAG_compile_unit) + .addString(DWARFAttribute.DW_AT_name, "MockCompUnit" + compUnitNumber) + .create(this); } @Override @@ -42,6 +46,10 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit { dies.addAll(mockEntries); } + public DebugInfoEntry getCompileUnitDIE() { + return compUnitDIE; + } + public void addMockEntry(DebugInfoEntry die) { mockEntries.add(die); }