diff --git a/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java b/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java index 6b4f87803f..59885a6062 100644 --- a/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/DWARF_ExtractorScript.java @@ -63,7 +63,7 @@ public class DWARF_ExtractorScript extends GhidraScript { @Override public void run() throws Exception { - if (!DWARFProgram.isDWARF(currentProgram, monitor)) { + if (!DWARFProgram.isDWARF(currentProgram)) { popup("Unable to find DWARF information, aborting"); return; } 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 45627bfbdf..6a501ea5e1 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 @@ -34,6 +34,8 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; public class DWARFAnalyzer extends AbstractAnalyzer { + private static final String DWARF_LOADED_OPTION_NAME = "DWARF Loaded"; + private static final String OPTION_IMPORT_DATATYPES = "Import data types"; private static final String OPTION_IMPORT_DATATYPES_DESC = "Import data types defined in the DWARF debug info."; @@ -84,6 +86,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer { "Automatically extracts DWARF info from an ELF file."; private DWARFImportOptions importOptions = new DWARFImportOptions(); + private long lastTxId = -1; public DWARFAnalyzer() { super(DWARF_ANALYZER_NAME, DWARF_ANALYZER_DESCRIPTION, AnalyzerType.BYTE_ANALYZER); @@ -105,22 +108,24 @@ public class DWARFAnalyzer extends AbstractAnalyzer { public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { - if (!canAnalyze(program)) { - // Check again because external DWARF data (ie. dSYM files) could have been moved - // between the time canAnalyze() was called the first time and when this method - // is called - log.appendMsg("Unable to find DWARF information, skipping DWARF analysis"); - return false; + long txId = program.getCurrentTransaction().getID(); + if (txId == lastTxId) { + // Only run once per analysis session - as denoted by being in the same transaction + return true; } + lastTxId = txId; - if (DWARFProgram.alreadyDWARFImported(program)) { - Msg.warn(this, "DWARF already imported, skipping. (Detected DWARF program module)"); + Options propList = program.getOptions(Program.PROGRAM_INFO); + boolean alreadyLoaded = propList.getBoolean(DWARF_LOADED_OPTION_NAME, false) || + oldCheckIfDWARFImported(program); + if (alreadyLoaded) { + Msg.info(this, "DWARF already imported, skipping."); return false; } DWARFSectionProvider dsp = DWARFSectionProviderFactory.createSectionProviderFor(program); if (dsp == null) { - // silently return, canAnalyze() was false positive + log.appendMsg("Unable to find DWARF information, skipping DWARF analysis"); return false; } @@ -139,6 +144,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer { DWARFImportSummary parseResults = dp.parse(); parseResults.logSummaryResults(); } + propList.setBoolean(DWARF_LOADED_OPTION_NAME, true); return true; } catch (CancelledException ce) { @@ -157,9 +163,16 @@ public class DWARFAnalyzer extends AbstractAnalyzer { return false; } + private boolean oldCheckIfDWARFImported(Program prog) { + // this was the old way of checking if the DWARF analyzer had already been run. Keep + // it around for a little bit so existing programs that have already imported DWARF data + // don't get re-run. Remove after a release or two. + return DWARFFunctionImporter.hasDWARFProgModule(prog, DWARFProgram.DWARF_ROOT_NAME); + } + @Override public boolean canAnalyze(Program program) { - return DWARFProgram.isDWARF(program, null); + return DWARFProgram.isDWARF(program); } @Override 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 6462efea8e..afd2246961 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 @@ -54,12 +54,8 @@ public class DWARFProgram implements Closeable { private static final int NAME_HASH_REPLACEMENT_SIZE = 8 + 2 + 2; private static final String ELLIPSES_STR = "..."; - public static boolean alreadyDWARFImported(Program prog) { - return DWARFFunctionImporter.hasDWARFProgModule(prog, DWARF_ROOT_NAME); - } - /** - * Returns true if the {@link Program program} probably DWARF information. + * Returns true if the {@link Program program} probably has DWARF information. *
* If the program is an Elf binary, it must have (at least) ".debug_info" and ".debug_abbr" program sections. *
@@ -67,17 +63,17 @@ public class DWARFProgram implements Closeable { * original binary file on the native filesystem. (ie. outside of Ghidra). See the DSymSectionProvider * for more info. *
- * @param program - * @param monitor - * @return + * @param program {@link Program} to test + * @return boolean true if program has DWARF info, false if not */ - public static boolean isDWARF(Program program, TaskMonitor monitor) { + public static boolean isDWARF(Program program) { String format = program.getExecutableFormat(); - if (ElfLoader.ELF_NAME.equals(format)) { + if (ElfLoader.ELF_NAME.equals(format) && + DWARFSectionProviderFactory.createSectionProviderFor(program) != null) { return true; } - else if (MachoLoader.MACH_O_NAME.equals(format) && + if (MachoLoader.MACH_O_NAME.equals(format) && DSymSectionProvider.getDSYMForProgram(program) != null) { return true; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProviderFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProviderFactory.java index 7ea6cdce59..7cb75f4a24 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProviderFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/DWARFSectionProviderFactory.java @@ -15,14 +15,14 @@ */ package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; -import ghidra.program.model.listing.Program; -import ghidra.util.Msg; - import java.io.Closeable; import java.util.ArrayList; import java.util.List; import java.util.function.Function; +import ghidra.program.model.listing.Program; +import ghidra.util.Msg; + /** * Auto-detects which {@link DWARFSectionProvider} matches a Ghidra program. */ @@ -44,7 +44,6 @@ public class DWARFSectionProviderFactory { static { sectionProviderFactoryFuncs.add(BaseSectionProvider::createSectionProviderFor); sectionProviderFactoryFuncs.add(DSymSectionProvider::createSectionProviderFor); - sectionProviderFactoryFuncs.add(ElfSectionProvider::createSectionProviderFor); } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/ElfSectionProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/ElfSectionProvider.java deleted file mode 100644 index 4eb692cabc..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf4/next/sectionprovider/ElfSectionProvider.java +++ /dev/null @@ -1,95 +0,0 @@ -/* ### - * 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.sectionprovider; - -import ghidra.app.util.bin.*; -import ghidra.app.util.bin.format.elf.*; -import ghidra.app.util.opinion.ElfLoader; -import ghidra.program.model.listing.Program; - -import java.io.File; -import java.io.IOException; - -import generic.continues.RethrowContinuesFactory; - -/** - * Fetches DWARF section data from ELF files, directly, without going through - * the Ghidra memory block api. This section provider usually isn't needed as - * ELF sections are normally provided as Ghidra memory blocks. In case of extra- - * large binaries, Ghidra may not be able to map the debug sections into memory - * and this section provider will allow the DWARF analyzer to still function. - */ -public class ElfSectionProvider implements DWARFSectionProvider { - - private ElfHeader header; - private RandomAccessByteProvider provider; - - public static ElfSectionProvider createSectionProviderFor(Program program) { - if (ElfLoader.ELF_NAME.equals(program.getExecutableFormat())) { - try { - File exePath = new File(program.getExecutablePath()); - return new ElfSectionProvider(exePath); - } - catch (IOException ioe) { - // ignore - } - } - return null; - } - - public ElfSectionProvider(File exeFile) throws IOException { - provider = new RandomAccessByteProvider(exeFile); - try { - // Parse the ELF header to get the sections - header = ElfHeader.createElfHeader(RethrowContinuesFactory.INSTANCE, provider); - header.parse(); - } - catch (ElfException e) { - provider.close(); - throw new IOException("Error parsing ELF", e); - } - } - - @Override - public ByteProvider getSectionAsByteProvider(String sectionName) throws IOException { - - ElfSectionHeader section = header.getSection("." + sectionName); - - return (section != null) ? new ByteProviderWrapper(section.getReader().getByteProvider(), - section.getOffset(), section.getSize()) : null; - } - - @Override - public void close() { - try { - provider.close(); - } - catch (IOException e) { - // ignore - } - } - - @Override - public boolean hasSection(String... sectionNames) { - for (String sectionName : sectionNames) { - if (header.getSection("." + sectionName) == null) { - return false; - } - } - return true; - } - -}