diff --git a/Ghidra/Features/Base/certification.manifest b/Ghidra/Features/Base/certification.manifest
index 4ceb05162e..627aaa2641 100644
--- a/Ghidra/Features/Base/certification.manifest
+++ b/Ghidra/Features/Base/certification.manifest
@@ -15,6 +15,7 @@
Module.manifest||GHIDRA||||END|
data/ElfFunctionsThatDoNotReturn||GHIDRA||||END|
data/ExtensionPoint.manifest||GHIDRA||||END|
+data/GolangFunctionsThatDoNotReturn||GHIDRA||||END|
data/MachOFunctionsThatDoNotReturn||GHIDRA||||END|
data/PEFunctionsThatDoNotReturn||GHIDRA||||END|
data/base.file.extensions.icons.theme.properties||GHIDRA||||END|
@@ -85,6 +86,7 @@ data/symbols/win64/mfc90u.exports||GHIDRA||||END|
data/symbols/win64/msvcrt.hints||GHIDRA||||END|
data/typeinfo/generic/generic_clib.gdt||GHIDRA||||END|
data/typeinfo/generic/generic_clib_64.gdt||GHIDRA||||END|
+data/typeinfo/golang/golang_1.18_anybit_any.gdt||GHIDRA||||END|
data/typeinfo/mac_10.9/mac_osx.gdt||GHIDRA||||END|
data/typeinfo/win32/msvcrt/clsids.txt||GHIDRA||reviewed||END|
data/typeinfo/win32/msvcrt/guids.txt||GHIDRA||reviewed||END|
diff --git a/Ghidra/Features/Base/data/ExtensionPoint.manifest b/Ghidra/Features/Base/data/ExtensionPoint.manifest
index 273587cd9c..9fb4bec9b9 100644
--- a/Ghidra/Features/Base/data/ExtensionPoint.manifest
+++ b/Ghidra/Features/Base/data/ExtensionPoint.manifest
@@ -19,4 +19,5 @@ InstructionSkipper
DataTypeReferenceFinder
ChecksumAlgorithm
OverviewColorService
+DWARFFunctionFixup
ElfInfoProducer
diff --git a/Ghidra/Features/Base/data/GolangFunctionsThatDoNotReturn b/Ghidra/Features/Base/data/GolangFunctionsThatDoNotReturn
new file mode 100644
index 0000000000..1c85fca0c5
--- /dev/null
+++ b/Ghidra/Features/Base/data/GolangFunctionsThatDoNotReturn
@@ -0,0 +1,66 @@
+# Golang function names which do not return
+runtime.abort.abi0
+runtime.exit.abi0
+runtime.dieFromSignal
+runtime.exitThread
+runtime.fatal
+runtime.fatalthrow
+runtime.fatalpanic
+runtime.gopanic
+runtime.panicdivide
+runtime.throw
+
+runtime.goPanicIndex
+runtime.goPanicIndexU
+runtime.goPanicSliceAlen
+runtime.goPanicSliceAlenU
+runtime.goPanicSliceAcap
+runtime.goPanicSliceAcapU
+runtime.goPanicSliceB
+runtime.goPanicSliceBU
+runtime.goPanicSlice3Alen
+runtime.goPanicSlice3AlenU
+runtime.goPanicSlice3Acap
+runtime.goPanicSlice3AcapU
+runtime.goPanicSlice3B
+runtime.goPanicSlice3BU
+runtime.goPanicSlice3C
+runtime.goPanicSlice3CU
+runtime.goPanicSliceConvert
+
+runtime.panicIndex
+runtime.panicIndexU
+runtime.panicSliceAlen
+runtime.panicSliceAlenU
+runtime.panicSliceAcap
+runtime.panicSliceAcapU
+runtime.panicSliceB
+runtime.panicSliceBU
+runtime.panicSlice3Alen
+runtime.panicSlice3AlenU
+runtime.panicSlice3Acap
+runtime.panicSlice3AcapU
+runtime.panicSlice3B
+runtime.panicSlice3BU
+runtime.panicSlice3C
+runtime.panicSlice3CU
+runtime.panicSliceConvert
+
+runtime.panicdottypeE
+runtime.panicdottypeI
+runtime.panicnildottype
+
+runtime.panicoverflow
+runtime.panicfloat
+runtime.panicmem
+runtime.panicmemAddr
+runtime.panicshift
+
+runtime.goexit0
+runtime.goexit0.abi0
+runtime.goexit1
+runtime.goexit.abi0
+runtime.Goexit
+
+runtime.sigpanic
+runtime.sigpanic0.abi0
diff --git a/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml b/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml
index d47b3c0f7b..5658ad2140 100644
--- a/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml
+++ b/Ghidra/Features/Base/data/noReturnFunctionConstraints.xml
@@ -1,5 +1,8 @@
+
+ GolangFunctionsThatDoNotReturn
+ ElfFunctionsThatDoNotReturn
@@ -9,6 +12,9 @@
MachOFunctionsThatDoNotReturn
+
+ GolangFunctionsThatDoNotReturn
+ PEFunctionsThatDoNotReturn
diff --git a/Ghidra/Features/Base/data/typeinfo/golang/golang_1.18_anybit_any.gdt b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.18_anybit_any.gdt
new file mode 100644
index 0000000000..85f3ebe9ee
Binary files /dev/null and b/Ghidra/Features/Base/data/typeinfo/golang/golang_1.18_anybit_any.gdt differ
diff --git a/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java b/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java
index 59885a6062..1a2c9f56e7 100644
--- a/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java
+++ b/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java
@@ -57,7 +57,6 @@
import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.format.dwarf4.next.*;
-import ghidra.program.model.data.BuiltInDataTypeManager;
public class DWARF_ExtractorScript extends GhidraScript {
@@ -70,8 +69,7 @@ public class DWARF_ExtractorScript extends GhidraScript {
DWARFImportOptions importOptions = new DWARFImportOptions();
importOptions.setImportLimitDIECount(Integer.MAX_VALUE);
try (DWARFProgram dwarfProg = new DWARFProgram(currentProgram, importOptions, monitor)) {
- BuiltInDataTypeManager dtms = BuiltInDataTypeManager.getDataTypeManager();
- DWARFParser dp = new DWARFParser(dwarfProg, dtms, monitor);
+ DWARFParser dp = new DWARFParser(dwarfProg, monitor);
DWARFImportSummary importSummary = dp.parse();
importSummary.logSummaryResults();
}
diff --git a/Ghidra/Features/Base/ghidra_scripts/FixupGolangFuncParamStorageScript.java b/Ghidra/Features/Base/ghidra_scripts/FixupGolangFuncParamStorageScript.java
new file mode 100644
index 0000000000..98768f34c7
--- /dev/null
+++ b/Ghidra/Features/Base/ghidra_scripts/FixupGolangFuncParamStorageScript.java
@@ -0,0 +1,47 @@
+/* ###
+ * 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.
+ */
+// Assigns custom storage for params of a golang function to match golang's abi-internal
+// register-based calling convention, or abi0 (all stack based) if abi-internal is not
+// specified for the arch.
+//@category Functions
+//@menupath Tools.Fix Golang Function Param Storage
+import java.util.List;
+
+import ghidra.app.script.GhidraScript;
+import ghidra.app.util.bin.format.golang.GoFunctionFixup;
+import ghidra.app.util.bin.format.golang.GoVer;
+import ghidra.program.model.listing.Function;
+
+public class FixupGolangFuncParamStorageScript extends GhidraScript {
+
+ @Override
+ protected void run() throws Exception {
+ Function func;
+ if (currentAddress == null || (func = getFunctionContaining(currentAddress)) == null) {
+ return;
+ }
+ GoVer goVersion = GoVer.fromProgramProperties(currentProgram);
+ if ( goVersion == GoVer.UNKNOWN ) {
+ List versions = List.of(GoVer.values());
+ goVersion =
+ askChoice("Golang Version", "What is the golang version?", versions, GoVer.UNKNOWN);
+ }
+ println("Fixing param storage for function %s@%s".formatted(func.getName(),
+ func.getEntryPoint()));
+ GoFunctionFixup.fixupFunction(func, goVersion);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java
index bbe8e2a995..d2e13ec137 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ApplyDataArchiveAnalyzer.java
@@ -82,6 +82,14 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
setDefaultEnablement(true);
}
+ @Override
+ public boolean getDefaultEnablement(Program program) {
+ if ("golang".equals(program.getCompilerSpec().getCompilerSpecID().toString())) {
+ return false;
+ }
+ return super.getDefaultEnablement(program);
+ }
+
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) {
dtmService = AutoAnalysisManager.getAnalysisManager(program).getDataTypeManagerService();
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java
index d15e87818c..f0a215c001 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java
@@ -26,7 +26,6 @@ import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.DWARFSectionProvid
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.data.BuiltInDataTypeManager;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
@@ -180,13 +179,14 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
return false;
}
- try (DWARFSectionProvider dsp =
- DWARFSectionProviderFactory.createSectionProviderFor(program, monitor)) {
- if (dsp == null) {
- Msg.info(this, "Unable to find DWARF information, skipping DWARF analysis");
- return false;
- }
+ DWARFSectionProvider dsp =
+ DWARFSectionProviderFactory.createSectionProviderFor(program, monitor); // closed by DWARFProgram
+ if (dsp == null) {
+ Msg.info(this, "Unable to find DWARF information, skipping DWARF analysis");
+ return false;
+ }
+ try {
try (DWARFProgram prog = new DWARFProgram(program, importOptions, monitor, dsp)) {
if (prog.getRegisterMappings() == null && importOptions.isImportFuncs()) {
log.appendMsg(
@@ -195,8 +195,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
"], function information may be incorrect / incomplete.");
}
- DWARFParser dp =
- new DWARFParser(prog, BuiltInDataTypeManager.getDataTypeManager(), monitor);
+ DWARFParser dp = new DWARFParser(prog, monitor);
DWARFImportSummary parseResults = dp.parse();
parseResults.logSummaryResults();
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java
new file mode 100644
index 0000000000..ea50023d5f
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java
@@ -0,0 +1,369 @@
+/* ###
+ * 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.analysis;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.Set;
+
+import generic.jar.ResourceFile;
+import ghidra.app.services.*;
+import ghidra.app.util.MemoryBlockUtils;
+import ghidra.app.util.bin.format.elf.info.ElfInfoItem.ItemWithAddress;
+import ghidra.app.util.bin.format.golang.*;
+import ghidra.app.util.bin.format.golang.rtti.GoModuledata;
+import ghidra.app.util.bin.format.golang.rtti.GoRttiContext;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.framework.options.Options;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.data.Structure;
+import ghidra.program.model.lang.Register;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.mem.MemoryBlock;
+import ghidra.program.model.symbol.*;
+import ghidra.util.Msg;
+import ghidra.util.NumericUtilities;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.exception.InvalidInputException;
+import ghidra.util.task.TaskMonitor;
+import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
+import ghidra.xml.XmlParseException;
+import utilities.util.FileUtilities;
+
+/**
+ * Analyzes Golang binaries for RTTI and function symbol information.
+ */
+public class GolangSymbolAnalyzer extends AbstractAnalyzer {
+
+ private final static String NAME = "Golang Symbol";
+ private final static String DESCRIPTION = """
+ Analyze Golang binaries for RTTI and function symbols.
+ 'Apply Data Archives' and 'Shared Return Calls' analyzers should be disabled \
+ for best results.""";
+ private final static String ARTIFICIAL_RUNTIME_ZEROBASE_SYMBOLNAME =
+ "ARTIFICIAL.runtime.zerobase";
+
+ private GolangAnalyzerOptions analyzerOptions = new GolangAnalyzerOptions();
+
+ public GolangSymbolAnalyzer() {
+ super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
+ setPriority(AnalysisPriority.FORMAT_ANALYSIS.after().after());
+ setDefaultEnablement(true);
+ }
+
+ @Override
+ public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
+ throws CancelledException {
+ monitor.setMessage("Golang symbol analyzer");
+
+ try (GoRttiContext programContext = GoRttiContext.getContextFor(program, log)) {
+ if (programContext == null) {
+ Msg.error(this, "Golang analyzer error: unable to get GoRttiContext");
+ return false;
+ }
+ programContext.discoverGoTypes(monitor);
+
+ GoModuledata firstModule = programContext.getFirstModule();
+ programContext.labelStructure(firstModule, "firstmoduledata");
+
+ UnknownProgressWrappingTaskMonitor upwtm =
+ new UnknownProgressWrappingTaskMonitor(monitor, 100);
+ programContext.setMarkupTaskMonitor(upwtm);
+ upwtm.initialize(0);
+ upwtm.setMessage("Marking up Golang RTTI structures");
+ programContext.markup(firstModule, false);
+ programContext.setMarkupTaskMonitor(null);
+
+ markupMiscInfoStructs(program);
+ markupWellknownSymbols(programContext);
+ fixupNoReturnFuncs(program);
+ setupProgramContext(programContext);
+ programContext.recoverDataTypes(monitor);
+
+ if (analyzerOptions.createBootstrapDatatypeArchive) {
+ createBootstrapGDT(programContext, program, monitor);
+ }
+ }
+ catch (IOException e) {
+ Msg.error(this, "Golang analysis failure", e);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void registerOptions(Options options, Program program) {
+ options.registerOption(GolangAnalyzerOptions.CREATE_BOOTSTRAP_GDT_OPTIONNAME,
+ analyzerOptions.createBootstrapDatatypeArchive, null,
+ GolangAnalyzerOptions.CREATE_BOOTSTRAP_GDT_DESC);
+ }
+
+ @Override
+ public void optionsChanged(Options options, Program program) {
+ analyzerOptions.createBootstrapDatatypeArchive =
+ options.getBoolean(GolangAnalyzerOptions.CREATE_BOOTSTRAP_GDT_OPTIONNAME,
+ analyzerOptions.createBootstrapDatatypeArchive);
+ }
+
+ private void markupWellknownSymbols(GoRttiContext programContext) throws IOException {
+ Program program = programContext.getProgram();
+
+ Symbol g0 = SymbolUtilities.getUniqueSymbol(program, "runtime.g0");
+ Structure gStruct = programContext.getGhidraDataType("runtime.g", Structure.class);
+ if (g0 != null && gStruct != null) {
+ programContext.markupAddressIfUndefined(g0.getAddress(), gStruct);
+ }
+
+ Symbol m0 = SymbolUtilities.getUniqueSymbol(program, "runtime.m0");
+ Structure mStruct = programContext.getGhidraDataType("runtime.m", Structure.class);
+ if (m0 != null && mStruct != null) {
+ programContext.markupAddressIfUndefined(m0.getAddress(), mStruct);
+ }
+ }
+
+ private void markupMiscInfoStructs(Program program) {
+ // this also adds "golang" info to program properties
+
+ ItemWithAddress wrappedBuildInfo = GoBuildInfo.findBuildInfo(program);
+ if (wrappedBuildInfo != null && program.getListing()
+ .isUndefined(wrappedBuildInfo.address(), wrappedBuildInfo.address())) {
+ // this will mostly be PE binaries that don't have Elf markup magic stuff
+ wrappedBuildInfo.item().markupProgram(program, wrappedBuildInfo.address());
+ }
+ ItemWithAddress wrappedPeBuildId = PEGoBuildId.findBuildId(program);
+ if (wrappedPeBuildId != null && program.getListing()
+ .isUndefined(wrappedPeBuildId.address(), wrappedPeBuildId.address())) {
+ // HACK to handle golang hack: check if a function symbol was laid down at the location
+ // of the buildId string. If true, convert it to a plain label
+ Symbol[] buildIdSymbols =
+ program.getSymbolTable().getSymbols(wrappedPeBuildId.address());
+ for (Symbol sym : buildIdSymbols) {
+ if (sym.getSymbolType() == SymbolType.FUNCTION) {
+ String symName = sym.getName();
+ sym.delete();
+ try {
+ program.getSymbolTable()
+ .createLabel(wrappedPeBuildId.address(), symName,
+ SourceType.IMPORTED);
+ }
+ catch (InvalidInputException e) {
+ // ignore
+ }
+ break;
+ }
+ }
+ wrappedPeBuildId.item().markupProgram(program, wrappedPeBuildId.address());
+ }
+ }
+
+ private void fixupNoReturnFuncs(Program program) {
+ Set noreturnFuncnames = new HashSet<>();
+
+ try {
+ for (ResourceFile file : NonReturningFunctionNames.findDataFiles(program)) {
+ FileUtilities.getLines(file)
+ .stream()
+ .map(String::trim)
+ .filter(s -> !s.isBlank() && !s.startsWith("#"))
+ .forEach(noreturnFuncnames::add);
+ }
+ }
+ catch (IOException | XmlParseException e) {
+ Msg.error(this, "Failed to read Golang noreturn func data file", e);
+ }
+
+ int count = 0;
+ SymbolTable symbolTable = program.getSymbolTable();
+ for (Symbol symbol : symbolTable.getPrimarySymbolIterator(true)) {
+ String name = symbol.getName(false);
+ if (symbol.isExternal() /* typically not an issue with golang */
+ || !noreturnFuncnames.contains(name)) {
+ continue;
+ }
+
+ Function functionAt = program.getFunctionManager().getFunctionAt(symbol.getAddress());
+ if (functionAt == null) {
+ continue;
+ }
+ if (!functionAt.hasNoReturn()) {
+
+ functionAt.setNoReturn(true);
+
+ program.getBookmarkManager()
+ .setBookmark(symbol.getAddress(), BookmarkType.ANALYSIS,
+ "Non-Returning Function", "Non-Returning Golang Function Identified");
+ count++;
+ }
+ }
+ Msg.info(this, "Marked %d golang funcs as NoReturn".formatted(count));
+ }
+
+ private Address createFakeContextMemory(Program program, long len) {
+ long offset_from_eom = 0x100_000;
+ Address max = program.getAddressFactory().getDefaultAddressSpace().getMaxAddress();
+ Address mbStart = max.subtract(offset_from_eom + len - 1);
+ MemoryBlock newMB =
+ MemoryBlockUtils.createUninitializedBlock(program, false, "ARTIFICAL_GOLANG_CONTEXT",
+ mbStart, len, "Artifical memory block created to hold golang context data types",
+ null, true, true, false, null);
+ return newMB.getStart();
+ }
+
+ private void setupProgramContext(GoRttiContext programContext) throws IOException {
+ Program program = programContext.getProgram();
+ GoRegisterInfo goRegInfo = GoRegisterInfoManager.getInstance()
+ .getRegisterInfoForLang(program.getLanguage(),
+ programContext.getGolangVersion());
+
+ MemoryBlock txtMemblock = program.getMemory().getBlock(".text");
+ if (txtMemblock != null && goRegInfo.getZeroRegister() != null) {
+ try {
+ program.getProgramContext()
+ .setValue(goRegInfo.getZeroRegister(), txtMemblock.getStart(),
+ txtMemblock.getEnd(), BigInteger.ZERO);
+ }
+ catch (ContextChangeException e) {
+ Msg.error(this, "Unexpected Error", e);
+ }
+ }
+
+ int alignment = programContext.getPtrSize();
+ long sizeNeeded = 0;
+
+ Symbol zerobase = SymbolUtilities.getUniqueSymbol(program, "runtime.zerobase");
+ long zerobaseSymbol = sizeNeeded;
+ sizeNeeded += zerobase == null
+ ? NumericUtilities.getUnsignedAlignedValue(1 /* sizeof(byte) */, alignment)
+ : 0;
+
+ long gStructOffset = sizeNeeded;
+ Structure gStruct = programContext.getGhidraDataType("runtime.g", Structure.class);
+ sizeNeeded += gStruct != null
+ ? NumericUtilities.getUnsignedAlignedValue(gStruct.getLength(), alignment)
+ : 0;
+
+ long mStructOffset = sizeNeeded;
+ Structure mStruct = programContext.getGhidraDataType("runtime.m", Structure.class);
+ sizeNeeded += mStruct != null
+ ? NumericUtilities.getUnsignedAlignedValue(mStruct.getLength(), alignment)
+ : 0;
+
+ Address contextMemoryAddr = sizeNeeded > 0
+ ? createFakeContextMemory(program, sizeNeeded)
+ : null;
+
+ if (zerobase == null) {
+ programContext.labelAddress(contextMemoryAddr.add(zerobaseSymbol),
+ ARTIFICIAL_RUNTIME_ZEROBASE_SYMBOLNAME);
+ }
+
+ if (gStruct != null) {
+ Address gAddr = contextMemoryAddr.add(gStructOffset);
+ programContext.markupAddressIfUndefined(gAddr, gStruct);
+ programContext.labelAddress(gAddr, "CURRENT_G");
+
+ Register currentGoroutineReg = goRegInfo.getCurrentGoroutineRegister();
+ if (currentGoroutineReg != null && txtMemblock != null) {
+ // currentGoroutineReg is set in a platform's arch-golang.register.info in
+ // the element for arch's that have a dedicated processor
+ // register that points at G
+ try {
+ program.getProgramContext()
+ .setValue(currentGoroutineReg, txtMemblock.getStart(),
+ txtMemblock.getEnd(), gAddr.getOffsetAsBigInteger());
+ }
+ catch (ContextChangeException e) {
+ Msg.error(this, "Unexpected Error", e);
+ }
+ }
+ }
+ if (mStruct != null) {
+ Address mAddr = contextMemoryAddr.add(mStructOffset);
+ programContext.markupAddressIfUndefined(mAddr, mStruct);
+ }
+ }
+
+ private void createBootstrapGDT(GoRttiContext programContext, Program program,
+ TaskMonitor monitor) throws IOException {
+ GoVer goVer = programContext.getGolangVersion();
+ String osName = GoRttiContext.getGolangOSString(program);
+ String gdtFilename =
+ GoRttiContext.getGDTFilename(goVer, programContext.getPtrSize(), osName);
+ gdtFilename =
+ gdtFilename.replace(".gdt", "_%d.gdt".formatted(System.currentTimeMillis()));
+ File gdt = new File(System.getProperty("user.home"), gdtFilename);
+ programContext.exportTypesToGDT(gdt, monitor);
+ Msg.info(this, "Golang bootstrap GDT created: " + gdt);
+ }
+
+ @Override
+ public void analysisEnded(Program program) {
+ }
+
+ @Override
+ public boolean canAnalyze(Program program) {
+ return "golang".equals(
+ program.getCompilerSpec().getCompilerSpecDescription().getCompilerSpecName());
+ }
+
+ @Override
+ public boolean getDefaultEnablement(Program program) {
+ return true;
+ }
+
+ private static Address getArtificalZerobaseAddress(Program program) {
+ Symbol zerobaseSym =
+ SymbolUtilities.getUniqueSymbol(program, ARTIFICIAL_RUNTIME_ZEROBASE_SYMBOLNAME);
+ return zerobaseSym != null ? zerobaseSym.getAddress() : null;
+ }
+
+ /**
+ * Return the address of the golang zerobase symbol, or an artificial substitute.
+ *
+ * The zerobase symbol is used as the location of parameters that are zero-length.
+ *
+ * @param prog
+ * @return
+ */
+ public static Address getZerobaseAddress(Program prog) {
+ Symbol zerobaseSym = SymbolUtilities.getUniqueSymbol(prog, "runtime.zerobase");
+ Address zerobaseAddr = zerobaseSym != null
+ ? zerobaseSym.getAddress()
+ : getArtificalZerobaseAddress(prog);
+ if (zerobaseAddr == null) {
+ zerobaseAddr = prog.getImageBase().getAddressSpace().getMinAddress(); // ICKY HACK
+ Msg.warn(GoFunctionFixup.class,
+ "Unable to find Golang runtime.zerobase, using " + zerobaseAddr);
+ }
+ return zerobaseAddr;
+ }
+
+ //--------------------------------------------------------------------------------------------
+ private static class GolangAnalyzerOptions {
+ static final String CREATE_BOOTSTRAP_GDT_OPTIONNAME = "Create Bootstrap GDT";
+ static final String CREATE_BOOTSTRAP_GDT_DESC = """
+ Creates a Ghidra data type archive that contains just the necessary \
+ data types to parse other golang binaries. \
+ DWARF data is needed for this to succeed. \
+ The new GDT file will be placed in the user's home directory and will \
+ be called golang_MajorVer.MinorVer_XXbit_osname.NNNNNNNNN.gdt, where NNNNNN \
+ is a timestamp.""";
+ boolean createBootstrapDatatypeArchive;
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/NonReturningFunctionNames.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/NonReturningFunctionNames.java
index 6749b61860..a77a3bad05 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/NonReturningFunctionNames.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/NonReturningFunctionNames.java
@@ -15,11 +15,12 @@
*/
package ghidra.app.plugin.core.analysis;
-import java.io.FileNotFoundException;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
import org.xml.sax.SAXException;
import generic.constraint.DecisionSet;
@@ -30,7 +31,7 @@ import ghidra.util.Msg;
import ghidra.util.constraint.ProgramDecisionTree;
import ghidra.xml.XmlParseException;
-class NonReturningFunctionNames {
+public class NonReturningFunctionNames {
private static final String CONSTRAINED_FILENAME_PROPERTY = "functionNamesFile";
private static final String DATA_DIR = "data";
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/SharedReturnAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/SharedReturnAnalyzer.java
index e261d446e4..01e551da70 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/SharedReturnAnalyzer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/SharedReturnAnalyzer.java
@@ -20,8 +20,9 @@ import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
-import ghidra.program.model.lang.*;
-import ghidra.program.model.listing.*;
+import ghidra.program.model.lang.GhidraLanguagePropertyKeys;
+import ghidra.program.model.lang.Language;
+import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@@ -86,6 +87,9 @@ public class SharedReturnAnalyzer extends AbstractAnalyzer {
boolean sharedReturnEnabled = language.getPropertyAsBoolean(
GhidraLanguagePropertyKeys.ENABLE_SHARED_RETURN_ANALYSIS, true);
+ if ("golang".equals(program.getCompilerSpec().getCompilerSpecID().toString())) {
+ sharedReturnEnabled = false;
+ }
// If the language (in the .pspec file) overrides this setting, use that value
boolean contiguousFunctionsEnabled = language.getPropertyAsBoolean(
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/ProgramStartingLocationOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/ProgramStartingLocationOptions.java
index ff58bd0e52..7fe38e2dfe 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/ProgramStartingLocationOptions.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/navigation/ProgramStartingLocationOptions.java
@@ -60,7 +60,7 @@ public class ProgramStartingLocationOptions implements OptionsChangeListener {
"a newly discovered starting symbol, provided the user hasn't manually moved.";
private static final String DEFAULT_STARTING_SYMBOLS =
- "main, WinMain, libc_start_main, WinMainStartup, start, entry";
+ "main, WinMain, libc_start_main, WinMainStartup, start, entry, main.main";
public static enum StartLocationType {
LOWEST_ADDRESS("Lowest Address"),
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java
index 395dadb12f..19a226f915 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/BinaryReader.java
@@ -98,9 +98,9 @@ public class BinaryReader {
T get(InputStream is) throws IOException;
}
- private final ByteProvider provider;
- private DataConverter converter;
- private long currentIndex;
+ protected final ByteProvider provider;
+ protected DataConverter converter;
+ protected long currentIndex;
/**
* Constructs a reader using the given ByteProvider and endian-order.
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java
index a4794fe773..938eef22e7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/MemoryByteProvider.java
@@ -151,7 +151,7 @@ public class MemoryByteProvider implements ByteProvider {
* @param maxAddress the highest address accessible by this provider (inclusive), or null
* if there is no memory
*/
- private MemoryByteProvider(Memory memory, Address baseAddress, Address maxAddress) {
+ public MemoryByteProvider(Memory memory, Address baseAddress, Address maxAddress) {
this.memory = memory;
this.baseAddress = baseAddress;
this.maxOffset = maxAddress != null
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 dd7d79d606..ef0eacc14a 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
@@ -17,9 +17,8 @@ package ghidra.app.util.bin.format.dwarf4;
import static ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_formal_parameter;
-import java.util.*;
-
import java.io.IOException;
+import java.util.*;
import org.apache.commons.lang3.ArrayUtils;
@@ -301,6 +300,32 @@ public class DIEAggregate {
return null;
}
+ /**
+ * Return an attribute that is present in this {@link DIEAggregate}, or in any of its
+ * direct children (of a specific type)
+ *
+ * @param
+ * @param attribute the attribute to find
+ * @param childTag the type of children to search
+ * @param clazz type of the attribute to return
+ * @return attribute value, or null if not found
+ */
+ public T findAttributeInChildren(int attribute, int childTag,
+ Class clazz) {
+ T attributeValue = getAttribute(attribute, clazz);
+ if (attributeValue != null) {
+ return attributeValue;
+ }
+ for (DebugInfoEntry childDIE : getChildren(childTag)) {
+ DIEAggregate childDIEA = getProgram().getAggregate(childDIE);
+ attributeValue = childDIEA.getAttribute(attribute, clazz);
+ if (attributeValue != null) {
+ return attributeValue;
+ }
+ }
+ return null;
+ }
+
/**
* Finds a {@link DWARFAttributeValue attribute} with a matching {@link DWARFAttribute} type
*
@@ -620,11 +645,13 @@ public class DIEAggregate {
* Blob attributes are treated as a single location record for the current CU, using the
* blob bytes as the DWARF expression of the location record.
*
- * @param attribute
- * @return
+ * @param attribute the attribute to evaluate
+ * @param range the address range the location covers (may be discarded if the attribute
+ * value is a location list with its own range values)
+ * @return list of locations, empty if missing, never null
* @throws IOException
*/
- public List getAsLocation(int attribute) throws IOException {
+ public List getAsLocation(int attribute, DWARFRange range) throws IOException {
AttrInfo attrInfo = findAttribute(attribute);
if (attrInfo == null) {
return List.of();
@@ -633,7 +660,7 @@ public class DIEAggregate {
return readDebugLocList(dnum.getUnsignedValue());
}
else if (attrInfo.attr instanceof DWARFBlobAttribute dblob) {
- return _exprBytesAsLocation(dblob);
+ return _exprBytesAsLocation(dblob, range);
}
else {
throw new UnsupportedOperationException(
@@ -727,22 +754,8 @@ public class DIEAggregate {
return results;
}
- private List _exprBytesAsLocation(DWARFBlobAttribute attr) {
- List list = new ArrayList<>(1);
-
- Number highPc = getCompilationUnit().getCompileUnit().getHighPC();
- Number lowPc = getCompilationUnit().getCompileUnit().getLowPC();
- if (highPc == null) {
- // a DW_AT_high_pc is not required
- // in this case presumably we don't have to choose from a location list based on range
- // Make a 1-byte range
- highPc = lowPc;
- }
- // If there is no low either, assume we don't need a range, and make a (0,0) range
- DWARFRange range = (lowPc == null) ? new DWARFRange(0, 0)
- : new DWARFRange(lowPc.longValue(), highPc.longValue());
- list.add(new DWARFLocation(range, attr.getBytes()));
- return list;
+ private List _exprBytesAsLocation(DWARFBlobAttribute attr, DWARFRange range) {
+ return List.of(new DWARFLocation(range, attr.getBytes()));
}
/**
@@ -997,8 +1010,9 @@ public class DIEAggregate {
}
}
if ( !params.isEmpty() ) {
- Msg.warn(this, "Extra params in concrete DIE instance: " + params);
- Msg.warn(this, this.toString());
+ //Msg.warn(this, "Extra params in concrete DIE instance: " + params);
+ //Msg.warn(this, this.toString());
+ newParams.addAll(params);
}
params = newParams;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLocation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLocation.java
index 023c04bfce..f37b4e14ee 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLocation.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFLocation.java
@@ -15,6 +15,8 @@
*/
package ghidra.app.util.bin.format.dwarf4;
+import java.util.List;
+
public class DWARFLocation {
private DWARFRange addressRange;
private byte[] location;
@@ -37,6 +39,39 @@ public class DWARFLocation {
return this.location;
}
+ /**
+ * Get the location that corresponds to the entry point of the function If
+ * there is only a single location, assume it applies to whole function
+ *
+ * @param locList
+ * @param funcAddr
+ * @return the byte array corresponding to the location expression
+ */
+ public static DWARFLocation getTopLocation(List locList, long funcAddr) {
+ if (locList.size() == 1) {
+ return locList.get(0);
+ }
+ for (DWARFLocation loc : locList) {
+ if (loc.getRange().getFrom() == funcAddr) {
+ return loc;
+ }
+ }
+ return null;
+ }
+
+ public static DWARFLocation getEntryLocation(List locList, long funcAddr) {
+ for (DWARFLocation loc : locList) {
+ if (loc.getRange().getFrom() == funcAddr) {
+ return loc;
+ }
+ }
+ return null;
+ }
+
+ public static DWARFLocation getFirstLocation(List locList) {
+ return !locList.isEmpty() ? locList.get(0) : null;
+ }
+
/*
* I know we frown on keeping large chunks of code around that have been commented
* out, but...
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFRange.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFRange.java
index df5b3b6a96..d764c6a8fd 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFRange.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFRange.java
@@ -19,6 +19,7 @@ package ghidra.app.util.bin.format.dwarf4;
* Holds the start (inclusive) and end (exclusive) addresses of a range.
*/
public class DWARFRange implements Comparable {
+ public static final DWARFRange EMPTY = new DWARFRange(0, 1);
private final long start;
private final long end;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFUtil.java
index 393716f2fc..8462f7936e 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFUtil.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/DWARFUtil.java
@@ -23,6 +23,8 @@ import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import generic.jar.ResourceFile;
+import ghidra.app.cmd.comments.AppendCommentCmd;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeValue;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute;
@@ -30,9 +32,13 @@ import ghidra.app.util.bin.format.dwarf4.encoding.DWARFAttribute;
import ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag;
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
import ghidra.app.util.bin.format.dwarf4.next.DWARFProgram;
-import ghidra.program.model.data.DataType;
-import ghidra.program.model.data.DataTypeComponent;
-import ghidra.program.model.listing.Program;
+import ghidra.program.database.data.DataTypeUtilities;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSpace;
+import ghidra.program.model.data.*;
+import ghidra.program.model.lang.*;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.Conv;
@@ -451,6 +457,38 @@ public class DWARFUtil {
dtc.setComment(prev + description);
}
+ public static void appendComment(Program program, Address address, int commentType,
+ String prefix, String comment, String sep) {
+ if (comment == null || comment.isBlank()) {
+ return;
+ }
+ CodeUnit cu = getCodeUnitForComment(program, address);
+ if (cu != null) {
+ String existingComment = cu.getComment(commentType);
+ if (existingComment != null && existingComment.contains(comment)) {
+ // don't add same comment twice
+ return;
+ }
+ }
+ AppendCommentCmd cmd = new AppendCommentCmd(address, commentType,
+ Objects.requireNonNullElse(prefix, "") + comment, sep);
+ cmd.applyTo(program);
+ }
+
+ public static CodeUnit getCodeUnitForComment(Program program, Address address) {
+ Listing listing = program.getListing();
+ CodeUnit cu = listing.getCodeUnitContaining(address);
+ if (cu == null) {
+ return null;
+ }
+ Address cuAddr = cu.getMinAddress();
+ if (cu instanceof Data && !address.equals(cuAddr)) {
+ Data data = (Data) cu;
+ return data.getPrimitiveAt((int) address.subtract(cuAddr));
+ }
+ return cu;
+ }
+
/**
* Read an offset value who's size depends on the DWARF format: 32 vs 64.
*
@@ -558,16 +596,41 @@ public class DWARFUtil {
// A DW_AT_object_pointer property in the parent function is an explict way of
// referencing the param that points to the object instance (ie. "this").
//
+ String paramName = paramDIEA.getName();
if (paramDIEA.getBool(DWARFAttribute.DW_AT_artificial, false) ||
- "this".equals(paramDIEA.getName())) {
+ Function.THIS_PARAM_NAME.equals(paramName)) {
return true;
}
+ DIEAggregate funcDIEA = paramDIEA.getParent();
DWARFAttributeValue dwATObjectPointer =
- paramDIEA.getParent().getAttribute(DWARFAttribute.DW_AT_object_pointer);
- return dwATObjectPointer != null &&
- dwATObjectPointer instanceof DWARFNumericAttribute dnum &&
- paramDIEA.hasOffset(dnum.getUnsignedValue());
+ funcDIEA.getAttribute(DWARFAttribute.DW_AT_object_pointer);
+ if (dwATObjectPointer != null && dwATObjectPointer instanceof DWARFNumericAttribute dnum &&
+ paramDIEA.hasOffset(dnum.getUnsignedValue())) {
+ return true;
+ }
+
+ // If the variable is not named, check to see if the parent of the function
+ // is a struct/class, and the parameter points to it
+ DIEAggregate classDIEA = funcDIEA.getParent();
+ if (paramName == null && classDIEA != null && classDIEA.isStructureType()) {
+ // Check to see if the parent data type equals the parameters' data type
+ return isPointerTo(classDIEA, paramDIEA.getTypeRef());
+ }
+
+ return false;
+ }
+
+ public static boolean isPointerTo(DIEAggregate targetDIEA, DIEAggregate testDIEA) {
+ return testDIEA != null && testDIEA.getTag() == DWARFTag.DW_TAG_pointer_type &&
+ testDIEA.getTypeRef() == targetDIEA;
+ }
+
+ public static boolean isPointerDataType(DIEAggregate diea) {
+ while (diea.getTag() == DWARFTag.DW_TAG_typedef) {
+ diea = diea.getTypeRef();
+ }
+ return diea.getTag() == DWARFTag.DW_TAG_pointer_type;
}
/**
@@ -633,7 +696,7 @@ public class DWARFUtil {
// it writes a raw 64bit long (BE). The upper 32 bits (already read as length) will
// always be 0 since super-large binaries from that system weren't really possible.
// The next 32 bits will be the remainder of the value.
- if ( reader.isBigEndian() && program.getDefaultPointerSize() == 8) {
+ if (reader.isBigEndian() && program.getDefaultPointerSize() == 8) {
length = reader.readNextUnsignedInt();
format = DWARFCompilationUnit.DWARF_64;
}
@@ -649,4 +712,151 @@ public class DWARFUtil {
return new LengthResult(length, format);
}
+ /**
+ * Returns a file that has been referenced in the specified {@link Language language's}
+ * ldefs description via a
+ *
<external_name tool="name" name="value"/>
+ * entry.
+ *
+ * @param lang {@link Language} to query
+ * @param name name of the option in the ldefs file
+ * @return file pointed to by the specified external_name tool entry
+ * @throws IOException
+ */
+ public static ResourceFile getLanguageExternalFile(Language lang, String name)
+ throws IOException {
+ String filename = getLanguageExternalNameValue(lang, name);
+ return filename != null
+ ? new ResourceFile(getLanguageDefinitionDirectory(lang), filename)
+ : null;
+ }
+
+ /**
+ * Returns the base directory of a language definition.
+ *
+ * @param lang {@link Language} to get base definition directory
+ * @return base directory for language definition files
+ * @throws IOException
+ */
+ public static ResourceFile getLanguageDefinitionDirectory(Language lang) throws IOException {
+ LanguageDescription langDesc = lang.getLanguageDescription();
+ if (!(langDesc instanceof SleighLanguageDescription)) {
+ throw new IOException("Not a Sleigh Language: " + lang.getLanguageID());
+ }
+ SleighLanguageDescription sld = (SleighLanguageDescription) langDesc;
+ ResourceFile defsFile = sld.getDefsFile();
+ ResourceFile parentFile = defsFile.getParentFile();
+ return parentFile;
+ }
+
+ /**
+ * Returns a value specified in a {@link Language} definition via a
+ *
<external_name tool="name" name="value"/>
+ * entry.
+ *
+ * @param lang {@link Language} to query
+ * @param name name of the value
+ * @return String value
+ * @throws IOException
+ */
+ public static String getLanguageExternalNameValue(Language lang, String name)
+ throws IOException {
+ LanguageDescription langDesc = lang.getLanguageDescription();
+ if (!(langDesc instanceof SleighLanguageDescription)) {
+ throw new IOException("Not a Sleigh Language: " + lang.getLanguageID());
+ }
+ List values = langDesc.getExternalNames(name);
+ if (values == null || values.isEmpty()) {
+ return null;
+ }
+ if (values.size() > 1) {
+ throw new IOException(
+ String.format("Multiple external name values for %s found in language %s", name,
+ lang.getLanguageID()));
+ }
+ return values.get(0);
+ }
+
+ public static void packCompositeIfPossible(Composite original, DataTypeManager dtm) {
+ if (original.isZeroLength() || original.getNumComponents() == 0) {
+ // don't try to pack empty structs, this would throw off conflicthandler logic.
+ // also don't pack sized structs with no fields because when packed down to 0 bytes they
+ // cause errors when used as a param type
+ return;
+ }
+
+ Composite copy = (Composite) original.copy(dtm);
+ copy.setToDefaultPacking();
+ if (copy.getLength() != original.getLength()) {
+ // so far, typically because trailing zero-len flex array caused toolchain to
+ // bump struct size to next alignment value in a way that doesn't mesh with ghidra's
+ // logic
+ return; // fail
+ }
+
+ DataTypeComponent[] preComps = original.getDefinedComponents();
+ DataTypeComponent[] postComps = copy.getDefinedComponents();
+ if (preComps.length != postComps.length) {
+ return; // fail
+ }
+ for (int index = 0; index < preComps.length; index++) {
+ DataTypeComponent preDTC = preComps[index];
+ DataTypeComponent postDTC = postComps[index];
+ if (preDTC.getOffset() != postDTC.getOffset() ||
+ preDTC.getLength() != postDTC.getLength() ||
+ preDTC.isBitFieldComponent() != postDTC.isBitFieldComponent()) {
+ return; // fail
+ }
+ if (preDTC.isBitFieldComponent()) {
+ BitFieldDataType preBFDT = (BitFieldDataType) preDTC.getDataType();
+ BitFieldDataType postBFDT = (BitFieldDataType) postDTC.getDataType();
+ if (preBFDT.getBitOffset() != postBFDT.getBitOffset() ||
+ preBFDT.getBitSize() != postBFDT.getBitSize()) {
+ return; // fail
+ }
+ }
+ }
+
+ original.setToDefaultPacking();
+ }
+
+ public static List convertRegisterListToVarnodeStorage(List registers,
+ int dataTypeSize) {
+ List results = new ArrayList<>();
+ for (Register reg : registers) {
+ int regSize = reg.getMinimumByteSize();
+ int bytesUsed = Math.min(dataTypeSize, regSize);
+ Address addr = reg.getAddress();
+ if (reg.isBigEndian() && bytesUsed < regSize) {
+ addr = addr.add(regSize - bytesUsed);
+ }
+ results.add(new Varnode(addr, bytesUsed));
+ dataTypeSize -= bytesUsed;
+ }
+ return results;
+ }
+
+ public static boolean isEmptyArray(DataType dt) {
+ return dt instanceof Array array && array.getNumElements() == 0;
+ }
+
+ public static boolean isZeroByteDataType(DataType dt) {
+ if (VoidDataType.dataType.isEquivalent(dt)) {
+ return true;
+ }
+ if (!dt.isZeroLength() && dt instanceof Array) {
+ dt = DataTypeUtilities.getArrayBaseDataType((Array) dt);
+ }
+ return dt.isZeroLength();
+ }
+
+ public static boolean isVoid(DataType dt) {
+ return VoidDataType.dataType.isEquivalent(dt);
+ }
+
+ public static boolean isStackVarnode(Varnode varnode) {
+ return varnode != null &&
+ varnode.getAddress().getAddressSpace().getType() == AddressSpace.TYPE_STACK;
+ }
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAttribute.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAttribute.java
index df99a4f544..9048590952 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAttribute.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/encoding/DWARFAttribute.java
@@ -157,6 +157,16 @@ public final class DWARFAttribute {
public static final int DW_AT_GNU_pubtypes = 0x2135;
// end GNU DebugFission
+ // Golang
+ public static final int DW_AT_go_kind = 0x2900;
+ public static final int DW_AT_go_key = 0x2901;
+ public static final int DW_AT_go_elem = 0x2902;
+ public static final int DW_AT_go_embedded_field = 0x2903;
+ public static final int DW_AT_go_runtime_type = 0x2904;
+ public static final int DW_AT_go_package_name = 0x2905;
+ public static final int DW_AT_go_dict_index = 0x2906;
+ // end Golang
+
// Apple proprietary tags
public static final int DW_AT_APPLE_ptrauth_key = 0x3e04;
public static final int DW_AT_APPLE_ptrauth_address_discriminated = 0x3e05;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluator.java
index 7cfebc73b1..580d02d5f8 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluator.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/expression/DWARFExpressionEvaluator.java
@@ -277,6 +277,8 @@ public class DWARFExpressionEvaluator {
else {
useUnknownRegister = true;
if (offset == 0) {
+ // if offset is 0, we can represent the location as a ghidra register location
+ // also implies a deref by the user of this location info
registerLoc = true;
}
}
@@ -366,6 +368,7 @@ public class DWARFExpressionEvaluator {
case DW_OP_call_frame_cfa: {
push(registerMappings.getCallFrameCFA());
+ lastStackRelative = true;
break;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/DWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/DWARFFunctionFixup.java
new file mode 100644
index 0000000000..9839e054b4
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/DWARFFunctionFixup.java
@@ -0,0 +1,63 @@
+/* ###
+ * 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.funcfixup;
+
+import java.util.List;
+
+import ghidra.app.util.bin.format.dwarf4.DWARFException;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction;
+import ghidra.program.model.listing.Function;
+import ghidra.util.classfinder.ClassSearcher;
+import ghidra.util.classfinder.ExtensionPoint;
+
+/**
+ * Interface for add-in logic to fix/modify/tweak DWARF functions before they are written
+ * to the Ghidra program.
+ *
+ * Use {@code @ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_*)} to
+ * control the order of evaluation (higher numbers are run earlier).
+ *
+ * Fixups are found using {@link ClassSearcher}, and their class names must end
+ * in "DWARFFunctionFixup" (see ExtensionPoint.manifest).
+ */
+public interface DWARFFunctionFixup extends ExtensionPoint {
+ public static final int PRIORITY_NORMAL_EARLY = 4000;
+ public static final int PRIORITY_NORMAL = 3000;
+ public static final int PRIORITY_NORMAL_LATE = 2000;
+ public static final int PRIORITY_LAST = 1000;
+
+ /**
+ * Called before a {@link DWARFFunction} is used to create a Ghidra Function.
+ *
+ * If processing of the function should terminate (and the function be skipped), throw
+ * a {@link DWARFException}.
+ *
+ * @param dfunc {@link DWARFFunction} info read from DWARF about the function
+ * @param gfunc the Ghidra {@link Function} that will receive the DWARF information
+ */
+ void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) throws DWARFException;
+
+ /**
+ * Return a list of all current {@link DWARFFunctionFixup fixups} found in the classpath
+ * by ClassSearcher.
+ *
+ * @return list of all current fixups found in the classpath
+ */
+ public static List findFixups() {
+ return ClassSearcher.getInstances(DWARFFunctionFixup.class);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/OutputParamCheckDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/OutputParamCheckDWARFFunctionFixup.java
new file mode 100644
index 0000000000..6532a36d2d
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/OutputParamCheckDWARFFunctionFixup.java
@@ -0,0 +1,42 @@
+/* ###
+ * 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.funcfixup;
+
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable;
+import ghidra.program.model.listing.Function;
+import ghidra.util.Msg;
+import ghidra.util.classfinder.ExtensionPointProperties;
+
+/**
+ * Complains about function parameters that are marked as 'output' and don't have storage
+ * locations.
+ */
+@ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_NORMAL_LATE)
+public class OutputParamCheckDWARFFunctionFixup implements DWARFFunctionFixup {
+
+ @Override
+ public void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) {
+ // Complain about parameters that are marked as 'output' that haven't been handled by
+ // some other fixup, as we don't know what to do with them.
+ for (DWARFVariable dvar : dfunc.params) {
+ if (dvar.isOutputParameter && dvar.isMissingStorage()) {
+ Msg.warn(this, String.format("Unsupported output parameter for %s@%s: %s",
+ gfunc.getName(), gfunc.getEntryPoint(), dvar.name.getName()));
+ }
+ }
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamNameDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamNameDWARFFunctionFixup.java
new file mode 100644
index 0000000000..485c4ddb87
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamNameDWARFFunctionFixup.java
@@ -0,0 +1,52 @@
+/* ###
+ * 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.funcfixup;
+
+import ghidra.app.util.bin.format.dwarf4.next.*;
+import ghidra.program.model.listing.Function;
+import ghidra.util.classfinder.ExtensionPointProperties;
+
+/**
+ * Ensures that function parameter names are unique and valid
+ */
+@ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_LAST)
+public class ParamNameDWARFFunctionFixup implements DWARFFunctionFixup {
+
+ @Override
+ public void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) {
+
+ // Fix any dups among the parameters, to-be-added-local vars, and already present local vars
+ NameDeduper nameDeduper = new NameDeduper();
+ nameDeduper.addReservedNames(dfunc.getAllParamNames());
+ nameDeduper.addReservedNames(dfunc.getAllLocalVariableNames());
+ nameDeduper.addUsedNames(dfunc.getNonParamSymbolNames(gfunc));
+
+ for (DWARFVariable param : dfunc.params) {
+ String newName = nameDeduper.getUniqueName(param.name.getName());
+ if (newName != null) {
+ param.name = param.name.replaceName(newName, param.name.getOriginalName());
+ }
+ }
+
+ for (DWARFVariable localVar : dfunc.localVars) {
+ String newName = nameDeduper.getUniqueName(localVar.name.getName());
+ if (newName != null) {
+ localVar.name = localVar.name.replaceName(newName, localVar.name.getOriginalName());
+ }
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamSpillDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamSpillDWARFFunctionFixup.java
new file mode 100644
index 0000000000..3c08c7c021
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ParamSpillDWARFFunctionFixup.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.funcfixup;
+
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable;
+import ghidra.program.model.listing.Function;
+import ghidra.util.classfinder.ExtensionPointProperties;
+
+/**
+ * Steal storage location from parameters that are defined in a function's local variable
+ * area, because the storage location isn't the parameter location during call, but its location
+ * after being spilled.
+ *
+ * Create a local variable at that storage location.
+ */
+@ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_NORMAL_LATE)
+public class ParamSpillDWARFFunctionFixup implements DWARFFunctionFixup {
+
+ @Override
+ public void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) {
+ for (DWARFVariable param : dfunc.params) {
+ if (!param.isStackStorage()) {
+ continue;
+ }
+ long paramStackOffset = param.getStackOffset();
+ if (dfunc.isInLocalVarStorageArea(paramStackOffset) &&
+ dfunc.getLocalVarByOffset(paramStackOffset) == null) {
+
+ DWARFVariable paramSpill = DWARFVariable.fromDataType(dfunc, param.type);
+ String paramName = param.name.getName();
+ paramSpill.name =
+ param.name.replaceName(paramName + "-local", paramName + "-local");
+ paramSpill.setStackStorage(paramStackOffset);
+ dfunc.localVars.add(paramSpill);
+ param.clearStorage();
+ }
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/RustDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/RustDWARFFunctionFixup.java
new file mode 100644
index 0000000000..0818db1470
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/RustDWARFFunctionFixup.java
@@ -0,0 +1,43 @@
+/* ###
+ * 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.funcfixup;
+
+import ghidra.app.util.bin.format.dwarf4.DIEAggregate;
+import ghidra.app.util.bin.format.dwarf4.DWARFException;
+import ghidra.app.util.bin.format.dwarf4.encoding.DWARFSourceLanguage;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction;
+import ghidra.program.model.listing.Function;
+import ghidra.util.classfinder.ExtensionPointProperties;
+
+/**
+ * Prevent functions in a Rust compile unit from incorrectly being locked down to an empty signature.
+ */
+@ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_NORMAL_EARLY)
+public class RustDWARFFunctionFixup implements DWARFFunctionFixup {
+
+ @Override
+ public void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) throws DWARFException {
+ DIEAggregate diea = dfunc.diea;
+ int cuLang = diea.getCompilationUnit().getCompileUnit().getLanguage();
+ if (cuLang == DWARFSourceLanguage.DW_LANG_Rust && dfunc.params.isEmpty()) {
+ // 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.
+ throw new DWARFException("Rust empty param list" /* string doesnt matter */);
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/SanityCheckDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/SanityCheckDWARFFunctionFixup.java
new file mode 100644
index 0000000000..c6276d70c3
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/SanityCheckDWARFFunctionFixup.java
@@ -0,0 +1,45 @@
+/* ###
+ * 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.funcfixup;
+
+import ghidra.app.util.bin.format.dwarf4.DWARFException;
+import ghidra.app.util.bin.format.dwarf4.attribs.DWARFAttributeValue;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction;
+import ghidra.program.model.listing.Function;
+import ghidra.util.Msg;
+import ghidra.util.classfinder.ExtensionPointProperties;
+
+/**
+ * Check for errors and prevent probable bad function info from being locked in
+ */
+@ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_NORMAL_LATE)
+public class SanityCheckDWARFFunctionFixup implements DWARFFunctionFixup, DWARFAttributeValue {
+
+ @Override
+ public void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) throws DWARFException {
+ // if there were no defined parameters and we had problems decoding local variables,
+ // don't force the method to have an empty param signature because there are other
+ // issues afoot.
+ if (dfunc.params.isEmpty() && dfunc.localVarErrors) {
+ Msg.error(this,
+ String.format(
+ "Inconsistent function signature information, leaving undefined: %s@%s",
+ gfunc.getName(), gfunc.getEntryPoint()));
+ throw new DWARFException("Failed sanity check");
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/StorageVerificationDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/StorageVerificationDWARFFunctionFixup.java
new file mode 100644
index 0000000000..4e0a97e9f5
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/StorageVerificationDWARFFunctionFixup.java
@@ -0,0 +1,55 @@
+/* ###
+ * 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.funcfixup;
+
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction.CommitMode;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable;
+import ghidra.program.model.listing.Function;
+import ghidra.util.classfinder.ExtensionPointProperties;
+
+/**
+ * Downgrades the function's signature commit mode to FORMAL-param-info-only if there are
+ * problems with param storage info.
+ *
+ * Does not check the function's return value storage as that typically won't have information
+ * because DWARF does not specify that.
+ */
+@ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_LAST - 1)
+public class StorageVerificationDWARFFunctionFixup implements DWARFFunctionFixup {
+
+ @Override
+ public void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) {
+ boolean storageIsGood = true;
+ for (DWARFVariable param : dfunc.params) {
+ if (param.isMissingStorage() && !param.isZeroByte()) {
+ storageIsGood = false;
+ break;
+ }
+
+ // downgrade to formal if the location info for the param starts somewhere inside the
+ // function instead of at the entry point
+ if (!param.isLocationValidOnEntry()) {
+ storageIsGood = false;
+ break;
+ }
+ }
+ if (!storageIsGood) {
+ dfunc.signatureCommitMode = CommitMode.FORMAL;
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ThisCallingConventionDWARFFunctionFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ThisCallingConventionDWARFFunctionFixup.java
new file mode 100644
index 0000000000..0d739babb7
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/funcfixup/ThisCallingConventionDWARFFunctionFixup.java
@@ -0,0 +1,53 @@
+/* ###
+ * 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.funcfixup;
+
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFVariable;
+import ghidra.program.model.data.GenericCallingConvention;
+import ghidra.program.model.listing.Function;
+import ghidra.util.Msg;
+import ghidra.util.classfinder.ExtensionPointProperties;
+
+/**
+ * Update the function's calling convention (if unset) if there is a "this" parameter.
+ */
+@ExtensionPointProperties(priority = DWARFFunctionFixup.PRIORITY_NORMAL)
+public class ThisCallingConventionDWARFFunctionFixup implements DWARFFunctionFixup {
+
+ @Override
+ public void fixupDWARFFunction(DWARFFunction dfunc, Function gfunc) {
+ if (dfunc.params.isEmpty() || dfunc.callingConvention != null) {
+ // if someone else set calling convention, don't override it
+ return;
+ }
+
+ DWARFVariable firstParam = dfunc.params.get(0);
+ if (firstParam.isThis) {
+ if (!firstParam.name.isAnon() &&
+ !Function.THIS_PARAM_NAME.equals(firstParam.name.getOriginalName())) {
+ Msg.error(this,
+ String.format("WARNING: Renaming %s to %s in function %s@%s",
+ firstParam.name.getName(), Function.THIS_PARAM_NAME, gfunc.getName(),
+ gfunc.getEntryPoint()));
+ }
+ firstParam.name =
+ firstParam.name.replaceName(Function.THIS_PARAM_NAME, Function.THIS_PARAM_NAME);
+ dfunc.callingConvention = GenericCallingConvention.thiscall;
+ }
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataInstanceHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataInstanceHelper.java
new file mode 100644
index 0000000000..05e76383e7
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataInstanceHelper.java
@@ -0,0 +1,199 @@
+/* ###
+ * 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 ghidra.program.model.address.Address;
+import ghidra.program.model.data.*;
+import ghidra.program.model.data.Enum;
+import ghidra.program.model.listing.*;
+
+/**
+ * Logic to test if a Data instance is replaceable with a data type.
+ */
+public class DWARFDataInstanceHelper {
+ private Program program;
+ private Listing listing;
+
+ public DWARFDataInstanceHelper(Program program) {
+ this.program = program;
+ this.listing = program.getListing();
+ }
+
+ private boolean isArrayDataTypeCompatibleWithExistingData(Array arrayDT, Data existingData) {
+
+ DataType existingDataDT = existingData.getBaseDataType();
+ if (existingDataDT.isEquivalent(arrayDT)) {
+ return true;
+ }
+
+ DataType elementDT = arrayDT.getDataType();
+ if (elementDT instanceof TypeDef typedef) {
+ elementDT = typedef.getBaseDataType();
+ }
+
+ DataType existingElementDT = existingDataDT instanceof Array existingArrayDT
+ ? existingArrayDT.getDataType()
+ : null;
+ if (elementDT instanceof CharDataType && existingDataDT instanceof StringDataType) {
+ // hack to allow a char array to overwrite a string in memory
+ existingElementDT = elementDT;
+ }
+ if (existingElementDT instanceof TypeDef typedef) {
+ existingElementDT = typedef.getBaseDataType();
+ }
+
+ if (existingDataDT instanceof Array || existingDataDT instanceof StringDataType) {
+ if (!existingElementDT.isEquivalent(elementDT)) {
+ return false;
+ }
+
+ if (arrayDT.getLength() <= existingData.getLength()) {
+ // if proposed array is smaller than in-memory array: ok
+ return true;
+ }
+
+ // if proposed array is longer than in-memory array, check if there is only
+ // undefined data following the in-memory array
+ return hasTrailingUndefined(existingData, arrayDT);
+ }
+
+ // existing data wasn't an array, test each location the proposed array would overwrite
+ Address address = existingData.getAddress();
+ for (int i = 0; i < arrayDT.getNumElements(); i++) {
+ Address elementAddress = address.add(arrayDT.getElementLength() * i);
+ Data data = listing.getDataAt(elementAddress);
+ if (data != null && !isDataTypeCompatibleWithExistingData(elementDT, data)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean hasTrailingUndefined(Data data, DataType replacementDT) {
+ Address address = data.getAddress();
+ return DataUtilities.isUndefinedRange(program, address.add(data.getLength()),
+ address.add(replacementDT.getLength() - 1));
+ }
+
+ private boolean isStructDataTypeCompatibleWithExistingData(Structure structDT,
+ Data existingData) {
+ DataType existingDataDT = existingData.getBaseDataType();
+ if (existingDataDT instanceof Structure) {
+ return existingDataDT.isEquivalent(structDT);
+ }
+
+ // existing data wasn't a structure, test each location the proposed structure would overwrite
+ Address address = existingData.getAddress();
+ for (DataTypeComponent dtc : structDT.getDefinedComponents()) {
+ Address memberAddress = address.add(dtc.getOffset());
+ Data data = listing.getDataAt(memberAddress);
+ if (data != null && !isDataTypeCompatibleWithExistingData(dtc.getDataType(), data)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isPointerDataTypeCompatibleWithExistingData(Pointer pdt, Data existingData) {
+ DataType existingDT = existingData.getBaseDataType();
+
+ // allow 'upgrading' an integer type to a pointer
+ boolean isRightType =
+ (existingDT instanceof Pointer) || (existingDT instanceof AbstractIntegerDataType);
+ return isRightType && existingDT.getLength() == pdt.getLength();
+ }
+
+ private boolean isSimpleDataTypeCompatibleWithExistingData(DataType simpleDT,
+ Data existingData) {
+ // dataType will only be a base data type, not a typedef
+
+ DataType existingDT = existingData.getBaseDataType();
+ if (simpleDT instanceof CharDataType && existingDT instanceof StringDataType) {
+ // char overwriting a string
+ return true;
+ }
+
+ if (!simpleDT.getClass().isInstance(existingDT)) {
+ return false;
+ }
+ int dataTypeLen = simpleDT.getLength();
+ if (dataTypeLen > 0 && dataTypeLen != existingData.getLength()) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isEnumDataTypeCompatibleWithExistingData(Enum enumDT, Data existingData) {
+ // This is a very fuzzy check to see if the value located at address is compatible.
+ // Match if its an enum or integer with correct size. The details about enum
+ // members are ignored.
+ DataType existingDT = existingData.getBaseDataType();
+ if (!(existingDT instanceof Enum || existingDT instanceof AbstractIntegerDataType)) {
+ return false;
+ }
+ if (existingDT instanceof BooleanDataType) {
+ return false;
+ }
+ if (existingDT.getLength() != enumDT.getLength()) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isDataTypeCompatibleWithExistingData(DataType dataType, Data existingData) {
+ if (existingData == null || !existingData.isDefined()) {
+ return true;
+ }
+
+ if (dataType instanceof Array) {
+ return isArrayDataTypeCompatibleWithExistingData((Array) dataType, existingData);
+ }
+ if (dataType instanceof Pointer) {
+ return isPointerDataTypeCompatibleWithExistingData((Pointer) dataType, existingData);
+ }
+ if (dataType instanceof Structure) {
+ return isStructDataTypeCompatibleWithExistingData((Structure) dataType, existingData);
+ }
+ if (dataType instanceof TypeDef) {
+ return isDataTypeCompatibleWithExistingData(((TypeDef) dataType).getBaseDataType(),
+ existingData);
+ }
+ if (dataType instanceof Enum) {
+ return isEnumDataTypeCompatibleWithExistingData((Enum) dataType, existingData);
+ }
+
+ if (dataType instanceof AbstractIntegerDataType ||
+ dataType instanceof AbstractFloatDataType || dataType instanceof StringDataType ||
+ dataType instanceof WideCharDataType || dataType instanceof WideChar16DataType ||
+ dataType instanceof WideChar32DataType) {
+ return isSimpleDataTypeCompatibleWithExistingData(dataType, existingData);
+ }
+
+ return false;
+ }
+
+ public boolean isDataTypeCompatibleWithAddress(DataType dataType, Address address) {
+ if (DataUtilities.isUndefinedRange(program, address,
+ address.add(dataType.getLength() - 1))) {
+ return true;
+ }
+
+ Data data = listing.getDataAt(address);
+ return data == null || isDataTypeCompatibleWithExistingData(dataType, data);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java
index f6a4228f34..2429b8be40 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeConflictHandler.java
@@ -55,9 +55,9 @@ import ghidra.util.SystemUtilities;
* structures.
*
*/
-class DWARFDataTypeConflictHandler extends DataTypeConflictHandler {
+public class DWARFDataTypeConflictHandler extends DataTypeConflictHandler {
- static final DWARFDataTypeConflictHandler INSTANCE = new DWARFDataTypeConflictHandler();
+ public static final DWARFDataTypeConflictHandler INSTANCE = new DWARFDataTypeConflictHandler();
private DWARFDataTypeConflictHandler() {
// do not create instances of this class
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java
index 81b0d4bf1f..5cf87925a9 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeImporter.java
@@ -27,8 +27,9 @@ import org.apache.commons.lang3.StringUtils;
import ghidra.app.util.DataTypeNamingUtil;
import ghidra.app.util.bin.format.dwarf4.*;
import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute;
-import ghidra.app.util.bin.format.dwarf4.encoding.*;
+import ghidra.app.util.bin.format.dwarf4.encoding.DWARFEndianity;
import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
+import ghidra.app.util.bin.format.golang.rtti.types.GoKind;
import ghidra.program.database.DatabaseObject;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
@@ -87,12 +88,11 @@ public class DWARFDataTypeImporter {
* @param dwarfDTM {@link DWARFDataTypeManager} helper
* @param importOptions {@link DWARFImportOptions} control optional features during import
*/
- public DWARFDataTypeImporter(DWARFProgram prog, DWARFDataTypeManager dwarfDTM,
- DWARFImportOptions importOptions) {
+ public DWARFDataTypeImporter(DWARFProgram prog, DWARFDataTypeManager dwarfDTM) {
this.prog = prog;
this.dataTypeManager = prog.getGhidraProgram().getDataTypeManager();
this.dwarfDTM = dwarfDTM;
- this.importOptions = importOptions;
+ this.importOptions = prog.getImportOptions();
this.voidDDT = new DWARFDataType(dwarfDTM.getVoidType(),
DWARFNameInfo.fromDataType(dwarfDTM.getVoidType()), -1);
}
@@ -109,7 +109,7 @@ public class DWARFDataTypeImporter {
//Msg.warn(this, "Allowed recursive loop in datatype detected at " + id);
break;
case 3:
- Msg.error(this, "Recursive loop in datatype detected at " + id);
+ Msg.error(this, "Recursive loop in datatype detected at " + Long.toHexString(id));
return false;
}
recursionTrackingOffsetToLoopCount.put(id, count);
@@ -155,39 +155,39 @@ public class DWARFDataTypeImporter {
// Fall back to creating a new Ghidra DataType from the info in the DIEA.
switch (diea.getTag()) {
- case DWARFTag.DW_TAG_pointer_type:
- case DWARFTag.DW_TAG_reference_type:
- case DWARFTag.DW_TAG_rvalue_reference_type:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ case DW_TAG_rvalue_reference_type:
result = makeDataTypeForPointer(diea);
break;
- case DWARFTag.DW_TAG_ptr_to_member_type:
+ case DW_TAG_ptr_to_member_type:
result = makeDataTypeForPtrToMemberType(diea);
break;
- case DWARFTag.DW_TAG_base_type:
+ case DW_TAG_base_type:
result = makeDataTypeForBaseType(diea);
break;
- case DWARFTag.DW_TAG_typedef:
+ case DW_TAG_typedef:
result = makeDataTypeForTypedef(diea);
break;
- case DWARFTag.DW_TAG_unspecified_type:
+ case DW_TAG_unspecified_type:
result = makeDataTypeForUnspecifiedType(diea);
break;
- case DWARFTag.DW_TAG_const_type:
- case DWARFTag.DW_TAG_volatile_type:
- case DWARFTag.DW_TAG_restrict_type:
- case DWARFTag.DW_TAG_shared_type:
- case DWARFTag.DW_TAG_APPLE_ptrauth_type:
+ case DW_TAG_const_type:
+ case DW_TAG_volatile_type:
+ case DW_TAG_restrict_type:
+ case DW_TAG_shared_type:
+ case DW_TAG_APPLE_ptrauth_type:
result = makeDataTypeForConst(diea);
break;
- case DWARFTag.DW_TAG_enumeration_type:
+ case DW_TAG_enumeration_type:
result = makeDataTypeForEnum(diea);
break;
- case DWARFTag.DW_TAG_array_type:
+ case DW_TAG_array_type:
result = makeDataTypeForArray(diea);
break;
- case DWARFTag.DW_TAG_structure_type:
- case DWARFTag.DW_TAG_class_type:
- case DWARFTag.DW_TAG_union_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_class_type:
+ case DW_TAG_union_type:
result = makeDataTypeForStruct(diea);
// push partial datatype info into currentTypes mapping to handle
@@ -197,10 +197,10 @@ public class DWARFDataTypeImporter {
finishStruct(diea, result);
break;
- case DWARFTag.DW_TAG_subroutine_type:
+ case DW_TAG_subroutine_type:
result = makeDataTypeForFunctionDefinition(diea, true);
break;
- case DWARFTag.DW_TAG_subprogram:
+ case DW_TAG_subprogram:
result = makeDataTypeForFunctionDefinition(diea, false);
break;
default:
@@ -287,12 +287,23 @@ public class DWARFDataTypeImporter {
String paramName = paramDIEA.getName();
DWARFDataType paramDT = getDataType(paramDIEA.getTypeRef(), null);
- if (paramDT == null || paramDT.dataType.getLength() <= 0) {
+ DataType dt = fixupDataTypeInconsistencies(paramDT);
+
+ if (dt == null && DWARFUtil.isPointerDataType(paramDIEA.getTypeRef())) {
+ // Hack to handle Golang self-referencing func defs.
+ Msg.error(this,
+ "Error resolving parameter data type, probable recursive definition, replacing with void*: " +
+ dni.getName());
+ Msg.debug(this, "Problem funcDef: " + diea.toString());
+ Msg.debug(this, "Problem param: " + paramDIEA);
+ dt = dwarfDTM.getPtrTo(dwarfDTM.getVoidType());
+ }
+ if (dt == null || dt.getLength() <= 0) {
Msg.error(this, "Bad function parameter type for " + dni.asCategoryPath());
return null;
}
- ParameterDefinition pd = new ParameterDefinitionImpl(paramName, paramDT.dataType, null);
+ ParameterDefinition pd = new ParameterDefinitionImpl(paramName, dt, null);
params.add(pd);
foundThisParam |= DWARFUtil.isThisParam(paramDIEA);
@@ -304,7 +315,7 @@ public class DWARFDataTypeImporter {
funcDef.setNoReturn(diea.getBool(DW_AT_noreturn, false));
funcDef.setArguments(params.toArray(new ParameterDefinition[params.size()]));
- if (!diea.getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty()) {
+ if (!diea.getChildren(DW_TAG_unspecified_parameters).isEmpty()) {
funcDef.setVarArgs(true);
}
@@ -329,7 +340,17 @@ public class DWARFDataTypeImporter {
updateMapping(origPD.getDataType(), newPD.getDataType());
}
- return new DWARFDataType(funcDef, dni, diea.getOffset());
+ DataType dtToAdd = funcDef;
+ if (diea.hasAttribute(DW_AT_byte_size)) {
+ // if the funcdef has a bytesize attribute, we should convert this data type to a ptr
+ long ptrSize = diea.getUnsignedLong(DW_AT_byte_size, -1);
+ if (ptrSize == dataTypeManager.getDataOrganization().getPointerSize()) {
+ ptrSize = -1;// use default pointer size
+ }
+ dtToAdd = dwarfDTM.getPtrTo(dtToAdd, (int) ptrSize);
+ }
+
+ return new DWARFDataType(dtToAdd, dni, diea.getOffset());
}
/**
@@ -357,12 +378,38 @@ public class DWARFDataTypeImporter {
"Warning: Base type bit size and bit offset not currently handled for data type %s, DIE %s"
.formatted(dni.toString(), diea.getHexOffset()));
}
+ boolean explictSize = false;
+ if (diea.hasAttribute(DW_AT_go_kind)) {
+ long goKindInt = diea.getLong(DW_AT_go_kind, 0);
+ GoKind kind = GoKind.parseByte((byte) goKindInt);
+ explictSize = isExplictSizedGolangType(kind);
+ }
- DataType dt =
- dwarfDTM.getBaseType(dni.getOriginalName(), dwarfSize, dwarfEncoding, isBigEndian);
+ DataType dt = dwarfDTM.getBaseType(dni.getOriginalName(), dwarfSize, dwarfEncoding,
+ isBigEndian, explictSize);
return new DWARFDataType(dt, dni, diea.getOffset());
}
+ private boolean isExplictSizedGolangType(GoKind kind) {
+ switch (kind) {
+ case Int8:
+ case Int16:
+ case Int32:
+ case Int64:
+ case Uint8:
+ case Uint16:
+ case Uint32:
+ case Uint64:
+ case Float32:
+ case Float64:
+ case Complex64:
+ case Complex128:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/**
* Simple passthru, returns whatever type this "const" modifier applies to.
*
@@ -396,7 +443,7 @@ public class DWARFDataTypeImporter {
private DWARFDataType makeDataTypeForEnum(DIEAggregate diea) {
DWARFNameInfo dni = prog.getName(diea);
- int enumSize = (int) diea.getUnsignedLong(DWARFAttribute.DW_AT_byte_size, -1);
+ int enumSize = (int) diea.getUnsignedLong(DW_AT_byte_size, -1);
// in addition to byte_size, enums can have encoding (signed/unsigned) and a basetype, which
// itself might have a signed/unsigned encoding.
// Which attributes are present varies wildly between versions and vendors, so seems
@@ -435,12 +482,12 @@ public class DWARFDataTypeImporter {
private void populateStubEnum(Enum enumDT, DIEAggregate diea, boolean defaultSignedness) {
// NOTE: gcc tends to emit values without an explicit signedness. The caller
// can specify a default signedness, but this should probably always be unsigned.
- for (DebugInfoEntry childEntry : diea.getChildren(DWARFTag.DW_TAG_enumerator)) {
+ for (DebugInfoEntry childEntry : diea.getChildren(DW_TAG_enumerator)) {
DIEAggregate childDIEA = prog.getAggregate(childEntry);
String valueName = childDIEA.getName();
DWARFNumericAttribute enumValAttr = childDIEA
- .getAttribute(DWARFAttribute.DW_AT_const_value, DWARFNumericAttribute.class);
+ .getAttribute(DW_AT_const_value, DWARFNumericAttribute.class);
if (enumValAttr != null) {
long enumVal = enumValAttr.getValueWithSignednessHint(defaultSignedness);
@@ -531,15 +578,15 @@ public class DWARFDataTypeImporter {
DWARFNameInfo dni = prog.getName(diea);
- long structSize = diea.getUnsignedLong(DWARFAttribute.DW_AT_byte_size, 0);
+ long structSize = diea.getUnsignedLong(DW_AT_byte_size, 0);
long origStructSize = structSize;
if (isStructTooBigForGhidra(structSize)) {
Msg.error(this, "Large DWARF structure encountered, substituting empty struct for " +
dni + ", size: " + Long.toString(structSize) + " at DIE " + diea.getHexOffset());
structSize = 0;
}
- boolean isUnion = diea.getTag() == DWARFTag.DW_TAG_union_type;
- boolean isDecl = diea.getBool(DWARFAttribute.DW_AT_declaration, false);
+ boolean isUnion = diea.getTag() == DW_TAG_union_type;
+ boolean isDecl = diea.getBool(DW_AT_declaration, false);
DataType struct =
isUnion ? new UnionDataType(dni.getParentCP(), dni.getName(), dataTypeManager)
@@ -617,19 +664,19 @@ public class DWARFDataTypeImporter {
*/
private void populateStubUnion(DWARFDataType ddt, DIEAggregate diea)
throws IOException, DWARFExpressionException {
- long unionSize = diea.getUnsignedLong(DWARFAttribute.DW_AT_byte_size, -1);
+ long unionSize = diea.getUnsignedLong(DW_AT_byte_size, -1);
UnionDataType union = (UnionDataType) ddt.dataType;
- for (DebugInfoEntry childEntry : diea.getChildren(DWARFTag.DW_TAG_member)) {
+ for (DebugInfoEntry childEntry : diea.getChildren(DW_TAG_member)) {
DIEAggregate childDIEA = prog.getAggregate(childEntry);
// skip static member vars as they do not have storage in the structure
// C does not allow static member vars in unions
- if (childDIEA.hasAttribute(DWARFAttribute.DW_AT_external)) {
+ if (childDIEA.hasAttribute(DW_AT_external)) {
continue;
}
- int bitSize = childDIEA.parseInt(DWARFAttribute.DW_AT_bit_size, -1);
+ int bitSize = childDIEA.parseInt(DW_AT_bit_size, -1);
boolean isBitField = bitSize != -1;
String memberName = childDIEA.getName();
@@ -644,13 +691,14 @@ public class DWARFDataTypeImporter {
continue;
}
+ DataType dt = fixupDataTypeInconsistencies(childDT);
String memberComment = null;
- if (childDT.dataType instanceof Dynamic ||
- childDT.dataType instanceof FactoryDataType) {
- memberComment = "Unsupported dynamic size data type: " + childDT.dataType;
- childDT.dataType = Undefined.getUndefinedDataType(1);
+ if (dt instanceof Dynamic ||
+ dt instanceof FactoryDataType) {
+ memberComment = "Unsupported dynamic size data type: " + dt;
+ dt = Undefined.getUndefinedDataType(1);
}
- int dtLen = childDT.dataType.getLength();
+ int dtLen = dt.getLength();
if (unionSize != -1 && !isBitField && dtLen > unionSize) {
// if we can, ensure that the member being added to the union isn't larger
// than what DWARF specifies.
@@ -658,23 +706,23 @@ public class DWARFDataTypeImporter {
if (dtLen > 1) {
// replace problematic datatype with 1 byte undefined placeholder
memberComment =
- "Data type larger than union's declared size: " + childDT.dataType;
- childDT.dataType = Undefined.getUndefinedDataType(1);
+ "Data type larger than union's declared size: " + dt;
+ dt = Undefined.getUndefinedDataType(1);
}
else {
// can't do any fancy replacement, just add warning to union's description
DWARFUtil.appendDescription(union, memberDesc("Missing member",
- "data type larger than union", memberName, childDT, -1, bitSize, -1), "\n");
+ "data type larger than union", memberName, dt, -1, bitSize, -1), "\n");
continue;
}
}
if (isBitField) {
- if (!BitFieldDataType.isValidBaseDataType(childDT.dataType)) {
+ if (!BitFieldDataType.isValidBaseDataType(dt)) {
DWARFUtil.appendDescription(union,
memberDesc("Missing member",
- "Bad data type for bitfield: " + childDT.dataType.getName(), memberName,
- childDT, -1, bitSize, -1),
+ "Bad data type for bitfield: " + dt.getName(), memberName,
+ dt, -1, bitSize, -1),
"\n");
continue;
}
@@ -682,7 +730,7 @@ public class DWARFDataTypeImporter {
// DWARF has attributes (DWARFAttribute.DW_AT_data_bit_offset, DWARFAttribute.DW_AT_bit_offset)
// that specify the bit_offset of the field in the union. We don't use them.
try {
- union.addBitField(childDT.dataType, bitSize, memberName, memberComment);
+ union.addBitField(dt, bitSize, memberName, memberComment);
}
catch (InvalidDataTypeException e) {
Msg.error(this,
@@ -690,17 +738,17 @@ public class DWARFDataTypeImporter {
union.getDataTypePath() + "[DWARF DIE " + diea.getHexOffset() +
"], skipping: " + e.getMessage());
DWARFUtil.appendDescription(union, memberDesc("Missing member ",
- "Failed to add bitfield", memberName, childDT, -1, bitSize, -1), "\n");
+ "Failed to add bitfield", memberName, dt, -1, bitSize, -1), "\n");
}
}
else {
// just a normal field
try {
DataTypeComponent dataTypeComponent =
- union.add(childDT.dataType, memberName, memberComment);
+ union.add(dt, memberName, memberComment);
// adding a member to a composite can cause a clone() of the datatype instance, so
// update the instance mapping to keep track of the new instance.
- updateMapping(childDT.dataType, dataTypeComponent.getDataType());
+ updateMapping(dt, dataTypeComponent.getDataType());
}
catch (IllegalArgumentException exc) {
Msg.error(this,
@@ -731,7 +779,7 @@ public class DWARFDataTypeImporter {
") is larger than DWARF value (" + unionSize + ")", "\n");
}
if (importOptions.isTryPackStructs()) {
- packCompositeIfPossible(ddt);
+ DWARFUtil.packCompositeIfPossible((Composite) ddt.dataType, dataTypeManager);
}
}
@@ -747,7 +795,7 @@ public class DWARFDataTypeImporter {
StructureDataType structure = (StructureDataType) ddt.dataType;
- long structSize = diea.getUnsignedLong(DWARFAttribute.DW_AT_byte_size, 0);
+ long structSize = diea.getUnsignedLong(DW_AT_byte_size, 0);
if (isStructTooBigForGhidra(structSize)) {
return;
}
@@ -756,58 +804,14 @@ public class DWARFDataTypeImporter {
// location can conflict with the first member field's offset.
// This means that member fields will be successfully added and the field
// that represents the base class will fail in these cases.
- populateStubStruct_worker(ddt, structure, diea, DWARFTag.DW_TAG_member);
- populateStubStruct_worker(ddt, structure, diea, DWARFTag.DW_TAG_inheritance);
+ populateStubStruct_worker(ddt, structure, diea, DW_TAG_member);
+ populateStubStruct_worker(ddt, structure, diea, DW_TAG_inheritance);
removeUneededStructMemberShrinkage(structure);
if (importOptions.isTryPackStructs()) {
- packCompositeIfPossible(ddt);
+ DWARFUtil.packCompositeIfPossible((Composite) ddt.dataType, dataTypeManager);
}
}
- private void packCompositeIfPossible(DWARFDataType ddt) {
- Composite original = (Composite) ddt.dataType;
- if (original.isZeroLength() || original.getNumComponents() == 0) {
- // don't try to pack empty structs, this would throw off conflicthandler logic.
- // also don't pack sized structs with no fields because when packed down to 0 bytes they
- // cause errors when used as a param type
- return;
- }
-
- Composite copy = (Composite) original.copy(dataTypeManager);
- copy.setToDefaultPacking();
- if (copy.getLength() != original.getLength()) {
- // so far, typically because trailing zero-len flex array caused toolchain to
- // bump struct size to next alignment value in a way that doesn't mesh with ghidra's
- // logic
- return; // fail
- }
-
- DataTypeComponent[] preComps = original.getDefinedComponents();
- DataTypeComponent[] postComps = copy.getDefinedComponents();
- if (preComps.length != postComps.length) {
- return; // fail
- }
- for (int index = 0; index < preComps.length; index++) {
- DataTypeComponent preDTC = preComps[index];
- DataTypeComponent postDTC = postComps[index];
- if (preDTC.getOffset() != postDTC.getOffset() ||
- preDTC.getLength() != postDTC.getLength() ||
- preDTC.isBitFieldComponent() != postDTC.isBitFieldComponent()) {
- return; // fail
- }
- if (preDTC.isBitFieldComponent()) {
- BitFieldDataType preBFDT = (BitFieldDataType) preDTC.getDataType();
- BitFieldDataType postBFDT = (BitFieldDataType) postDTC.getDataType();
- if (preBFDT.getBitOffset() != postBFDT.getBitOffset() ||
- preBFDT.getBitSize() != postBFDT.getBitSize()) {
- return; // fail
- }
- }
- }
-
- original.setToDefaultPacking();
- }
-
/**
* Restore structure fields to their regular size (if there is room) to ensure
* future DataType equiv and comparisons are successful.
@@ -867,11 +871,11 @@ public class DWARFDataTypeImporter {
DIEAggregate childDIEA = prog.getAggregate(childEntry);
// skip static member vars as they do not have storage in the structure
- if (childDIEA.hasAttribute(DWARFAttribute.DW_AT_external)) {
+ if (childDIEA.hasAttribute(DW_AT_external)) {
continue;
}
- int bitSize = childDIEA.parseInt(DWARFAttribute.DW_AT_bit_size, -1);
+ int bitSize = childDIEA.parseInt(DW_AT_bit_size, -1);
boolean isBitField = bitSize != -1;
DWARFDataType childDT = getDataType(childDIEA.getTypeRef(), null);
@@ -880,6 +884,7 @@ public class DWARFDataTypeImporter {
"Failed to get data type for struct field: " + childDIEA.getHexOffset());
continue;
}
+ DataType dt = fixupDataTypeInconsistencies(childDT);
String memberName = childDIEA.getName();
@@ -888,8 +893,8 @@ public class DWARFDataTypeImporter {
if (memberName == null) {
// If the member is an inheritance type, then set the name
// to be the name of the data type
- if (childDIEA.getTag() == DWARFTag.DW_TAG_inheritance) {
- memberName = "super_" + childDT.dataType.getName();
+ if (childDIEA.getTag() == DW_TAG_inheritance) {
+ memberName = "super_" + dt.getName();
}
else {
memberName = "field_" + structure.getNumDefinedComponents();
@@ -897,17 +902,16 @@ public class DWARFDataTypeImporter {
}
boolean hasMemberOffset =
- childDIEA.hasAttribute(DWARFAttribute.DW_AT_data_member_location);
+ childDIEA.hasAttribute(DW_AT_data_member_location);
int memberOffset = 0;
if (hasMemberOffset) {
try {
- memberOffset = childDIEA.parseDataMemberOffset(
- DWARFAttribute.DW_AT_data_member_location, 0);
+ memberOffset = childDIEA.parseDataMemberOffset(DW_AT_data_member_location, 0);
}
catch (DWARFExpressionException e) {
DWARFUtil.appendDescription(structure, memberDesc("Missing member",
- "failed to parse location", memberName, childDT, -1, bitSize, -1), "\n");
+ "failed to parse location", memberName, dt, -1, bitSize, -1), "\n");
continue;
}
}
@@ -922,30 +926,29 @@ public class DWARFDataTypeImporter {
//}
if (isBitField) {
- if (!BitFieldDataType.isValidBaseDataType(childDT.dataType)) {
+ if (!BitFieldDataType.isValidBaseDataType(dt)) {
DWARFUtil.appendDescription(structure,
- memberDesc("Missing member",
- "Bad data type for bitfield: " + childDT.dataType.getName(), memberName,
- childDT, -1, bitSize, -1),
+ memberDesc("Missing member", "Bad data type for bitfield: " + dt.getName(),
+ memberName, dt, -1, bitSize, -1),
"\n");
continue;
}
int containerLen;
if (hasMemberOffset) {
- int byteSize = childDIEA.parseInt(DWARFAttribute.DW_AT_byte_size, -1);
- containerLen = byteSize <= 0 ? childDT.dataType.getLength() : byteSize;
+ int byteSize = childDIEA.parseInt(DW_AT_byte_size, -1);
+ containerLen = byteSize <= 0 ? dt.getLength() : byteSize;
}
else {
containerLen = structure.getLength();
}
int containerBitLen = containerLen * 8;
- int bitOffset = childDIEA.parseInt(DWARFAttribute.DW_AT_data_bit_offset, -1);
+ int bitOffset = childDIEA.parseInt(DW_AT_data_bit_offset, -1);
int ghidraBitOffset;
if (bitOffset == -1) {
// try to fall back to previous dwarf version's bit_offset attribute that has slightly different info
- bitOffset = childDIEA.parseInt(DWARFAttribute.DW_AT_bit_offset, -1);
+ bitOffset = childDIEA.parseInt(DW_AT_bit_offset, -1);
// convert DWARF bit offset value to Ghidra bit offset
ghidraBitOffset = containerBitLen - bitOffset - bitSize;
@@ -961,7 +964,7 @@ public class DWARFDataTypeImporter {
if (bitOffset < 0 || ghidraBitOffset < 0 || ghidraBitOffset >= containerBitLen) {
DWARFUtil.appendDescription(structure, memberDesc("Missing member",
- "bad bitOffset", memberName, childDT, memberOffset, bitSize, bitOffset),
+ "bad bitOffset", memberName, dt, memberOffset, bitSize, bitOffset),
"\n");
continue;
}
@@ -970,7 +973,7 @@ public class DWARFDataTypeImporter {
// TODO: need safety checks here to make sure that using insertAt() doesn't
// modify the struct
structure.insertBitFieldAt(memberOffset, containerLen, ghidraBitOffset,
- childDT.dataType, bitSize, memberName, null);
+ dt, bitSize, memberName, null);
}
catch (InvalidDataTypeException e) {
Msg.error(this,
@@ -978,23 +981,23 @@ public class DWARFDataTypeImporter {
structure.getDataTypePath() + "[DWARF DIE " + diea.getHexOffset() +
"], skipping: " + e.getMessage());
DWARFUtil.appendDescription(structure,
- memberDesc("Missing member ", "Failed to add bitfield", memberName, childDT,
+ memberDesc("Missing member ", "Failed to add bitfield", memberName, dt,
memberOffset, bitSize, bitOffset),
"\n");
}
}
else {
String memberComment = null;
- boolean isDynamicSizedType = (childDT.dataType instanceof Dynamic ||
- childDT.dataType instanceof FactoryDataType);
+ boolean isDynamicSizedType =
+ (dt instanceof Dynamic || dt instanceof FactoryDataType);
if (isDynamicSizedType) {
- memberComment = "Unsupported dynamic size data type: " + childDT.dataType;
- childDT.dataType = Undefined.getUndefinedDataType(1);
+ memberComment = "Unsupported dynamic size data type: " + dt;
+ dt = Undefined.getUndefinedDataType(1);
}
- int childLength = getUnpaddedDataTypeLength(childDT.dataType);
+ int childLength = getUnpaddedDataTypeLength(dt);
if (memberOffset + childLength > structure.getLength()) {
DWARFUtil.appendDescription(structure, memberDesc("Missing member",
- "exceeds parent struct len", memberName, childDT, memberOffset, -1, -1),
+ "exceeds parent struct len", memberName, dt, memberOffset, -1, -1),
"\n");
continue;
@@ -1002,16 +1005,16 @@ public class DWARFDataTypeImporter {
try {
DataTypeComponent dtc;
- if (DataTypeComponent.usesZeroLengthComponent(childDT.dataType)) {
+ if (DataTypeComponent.usesZeroLengthComponent(dt)) {
if (!isUndefinedOrZeroLenAtOffset(structure, memberOffset)) {
DWARFUtil.appendDescription(structure, memberDesc("Missing member",
- "conflicting member at same offset", memberName, childDT,
+ "conflicting member at same offset", memberName, dt,
memberOffset, -1, -1), "\n");
continue;
}
// use insertAt for zero len members to allow multiple at same offset
dtc =
- structure.insertAtOffset(memberOffset, childDT.dataType, 0, memberName,
+ structure.insertAtOffset(memberOffset, dt, 0, memberName,
memberComment);
}
else {
@@ -1023,18 +1026,18 @@ public class DWARFDataTypeImporter {
DWARFUtil.appendDescription(structure,
memberDesc("Missing member",
"conflict with " + existingDTC.getFieldName(),
- memberName, childDT, memberOffset, -1, -1),
+ memberName, dt, memberOffset, -1, -1),
"\n");
}
continue;
}
- dtc = structure.replace(ordinalToReplace, childDT.dataType, childLength,
- memberName, memberComment);
+ dtc = structure.replace(ordinalToReplace, dt, childLength, memberName,
+ memberComment);
}
// struct.replaceAtOffset() and insertAtOffset() clones the childDT, which will mess up our
// identity based mapping in currentImplDataTypeToDDT.
// Update the mapping to prevent that.
- updateMapping(childDT.dataType, dtc.getDataType());
+ updateMapping(dt, dtc.getDataType());
}
catch (IllegalArgumentException exc) {
Msg.error(this,
@@ -1042,7 +1045,7 @@ public class DWARFDataTypeImporter {
structure.getDataTypePath() + "[DWARF DIE " + diea.getHexOffset() +
"], skipping: " + exc.getMessage());
DWARFUtil.appendDescription(structure, memberDesc("Missing member ", "",
- memberName, childDT, memberOffset, -1, -1), "\n");
+ memberName, dt, memberOffset, -1, -1), "\n");
}
}
}
@@ -1074,14 +1077,25 @@ public class DWARFDataTypeImporter {
}
private static String memberDesc(String prefix, String errorStr, String memberName,
- DWARFDataType ddt, int memberOffset, int bitSize, int bitOffset) {
+ DataType dt, int memberOffset, int bitSize, int bitOffset) {
return (!StringUtils.isBlank(prefix) ? prefix + " " : "") + memberName + " : " +
- ddt.dataType.getName() + (bitSize != -1 ? ":" + bitSize : "") + " at offset " +
+ dt.getName() + (bitSize != -1 ? ":" + bitSize : "") + " at offset " +
(memberOffset != -1 ? "0x" + Long.toHexString(memberOffset) : "unknown") +
(bitOffset != -1 ? ":" + bitOffset : "") +
(!StringUtils.isBlank(errorStr) ? " [" + errorStr + "]" : "");
}
+ private DataType fixupDataTypeInconsistencies(DWARFDataType ddt) {
+ if (ddt == null) {
+ return null;
+ }
+ DataType result = ddt.dataType;
+ if (result instanceof FunctionDefinition) {
+ result = dwarfDTM.getPtrTo(result);
+ }
+ return result;
+ }
+
/**
* Creates a Ghidra {@link ArrayDataType}.
*
@@ -1102,6 +1116,14 @@ public class DWARFDataTypeImporter {
if (self != null) {
return self;
}
+ DataType elementDT = fixupDataTypeInconsistencies(elementType);
+
+ long explictArraySize = diea.getUnsignedLong(DW_AT_byte_size, -1);
+ if (elementType.dataType.isZeroLength() || explictArraySize == 0) {
+ // don't bother checking range info, we are going to force a zero-element array
+ DataType zeroLenArray = new ArrayDataType(elementDT, 0, -1, dataTypeManager);
+ return new DWARFDataType(zeroLenArray, null, diea.getOffset());
+ }
// Build a list of the defined dimensions for this array type.
// The first element in the DWARF dimension list would be where a wild-card (-1 length)
@@ -1142,19 +1164,19 @@ public class DWARFDataTypeImporter {
}
else if (numElements > Integer.MAX_VALUE) {
Msg.error(this, "Bad value [" + numElements + "] for array's size in DIE: " +
- diea.getOffset() + ", forcing to 1");
+ diea.getHexOffset() + ", forcing to 1");
numElements = 1;
}
dimensions.add((int) numElements);
}
- DataType dt = elementType.dataType;
+ DataType dt = elementDT;
for (int i = dimensions.size() - 1; i >= 0; i--) {
int numElements = dimensions.get(i);
ArrayDataType subArray =
new ArrayDataType(dt, numElements, -1, dataTypeManager);
- if (dt == elementType.dataType) {
+ if (dt == elementDT) {
updateMapping(dt, subArray.getDataType());
}
dt = subArray;
@@ -1191,8 +1213,7 @@ public class DWARFDataTypeImporter {
throws IOException, DWARFExpressionException {
DWARFDataType refdDT = getDataType(diea.getTypeRef(), voidDDT);
- int byteSize = diea.parseInt(DWARFAttribute.DW_AT_byte_size,
- diea.getCompilationUnit().getPointerSize());
+ int byteSize = diea.parseInt(DW_AT_byte_size, diea.getCompilationUnit().getPointerSize());
// do a second query to see if there was a recursive loop in the call above back
// to this datatype that resulted in this datatype being created.
@@ -1225,8 +1246,7 @@ public class DWARFDataTypeImporter {
return null;
}
- int byteSize = diea.parseInt(DWARFAttribute.DW_AT_byte_size,
- diea.getCompilationUnit().getPointerSize());
+ int byteSize = diea.parseInt(DW_AT_byte_size, diea.getCompilationUnit().getPointerSize());
DataType offsetType = dwarfDTM.getOffsetType(byteSize);
// create a typedef to the offsetType and put containing type and var type info in the typedef name.
@@ -1282,6 +1302,12 @@ public class DWARFDataTypeImporter {
boolean typedefWithSameName = DataTypeUtilities.equalsIgnoreConflict(
typedefDNI.asDataTypePath().getPath(), refdDT.dataType.getPathName());
+ if (!typedefWithSameName && refdDT.dataType instanceof Pointer ptrDT &&
+ ptrDT.getDataType() instanceof FunctionDefinition pointedToFuncDefDT) {
+ // hack to handle funcDefs that produce a ptr_to_funcdef instead of a funcdef type, which messes with name compare
+ typedefWithSameName = DataTypeUtilities.equalsIgnoreConflict(
+ typedefDNI.asDataTypePath().getPath(), pointedToFuncDefDT.getPathName());
+ }
if (typedefWithSameName) {
if (importOptions.isElideTypedefsWithSameName()) {
@@ -1340,7 +1366,7 @@ public class DWARFDataTypeImporter {
@Override
public String toString() {
return dataType.getName() + " | " + (dni != null ? dni.toString() : "na") + " | " +
- hexOffsets();
+ hexOffsets() + " | zerolen: " + dataType.isZeroLength();
}
public String hexOffsets() {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java
index 39d3b540e3..b4d8540067 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFDataTypeManager.java
@@ -79,15 +79,13 @@ public class DWARFDataTypeManager {
* @param prog {@link DWARFProgram} that holds the Ghidra {@link Program} being imported.
* @param dataTypeManager {@link DataTypeManager} of the Ghidra Program.
* @param builtInDTM {@link DataTypeManager} with built-in data types.
- * @param importSummary {@link DWARFImportSummary} where summary information will be stored
* during the import session.
*/
- public DWARFDataTypeManager(DWARFProgram prog, DataTypeManager dataTypeManager,
- DataTypeManager builtInDTM, DWARFImportSummary importSummary) {
+ public DWARFDataTypeManager(DWARFProgram prog, DataTypeManager dataTypeManager) {
this.prog = prog;
this.dataTypeManager = dataTypeManager;
- this.builtInDTM = builtInDTM;
- this.importSummary = importSummary;
+ this.builtInDTM = BuiltInDataTypeManager.getDataTypeManager();
+ this.importSummary = prog.getImportSummary();
this.importOptions = prog.getImportOptions();
initBaseDataTypes();
}
@@ -122,8 +120,7 @@ public class DWARFDataTypeManager {
// This does slow us down a little bit but this makes the GUI responsive to the user.
Swing.runNow(Dummy.runnable());
- DWARFDataTypeImporter ddtImporter =
- new DWARFDataTypeImporter(prog, this, prog.getImportOptions());
+ DWARFDataTypeImporter ddtImporter = new DWARFDataTypeImporter(prog, this);
// Convert the DWARF DIE record into a Ghidra DataType (probably impls)
DWARFDataType pre = ddtImporter.getDataType(diea, null);
@@ -255,6 +252,15 @@ public class DWARFDataTypeManager {
return null;
}
+ public DataType getDataTypeForVariable(DIEAggregate diea) {
+ DataType type = getDataType(diea, getVoidType());
+ if (type instanceof FunctionDefinition) {
+ type = getPtrTo(type);
+ }
+ return type;
+
+ }
+
/**
* Returns a pointer to the specified data type.
*
@@ -265,6 +271,10 @@ public class DWARFDataTypeManager {
return dataTypeManager.getPointer(dt);
}
+ public DataType getPtrTo(DataType dt, int ptrSize) {
+ return dataTypeManager.getPointer(dt, ptrSize);
+ }
+
/**
* Iterate all {@link DataType}s that match the CategoryPath / name given
* in the {@link DataTypePath} parameter, including "conflict" datatypes
@@ -372,10 +382,15 @@ public class DWARFDataTypeManager {
* @param dwarfSize
* @param dwarfEncoding
* @param isBigEndian
+ * @param isExplictSize boolean flag, if true the returned data type will not be linked to
+ * the dataOrganization's compiler specified data types (eg. if type is something like int32_t,
+ * the returned type should never change size, even if the dataOrg changes). If false,
+ * the returned type will be linked to the dataOrg's compiler specified data types if possible,
+ * except for data types that have a name that include a bitsize in the name, such as "int64_t".
* @return
*/
public DataType getBaseType(String name, int dwarfSize, int dwarfEncoding,
- boolean isBigEndian) {
+ boolean isBigEndian, boolean isExplictSize) {
DataType dt = null;
String mangledName = null;
@@ -398,11 +413,11 @@ public class DWARFDataTypeManager {
// may be duplicated across different float types. Lookup by name is preferred.
// May need to add name lookup capability to AbstractFloatDataType
case DWARFEncoding.DW_ATE_float -> AbstractFloatDataType.getFloatDataType(dwarfSize,
- getCorrectDTMForFixedLengthTypes(name, dwarfSize));
+ getCorrectDTMForFixedLengthTypes(name, dwarfSize, isExplictSize));
case DWARFEncoding.DW_ATE_signed -> AbstractIntegerDataType.getSignedDataType(dwarfSize,
- getCorrectDTMForFixedLengthTypes(name, dwarfSize));
+ getCorrectDTMForFixedLengthTypes(name, dwarfSize, isExplictSize));
case DWARFEncoding.DW_ATE_unsigned -> AbstractIntegerDataType.getUnsignedDataType(
- dwarfSize, getCorrectDTMForFixedLengthTypes(name, dwarfSize));
+ dwarfSize, getCorrectDTMForFixedLengthTypes(name, dwarfSize, isExplictSize));
case DWARFEncoding.DW_ATE_signed_char -> baseDataTypeChar;
case DWARFEncoding.DW_ATE_unsigned_char -> baseDataTypeUchar;
case DWARFEncoding.DW_ATE_UTF -> findMatchingDataTypeBySize(baseDataTypeChars,
@@ -430,16 +445,18 @@ public class DWARFDataTypeManager {
}
- private DataTypeManager getCorrectDTMForFixedLengthTypes(String name, int dwarfSize) {
+ private DataTypeManager getCorrectDTMForFixedLengthTypes(String name, int dwarfSize,
+ boolean predeterminedHasExplictSize) {
// If the requested name of the base type appears to have a bitsize string
// embedded in it, this chunk of code will switch between using the normal DTM
// to using a null DTM to force the Abstract(Integer|Float)DataType helper method to
// create compiler independent data types that don't change size when the architecture is
// changed.
int typenameExplicitSize;
- boolean usedFixedSizeType = importOptions.isSpecialCaseSizedBaseTypes() &&
- (typenameExplicitSize = getExplicitSizeFromTypeName(name)) != -1 &&
- typenameExplicitSize / 8 == dwarfSize;
+ boolean usedFixedSizeType = predeterminedHasExplictSize ||
+ (importOptions.isSpecialCaseSizedBaseTypes() &&
+ (typenameExplicitSize = getExplicitSizeFromTypeName(name)) != -1 &&
+ typenameExplicitSize / 8 == dwarfSize);
return usedFixedSizeType ? null : dataTypeManager;
}
@@ -706,13 +723,14 @@ public class DWARFDataTypeManager {
*/
private FunctionDefinitionDataType createFunctionDefinitionDataType(DIEAggregate diea,
DWARFNameInfo dni) {
- DataType returnDataType = getDataType(diea.getTypeRef(), baseDataTypeVoid);
+ DataType returnDataType = getDataTypeForVariable(diea.getTypeRef());
boolean foundThisParam = false;
List params = new ArrayList<>();
for (DIEAggregate paramDIEA : diea.getFunctionParamList()) {
String paramName = paramDIEA.getName();
- DataType paramDT = getDataType(paramDIEA.getTypeRef(), null);
+ DataType paramDT = getDataTypeForVariable(paramDIEA.getTypeRef());
+
if (paramDT == null || paramDT.getLength() <= 0) {
Msg.error(this,
"Bad function parameter type for function " + dni.asCategoryPath() +
@@ -747,7 +765,7 @@ public class DWARFDataTypeManager {
return funcDef;
}
-
+
/**
* Regex to match common fixed-size type names like "int64", "int64_t", etc, by triggering
* off some known size designators in the string.
@@ -777,4 +795,5 @@ public class DWARFDataTypeManager {
return -1;
}
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunction.java
new file mode 100644
index 0000000000..311f5676d3
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunction.java
@@ -0,0 +1,361 @@
+/* ###
+ * 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 ghidra.app.util.bin.format.dwarf4.encoding.DWARFTag.DW_TAG_unspecified_parameters;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import java.io.IOException;
+
+import ghidra.app.util.bin.format.dwarf4.*;
+import ghidra.app.util.bin.format.dwarf4.attribs.DWARFNumericAttribute;
+import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.data.*;
+import ghidra.program.model.lang.PrototypeModel;
+import ghidra.program.model.listing.*;
+import ghidra.program.model.symbol.*;
+import ghidra.util.exception.DuplicateNameException;
+import ghidra.util.exception.InvalidInputException;
+
+/**
+ * Represents a function that was read from DWARF information.
+ */
+public class DWARFFunction {
+ public enum CommitMode {
+ SKIP, FORMAL, STORAGE,
+ }
+
+ public DIEAggregate diea;
+ public DWARFNameInfo name;
+ public Namespace namespace;
+ public Address address;
+ public Address highAddress;
+ public long frameBase; // TODO: change this to preserve the func's frameBase expr instead of value
+
+ public GenericCallingConvention callingConvention;
+ public PrototypeModel prototypeModel;
+
+ public DWARFVariable retval;
+ public List params = new ArrayList<>();
+ public boolean varArg;
+ public List localVars = new ArrayList<>();
+ // We keep track of local var errors here because local variables aren't added
+ // to the local's list if they are problematic
+ public boolean localVarErrors;
+ public CommitMode signatureCommitMode = CommitMode.STORAGE;
+
+ public boolean noReturn;
+ public DWARFSourceInfo sourceInfo;
+ public boolean isExternal;
+
+ /**
+ * Create a function instance from the information found in the specified DIEA.
+ *
+ * @param diea DW_TAG_subprogram {@link DIEAggregate}
+ * @return new {@link DWARFFunction}, or null if invalid DWARF information
+ * @throws IOException if error accessing attribute values
+ * @throws DWARFExpressionException if error accessing attribute values
+ */
+ public static DWARFFunction read(DIEAggregate diea)
+ throws IOException, DWARFExpressionException {
+ if (isBadSubprogramDef(diea)) {
+ return null;
+ }
+
+ DWARFProgram prog = diea.getProgram();
+ DWARFDataTypeManager dwarfDTM = prog.getDwarfDTM();
+
+ Address funcAddr = prog.getCodeAddress(diea.getLowPC(0));
+ DWARFFunction dfunc = new DWARFFunction(diea, prog.getName(diea), funcAddr);
+
+ dfunc.namespace = dfunc.name.getParentNamespace(prog.getGhidraProgram());
+ dfunc.sourceInfo = DWARFSourceInfo.create(diea);
+
+ dfunc.highAddress =
+ diea.hasAttribute(DW_AT_high_pc) ? prog.getCodeAddress(diea.getHighPC()) : null;
+
+ // Check if the function is an external function
+ dfunc.isExternal = diea.getBool(DW_AT_external, false);
+ dfunc.noReturn = diea.getBool(DW_AT_noreturn, false);
+
+ // Retrieve the frame base if it exists
+ DWARFLocation frameLoc = null;
+ if (diea.hasAttribute(DW_AT_frame_base)) {
+ List frameBase = diea.getAsLocation(DW_AT_frame_base, dfunc.getRange());
+ // get the framebase register, find where the frame is finally setup.
+ frameLoc = DWARFLocation.getTopLocation(frameBase, dfunc.address.getOffset());
+ if (frameLoc != null) {
+ dfunc.frameBase = (int) diea.evaluateLocation(frameLoc);
+ }
+ }
+
+ dfunc.retval =
+ DWARFVariable.fromDataType(dfunc, dwarfDTM.getDataTypeForVariable(diea.getTypeRef()));
+
+ int paramOrdinal = 0;
+ for (DIEAggregate paramDIEA : diea.getFunctionParamList()) {
+ DWARFVariable param = DWARFVariable.readParameter(paramDIEA, dfunc, paramOrdinal++);
+ dfunc.params.add(param);
+ }
+ dfunc.varArg = !diea.getChildren(DW_TAG_unspecified_parameters).isEmpty();
+
+ return dfunc;
+ }
+
+ private DWARFFunction(DIEAggregate diea, DWARFNameInfo dni, Address address) {
+ this.diea = diea;
+ this.name = dni;
+ this.address = address;
+ }
+
+ public DWARFProgram getProgram() {
+ return diea.getProgram();
+ }
+
+ public DWARFRange getRange() {
+ return new DWARFRange(address.getOffset(),
+ highAddress != null ? highAddress.getOffset() : address.getOffset() + 1);
+ }
+
+ public String getCallingConventionName() {
+ return prototypeModel != null
+ ? prototypeModel.getName()
+ : callingConvention != null
+ ? callingConvention.getDeclarationName()
+ : null;
+ }
+
+ /**
+ * Returns the DWARFVariable that starts at the specified stack offset.
+ *
+ * @param offset stack offset
+ * @return local variable that starts at offset, or null if not present
+ */
+ public DWARFVariable getLocalVarByOffset(long offset) {
+ for (DWARFVariable localVar : localVars) {
+ if (localVar.isStackStorage() && localVar.getStackOffset() == offset) {
+ return localVar;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the specified stack offset is within the function's local variable
+ * storage area.
+ *
+ * @param offset stack offset to test
+ * @return true if stack offset is within this function's local variable area
+ */
+ public boolean isInLocalVarStorageArea(long offset) {
+ boolean paramsHavePositiveOffset = diea.getProgram().stackGrowsNegative();
+ return (paramsHavePositiveOffset && offset < 0) ||
+ (!paramsHavePositiveOffset && offset >= 0);
+ }
+
+ public boolean hasConflictWithParamStorage(DWARFVariable dvar) throws InvalidInputException {
+ if (dvar.lexicalOffset != 0) {
+ return false;
+ }
+ VariableStorage storage = dvar.getVariableStorage();
+ for (DWARFVariable param : params) {
+ VariableStorage paramStorage = param.getVariableStorage();
+ if (paramStorage.intersects(storage)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasConflictWithExistingLocalVariableStorage(DWARFVariable dvar, Function gfunc)
+ throws InvalidInputException {
+ VariableStorage newVarStorage = dvar.getVariableStorage();
+ for (Variable existingVar : gfunc.getAllVariables()) {
+ if (existingVar.getFirstUseOffset() == dvar.lexicalOffset &&
+ existingVar.getVariableStorage().intersects(newVarStorage)) {
+ if ((existingVar instanceof LocalVariable) &&
+ Undefined.isUndefined(existingVar.getDataType())) {
+ continue;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List getAllParamNames() {
+ return params.stream()
+ .filter(dvar -> !dvar.name.isAnon())
+ .map(dvar -> dvar.name.getName())
+ .collect(Collectors.toList());
+ }
+
+ public List getAllLocalVariableNames() {
+ return localVars.stream()
+ .filter(dvar -> !dvar.name.isAnon())
+ .map(dvar -> dvar.name.getName())
+ .collect(Collectors.toList());
+ }
+
+ public List getExistingLocalVariableNames(Function gfunc) {
+ return Arrays.stream(gfunc.getLocalVariables())
+ .filter(var -> var.getName() != null && !Undefined.isUndefined(var.getDataType()))
+ .map(var -> var.getName())
+ .collect(Collectors.toList());
+ }
+
+ public List getNonParamSymbolNames(Function gfunc) {
+ SymbolIterator symbols = gfunc.getProgram().getSymbolTable().getSymbols(gfunc);
+ return StreamSupport.stream(symbols.spliterator(), false)
+ .filter(symbol -> symbol.getSymbolType() != SymbolType.PARAMETER)
+ .map(Symbol::getName)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns this function's parameters as a list of {@link Parameter} instances.
+ *
+ * @param includeStorageDetail boolean flag, if true storage information will be included, if
+ * false, VariableStorage.UNASSIGNED_STORAGE will be used
+ * @param program Ghidra program that contains the parameter
+ * @return list of Parameters
+ * @throws InvalidInputException
+ */
+ public List getParameters(boolean includeStorageDetail)
+ throws InvalidInputException {
+ List result = new ArrayList<>();
+ for (DWARFVariable dvar : params) {
+ result.add(dvar.asParameter(includeStorageDetail, getProgram().getGhidraProgram()));
+ }
+ return result;
+ }
+
+ /**
+ * Returns a {@link FunctionDefinition} that reflects this function's information.
+ *
+ * @param prog {@link DWARFProgram} that contains this function
+ * @return {@link FunctionDefinition} that reflects this function's information
+ */
+ public FunctionDefinition asFuncDef() {
+ List funcDefParams = new ArrayList<>();
+ for (DWARFVariable param : params) {
+ funcDefParams.add(param.asParameterDef());
+ }
+
+ FunctionDefinitionDataType funcDef =
+ new FunctionDefinitionDataType(name.getParentCP(), name.getName(),
+ getProgram().getGhidraProgram().getDataTypeManager());
+ funcDef.setReturnType(retval.type);
+ funcDef.setArguments(funcDefParams.toArray(ParameterDefinition[]::new));
+ funcDef.setGenericCallingConvention(
+ Objects.requireNonNullElse(callingConvention, GenericCallingConvention.unknown));
+ funcDef.setVarArgs(varArg);
+
+ DWARFSourceInfo sourceInfo = null;
+ if (getProgram().getImportOptions().isOutputSourceLocationInfo() &&
+ (sourceInfo = DWARFSourceInfo.create(diea)) != null) {
+ funcDef.setComment(sourceInfo.getDescriptionStr());
+ }
+
+ return funcDef;
+ }
+
+ public void commitLocalVariable(DWARFVariable dvar, Function gfunc) {
+
+ VariableStorage varStorage = null;
+ try {
+ varStorage = dvar.getVariableStorage();
+ if (hasConflictWithParamStorage(dvar)) {
+ appendComment(gfunc.getEntryPoint(), CodeUnit.PLATE_COMMENT,
+ "Local variable %s[%s] conflicts with parameter, skipped.".formatted(
+ dvar.getDeclInfoString(), varStorage),
+ "\n");
+ return;
+ }
+
+ if (hasConflictWithExistingLocalVariableStorage(dvar, gfunc)) {
+ appendComment(gfunc.getEntryPoint().add(dvar.lexicalOffset), CodeUnit.EOL_COMMENT,
+ "Local omitted variable %s[%s] scope starts here".formatted(
+ dvar.getDeclInfoString(), varStorage),
+ "; ");
+ return;
+ }
+
+ NameDeduper nameDeduper = new NameDeduper();
+ nameDeduper.addReservedNames(getAllLocalVariableNames());
+ nameDeduper.addUsedNames(getAllParamNames());
+ nameDeduper.addUsedNames(getExistingLocalVariableNames(gfunc));
+
+ Variable var = dvar.asLocalVariable();
+ String origName = var.getName();
+ String newName = nameDeduper.getUniqueName(origName);
+ if (newName != null) {
+ try {
+ var.setName(newName, null);
+ }
+ catch (DuplicateNameException | InvalidInputException e) {
+ // can't happen
+ }
+ var.setComment("Original name: " + origName);
+ }
+
+ VariableUtilities.checkVariableConflict(gfunc, var, varStorage, true);
+ gfunc.addLocalVariable(var, SourceType.IMPORTED);
+ }
+ catch (InvalidInputException | DuplicateNameException e) {
+ appendComment(gfunc.getEntryPoint().add(dvar.lexicalOffset), CodeUnit.EOL_COMMENT,
+ "Local omitted variable %s[%s] scope starts here".formatted(
+ dvar.getDeclInfoString(),
+ varStorage != null ? varStorage.toString() : "UNKNOWN"),
+ "; ");
+
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "DWARFFunction [\n\tdni=%s,\n\taddress=%s,\n\tparams=%s,\n\tsourceInfo=%s,\n\tlocalVarErrors=%s,\n\tretval=%s\n]",
+ name, address, params, sourceInfo, localVarErrors, retval);
+ }
+
+ private static boolean isBadSubprogramDef(DIEAggregate diea) {
+ if (diea.isDanglingDeclaration() || !diea.hasAttribute(DW_AT_low_pc)) {
+ return true;
+ }
+
+ // fetch the low_pc attribute directly instead of calling diea.getLowPc() to avoid
+ // any fixups applied by lower level code
+ DWARFNumericAttribute attr =
+ diea.getAttribute(DW_AT_low_pc, DWARFNumericAttribute.class);
+ if (attr != null && attr.getUnsignedValue() == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void appendComment(Address address, int commentType, String comment, String sep) {
+ DWARFUtil.appendComment(getProgram().getGhidraProgram(), address, commentType, "", comment,
+ sep);
+ }
+
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java
index 0f9f1a5ffb..e406e55217 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFFunctionImporter.java
@@ -22,24 +22,18 @@ import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
-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.attribs.DWARFNumericAttribute;
-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.expression.*;
-import ghidra.program.database.data.DataTypeUtilities;
+import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
+import ghidra.app.util.bin.format.dwarf4.funcfixup.DWARFFunctionFixup;
+import ghidra.app.util.bin.format.dwarf4.next.DWARFFunction.CommitMode;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
-import ghidra.program.model.data.Enum;
-import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Function.FunctionUpdateType;
-import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
@@ -62,31 +56,29 @@ public class DWARFFunctionImporter {
private final Program currentProgram;
private final DWARFDataTypeManager dwarfDTM;
private final DWARFImportOptions importOptions;
+ private final DWARFImportSummary importSummary;
private ProgramModule rootModule;// Program tree module for DWARF
private Set processedOffsets = new HashSet<>();
- private Map functionsProcessed = new HashMap<>();
+ private Set functionsProcessed = new HashSet<>();
private Set variablesProcesesed = new HashSet<>();
+ private List functionFixups = DWARFFunctionFixup.findFixups();
private TaskMonitor monitor;
- private DWARFImportSummary importSummary;
-
public static boolean hasDWARFProgModule(Program prog, String progModuleName) {
ProgramModule dwarfModule = prog.getListing().getRootModule(progModuleName);
return dwarfModule != null;
}
- public DWARFFunctionImporter(DWARFProgram prog, DWARFDataTypeManager dwarfDTM,
- DWARFImportOptions importOptions, DWARFImportSummary importSummary,
- TaskMonitor monitor) {
+ public DWARFFunctionImporter(DWARFProgram prog, TaskMonitor monitor) {
this.prog = prog;
this.monitor = monitor;
this.currentProgram = prog.getGhidraProgram();
- this.dwarfDTM = dwarfDTM;
- this.importOptions = importOptions;
- this.importSummary = importSummary;
+ this.dwarfDTM = prog.getDwarfDTM();
+ this.importOptions = prog.getImportOptions();
+ this.importSummary = prog.getImportSummary();
}
private boolean shouldProcess(DIEAggregate diea) {
@@ -115,7 +107,10 @@ public class DWARFFunctionImporter {
try {
switch (diea.getTag()) {
- case DW_TAG_subprogram:
+ case DW_TAG_gnu_call_site: // needs skip head
+ case DW_TAG_call_site:
+ diea = DIEAggregate.createSkipHead(diea); // fallthru to next switch case
+ case DW_TAG_subprogram: // normal
try {
processSubprogram(diea);
}
@@ -125,29 +120,16 @@ public class DWARFFunctionImporter {
break;
case DW_TAG_variable:
// only process variable definitions that are static variables
- // (ie. they are children of the compunit root, ie. depth == 1)
- // local variables should be children of dw_tag_subprograms
+ // (ie. they are children of the compunit root, ie. depth == 1).
+ // Local variables should be children of dw_tag_subprograms
// and will be handled in processFuncChildren()
if (diea.getDepth() == 1) {
- try {
- processVariable(diea, null, null, -1);
- }
- catch (InvalidInputException e) {
- Msg.error(this, "Failed to process var " + diea.getHexOffset(), e);
- }
+ outputGlobal(DWARFVariable.readGlobalVariable(diea));
}
break;
case DW_TAG_label:
processLabel(diea);
break;
-
- case DW_TAG_gnu_call_site:
- case DW_TAG_call_site:
- DIEAggregate partDIEA = DIEAggregate.createSkipHead(diea);
- if (partDIEA != null && !isBadSubprogramDef(partDIEA)) {
- processSubprogram(partDIEA);
- }
- break;
}
}
catch (OutOfMemoryError oom) {
@@ -171,28 +153,13 @@ public class DWARFFunctionImporter {
Collections.sort(sortedUnknownRegs);
Msg.error(this,
" unknown registers: " +
- sortedUnknownRegs.stream().map(i -> Integer.toString(i)).collect(
- Collectors.joining(", ")));
+ sortedUnknownRegs.stream()
+ .map(i -> Integer.toString(i))
+ .collect(
+ Collectors.joining(", ")));
}
}
- private boolean isBadSubprogramDef(DIEAggregate diea) {
- if (diea.isDanglingDeclaration() || !diea.hasAttribute(DWARFAttribute.DW_AT_low_pc)) {
- return true;
- }
-
- long lowPC = diea.getLowPC(0); // adjusted by program base addr fixup
- DWARFNumericAttribute attr =
- diea.getAttribute(DWARFAttribute.DW_AT_low_pc, DWARFNumericAttribute.class);
- if (attr != null && attr.getUnsignedValue() == 0 && lowPC != 0) {
- // don't process this func if its raw lowpc is 0, with the exception of a binary (a .o)
- // that starts at 0 and has a function at 0
- return true;
- }
-
- return false;
- }
-
private void markAllChildrenAsProcessed(DebugInfoEntry die) {
for (DebugInfoEntry child : die.getChildren()) {
processedOffsets.add(child.getOffset());
@@ -203,230 +170,177 @@ public class DWARFFunctionImporter {
private void processSubprogram(DIEAggregate diea)
throws IOException, InvalidInputException, DWARFExpressionException {
- if (!shouldProcess(diea)) {
+ if (diea == null || !shouldProcess(diea)) {
return;
}
- if (isBadSubprogramDef(diea)) {
+ // read the dwarf function info (name, addr, params)
+ DWARFFunction dfunc = DWARFFunction.read(diea);
+ if (dfunc == null) {
markAllChildrenAsProcessed(diea.getHeadFragment());
return;
}
- DWARFFunction dfunc = new DWARFFunction(prog.getName(diea));
- dfunc.namespace = dfunc.dni.getParentNamespace(currentProgram);
+ FunctionDefinition origFuncDef = dfunc.asFuncDef(); // before any fixups
- Number lowPC = diea.getLowPC(0);
- dfunc.address = toAddr(lowPC);
- dfunc.highAddress =
- diea.hasAttribute(DWARFAttribute.DW_AT_high_pc) ? toAddr(diea.getHighPC()) : null;
+ if (functionsProcessed.contains(dfunc.address)) {
+ markAllChildrenAsProcessed(dfunc.diea.getHeadFragment());
- String previousFunctionProcessed = functionsProcessed.get(dfunc.address);
- if (previousFunctionProcessed != null) {
-// Msg.info(this, "Duplicate function defintion found for " + dni.getCategoryPath() +
-// " at " + function.address + " in DIE " + diea.getHexOffset() + ", skipping");
- markAllChildrenAsProcessed(diea.getHeadFragment());
+ Function currentFunction = currentProgram.getListing().getFunctionAt(dfunc.address);
+ if (currentFunction != null) {
+ decorateFunctionWithAlternateInfo(dfunc, currentFunction, origFuncDef);
+ }
return;
}
- functionsProcessed.put(dfunc.address,
- dfunc.dni.getNamespacePath() + " DIE: " + diea.getHexOffset());
+ functionsProcessed.add(dfunc.address);
- // Check if the function is an external function
- dfunc.isExternal = diea.getBool(DWARFAttribute.DW_AT_external, false);
+ // only process the children (lexical blocks, local vars, etc) if we are going
+ // to emit a new ghidra function, otherwise if 2 dwarf function defs point to same
+ // location, we will get multiple side-effect output from processFuncChildren
+ processFuncChildren(diea, dfunc, 0);
- // Retrieve the frame base if it exists
- DWARFLocation frameLoc = null;
- if (diea.hasAttribute(DWARFAttribute.DW_AT_frame_base)) {
- List frameBase = diea.getAsLocation(DWARFAttribute.DW_AT_frame_base);
- // get the framebase register, find where the frame is finally set
- // up.
- frameLoc = getTopLocation(frameBase, dfunc.address.getOffset());
- if (frameLoc != null) {
- dfunc.frameBase = (int) diea.evaluateLocation(frameLoc);
- }
+ Function gfunc = createFunction(dfunc, diea); // create empty func with no info
+ if (gfunc == null) {
+ return;
}
- // Get it's return type
- // TODO: Sometimes the return type may actually be a pointer parameter
- // passed into
- // the given function - figure out how to determine this. For example,
- // C++ can
- // return object types defined in the function but may be implemented as
- // the caller
- // function passing a pointer to the callee function where the object is
- // then operated on.
- DIEAggregate typeRef = diea.getTypeRef();
- DataType formalReturnType = (typeRef != null)
- ? dwarfDTM.getDataType(typeRef, DataType.DEFAULT)
- : dwarfDTM.getVoidType();
- dfunc.retval = new DWARFVariable();
- dfunc.retval.type = formalReturnType;
+ if (gfunc.hasNoReturn() && !dfunc.noReturn) {
+ // preserve the noReturn flag if set by earlier analyzer
+ dfunc.noReturn = true;
+ }
- boolean formalParamsOnly = false;
- boolean skipFuncSignature = false;
- List formalParams = new ArrayList<>();
-
- for (DIEAggregate paramDIEA : diea.getFunctionParamList()) {
-
- DataType paramDT = dwarfDTM.getDataType(paramDIEA.getTypeRef(), null);
- if (paramDT == null || DataTypeComponent.usesZeroLengthComponent(paramDT)) {
- String paramName = paramDIEA.getString(DW_AT_name, "param" + formalParams.size());
- Msg.warn(this, "DWARF: zero-length function parameter " + paramName +
- ":" + paramDT.getName() + ", omitting from definition of " +
- dfunc.dni.getName() + "@" + dfunc.address);
- // skip this parameter because its data type is a zero-width type that typically does
- // not generate code. If this varies compiler-to-compiler, setting
- // skipFuncSignature=true may be a better choice
- continue;
+ // Run all the DWARFFunctionFixup instances
+ for (DWARFFunctionFixup fixup : functionFixups) {
+ try {
+ fixup.fixupDWARFFunction(dfunc, gfunc);
}
-
- Parameter formalParam = createFormalParameter(paramDIEA);
- if (formalParam == null) {
- skipFuncSignature = true;
+ catch (DWARFException e) {
+ dfunc.signatureCommitMode = CommitMode.SKIP;
break;
}
- formalParams.add(formalParam);
-
- if (!formalParamsOnly) {
- DWARFVariable var = processVariable(paramDIEA, dfunc, null, -1);
- if (var == null) {
- // we had an error, can't rely on detailed param data, fallback to
- // formal params
- formalParamsOnly = true;
- dfunc.params.clear();
- }
- else {
- dfunc.params.add(var);
- }
- }
}
- dfunc.varArg = !diea.getChildren(DW_TAG_unspecified_parameters).isEmpty();
- processFuncChildren(diea, dfunc);
+ decorateFunctionWithDWARFInfo(dfunc, gfunc, origFuncDef);
- Function gfunc = createFunction(dfunc, diea);
+ if (dfunc.signatureCommitMode != CommitMode.SKIP) {
+ updateFunctionSignature(gfunc, dfunc);
+ }
+ else {
+ Msg.error(this,
+ String.format(
+ "Failed to get DWARF function signature information, leaving undefined: %s@%s",
+ gfunc.getName(), gfunc.getEntryPoint()));
+ Msg.debug(this, "DIE info: " + diea.toString());
+ }
- if (gfunc != null) {
- if (diea.getBool(DW_AT_noreturn, false)) {
- gfunc.setNoReturn(true);
- }
- if (formalParams.isEmpty() && dfunc.localVarErrors) {
- // if there were no defined parameters and we had problems decoding local variables,
- // don't force the method to have an empty param signature because there are other
- // issues afoot.
- skipFuncSignature = true;
- }
- 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.
- skipFuncSignature = true;
- }
-
- if (skipFuncSignature) {
- Msg.error(this,
- "Failed to get function signature information, leaving undefined: " +
- gfunc.getName() + "@" + gfunc.getEntryPoint());
- Msg.debug(this, "DIE info: " + diea.toString());
- return;
- }
-
- if (formalParamsOnly) {
- updateFunctionSignatureWithFormalParams(gfunc, formalParams,
- formalReturnType, dfunc.varArg, diea);
+ for (DWARFVariable localVar : dfunc.localVars) {
+ if (localVar.isRamStorage()) {
+ outputGlobal(localVar); // static variable scoped to the function
}
else {
- updateFunctionSignatureWithDetailParams(gfunc, dfunc, diea);
+ dfunc.commitLocalVariable(localVar, gfunc);
}
}
+ }
+
+ private void decorateFunctionWithAlternateInfo(DWARFFunction dfunc, Function gfunc,
+ FunctionDefinition funcDef) {
+ // Don't include the calling conv as it generates excessive false positives
+ // because we haven't run the dfunc through any fixups yet.
+ // Unnamed parameters still cause false positives because they render differently between
+ // funcdefs and actual functions
+ String newAlternatePrototype = funcDef.getPrototypeString(false);
+
+ String currentPrototype = gfunc.getSignature(true).getPrototypeString(false);
+ if (!currentPrototype.equals(newAlternatePrototype)) {
+ appendPlateComment(dfunc.address, "DWARF alternate signature: ", newAlternatePrototype);
+ }
}
-
- private void updateFunctionSignatureWithFormalParams(Function gfunc, List params,
- DataType returnType, boolean varArgs, DIEAggregate diea) {
- try {
- String callingConventionName = null;
- ReturnParameterImpl returnVar = new ReturnParameterImpl(returnType, currentProgram);
- try {
- if (!params.isEmpty() && Function.THIS_PARAM_NAME.equals(params.get(0).getName())) {
- // this handles the common / simple case. More nuanced cases where the param
- // didn't have the correct "this" name, but were marked with DW_AT_object_pointer
- // or DW_AT_artifical won't be handled by this.
- callingConventionName = GenericCallingConvention.thiscall.getDeclarationName();
- }
- gfunc.setVarArgs(varArgs);
- gfunc.updateFunction(callingConventionName, returnVar, params,
- FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
+ private void decorateFunctionWithDWARFInfo(DWARFFunction dfunc, Function gfunc,
+ FunctionDefinition origFuncDef) {
+ if (dfunc.sourceInfo != null) {
+ // Move the function into the program tree of the file
+ moveIntoFragment(gfunc.getName(), dfunc.address,
+ dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1),
+ dfunc.sourceInfo.getFilename());
+
+ if (importOptions.isOutputSourceLocationInfo()) {
+ appendPlateComment(dfunc.address, "", dfunc.sourceInfo.getDescriptionStr());
}
- catch (DuplicateNameException e) {
- // try again after adjusting param names
- setUniqueParameterNames(gfunc, params);
- gfunc.updateFunction(callingConventionName, returnVar, params,
+ }
+ if (importOptions.isOutputDIEInfo()) {
+ appendPlateComment(dfunc.address, "DWARF DIE: ", dfunc.diea.getHexOffset());
+ appendPlateComment(dfunc.address, "DWARF signature update mode: ",
+ dfunc.signatureCommitMode.toString());
+ }
+
+ if (dfunc.name.isNameModified()) {
+ appendPlateComment(dfunc.address, "DWARF original name: ",
+ dfunc.name.getOriginalName());
+ }
+
+ FunctionDefinition newFuncDef = dfunc.asFuncDef();
+ String origFuncDefStr = origFuncDef.getPrototypeString(true);
+ if (!newFuncDef.getPrototypeString(true).equals(origFuncDefStr)) {
+ // if the prototype of the function was modified during the fixup phase, append
+ // the original version (according to dwarf) to the comment
+ appendPlateComment(dfunc.address, "DWARF original prototype: ", origFuncDefStr);
+ }
+
+
+ }
+
+ private void updateFunctionSignature(Function gfunc, DWARFFunction dfunc) {
+ try {
+ boolean includeStorageDetail = dfunc.signatureCommitMode == CommitMode.STORAGE;
+ FunctionUpdateType functionUpdateType = includeStorageDetail
+ ? FunctionUpdateType.CUSTOM_STORAGE
+ : FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS;
+
+ Parameter returnVar = dfunc.retval.asReturnParameter(includeStorageDetail);
+ List parameters = dfunc.getParameters(includeStorageDetail);
+
+ if (includeStorageDetail && !dfunc.retval.isZeroByte() &&
+ dfunc.retval.isMissingStorage()) {
+ // Update return value in a separate step as its storage isn't typically specified
+ // in dwarf info.
+ // This will allow automagical storage assignment for return value by ghidra.
+ gfunc.updateFunction(dfunc.getCallingConventionName(), returnVar, List.of(),
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
+ returnVar = null; // don't update it in the second call to updateFunction()
}
+
+ gfunc.updateFunction(dfunc.getCallingConventionName(), returnVar, parameters,
+ functionUpdateType, true, SourceType.IMPORTED);
+ gfunc.setVarArgs(dfunc.varArg);
+ gfunc.setNoReturn(dfunc.noReturn);
}
catch (InvalidInputException | DuplicateNameException e) {
Msg.error(this,
- "Error updating function " + gfunc.getName() + " with formal params at " +
- gfunc.getEntryPoint().toString() + ": " + e.getMessage());
- Msg.error(this, "DIE info: " + diea.toString());
+ String.format("Error updating function %s@%s with params: %s",
+ gfunc.getName(), gfunc.getEntryPoint().toString(), e.getMessage()));
+ Msg.error(this, "DIE info: " + dfunc.diea.toString());
}
}
- private void updateFunctionSignatureWithDetailParams(Function gfunc, DWARFFunction dfunc,
- DIEAggregate diea) {
- try {
- CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
- PrototypeModel convention = null;
- Variable returnVariable;
- List params = new ArrayList<>();
-
- returnVariable = buildReturnVariable(dfunc.retval);
- for (int i = 0; i < dfunc.params.size(); ++i) {
- Parameter curparam = buildParameter(gfunc, i, dfunc.params.get(i), diea);
- params.add(curparam);
- if (i == 0 && checkThisParameter(dfunc.params.get(0), diea)) {
- convention =
- compilerSpec.matchConvention(CompilerSpec.CALLING_CONVENTION_thiscall);
- }
- }
-
- for (int i = 0; i < dfunc.local.size(); ++i) {
- commitLocal(gfunc, dfunc.local.get(i));
- }
-
- if (dfunc.retval != null || params.size() > 0) {
- // Add the function signature definition into the data type manager
-// TODO: createFunctionDefinition(dfunc, infopath);
-
- // NOTE: Storage is computed above for the purpose of identifying
- // a best fit calling convention. The commitPrototype method currently
- // always employs dynamic storage.
- commitPrototype(gfunc, returnVariable, params, convention);
- gfunc.setVarArgs(dfunc.varArg);
- }
- }
- catch (InvalidInputException | DuplicateNameException iie) {
- Msg.error(this, "Error updating function " + dfunc.dni.getName() + " at " +
- dfunc.address.toString() + ": " + iie.getMessage());
- }
- }
-
- private void processFuncChildren(DIEAggregate diea, DWARFFunction dfunc)
+ private void processFuncChildren(DIEAggregate diea, DWARFFunction dfunc,
+ long offsetFromFuncStart)
throws InvalidInputException, IOException, DWARFExpressionException {
+ // offsetFromFuncStart will be -1 if the containing block didn't have location info
for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren()) {
DIEAggregate childDIEA = prog.getAggregate(childEntry);
switch (childDIEA.getTag()) {
case DW_TAG_variable: {
- DWARFVariable var =
- processVariable(childDIEA, dfunc, null, dfunc.address.getOffset());
-
- if ((var != null) && var.isStackOffset) {
- dfunc.local.add(var);
+ if (offsetFromFuncStart >= 0) {
+ DWARFVariable localVar =
+ DWARFVariable.readLocalVariable(childDIEA, dfunc, offsetFromFuncStart);
+ if (localVar != null) {
+ dfunc.localVars.add(localVar);
+ }
}
break;
}
@@ -443,258 +357,95 @@ public class DWARFFunctionImporter {
case DW_TAG_gnu_call_site:
case DW_TAG_call_site:
DIEAggregate partDIEA = DIEAggregate.createSkipHead(diea);
- if (partDIEA != null && !isBadSubprogramDef(partDIEA)) {
- processSubprogram(partDIEA);
- }
+ processSubprogram(partDIEA);
break;
}
}
}
- private Parameter createFormalParameter(DIEAggregate diea) {
- String name = diea.getString(DW_AT_name, null);
- DataType dt = dwarfDTM.getDataType(diea.getTypeRef(), dwarfDTM.getVoidType());
-
- try {
- return new ParameterImpl(name, dt, currentProgram);
- }
- catch (InvalidInputException e) {
- Msg.debug(this, "Failed to create parameter for " + diea.toString());
- }
- return null;
- }
-
- /**
- * Creates a new {@link DWARFVariable} from the specified {@link DIEAggregate DIEA} and
- * as a child of the specified function (if not null).
- *
- * Used to process DW_TAG_variable as well as DW_TAG_formal_parameters.
- *
- * @param diea - the diea that specifies the variable
- * @param dfunc - function that contains this variable, or null if static variable
- * @param lexicalStart - not used by any caller
- * @param firstUseAddr offset dfunc or -1 if formal parameter
- * @return
- * @throws IOException
- * @throws InvalidInputException
- */
- private DWARFVariable processVariable(DIEAggregate diea, DWARFFunction dfunc,
- Address lexicalStart, long firstUseAddr) throws IOException, InvalidInputException {
-
- if (!shouldProcess(diea)) {
- return null;
+ private void outputGlobal(DWARFVariable globalVar) {
+ if (globalVar == null) {
+ return;
}
- long funcAddr = (dfunc != null && dfunc.address != null) ? dfunc.address.getOffset() : -1;
-
- DWARFVariable dvar = new DWARFVariable();
- dvar.dni = prog.getName(diea);
- dvar.lexicalOffset = dfunc != null && dfunc.address != null && lexicalStart != null
- ? lexicalStart.subtract(dfunc.address)
- : -1;
-
- // Unknown variable location
- if (!diea.hasAttribute(DWARFAttribute.DW_AT_location)) {
- return null;
- }
-
- List locList = diea.getAsLocation(DWARFAttribute.DW_AT_location);
-
- // If we are trying to recover a local variable, only process the
- // variable if it has a single location over the entire function
- if ((firstUseAddr != -1) && locList.size() > 1) {
- return null;
- }
-
- DWARFLocation topLocation = getTopLocation(locList, funcAddr);
- if (topLocation == null) {
- if (dfunc != null) {
- dfunc.localVarErrors = true;
- }
- return null;
- }
-
- // Get the base type of this variable
- dvar.type = dwarfDTM.getDataType(diea.getTypeRef(), dwarfDTM.getVoidType());
-
- long frameBase = (dfunc != null) ? dfunc.frameBase : -1;
- DWARFExpressionEvaluator exprEvaluator =
- DWARFExpressionEvaluator.create(diea.getHeadFragment());
- exprEvaluator.setFrameBase(frameBase);
- long res;
- try {
- DWARFExpression expr = exprEvaluator.readExpr(topLocation.getLocation());
- exprEvaluator.evaluate(expr);
- res = exprEvaluator.pop();
- }
- catch (DWARFExpressionException | UnsupportedOperationException
- | IndexOutOfBoundsException ex) {
- importSummary.exprReadError++;
- if (dfunc != null) {
- dfunc.localVarErrors = true;
- }
-
- return null;
- }
-
- if (exprEvaluator.isDwarfStackValue()) {
- importSummary.varDWARFExpressionValue++;
- if (dfunc != null) {
- dfunc.localVarErrors = true;
- }
- return null;
- }
- else if (exprEvaluator.useUnknownRegister() && exprEvaluator.isRegisterLocation()) {
- dvar.reg = exprEvaluator.getLastRegister();
- dvar.type = dwarfDTM.getPtrTo(dvar.type);
-
- // TODO: fix this later. Lie and use lexicalOffset-1 so the GUI correctly shows the first use
- dvar.offset = dvar.lexicalOffset != -1 ? dvar.lexicalOffset - 1 : -1;
- return dvar;
- }
- else if (exprEvaluator.useUnknownRegister()) {
- importSummary.varDynamicRegisterError++;
- if (dfunc != null) {
- dfunc.localVarErrors = true;
- }
- return null;
- }
- else if (exprEvaluator.isStackRelative()) {
- dvar.offset = res;
- dvar.reg = null;
- dvar.isStackOffset = true;
- if (exprEvaluator.isDeref()) {
- dvar.type = dwarfDTM.getPtrTo(dvar.type);
- }
- }
- else if (exprEvaluator.isRegisterLocation()) {
- // The DWARF expression evaluated to a simple register. If we have a mapping
- // for it in the "processor.dwarf" register mapping file, try to create
- // a variable, otherwise log the unknown register for later logging.
- dvar.reg = exprEvaluator.getLastRegister();
- if (dvar.reg != null) {
- dvar.offset = -1;
- if (firstUseAddr != -1) {
- dvar.offset = findFirstUse(currentProgram, dvar.reg, funcAddr, firstUseAddr);
- }
- if ((dvar.type != null) &&
- (dvar.type.getLength() > dvar.reg.getMinimumByteSize())) {
- importSummary.varFitError++;
-
- String contextStr = (dfunc != null)
- ? " for function " + dfunc.dni.getName() + "@" + dfunc.address
- : "";
- if (diea.getTag() != DW_TAG_formal_parameter) {
- Msg.warn(this,
- "Variable " + dvar.dni.getName() + "[" + dvar.type.getName() +
- ", size=" + dvar.type.getLength() + "]" + contextStr +
- " can not fit into specified register " + dvar.reg.getName() +
- ", size=" + dvar.reg.getMinimumByteSize() +
- ", skipping. DWARF DIE: " + diea.getHexOffset());
- if (dfunc != null) {
- dfunc.localVarErrors = true;
- }
- return null;
- }
-
- dvar.type = dwarfDTM.getUndefined1Type();
- }
- }
- else {
- // The DWARF register did not have a mapping to a Ghidra register, so
- // log it to be displayed in an error summary at end of import phase.
- importSummary.unknownRegistersEncountered.add(exprEvaluator.getRawLastRegister());
- if (dfunc != null) {
- dfunc.localVarErrors = true;
- }
- return null;
- }
- }
- else if (exprEvaluator.getLastRegister() == null) {
- processStaticVar(res, dvar, diea);
- return null;// Don't return the variable to be associated with the function
- }
- else {
- Msg.error(this,
- "LOCAL VAR: " + dvar.dni.getName() + " : " +
- ghidra.app.util.bin.format.dwarf4.expression.DWARFExpression.exprToString(
- topLocation.getLocation(), diea) +
- ", DWARF DIE: " + diea.getHexOffset());
- return null;
- }
- return dvar;
- }
-
- private void processStaticVar(long address, DWARFVariable dvar, DIEAggregate diea)
- throws InvalidInputException {
- dvar.dni = dvar.dni.replaceType(null /*nothing matches static global var*/);
- if (address != 0) {
- Address staticVariableAddress = toAddr(address + prog.getProgramBaseAddressFixup());
- if (isZeroByteDataType(dvar.type)) {
- processZeroByteStaticVar(staticVariableAddress, dvar);
- return;
- }
-
- if (variablesProcesesed.contains(staticVariableAddress)) {
- return;
- }
-
- boolean external = diea.getBool(DW_AT_external, false);
-
- outputGlobal(staticVariableAddress, dvar.type, external,
- DWARFSourceInfo.create(diea), dvar.dni);
- }
- else {
- // If the expression evaluated to a static address of '0'.
- // This case is probably caused by relocation fixups not being applied to the
- // .debug_info section.
- importSummary.relocationErrorVarDefs.add(
- dvar.dni.getNamespacePath().asFormattedString() + " : " +
- dvar.type.getPathName());
- }
- }
-
- private void processZeroByteStaticVar(Address staticVariableAddress, DWARFVariable dvar)
- throws InvalidInputException {
- // because this is a zero-length data type (ie. array[0]),
- // don't create a variable at the location since it will prevent other elements
- // from occupying the same offset
- Listing listing = currentProgram.getListing();
- String comment =
- listing.getComment(CodeUnit.PRE_COMMENT, staticVariableAddress);
- comment = (comment != null) ? comment + "\n" : "";
- comment += String.format("Zero length variable: %s: %s", dvar.dni.getOriginalName(),
- dvar.type.getDisplayName());
- listing.setComment(staticVariableAddress, CodeUnit.PRE_COMMENT, comment);
+ Namespace namespace = globalVar.name.getParentNamespace(currentProgram);
+ String name = globalVar.name.getName();
+ Address address = globalVar.getRamAddress();
+ DataType dataType = globalVar.type;
SymbolTable symbolTable = currentProgram.getSymbolTable();
- symbolTable.createLabel(staticVariableAddress, dvar.dni.getName(),
- dvar.dni.getParentNamespace(currentProgram),
- SourceType.IMPORTED);
- }
+ Symbol labelSym = null;
- private boolean isZeroByteDataType(DataType dt) {
- if (!dt.isZeroLength() && dt instanceof Array) {
- dt = DataTypeUtilities.getArrayBaseDataType((Array) dt);
+ if (globalVar.isZeroByte() || !variablesProcesesed.contains(address)) {
+ try {
+ labelSym = symbolTable.createLabel(address, name, namespace, SourceType.IMPORTED);
+ }
+ catch (InvalidInputException e) {
+ Msg.error(this,
+ String.format("Error creating label for global variable %s/%s at %s",
+ namespace, name, address));
+ return;
+ }
+ }
+
+ if (globalVar.isZeroByte()) {
+ // because this is a zero-length data type (ie. array[0]),
+ // don't create a variable at the location since it will prevent other elements
+ // from occupying the same offset
+ appendComment(address, CodeUnit.PRE_COMMENT, String.format(
+ "Zero length variable: %s: %s", name, dataType.getDisplayName()), "\n");
+
+ return;
+ }
+
+ if (variablesProcesesed.contains(address)) {
+ return;
+ }
+
+ labelSym.setPrimary();
+
+ if (globalVar.isExternal) {
+ setExternalEntryPoint(true, address);
+ }
+
+ try {
+ if (dataType instanceof Dynamic || dataType instanceof FactoryDataType) {
+ appendComment(address, CodeUnit.EOL_COMMENT,
+ "Unsupported dynamic data type: " + dataType, "\n");
+ dataType = Undefined.getUndefinedDataType(1);
+ }
+ DWARFDataInstanceHelper dih = new DWARFDataInstanceHelper(currentProgram);
+ if (!dih.isDataTypeCompatibleWithAddress(dataType, address)) {
+ appendComment(address, CodeUnit.EOL_COMMENT, String.format(
+ "Could not place DWARF static variable %s: %s @%s because existing data type conflicts.",
+ globalVar.name.getName(), dataType.getName(), address), "\n");
+ }
+ else {
+ Data varData = DataUtilities.createData(currentProgram, address, dataType, -1,
+ ClearDataMode.CLEAR_ALL_CONFLICT_DATA);
+ if (varData != null && globalVar.sourceInfo != null) {
+ moveIntoFragment(name, varData.getMinAddress(), varData.getMaxAddress(),
+ globalVar.sourceInfo.getFilename());
+ }
+ variablesProcesesed.add(address);
+ }
+ }
+ catch (CodeUnitInsertionException e) {
+ Msg.error(this, "Error creating data object at " + address, e);
+ }
+ importSummary.globalVarsAdded++;
+
+ if (globalVar.sourceInfo != null) {
+ appendComment(address, CodeUnit.EOL_COMMENT, globalVar.sourceInfo.getDescriptionStr(),
+ "\n");
}
- return dt.isZeroLength();
}
- /**
- * Process lexical block entries.
- *
- * @param entry
- * DIE
- * @param unit
- * current compilation unit
- * @param frameBase
- * Location list of the current frame
- * @param function
- * parent function of the lexical block
- * @throws IOException
- * @throws InvalidInputException
- * @throws DWARFExpressionException
+ /*
+ * Process lexical block entries inside of a function.
+ *
+ * This recursively processes any children of the lexical block diea via processFuncChildren().
*/
private void processLexicalBlock(DIEAggregate diea, DWARFFunction dfunc)
throws IOException, InvalidInputException, DWARFExpressionException {
@@ -702,9 +453,6 @@ public class DWARFFunctionImporter {
return;
}
- DWARFNameInfo dni = prog.getName(diea);
-
- String name = dni.getName();
Number lowPC = null;
boolean disjoint = false;
@@ -717,7 +465,7 @@ public class DWARFFunctionImporter {
}
// Otherwise process a range list
else if (diea.hasAttribute(DW_AT_ranges)) {
- List ranges = diea.parseDebugRange(DWARFAttribute.DW_AT_ranges);
+ List ranges = diea.parseDebugRange(DW_AT_ranges);
// No range found
if (ranges.isEmpty()) {
@@ -727,18 +475,15 @@ public class DWARFFunctionImporter {
lowPC = ranges.get(0).getFrom();
disjoint = ranges.size() > 1;
}
- else {
- Msg.error(this, "LEXICAL BLOCK: No start and end ranges were found so the lexical " +
- "block could not be processed.");
- return;
- }
- Address blockStart = toAddr(lowPC);
- if (name != null && importOptions.isOutputLexicalBlockComments()) {
+ Address blockStart = lowPC != null ? prog.getCodeAddress(lowPC) : null;
+ if (blockStart != null && importOptions.isOutputLexicalBlockComments()) {
+ DWARFNameInfo dni = prog.getName(diea);
appendComment(blockStart, CodeUnit.PRE_COMMENT,
- "Begin: " + name + (disjoint ? " - Disjoint" : ""), "\n");
+ "Begin: " + dni.getName() + (disjoint ? " - Disjoint" : ""), "\n");
}
- processFuncChildren(diea, dfunc);
+ processFuncChildren(diea, dfunc,
+ blockStart != null ? blockStart.subtract(dfunc.address) : -1);
}
private void processInlinedSubroutine(DIEAggregate diea, DWARFFunction dfunc)
@@ -771,19 +516,18 @@ public class DWARFFunctionImporter {
return;
}
+ Address startAddr = prog.getCodeAddress(lowPC);
+ Address endAddr = prog.getCodeAddress(highPC);
if (importOptions.isOutputInlineFuncComments()) {
- addCommentsForInlineFunc(diea, toAddr(lowPC), toAddr(highPC));
+ addCommentsForInlineFunc(diea, startAddr, endAddr);
}
- processFuncChildren(diea, dfunc);
+ processFuncChildren(diea, dfunc, startAddr.subtract(dfunc.address));
}
- /**
+ /*
* Constructs a function def signature for the function and adds it as a comment, either
* EOL or PRE depending on how small the inline func is.
- * @param diea
- * @param blockStart
- * @param blockEnd
*/
private void addCommentsForInlineFunc(DIEAggregate diea, Address blockStart, Address blockEnd) {
FunctionDefinition funcDef = dwarfDTM.getFunctionSignature(diea);
@@ -801,22 +545,13 @@ public class DWARFFunctionImporter {
}
}
- /**
- * Appends a comment at the specified address
- * @param address the address to set the PRE comment
- * @param commentType ie. CodeUnit.PRE_COMMENT
- * @param comment the PRE comment
- * @param sep the characters to use to separate existing comments
- * @return true if the comment was successfully set
- */
- private boolean appendComment(Address address, int commentType, String comment, String sep) {
- AppendCommentCmd cmd = new AppendCommentCmd(address, commentType, comment, sep);
- return cmd.applyTo(currentProgram);
+ private void appendComment(Address address, int commentType, String comment, String sep) {
+ DWARFUtil.appendComment(currentProgram, address, commentType, "", comment, sep);
}
- private final Address toAddr(Number offset) {
- return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(
- offset.longValue(), true);
+ private void appendPlateComment(Address address, String prefix, String comment) {
+ DWARFUtil.appendComment(currentProgram, address, CodeUnit.PLATE_COMMENT, prefix, comment,
+ "\n");
}
/**
@@ -834,243 +569,6 @@ public class DWARFFunctionImporter {
}
}
- private boolean isArrayDataTypeCompatibleWithExistingData(Array arrayDT, Address address) {
- Listing listing = currentProgram.getListing();
-
- // quick success
- Data arrayData = listing.getDataAt(address);
- if (arrayData != null && arrayData.getBaseDataType().isEquivalent(arrayDT)) {
- return true;
- }
-
- if (arrayData != null && arrayDT.getDataType() instanceof CharDataType &&
- arrayData.getBaseDataType() instanceof StringDataType) {
- if (arrayData.getLength() >= arrayDT.getLength()) {
- return true;
- }
- return DataUtilities.isUndefinedRange(currentProgram,
- address.add(arrayData.getLength()), address.add(arrayDT.getLength() - 1));
- }
-
- // test each element
- for (int i = 0; i < arrayDT.getNumElements(); i++) {
- Address elementAddress = address.add(arrayDT.getElementLength() * i);
- Data data = listing.getDataAt(elementAddress);
- if (data != null &&
- !isDataTypeCompatibleWithExistingData(arrayDT.getDataType(), elementAddress)) {
- return false;
- }
- }
-
- return true;
- }
-
- private boolean isStructDataTypeCompatibleWithExistingData(Structure structDT,
- Address address) {
- for (DataTypeComponent dtc : structDT.getDefinedComponents()) {
- Address memberAddress = address.add(dtc.getOffset());
- if (!isDataTypeCompatibleWithExistingData(dtc.getDataType(), memberAddress)) {
- return false;
- }
- }
- return true;
- }
-
- private boolean isPointerDataTypeCompatibleWithExistingData(Pointer pdt, Address address) {
- Listing listing = currentProgram.getListing();
- Data data = listing.getDataAt(address);
- if (data == null) {
- return true;
- }
-
- DataType dataDT = data.getBaseDataType();
- return dataDT instanceof Pointer;
- }
-
- private boolean isSimpleDataTypeCompatibleWithExistingData(DataType dataType, Address address) {
- Listing listing = currentProgram.getListing();
-
- Data data = listing.getDataAt(address);
- if (data == null) {
- return true;
- }
-
- DataType dataDT = data.getBaseDataType();
- if (dataType instanceof CharDataType && dataDT instanceof StringDataType) {
- return true;
- }
-
- if (!dataType.getClass().isInstance(dataDT)) {
- return false;
- }
- int dataTypeLen = dataType.getLength();
- if (dataTypeLen > 0 && dataTypeLen != data.getLength()) {
- return false;
- }
- return true;
- }
-
- private boolean isEnumDataTypeCompatibleWithExistingData(Enum enumDT, Address address) {
- Listing listing = currentProgram.getListing();
- Data data = listing.getDataAt(address);
- if (data == null) {
- return true;
- }
-
- DataType dataDT = data.getBaseDataType();
- if (!(dataDT instanceof Enum || dataDT instanceof AbstractIntegerDataType)) {
- return false;
- }
- if (dataDT instanceof BooleanDataType) {
- return false;
- }
- if (dataDT.getLength() != enumDT.getLength()) {
- return false;
- }
- return true;
- }
-
- private boolean isDataTypeCompatibleWithExistingData(DataType dataType, Address address) {
- if (DataUtilities.isUndefinedRange(currentProgram, address,
- address.add(dataType.getLength() - 1))) {
- return true;
- }
-
- if (dataType instanceof Array) {
- return isArrayDataTypeCompatibleWithExistingData((Array) dataType, address);
- }
- if (dataType instanceof Pointer) {
- return isPointerDataTypeCompatibleWithExistingData((Pointer) dataType, address);
- }
- if (dataType instanceof Structure) {
- return isStructDataTypeCompatibleWithExistingData((Structure) dataType, address);
- }
- if (dataType instanceof TypeDef) {
- return isDataTypeCompatibleWithExistingData(((TypeDef) dataType).getBaseDataType(),
- address);
- }
- if (dataType instanceof Enum) {
- return isEnumDataTypeCompatibleWithExistingData((Enum) dataType, address);
- }
-
- if (dataType instanceof CharDataType || dataType instanceof StringDataType ||
- dataType instanceof IntegerDataType || dataType instanceof UnsignedIntegerDataType ||
- dataType instanceof BooleanDataType) {
- return isSimpleDataTypeCompatibleWithExistingData(dataType, address);
- }
-
- return false;
- }
-
- private Data createVariable(Address address, DataType dataType, DWARFNameInfo dni) {
- try {
- String eolComment = null;
- if (dataType instanceof Dynamic || dataType instanceof FactoryDataType) {
- eolComment = "Unsupported dynamic data type: " + dataType;
- dataType = Undefined.getUndefinedDataType(1);
- }
- if (!isDataTypeCompatibleWithExistingData(dataType, address)) {
- appendComment(address, CodeUnit.EOL_COMMENT,
- "Could not place DWARF static variable " +
- dni.getNamespacePath().asFormattedString() + " : " + dataType +
- " because existing data type conflicts.",
- "\n");
- return null;
- }
- Data result = DataUtilities.createData(currentProgram, address, dataType, -1,
- ClearDataMode.CLEAR_ALL_CONFLICT_DATA);
- variablesProcesesed.add(address);
- if (eolComment != null) {
- appendComment(address, CodeUnit.EOL_COMMENT, eolComment, "\n");
- }
- return result;
- }
- catch (CodeUnitInsertionException e) {
- Msg.error(this, "Error creating data object at " + address, e);
- }
- return null;
- }
-
- private void outputGlobal(Address address, DataType baseDataType, boolean external,
- DWARFSourceInfo sourceInfo, DWARFNameInfo dni) {
-
- Namespace namespace = dni.getParentNamespace(currentProgram);
-
- SymbolTable symbolTable = currentProgram.getSymbolTable();
- try {
- symbolTable.createLabel(address, dni.getName(), namespace, SourceType.IMPORTED);
- SetLabelPrimaryCmd cmd = new SetLabelPrimaryCmd(address, dni.getName(), namespace);
- cmd.applyTo(currentProgram);
- }
- catch (InvalidInputException e) {
- Msg.error(this,
- "Error creating symbol " + namespace + "/" + dni.getName() + " at " + address);
- return;
- }
-
- setExternalEntryPoint(external, address);
-
- Data varData = createVariable(address, baseDataType, dni);
- importSummary.globalVarsAdded++;
-
- if (sourceInfo != null) {
- appendComment(address, CodeUnit.EOL_COMMENT, sourceInfo.getDescriptionStr(), "\n");
-
- if (varData != null) {
- moveIntoFragment(dni.getName(), varData.getMinAddress(), varData.getMaxAddress(),
- sourceInfo.getFilename());
- }
- }
- }
-
- /**
- * Get the location that corresponds to the entry point of the function If
- * there is only a single location, assume it applies to whole function
- *
- * @param locList
- * @param funcAddr
- * @return the byte array corresponding to the location expression
- */
- private static DWARFLocation getTopLocation(List locList, long funcAddr) {
- if (locList.size() == 1) {
- return locList.get(0);
- }
- for (DWARFLocation loc : locList) {
- if (loc.getRange().getFrom() == funcAddr) {
- return loc;
- }
- }
- return null;
- }
-
- private static int findFirstUse(Program currentProgram, Register register, long funcAddr,
- long firstUseAddr) {
- // look for the first write to this register within this range.
- Address entry = currentProgram.getMinAddress().getNewAddress(firstUseAddr);
- InstructionIterator instructions = currentProgram.getListing().getInstructions(entry, true);
- while (instructions.hasNext()) {
- Instruction instruction = instructions.next();
-
- FlowType flowType = instruction.getFlowType();
- if (flowType.isTerminal()) {
- return 0;
- }
- Object[] resultObjects = instruction.getResultObjects();
- for (int i = 0; i < resultObjects.length; i++) {
- if (!(resultObjects[i] instanceof Register)) {
- continue;
- }
- Register outReg = (Register) resultObjects[i];
- if (register.equals(outReg)) {
- long offset = instruction.getMinAddress().getOffset() - funcAddr;
- return (int) offset;
- }
- }
- }
- // return the offset from the function entry to the real first use
- return 0;
- }
-
/**
* Move an address range into a fragment.
* @param cu current compile unit
@@ -1121,136 +619,16 @@ public class DWARFFunctionImporter {
}
}
- /**
- * For some DWARF debugger strategies, the storage location provided for a formal parameter is NOT the initial storage
- * of the parameter and does not match the calling convention. If the storage location provided is in the local variable
- * range for the function, this is an indication the storage does not represent the calling convention
- * @param dfunc is the DWARF function data to test
- * @return true if the storage locations represent the calling convention
- */
-// private boolean evaluateParameterStorage(DWARFFunction dfunc) {
-// if (!prog.getRegisterMappings().isUseFormalParameterStorage()) {
-// return false;
-// }
-// for (int i = 0; i < dfunc.params.size(); ++i) {
-// DWARFVariable var = dfunc.params.get(i);
-// if (var.reg == null) {
-// boolean paramsHavePositiveOffset = stackGrowsNegative;
-// if (!var.isStackOffset ||
-// // double check for valid param offset
-// (paramsHavePositiveOffset && var.offset < 0) ||
-// (!paramsHavePositiveOffset && var.offset >= 0)) {
-// return false;
-// }
-// }
-// if (var.type == null) {
-// // this can happen when a parameter doesn't fit into the register that
-// // the dwarf expression helper decoded as the parameter's location.
-// return false;
-// }
-// }
-// return true;
-// }
-
- private Variable buildVariable(DWARFVariable dvar) throws InvalidInputException {
- Varnode[] vnarray = buildVarnodes(dvar);
- VariableStorage storage = new VariableStorage(currentProgram, vnarray);
- int firstUseOffset = 0;
- if ((dvar.reg != null) && (dvar.offset != -1)) {
- firstUseOffset = (int) dvar.offset;
- }
- return new LocalVariableImpl(dvar.dni.getName(), firstUseOffset, dvar.type, storage,
- currentProgram);
- }
-
- private Variable buildReturnVariable(DWARFVariable dvar) throws InvalidInputException {
- if (dvar == null) {
- return new ReturnParameterImpl(DataType.VOID, currentProgram);
- }
- VariableStorage storage;
- Varnode[] vnarray = buildVarnodes(dvar);
- if (vnarray == null) {
- storage = VariableStorage.UNASSIGNED_STORAGE;
- }
- else {
- storage = new VariableStorage(currentProgram, vnarray);
- }
- return new ReturnParameterImpl(dvar.type, storage, currentProgram);
- }
-
- private Parameter buildParameter(Function function, int i, DWARFVariable dvar,
- DIEAggregate funcDIEA) throws InvalidInputException {
- VariableStorage storage;
- Varnode[] vnarray = buildVarnodes(dvar);
- if (vnarray == null) {
- storage = VariableStorage.UNASSIGNED_STORAGE;
- }
- else {
- storage = new VariableStorage(currentProgram, vnarray);
- }
-
- return new ParameterImpl(dvar.dni.getName(), dvar.type, storage, currentProgram);
- }
-
- private Varnode[] buildVarnodes(DWARFVariable dvar) {
- if (dvar.type == null) {
- return null;
- }
- Varnode[] retarray = null;
- int typesize = dvar.type.getLength();
- if (dvar.reg != null) {
- retarray = new Varnode[1];
- if (prog.isBigEndian() && (dvar.reg.getMinimumByteSize() > typesize)) {
- retarray[0] = new Varnode(
- dvar.reg.getAddress().add(dvar.reg.getMinimumByteSize() - typesize), typesize);
- }
- else {
- retarray[0] = new Varnode(dvar.reg.getAddress(), typesize);
- }
- }
- else if (dvar.isStackOffset) {
- retarray = new Varnode[1];
- retarray[0] = new Varnode(
- currentProgram.getAddressFactory().getStackSpace().getAddress(dvar.offset),
- typesize);
- }
- return retarray;
- }
-
- private boolean checkThisParameter(DWARFVariable var, DIEAggregate diea) {
- // If the variable is not named, check to see if the datatype is the same
- // as the parent entry
- if (Function.THIS_PARAM_NAME.equals(var.dni.getName())) {
- return true;
- }
-
- // Check for a parent class
- DIEAggregate parentDIEA = diea.getParent();
- if (parentDIEA != null && parentDIEA.isStructureType()) {
- DataType parentDT = dwarfDTM.getDataType(parentDIEA, null);
- // Check to see if the parent data type equals the parameters' data type
- if (parentDT != null && parentDT == var.type) {
- if (!var.dni.isAnon()) {
- Msg.error(this, "WARNING: Renaming " + var.dni.getName() + " to " +
- Function.THIS_PARAM_NAME);
- }
- var.dni = var.dni.replaceName(Function.THIS_PARAM_NAME, Function.THIS_PARAM_NAME);
- return true;
- }
- }
- return false;
- }
-
private Function createFunction(DWARFFunction dfunc, DIEAggregate diea) {
try {
// create a new symbol if one does not exist (symbol table will figure this out)
SymbolTable symbolTable = currentProgram.getSymbolTable();
- symbolTable.createLabel(dfunc.address, dfunc.dni.getName(), dfunc.namespace,
+ symbolTable.createLabel(dfunc.address, dfunc.name.getName(), dfunc.namespace,
SourceType.IMPORTED);
// force new label to become primary (if already a function it will become function name)
SetLabelPrimaryCmd cmd =
- new SetLabelPrimaryCmd(dfunc.address, dfunc.dni.getName(), dfunc.namespace);
+ new SetLabelPrimaryCmd(dfunc.address, dfunc.name.getName(), dfunc.namespace);
cmd.applyTo(currentProgram);
setExternalEntryPoint(dfunc.isExternal, dfunc.address);
@@ -1260,40 +638,20 @@ public class DWARFFunctionImporter {
// TODO: If not contained within program memory should they be considered external?
- if (!currentProgram.getMemory().getLoadedAndInitializedAddressSet().contains(
- dfunc.address)) {
+ if (!currentProgram.getMemory()
+ .getLoadedAndInitializedAddressSet()
+ .contains(dfunc.address)) {
Msg.warn(this,
- "Unable to create function not contained within loaded memory (" +
- dfunc.address + ") " + dfunc.namespace + "/" + dfunc.dni.getName());
+ String.format(
+ "DWARF: unable to create function not contained within loaded memory: %s@%s",
+ dfunc.name, dfunc.address));
return null;
}
// create 1-byte function if one does not exist - primary label will become function names
- function = currentProgram.getFunctionManager().createFunction(null, dfunc.address,
- new AddressSet(dfunc.address), SourceType.IMPORTED);
- }
-
- DWARFSourceInfo sourceInfo = DWARFSourceInfo.create(diea);
- if (sourceInfo != null) {
- // Move the function into the program tree of the file
- moveIntoFragment(function.getName(), dfunc.address,
- dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1),
- sourceInfo.getFilename());
-
- if (importOptions.isOutputSourceLocationInfo()) {
- appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
- sourceInfo.getDescriptionStr(), "\n");
- }
- }
- if (importOptions.isOutputDIEInfo()) {
- appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
- "DWARF DIE: " + diea.getHexOffset(), "\n");
- }
-
- DWARFNameInfo dni = prog.getName(diea);
- if (dni.isNameModified()) {
- appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
- "Original name: " + dni.getOriginalName(), "\n");
+ function = currentProgram.getFunctionManager()
+ .createFunction(null, dfunc.address, new AddressSet(dfunc.address),
+ SourceType.IMPORTED);
}
return function;
@@ -1303,180 +661,11 @@ public class DWARFFunctionImporter {
}
catch (InvalidInputException e) {
Msg.error(this, "Failed to create function " + dfunc.namespace + "/" +
- dfunc.dni.getName() + ": " + e.getMessage());
+ dfunc.name.getName() + ": " + e.getMessage());
}
return null;
}
- /**
- * Changes the names of the parameters in the array to unique names that won't conflict with
- * any other names in the function's namespace when the parameters are used to replace
- * the existing parameters in the function. Appends an integer number to
- * the base name if necessary to create a unique name in the function's namespace.
- * @param function the function
- * @param parameters the parameters that need names that won't conflict. These should be
- * Impl objects and not DB objects since their names will be changed within this method.
- * @throws InvalidInputException invalid parameter name
- * @throws DuplicateNameException (should not occur on non-DB parameter)
- */
- private void setUniqueParameterNames(Function function, List parameters)
- throws DuplicateNameException, InvalidInputException {
- SymbolTable symbolTable = currentProgram.getSymbolTable();
- // Create a set containing all the unique parameter names determined so far so they can
- // be avoided as additional parameter names are determined.
- Set namesSoFar = new HashSet<>();
- for (int ordinal = 0; ordinal < parameters.size(); ordinal++) {
- Parameter parameter = parameters.get(ordinal);
- String baseName = parameter.getName();
- if (ordinal == 0 && Function.THIS_PARAM_NAME.equals(baseName)) {
- continue;
- }
- String uniqueName =
- getUniqueReplacementParameterName(symbolTable, function, baseName, namesSoFar);
- namesSoFar.add(uniqueName);
- parameter.setName(uniqueName, SourceType.IMPORTED);
- }
- }
-
- /**
- * Get a unique parameter name for a parameter when all parameter names are being replaced.
- * If the specified name is a default parameter name then the original default name passed
- * in is returned.
- * @param symbolTable the symbol table containing symbols for the indicated namespace
- * @param namespace the namespace containing symbol names to check.
- * @param baseName the base name to append with an integer number if necessary
- * to create a unique name.
- * @param namesNotToBeUsed set of names that should not be used when determining a unique name.
- * @return a unique parameter name
- */
- private static String getUniqueReplacementParameterName(SymbolTable symbolTable,
- Function function, String name, Set namesNotToBeUsed) {
- if (name == null || SymbolUtilities.isDefaultParameterName(name)) {
- return name;
- }
- return getUniqueNameIgnoringCurrentParameters(symbolTable, function, name,
- namesNotToBeUsed);
- }
-
- /**
- * Gets a unique name in the indicated namespace by appending an integer number if necessary
- * and ignoring any conflicts with existing parameters.
- * @param symbolTable the symbol table containing symbols for the indicated namespace
- * @param namespace the namespace containing symbol names to check.
- * @param baseName the base name to append with an integer number if necessary to create a
- * unique name.
- * @param namesNotToBeUsed set of names that should not be used when determining a unique name.
- * @return an unused unique name within the namespace ignoring current parameter names and
- * that doesn't conflict with any in the set of names not to be used.
- */
- private static String getUniqueNameIgnoringCurrentParameters(SymbolTable symbolTable,
- Namespace namespace, String baseName, Set namesNotToBeUsed) {
- String name = baseName;
- if (name != null) {
- // establish unique name
- int cnt = 0;
- List symbols = symbolTable.getSymbols(name, namespace);
- while (!symbols.isEmpty()) {
- if (namesNotToBeUsed.contains(name)) {
- continue;
- }
- if (areAllParamaters(symbols)) {
- return name;
- }
- name = baseName + "_" + (++cnt);
- symbols = symbolTable.getSymbols(name, namespace);
- }
- }
- return name;
- }
-
- private static boolean areAllParamaters(List symbols) {
- for (Symbol symbol : symbols) {
- if (symbol.getSymbolType() != SymbolType.PARAMETER) {
- return false;
- }
- }
- return true;
- }
-
- private void commitPrototype(Function function, Variable returnVariable,
- List params, PrototypeModel protoModel)
- throws InvalidInputException, DuplicateNameException {
-
- CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
-
- if (protoModel == null) {
- Parameter[] paramarray = params.toArray(Parameter[]::new);
- protoModel = compilerSpec.findBestCallingConvention(paramarray);
- }
-
- try {
- function.updateFunction(protoModel.getName(), returnVariable, params,
- FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
- }
- catch (DuplicateNameException e) {
- setUniqueParameterNames(function, params);
- function.updateFunction(protoModel.getName(), returnVariable, params,
- FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
- }
-
-// TODO: Determination of storage is unreliable and frequently forces incorrect storage to be used
-// if (useCustomStorageIfNeeded &&
-// !VariableUtilities.storageMatches(params, function.getParameters())) {
-// // try again if dynamic storage assignment does not match what DWARF specified
-// // force into custom storage mode
-// function.updateFunction(protoModel.getName(), null, params,
-// FunctionUpdateType.CUSTOM_STORAGE, true, SourceType.IMPORTED);
-// }
- }
-
- private void commitLocal(Function func, DWARFVariable dvar) throws InvalidInputException {
- // Attempt to add the variable
- Variable var = buildVariable(dvar);
-
- // check for an existing local variable with conflict storage.
- boolean hasConflict = false;
- for (Variable existingVar : func.getAllVariables()) {
- if (existingVar.getFirstUseOffset() == var.getFirstUseOffset() &&
- existingVar.getVariableStorage().intersects(var.getVariableStorage())) {
- if ((existingVar instanceof LocalVariable) &&
- Undefined.isUndefined(existingVar.getDataType())) {
- // ignore locals with undefined type - they will be removed below
- continue;
- }
- hasConflict = true;
- break;
- }
- }
- if (hasConflict) {
- appendComment(func.getEntryPoint().add(dvar.lexicalOffset), CodeUnit.EOL_COMMENT,
- "Scope for omitted local variable " + var.toString() + " starts here", "; ");
- return;
- }
-
- try {
- VariableUtilities.checkVariableConflict(func, null, var.getVariableStorage(), true);
- func.addLocalVariable(var, SourceType.IMPORTED);
- }
- catch (DuplicateNameException e) {
- int count = 1;
- // Add the variable with an unused name
- String baseName = var.getName();
- while (!monitor.isCancelled()) {
- try {
- var.setName(baseName + "_" + Integer.toString(count), SourceType.IMPORTED);
- func.addLocalVariable(var, SourceType.IMPORTED);
- }
- catch (DuplicateNameException e1) {
- count++;
- continue;
- }
- break;
- }
- }
-
- }
-
private void processLabel(DIEAggregate diea) {
if (!shouldProcess(diea)) {
return;
@@ -1484,7 +673,7 @@ public class DWARFFunctionImporter {
String name = prog.getEntryName(diea);
if (name != null && diea.hasAttribute(DW_AT_low_pc)) {
- Address address = toAddr(diea.getLowPC(0));
+ Address address = prog.getCodeAddress(diea.getLowPC(0));
if (address.getOffset() != 0) {
try {
SymbolTable symbolTable = currentProgram.getSymbolTable();
@@ -1504,36 +693,4 @@ public class DWARFFunctionImporter {
}
}
- /**
- * Holds values necessary to create a new variable / parameter.
- */
- static class DWARFVariable {
- public DWARFNameInfo dni;
- public DataType type;
- public long offset;// Offset on stack or firstuseoffset if this is a register
- public boolean isStackOffset;// true if offset represents stack offset
- public long lexicalOffset;
- public Register reg;
- }
-
- /**
- * Holds values necessary to create a new function
- */
- static class DWARFFunction {
- public Address address;
- public Address highAddress;
- public DWARFNameInfo dni;
- public Namespace namespace;
- public DWARFVariable retval;
- public boolean isExternal;
- public long frameBase;
- public List params = new ArrayList<>();
- public List local = new ArrayList<>();
- public boolean varArg;
- public boolean localVarErrors; // set to true if problem w/local var decoding
-
- public DWARFFunction(DWARFNameInfo dni) {
- this.dni = dni;
- }
- }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java
index 35686c66a6..454a46b964 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFParser.java
@@ -15,10 +15,11 @@
*/
package ghidra.app.util.bin.format.dwarf4.next;
-import java.io.IOException;
import java.util.Collections;
import java.util.List;
+import java.io.IOException;
+
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.util.bin.format.dwarf4.DWARFException;
import ghidra.program.model.data.*;
@@ -37,19 +38,11 @@ public class DWARFParser {
private DWARFProgram prog;
private DWARFDataTypeManager dwarfDTM;
private TaskMonitor monitor;
- private DWARFImportOptions importOptions;
- private DWARFImportSummary importSummary = new DWARFImportSummary();
- public DWARFParser(DWARFProgram prog, DataTypeManager builtInDTM, TaskMonitor monitor) {
+ public DWARFParser(DWARFProgram prog, TaskMonitor monitor) {
this.prog = prog;
this.monitor = monitor;
- this.importOptions = prog.getImportOptions();
- this.dwarfDTM = new DWARFDataTypeManager(prog, prog.getGhidraProgram().getDataTypeManager(),
- builtInDTM, importSummary);
- }
-
- public DWARFImportOptions getImportOptions() {
- return importOptions;
+ this.dwarfDTM = prog.getDwarfDTM();
}
/**
@@ -193,8 +186,10 @@ public class DWARFParser {
monitor.setIndeterminate(false);
monitor.setShowProgressValue(true);
- long start_ts = System.currentTimeMillis();
+ DWARFImportOptions importOptions = prog.getImportOptions();
+ DWARFImportSummary importSummary = prog.getImportSummary();
+ long start_ts = System.currentTimeMillis();
if (importOptions.isImportDataTypes()) {
dwarfDTM.importAllDataTypes(monitor);
prog.getGhidraProgram().flushEvents();
@@ -203,8 +198,7 @@ public class DWARFParser {
if (importOptions.isImportFuncs()) {
long funcstart_ts = System.currentTimeMillis();
- DWARFFunctionImporter dfi =
- new DWARFFunctionImporter(prog, dwarfDTM, importOptions, importSummary, monitor);
+ DWARFFunctionImporter dfi = new DWARFFunctionImporter(prog, monitor);
dfi.importFunctions();
importSummary.funcsElapsedMS = System.currentTimeMillis() - funcstart_ts;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java
index 3d8f0879fa..339b04f680 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/DWARFProgram.java
@@ -31,6 +31,8 @@ import ghidra.app.util.bin.format.dwarf4.expression.DWARFExpressionException;
import ghidra.app.util.bin.format.dwarf4.external.ExternalDebugInfo;
import ghidra.app.util.bin.format.dwarf4.next.sectionprovider.*;
import ghidra.app.util.opinion.*;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Program;
@@ -47,9 +49,13 @@ import ghidra.util.task.TaskMonitor;
*/
public class DWARFProgram implements Closeable {
public static final String DWARF_ROOT_NAME = "DWARF";
+ public static final CategoryPath DWARF_ROOT_CATPATH = CategoryPath.ROOT.extend(DWARF_ROOT_NAME);
+ public static final CategoryPath UNCAT_CATPATH = DWARF_ROOT_CATPATH.extend("_UNCATEGORIZED_");
+
public static final int DEFAULT_NAME_LENGTH_CUTOFF = SymbolUtilities.MAX_SYMBOL_NAME_LENGTH;
public static final int MAX_NAME_LENGTH_CUTOFF = SymbolUtilities.MAX_SYMBOL_NAME_LENGTH;
public static final int MIN_NAME_LENGTH_CUTOFF = 20;
+
private static final int NAME_HASH_REPLACEMENT_SIZE = 8 + 2 + 2;
private static final String ELLIPSES_STR = "...";
@@ -61,7 +67,7 @@ public class DWARFProgram implements Closeable {
* program sections, or their compressed "z" versions.
*
* If the program is a MachO binary (ie. Mac), it must have a ".dSYM" directory co-located next to the
- * original binary file on the native filesystem. (ie. outside of Ghidra). See the DSymSectionProvider
+ * original binary file on the native filesystem. (lie. outside of Ghidra). See the DSymSectionProvider
* for more info.
*
* @param program {@link Program} to test
@@ -114,10 +120,9 @@ public class DWARFProgram implements Closeable {
private final Program program;
private DWARFImportOptions importOptions;
- private DWARFNameInfo rootDNI =
- DWARFNameInfo.createRoot(new CategoryPath(CategoryPath.ROOT, DWARF_ROOT_NAME));
- private DWARFNameInfo unCatDataTypeRoot = DWARFNameInfo.createRoot(
- new CategoryPath(rootDNI.getOrganizationalCategoryPath(), "_UNCATEGORIZED_"));
+ private DWARFImportSummary importSummary;
+ private DWARFNameInfo rootDNI = DWARFNameInfo.createRoot(DWARF_ROOT_CATPATH);
+ private DWARFNameInfo unCatDataTypeRoot = DWARFNameInfo.createRoot(UNCAT_CATPATH);
private DWARFSectionProvider sectionProvider;
private StringTable debugStrings;
@@ -173,6 +178,12 @@ public class DWARFProgram implements Closeable {
*/
private ListValuedMap typeReferers = new ArrayListValuedHashMap<>();
+ private final DWARFDataTypeManager dwarfDTM;
+
+ private final boolean stackGrowsNegative;
+
+ private final Map