GP-1121 PR #3060 Handle stub DWARF info in Rust compilation units

Don't lock down the Ghidra function's prototype when the DWARF info was
emitted by Rust and it is probably an empty stub.
This commit is contained in:
dev747368 2021-07-27 18:38:35 -04:00
parent b87be05fd2
commit d736aea825
6 changed files with 223 additions and 65 deletions

View File

@ -198,6 +198,18 @@ public class DWARFCompilationUnit {
/**
* This ctor is public only for junit tests. Do not use directly.
*
* @param dwarfProgram {@link DWARFProgram}
* @param startOffset offset in provider where it starts
* @param endOffset offset in provider where it ends
* @param length how many bytes following the header the DIEs of this unit take
* @param format DWARF_32 or DWARF_64
* @param version 2, 3, 4
* @param abbreviationOffset offset into abbrev section
* @param pointerSize default size of pointers
* @param compUnitNumber this compunits ordinal in the file
* @param firstDIEOffset start of DIEs in the provider
* @param codeToAbbreviationMap map of abbreviation numbers to {@link DWARFAbbreviation} instances
*/
public DWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset,
long length, int format, short version, long abbreviationOffset, byte pointerSize,

View File

@ -190,6 +190,9 @@ public class DWARFCompileUnit {
/**
* Get the source language of the compile unit.
* <p>
* See {@link DWARFSourceLanguage} for values.
*
* @return the source language of the compile unit
*/
public int getLanguage() {

View File

@ -15,17 +15,14 @@
*/
package ghidra.app.util.bin.format.dwarf4.next;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import java.io.IOException;
import ghidra.app.cmd.comments.AppendCommentCmd;
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
import ghidra.app.util.bin.format.dwarf4.*;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
import ghidra.app.util.bin.format.dwarf4.encoding.*;
import ghidra.app.util.bin.format.dwarf4.expression.*;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.Address;
@ -288,14 +285,6 @@ public class DWARFFunctionImporter {
Function gfunc = createFunction(dfunc, diea);
Number dwarfLanguage = -1;
DWARFCompilationUnit firstCompilationUnit = prog.getCompilationUnits().get(0);
if (firstCompilationUnit != null) {
dwarfLanguage = firstCompilationUnit.getCompileUnit().getLanguage();
}
if (gfunc != null) {
if (formalParams.isEmpty() && dfunc.localVarErrors) {
@ -303,7 +292,10 @@ public class DWARFFunctionImporter {
// don't force the method to have an empty param signature because there are other
// issues afoot.
skipFuncSignature = true;
} else if (formalParams.isEmpty() && dwarfLanguage == (Number) DWARFSourceLanguage.DW_LANG_Rust) {
}
else if (formalParams.isEmpty() && diea.getCompilationUnit()
.getCompileUnit()
.getLanguage() == DWARFSourceLanguage.DW_LANG_Rust) {
// if there were no defined parameters and the language is Rust, don't force an
// empty param signature. Rust language emit dwarf info without types (signatures)
// when used without -g.

View File

@ -0,0 +1,116 @@
/* ###
* 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 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.DWARFSourceLanguage;
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionOpCodes;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Parameter;
import ghidra.util.exception.CancelledException;
public class DWARFFunctionImporterTest extends DWARFTestBase {
@Test
public void testRustMethod_HasParamDefs()
throws CancelledException, IOException, DWARFException {
// test that Ghidra functions in a Rust compilation unit do have their info set
// if they look like they have normal param info
cu = new MockDWARFCompilationUnit(dwarfProg, 0x1000, 0x2000, 0,
DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 0,
DWARFSourceLanguage.DW_LANG_Rust);
cu2 = null;
setMockCompilationUnits(cu);
DebugInfoEntry intDIE = addInt(cu);
DebugInfoEntry fooDIE = newSubprogram("foo", intDIE, 0x410, 10).create(cu);
newFormalParam(fooDIE, "param1", intDIE, DWARFExpressionOpCodes.DW_OP_fbreg, 0x6c)
.create(cu);
importFunctions();
Function fooFunc = program.getListing().getFunctionAt(addr(0x410));
assertNotNull(fooFunc);
assertEquals("foo", fooFunc.getName());
DataType returnType = fooFunc.getReturnType();
assertNotNull(returnType);
assertEquals("int", returnType.getName());
Parameter[] fooParams = fooFunc.getParameters();
assertEquals(fooParams.length, 1);
assertEquals("param1", fooParams[0].getName());
assertEquals("int", fooParams[0].getDataType().getName());
}
@Test
public void testRustMethod_NoParamDefs()
throws CancelledException, IOException, DWARFException {
// test that Ghidra functions in a Rust compilation unit don't have their info set
// if they look like they are one of the stub DIE entries that Rust creates
cu = new MockDWARFCompilationUnit(dwarfProg, 0x1000, 0x2000, 0,
DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 0,
DWARFSourceLanguage.DW_LANG_Rust);
cu2 = null;
setMockCompilationUnits(cu);
DebugInfoEntry intDIE = addInt(cu);
newSubprogram("foo", intDIE, 0x410, 10).create(cu);
importFunctions();
Function fooFunc = program.getListing().getFunctionAt(addr(0x410));
assertNotNull(fooFunc);
assertEquals("foo", fooFunc.getName());
DataType returnType = fooFunc.getReturnType();
assertNotNull(returnType);
assertEquals("undefined", returnType.getName());
}
@Test
public void testNotRustMethod_NoParamDefs()
throws CancelledException, IOException, DWARFException {
// test that Ghidra functions in a non-Rust compilation unit do have their info set
// even if their param info is empty.
cu = new MockDWARFCompilationUnit(dwarfProg, 0x1000, 0x2000, 0,
DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 0,
DWARFSourceLanguage.DW_LANG_C);
cu2 = null;
setMockCompilationUnits(cu);
DebugInfoEntry intDIE = addInt(cu);
newSubprogram("foo", intDIE, 0x410, 10).create(cu);
importFunctions();
Function fooFunc = program.getListing().getFunctionAt(addr(0x410));
assertNotNull(fooFunc);
assertEquals("foo", fooFunc.getName());
DataType returnType = fooFunc.getReturnType();
assertNotNull(returnType);
assertEquals("int", returnType.getName());
}
}

View File

@ -16,6 +16,8 @@
package ghidra.app.util.bin.format.dwarf4;
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute.*;
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFEncoding.*;
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.*;
import static org.junit.Assert.*;
import java.io.IOException;
@ -25,7 +27,7 @@ import org.junit.Before;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFEncoding;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
import ghidra.app.util.bin.format.dwarf4.next.*;
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.NullSectionProvider;
@ -81,17 +83,19 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
DataTypeManagerService dtms = mgr.getDataTypeManagerService();
builtInDTM = dtms.getBuiltInDataTypesManager();
dwarfProg = new DWARFProgram(program, new DWARFImportOptions(), TaskMonitor.DUMMY,
new NullSectionProvider());
importOptions = new DWARFImportOptions();
dwarfProg =
new DWARFProgram(program, importOptions, TaskMonitor.DUMMY, new NullSectionProvider());
rootCP = dwarfProg.getUncategorizedRootDNI().asCategoryPath();
cu = new MockDWARFCompilationUnit(dwarfProg, 0x1000, 0x2000, 0,
DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 0);
DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 0,
DWARFSourceLanguage.DW_LANG_C);
cu2 = new MockDWARFCompilationUnit(dwarfProg, 0x3000, 0x4000, 0,
DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 1);
DWARFCompilationUnit.DWARF_32, (short) 4, 0, (byte) 8, 1,
DWARFSourceLanguage.DW_LANG_C);
dwarfProg.getCompilationUnits().add(cu);
dwarfProg.getCompilationUnits().add(cu2);
setMockCompilationUnits(cu, cu2);
DWARFImportSummary importSummary = new DWARFImportSummary();
dwarfDTM = new DWARFDataTypeManager(dwarfProg, dataMgr, builtInDTM, importSummary);
@ -107,6 +111,13 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
program.release(this);
}
protected void setMockCompilationUnits(DWARFCompilationUnit... compilationUnits) {
dwarfProg.getCompilationUnits().clear();
for (DWARFCompilationUnit compilationUnit : compilationUnits) {
dwarfProg.getCompilationUnits().add(compilationUnit);
}
}
protected void startTransaction() {
transactionID = program.startTransaction("Test");
}
@ -127,7 +138,7 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
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);
@ -142,9 +153,9 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
protected DebugInfoEntry addBaseType(String name, int size, int encoding,
MockDWARFCompilationUnit dcu) {
DIECreator tmp =
new DIECreator(DWARFTag.DW_TAG_base_type).addInt(DW_AT_byte_size, size).addInt(
DW_AT_encoding, encoding);
DIECreator tmp = new DIECreator(DW_TAG_base_type)
.addInt(DW_AT_byte_size, size)
.addInt(DW_AT_encoding, encoding);
if (name != null) {
tmp.addString(DW_AT_name, name);
}
@ -152,28 +163,30 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
}
protected DebugInfoEntry addInt(MockDWARFCompilationUnit dcu) {
return addBaseType("int", 4, DWARFEncoding.DW_ATE_signed, dcu);
return addBaseType("int", 4, DW_ATE_signed, dcu);
}
protected DebugInfoEntry addFloat(MockDWARFCompilationUnit dcu) {
return addBaseType("float", 4, DWARFEncoding.DW_ATE_float, dcu);
return addBaseType("float", 4, DW_ATE_float, dcu);
}
protected DebugInfoEntry addDouble(MockDWARFCompilationUnit dcu) {
return addBaseType("double", 8, DWARFEncoding.DW_ATE_float, dcu);
return addBaseType("double", 8, DW_ATE_float, dcu);
}
protected DebugInfoEntry addTypedef(String name, DebugInfoEntry die,
MockDWARFCompilationUnit dcu) {
assertTrue(die.getCompilationUnit() == dcu);
return new DIECreator(DWARFTag.DW_TAG_typedef).addString(DW_AT_name, name).addRef(
DW_AT_type, die).create(dcu);
return new DIECreator(DW_TAG_typedef)
.addString(DW_AT_name, name)
.addRef(DW_AT_type, die)
.create(dcu);
}
protected DebugInfoEntry addSubprogram(String name, DebugInfoEntry returnTypeDIE,
MockDWARFCompilationUnit dcu) {
assertTrue(returnTypeDIE == null || returnTypeDIE.getCompilationUnit() == dcu);
DIECreator tmp = new DIECreator(DWARFTag.DW_TAG_subprogram);
DIECreator tmp = new DIECreator(DW_TAG_subprogram);
if (name != null) {
tmp.addString(DW_AT_name, name);
}
@ -186,7 +199,7 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
protected DebugInfoEntry addSubroutineType(String name, DebugInfoEntry returnTypeDIE,
MockDWARFCompilationUnit dcu) {
assertTrue(returnTypeDIE == null || returnTypeDIE.getCompilationUnit() == dcu);
DIECreator tmp = new DIECreator(DWARFTag.DW_TAG_subroutine_type);
DIECreator tmp = new DIECreator(DW_TAG_subroutine_type);
if (name != null) {
tmp.addString(DW_AT_name, name);
}
@ -200,26 +213,28 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
MockDWARFCompilationUnit dcu) {
assertTrue(typeDIE == null || typeDIE.getCompilationUnit() == dcu);
assertTrue(parent.getCompilationUnit() == dcu);
return new DIECreator(DWARFTag.DW_TAG_formal_parameter).addRef(DW_AT_type,
typeDIE).setParent(parent).create(dcu);
return new DIECreator(DW_TAG_formal_parameter)
.addRef(DW_AT_type, typeDIE)
.setParent(parent)
.create(dcu);
}
protected DIECreator newSpecStruct(DebugInfoEntry declDIE, int size) {
DIECreator struct =
new DIECreator(DWARFTag.DW_TAG_structure_type).addRef(DW_AT_specification,
declDIE).addInt(DW_AT_byte_size, size);
DIECreator struct = new DIECreator(DW_TAG_structure_type)
.addRef(DW_AT_specification, declDIE)
.addInt(DW_AT_byte_size, size);
return struct;
}
protected DIECreator newDeclStruct(String name) {
DIECreator struct =
new DIECreator(DWARFTag.DW_TAG_structure_type).addBoolean(DW_AT_declaration,
true).addString(DW_AT_name, name);
DIECreator struct = new DIECreator(DW_TAG_structure_type)
.addBoolean(DW_AT_declaration, true)
.addString(DW_AT_name, name);
return struct;
}
protected DIECreator newStruct(String name, int size) {
DIECreator struct = new DIECreator(DWARFTag.DW_TAG_structure_type);
DIECreator struct = new DIECreator(DW_TAG_structure_type);
if (name != null) {
struct.addString(DW_AT_name, name);
}
@ -228,7 +243,7 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
}
protected DebugInfoEntry createEnum(String name, int size, MockDWARFCompilationUnit dcu) {
DIECreator resultEnum = new DIECreator(DWARFTag.DW_TAG_enumeration_type);
DIECreator resultEnum = new DIECreator(DW_TAG_enumeration_type);
if (name != null) {
resultEnum.addString(DW_AT_name, name);
}
@ -239,21 +254,22 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
protected DebugInfoEntry addEnumValue(DebugInfoEntry parentEnum, String valueName,
long valueValue, MockDWARFCompilationUnit dcu) {
assertTrue(parentEnum.getCompilationUnit() == dcu);
DIECreator enumValue =
new DIECreator(DWARFTag.DW_TAG_enumerator).addString(DW_AT_name, valueName).addInt(
DW_AT_const_value, valueValue).setParent(parentEnum);
DIECreator enumValue = new DIECreator(DW_TAG_enumerator)
.addString(DW_AT_name, valueName)
.addInt(DW_AT_const_value, valueValue)
.setParent(parentEnum);
return enumValue.create(dcu);
}
protected DebugInfoEntry addPtr(DebugInfoEntry targetDIE, MockDWARFCompilationUnit dcu) {
assertTrue(targetDIE.getCompilationUnit() == dcu);
return new DIECreator(DWARFTag.DW_TAG_pointer_type).addRef(DW_AT_type, targetDIE).create(
dcu);
return new DIECreator(DW_TAG_pointer_type).addRef(DW_AT_type, targetDIE).create(dcu);
}
protected DebugInfoEntry addFwdPtr(MockDWARFCompilationUnit dcu, int fwdRecordOffset) {
return new DIECreator(DWARFTag.DW_TAG_pointer_type).addRef(DW_AT_type,
getForwardOffset(dcu, fwdRecordOffset)).create(dcu);
return new DIECreator(DW_TAG_pointer_type)
.addRef(DW_AT_type, getForwardOffset(dcu, fwdRecordOffset))
.create(dcu);
}
protected long getForwardOffset(MockDWARFCompilationUnit dcu, int count) {
@ -269,11 +285,10 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
protected DIECreator newMember(DebugInfoEntry parentStruct, String fieldName,
long memberDIEOffset, int offset) {
DIECreator field =
new DIECreator(DWARFTag.DW_TAG_member).addString(DW_AT_name, fieldName)
.addRef(
DW_AT_type, memberDIEOffset)
.setParent(parentStruct);
DIECreator field = new DIECreator(DWARFTag.DW_TAG_member)
.addString(DW_AT_name, fieldName)
.addRef(DW_AT_type, memberDIEOffset)
.setParent(parentStruct);
if (offset != -1) {
field.addInt(DW_AT_data_member_location, offset);
}
@ -284,20 +299,21 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
int offset) {
assertTrue(
dataType == null || dataType.getCompilationUnit() == parentStruct.getCompilationUnit());
DIECreator field =
new DIECreator(DWARFTag.DW_TAG_inheritance).addRef(DW_AT_type, dataType).addInt(
DW_AT_data_member_location, offset).setParent(parentStruct);
DIECreator field = new DIECreator(DW_TAG_inheritance)
.addRef(DW_AT_type, dataType)
.addInt(DW_AT_data_member_location, offset)
.setParent(parentStruct);
return field;
}
protected DebugInfoEntry newArray(MockDWARFCompilationUnit dcu, DebugInfoEntry baseTypeDIE,
boolean elideEmptyDimRangeValue, int... dimensions) {
DebugInfoEntry arrayType = new DIECreator(DWARFTag.DW_TAG_array_type)
.addRef(DW_AT_type, baseTypeDIE).create(dcu);
DebugInfoEntry arrayType = new DIECreator(DW_TAG_array_type)
.addRef(DW_AT_type, baseTypeDIE)
.create(dcu);
for (int dimIndex = 0; dimIndex < dimensions.length; dimIndex++) {
int dim = dimensions[dimIndex];
DIECreator dimDIE = new DIECreator(DWARFTag.DW_TAG_subrange_type)
.setParent(arrayType);
DIECreator dimDIE = new DIECreator(DW_TAG_subrange_type).setParent(arrayType);
if (dim != -1 || !elideEmptyDimRangeValue) {
dimDIE.addInt(DW_AT_upper_bound, dimensions[dimIndex]);
}
@ -308,16 +324,35 @@ public class DWARFTestBase extends AbstractGhidraHeadedIntegrationTest {
protected DebugInfoEntry newArrayUsingCount(MockDWARFCompilationUnit dcu,
DebugInfoEntry baseTypeDIE, int count) {
DebugInfoEntry arrayType = new DIECreator(DWARFTag.DW_TAG_array_type)
DebugInfoEntry arrayType = new DIECreator(DW_TAG_array_type)
.addRef(DW_AT_type, baseTypeDIE)
.create(dcu);
DIECreator dimDIE = new DIECreator(DWARFTag.DW_TAG_subrange_type)
DIECreator dimDIE = new DIECreator(DW_TAG_subrange_type)
.setParent(arrayType);
dimDIE.addInt(DW_AT_count, count);
dimDIE.create(dcu);
return arrayType;
}
protected DIECreator newSubprogram(String name, DebugInfoEntry returnType, long startAddress,
long length) {
return new DIECreator(DW_TAG_subprogram)
.addString(DW_AT_name, name)
.addRef(DW_AT_type, returnType)
.addUInt(DW_AT_low_pc, startAddress)
.addUInt(DW_AT_high_pc, length);
}
protected DIECreator newFormalParam(DebugInfoEntry subprogram, String paramName,
DebugInfoEntry paramDataType, int... locationExpr) {
DIECreator param = new DIECreator(DW_TAG_formal_parameter)
.addString(DW_AT_name, paramName)
.addRef(DW_AT_type, paramDataType)
.addBlock(DW_AT_location, locationExpr)
.setParent(subprogram);
return param;
}
protected Address addr(long l) {
return space.getAddress(l);
}

View File

@ -29,13 +29,13 @@ public class MockDWARFCompilationUnit extends DWARFCompilationUnit {
public MockDWARFCompilationUnit(DWARFProgram dwarfProgram, long startOffset, long endOffset,
long length, int format, short version, long abbreviationOffset, byte pointerSize,
int compUnitNumber) {
int compUnitNumber, int language) {
super(dwarfProgram, startOffset, endOffset, length, format, version, abbreviationOffset,
pointerSize, compUnitNumber, startOffset, null);
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));
0, 0, language, 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);