From 3b01f15e1c69bae782dc30a7cac6e224362bc248 Mon Sep 17 00:00:00 2001 From: James <49045138+ghidracadabra@users.noreply.github.com> Date: Fri, 12 Sep 2025 12:47:14 +0000 Subject: [PATCH] GP-5984 create enums from DWARF macro info entries --- .../bin/format/dwarf/DWARFImportOptions.java | 30 ++++ .../bin/format/dwarf/DWARFImportSummary.java | 9 ++ .../util/bin/format/dwarf/DWARFImporter.java | 9 ++ .../format/dwarf/DWARFMacroEnumCreator.java | 130 ++++++++++++++++++ .../dwarf/expression/DWARFExpression.java | 6 +- 5 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java index 4604703b72..fdf524ac27 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportOptions.java @@ -141,6 +141,24 @@ public class DWARFImportOptions { private boolean showVariableStorageInfo = false; private boolean useStaticStackFrameRegisterValue = true; + /** + * Used to control which macro info entries are used to create enums. + */ + public static enum MacroEnumSetting { + NONE, + IGNORE_COMMAND_LINE, + ALL; + } + + private static final MacroEnumSetting OPTION_DEFAULT_MACRO_ENUM_SETTING = + MacroEnumSetting.IGNORE_COMMAND_LINE; + + private MacroEnumSetting macroEnumSetting = OPTION_DEFAULT_MACRO_ENUM_SETTING; + + private static final String OPTION_MACRO_ENUM_NAME = "Create Enums from Macros"; + private static final String OPTION_MACRO_ENUM_DESC = + "Controls which DWARF macro info entries are used to create enums"; + /** * Create new instance */ @@ -502,6 +520,14 @@ public class DWARFImportOptions { this.useStaticStackFrameRegisterValue = useStaticStackFrameRegisterValue; } + public MacroEnumSetting getMacroEnumSetting() { + return macroEnumSetting; + } + + public void setMacroEnumSetting(MacroEnumSetting setting) { + macroEnumSetting = setting; + } + /** * See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)} * @@ -553,6 +579,9 @@ public class DWARFImportOptions { options.registerOption(OPTION_SHOW_VARIABLE_STORAGE_INFO, isShowVariableStorageInfo(), null, OPTION_SHOW_VARIABLE_STORAGE_DESC); + + options.registerOption(OPTION_MACRO_ENUM_NAME, getMacroEnumSetting(), null, + OPTION_MACRO_ENUM_DESC); } /** @@ -587,5 +616,6 @@ public class DWARFImportOptions { setCharsetName(options.getString(OPTION_CHARSET_NAME, getCharsetName())); setShowVariableStorageInfo(options.getBoolean(OPTION_SHOW_VARIABLE_STORAGE_INFO, isShowVariableStorageInfo())); + setMacroEnumSetting(options.getEnum(OPTION_MACRO_ENUM_NAME, getMacroEnumSetting())); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java index 1445850acb..2cb7e4da9c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImportSummary.java @@ -27,6 +27,7 @@ public class DWARFImportSummary { // member variables are package access long dataTypeElapsedMS; long funcsElapsedMS; + long macroElapsedMS; long totalElapsedMS; int dataTypesAdded; @@ -39,6 +40,7 @@ public class DWARFImportSummary { Set typeRemappings = new HashSet<>(); int paramZeroLenDataType; public int badSourceFileCount; + public int numEnumsCreated; Set dwarfVers = new HashSet<>(); int compUnitCount; @@ -76,6 +78,13 @@ public class DWARFImportSummary { Msg.info(this, String.format("DWARF function signatures added: %d", funcSignaturesAdded)); } + if (numEnumsCreated > 0) { + Msg.info(this, + "DWARF enums created from macro info entries: %d".formatted(numEnumsCreated)); + } + if (macroElapsedMS > 0) { + Msg.info(this, "DWARF enums from macros - elapsed: %dms".formatted(macroElapsedMS)); + } if (!compDirs.isEmpty()) { Msg.info(this, "DWARF compile dirs: " + getSortedSet(compDirs).toString()); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImporter.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImporter.java index ce78149b0d..6f0f9528e8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImporter.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFImporter.java @@ -22,6 +22,7 @@ import org.apache.commons.io.FilenameUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.dwarf.DWARFImportOptions.MacroEnumSetting; import ghidra.app.util.bin.format.dwarf.line.DWARFLine; import ghidra.app.util.bin.format.dwarf.line.DWARFLine.SourceFileAddr; import ghidra.app.util.bin.format.dwarf.line.DWARFLine.SourceFileInfo; @@ -427,9 +428,17 @@ public class DWARFImporter { } } } + MacroEnumSetting setting = importOptions.getMacroEnumSetting(); + if (!setting.equals(MacroEnumSetting.NONE)) { + long macro_ts = System.currentTimeMillis(); + DWARFMacroEnumCreator enumCreator = new DWARFMacroEnumCreator(prog); + enumCreator.createEnumsFromMacroInfo(setting.equals(MacroEnumSetting.ALL), monitor); + importSummary.macroElapsedMS = System.currentTimeMillis() - macro_ts; + } importSummary.totalElapsedMS = System.currentTimeMillis() - start_ts; return importSummary; } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java new file mode 100644 index 0000000000..57f6d41a52 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/DWARFMacroEnumCreator.java @@ -0,0 +1,130 @@ +/* ### + * 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.dwarf; + +import java.io.IOException; +import java.util.*; + +import generic.expressions.*; +import ghidra.app.util.bin.format.dwarf.macro.DWARFMacroHeader; +import ghidra.app.util.bin.format.dwarf.macro.entry.*; +import ghidra.app.util.bin.format.dwarf.macro.entry.DWARFMacroDefine.MacroInfo; +import ghidra.program.model.data.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +/** + * Creates Enum data types in the program from DWARF macro info entries. Each macro + * that can be resolved to a long value results in an Enum datatype with a single + * value + */ +public class DWARFMacroEnumCreator { + + private DWARFProgram dprog; + private Set processedMacros; + public static final String ENUM_PATH = "_DEFINES_"; + + /** + * Constructor + * @param dprog DWARF Program + */ + public DWARFMacroEnumCreator(DWARFProgram dprog) { + this.dprog = dprog; + processedMacros = new HashSet<>(); + } + + /** + * Creates Ghidra Enums from DWARF macro info entries. + *

+ * The enums are placed in the program_name/DWARF/_DEFINES_ category path. + * Only one Enum is created for a given name. Creating multiple + * Enums for redefined or multiply-defined macros in not current supported. + * Each value in an enum given a comment consisting of the name of the + * corresponding compilation unit. + * + * + * @param includeCommandLineDefines if false, macros passed on the command line are ignored + * @param monitor monitor + * @throws CancelledException if user cancels + * @throws IOException if error reading macro info + */ + public void createEnumsFromMacroInfo(boolean includeCommandLineDefines, TaskMonitor monitor) + throws CancelledException, IOException { + monitor.initialize(dprog.getCompilationUnits().size()); + CategoryPath catPath = DWARFProgram.DWARF_ROOT_CATPATH.extend(ENUM_PATH); + for (DWARFCompilationUnit cu : dprog.getCompilationUnits()) { + monitor.increment(); + monitor.setMessage("DWARF: Processing Macros for " + cu.getName()); + Map macrosToValues = new HashMap<>(); + createEnums(cu.getMacros(), macrosToValues, catPath, includeCommandLineDefines, + monitor); + } + } + + private void createEnums(DWARFMacroHeader macroHeader, + Map macrosToValues, CategoryPath catPath, + boolean includeCommandLineDefines, TaskMonitor monitor) + throws IOException, CancelledException { + DataTypeManager dtManager = dprog.getGhidraProgram().getDataTypeManager(); + DWARFImportSummary importSummary = dprog.getImportSummary(); + for (DWARFMacroInfoEntry macroEntry : macroHeader.getEntries()) { + monitor.checkCancelled(); + switch (macroEntry) { + case DWARFMacroUndef undef: + macrosToValues.remove(undef.getMacroInfo().symbolName()); + break; + case DWARFMacroDefine define: + MacroInfo macroInfo = define.getMacroInfo(); + if (!macroInfo.isFunctionLike()) { + if (!includeCommandLineDefines && define.getLineNumber() == 0) { + break; + } + String symbolName = macroInfo.symbolName(); + if (processedMacros.contains(symbolName)) { + break; + } + processedMacros.add(symbolName); + try { + ExpressionEvaluator evaluator = + new ExpressionEvaluator(s -> macrosToValues.get(s)); + long value = evaluator.parseAsLong(macroInfo.definition()); + macrosToValues.put(symbolName, new LongExpressionValue(value)); + EnumDataType enumDT = + new EnumDataType(catPath, "define_" + symbolName, 8, + dtManager); + enumDT.add(symbolName, value, + macroHeader.getCompilationUnit().getName()); + importSummary.numEnumsCreated++; + enumDT.setLength(enumDT.getMinimumPossibleLength()); + dtManager.addDataType(enumDT, + DataTypeConflictHandler.KEEP_HANDLER); + } + catch (ExpressionException e) { + // couldn't get numeric value for macro, just skip + } + } + break; + case DWARFMacroImport importMacro: + createEnums(importMacro.getImportedMacroHeader(), macrosToValues, + catPath, includeCommandLineDefines, monitor); + break; + default: + break; + } + } + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java index 2f7924bed4..debab63e51 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/expression/DWARFExpression.java @@ -194,7 +194,10 @@ public class DWARFExpression { sb.append(" ] <=="); } if (instr.opcode == DW_OP_bra || instr.opcode == DW_OP_skip) { - long destOffset = instr.getOperandValue(0) + instr.getOffset(); + long destOffset = instr.getOffset(); + if (instr.getOperandCount() > 0) { + destOffset += instr.getOperandValue(0); + } int destIndex = findInstructionByOffset(destOffset); sb.append(String.format(" /* dest index: %d, offset: %03x */", destIndex, (int) destOffset)); @@ -221,5 +224,4 @@ public class DWARFExpression { return Objects.equals(instructions, other.instructions); } - }