GP-743 fix DWARF handling of empty compile units and older gcc dwarf info

This commit is contained in:
dev747368 2021-03-19 14:12:06 -04:00 committed by ghidra1
parent ce056f1ea9
commit c6de9dc493
6 changed files with 133 additions and 7 deletions

View file

@ -15,9 +15,10 @@
*/ */
package ghidra.app.util.bin.format.dwarf4; package ghidra.app.util.bin.format.dwarf4;
import java.io.IOException;
import java.util.*; import java.util.*;
import java.io.IOException;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import ghidra.app.util.bin.BinaryReader; 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"); 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.
* <p>
* This indicates an empty range, in which case the caller may want to take
* special steps to avoid issues with Ghidra ranges.
* <p>
* 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 * 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. * the DIE it was found in, and the DWARFForm type of the raw attribute.

View file

@ -49,11 +49,14 @@ public class DWARFCompileUnit {
String comp_dir = diea.getString(DWARFAttribute.DW_AT_comp_dir, null); String comp_dir = diea.getString(DWARFAttribute.DW_AT_comp_dir, null);
Number high_pc = null, low_pc = null, language = null, stmt_list = null; Number high_pc = null, low_pc = null, language = null, stmt_list = null;
if (diea.hasAttribute(DWARFAttribute.DW_AT_low_pc)) { if (diea.hasAttribute(DWARFAttribute.DW_AT_low_pc)) {
low_pc = diea.getLowPC(0); 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(); high_pc = diea.getHighPC();
} }

View file

@ -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);
}
}

View file

@ -87,6 +87,16 @@ public class DIECreator {
return this; 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) { DWARFAbbreviation createAbbreviation(MockDWARFCompilationUnit cu) {
DWARFAttributeSpecification[] attrSpecs = DWARFAttributeSpecification[] attrSpecs =
new DWARFAttributeSpecification[attributes.size()]; new DWARFAttributeSpecification[attributes.size()];
@ -121,13 +131,15 @@ public class DIECreator {
die.addChild(childDIE); die.addChild(childDIE);
} }
cu.addMockEntry(die); if (parent == null) {
parent = cu.getCompileUnitDIE();
}
if (parent != null) { if (parent != null) {
die.setParent(parent);
Assert.assertTrue(parent.getCompilationUnit() == cu); Assert.assertTrue(parent.getCompilationUnit() == cu);
parent.addChild(die); parent.addChild(die);
die.setParent(parent);
} }
cu.addMockEntry(die);
return die; return die;
} }

View file

@ -32,6 +32,8 @@ import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.NullSectionProvide
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
import ghidra.program.database.data.DataTypeManagerDB; 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.CategoryPath;
import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.DataTypeManager;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
@ -44,7 +46,10 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest { public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
protected static final long BaseAddress = 0x400;
protected ProgramDB program; protected ProgramDB program;
protected AddressSpace space;
protected DataTypeManagerDB dataMgr; protected DataTypeManagerDB dataMgr;
protected DataTypeManager builtInDTM; protected DataTypeManager builtInDTM;
protected int transactionID; protected int transactionID;
@ -62,10 +67,16 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
*/ */
@Before @Before
public void setUp() throws Exception { 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(); dataMgr = program.getDataTypeManager();
startTransaction(); startTransaction();
program.getMemory()
.createInitializedBlock("test", addr(BaseAddress), 500, (byte) 0, TaskMonitor.DUMMY,
false);
AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program); AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(program);
DataTypeManagerService dtms = mgr.getDataTypeManagerService(); DataTypeManagerService dtms = mgr.getDataTypeManagerService();
builtInDTM = dtms.getBuiltInDataTypesManager(); builtInDTM = dtms.getBuiltInDataTypesManager();
@ -113,6 +124,16 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
dwarfDTM.importAllDataTypes(monitor); 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) protected DIEAggregate getAggregate(DebugInfoEntry die)
throws CancelledException, IOException, DWARFException { throws CancelledException, IOException, DWARFException {
dwarfProg.setCurrentCompilationUnit(die.getCompilationUnit(), monitor); dwarfProg.setCurrentCompilationUnit(die.getCompilationUnit(), monitor);
@ -278,4 +299,7 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
return arrayType; return arrayType;
} }
protected Address addr(long l) {
return space.getAddress(l);
}
} }

View file

@ -18,13 +18,14 @@ package ghidra.app.util.bin.format.dwarf4;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.app.util.bin.format.dwarf4.next.DWARFProgram;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class MockDWARFCompilationUnit extends DWARFCompilationUnit { public class MockDWARFCompilationUnit extends DWARFCompilationUnit {
private List<DebugInfoEntry> mockEntries = new ArrayList<>(); private List<DebugInfoEntry> mockEntries = new ArrayList<>();
private DebugInfoEntry compUnitDIE;
public MockDWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset, public MockDWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset,
long length, int format, short version, long abbreviationOffset, byte pointerSize, long length, int format, short version, long abbreviationOffset, byte pointerSize,
@ -35,6 +36,9 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit {
setCompileUnit( setCompileUnit(
new DWARFCompileUnit("Mock Comp Unit", "Mock Comp Unit Producer", "Mock Comp Unit Dir", new DWARFCompileUnit("Mock Comp Unit", "Mock Comp Unit Producer", "Mock Comp Unit Dir",
0, 0, 0, 0, DWARFIdentifierCase.DW_ID_case_insensitive, false, null)); 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 @Override
@ -42,6 +46,10 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit {
dies.addAll(mockEntries); dies.addAll(mockEntries);
} }
public DebugInfoEntry getCompileUnitDIE() {
return compUnitDIE;
}
public void addMockEntry(DebugInfoEntry die) { public void addMockEntry(DebugInfoEntry die) {
mockEntries.add(die); mockEntries.add(die);
} }