GP-695 Improve DWARF analyzer's runOnce, remove parsing of raw Elf

This commit is contained in:
dev747368 2021-02-23 15:11:04 -05:00
parent 45927bb9c3
commit 33435048de
5 changed files with 34 additions and 121 deletions

View file

@ -63,7 +63,7 @@ public class DWARF_ExtractorScript extends GhidraScript {
@Override @Override
public void run() throws Exception { public void run() throws Exception {
if (!DWARFProgram.isDWARF(currentProgram, monitor)) { if (!DWARFProgram.isDWARF(currentProgram)) {
popup("Unable to find DWARF information, aborting"); popup("Unable to find DWARF information, aborting");
return; return;
} }

View file

@ -34,6 +34,8 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class DWARFAnalyzer extends AbstractAnalyzer { 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 = "Import data types";
private static final String OPTION_IMPORT_DATATYPES_DESC = private static final String OPTION_IMPORT_DATATYPES_DESC =
"Import data types defined in the DWARF debug info."; "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."; "Automatically extracts DWARF info from an ELF file.";
private DWARFImportOptions importOptions = new DWARFImportOptions(); private DWARFImportOptions importOptions = new DWARFImportOptions();
private long lastTxId = -1;
public DWARFAnalyzer() { public DWARFAnalyzer() {
super(DWARF_ANALYZER_NAME, DWARF_ANALYZER_DESCRIPTION, AnalyzerType.BYTE_ANALYZER); 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) public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException { throws CancelledException {
if (!canAnalyze(program)) { long txId = program.getCurrentTransaction().getID();
// Check again because external DWARF data (ie. dSYM files) could have been moved if (txId == lastTxId) {
// between the time canAnalyze() was called the first time and when this method // Only run once per analysis session - as denoted by being in the same transaction
// is called return true;
log.appendMsg("Unable to find DWARF information, skipping DWARF analysis");
return false;
} }
lastTxId = txId;
if (DWARFProgram.alreadyDWARFImported(program)) { Options propList = program.getOptions(Program.PROGRAM_INFO);
Msg.warn(this, "DWARF already imported, skipping. (Detected DWARF program module)"); boolean alreadyLoaded = propList.getBoolean(DWARF_LOADED_OPTION_NAME, false) ||
oldCheckIfDWARFImported(program);
if (alreadyLoaded) {
Msg.info(this, "DWARF already imported, skipping.");
return false; return false;
} }
DWARFSectionProvider dsp = DWARFSectionProviderFactory.createSectionProviderFor(program); DWARFSectionProvider dsp = DWARFSectionProviderFactory.createSectionProviderFor(program);
if (dsp == null) { if (dsp == null) {
// silently return, canAnalyze() was false positive log.appendMsg("Unable to find DWARF information, skipping DWARF analysis");
return false; return false;
} }
@ -139,6 +144,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
DWARFImportSummary parseResults = dp.parse(); DWARFImportSummary parseResults = dp.parse();
parseResults.logSummaryResults(); parseResults.logSummaryResults();
} }
propList.setBoolean(DWARF_LOADED_OPTION_NAME, true);
return true; return true;
} }
catch (CancelledException ce) { catch (CancelledException ce) {
@ -157,9 +163,16 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
return false; 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 @Override
public boolean canAnalyze(Program program) { public boolean canAnalyze(Program program) {
return DWARFProgram.isDWARF(program, null); return DWARFProgram.isDWARF(program);
} }
@Override @Override

View file

@ -54,12 +54,8 @@ public class DWARFProgram implements Closeable {
private static final int NAME_HASH_REPLACEMENT_SIZE = 8 + 2 + 2; private static final int NAME_HASH_REPLACEMENT_SIZE = 8 + 2 + 2;
private static final String ELLIPSES_STR = "..."; 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.
* <p> * <p>
* If the program is an Elf binary, it must have (at least) ".debug_info" and ".debug_abbr" program sections. * If the program is an Elf binary, it must have (at least) ".debug_info" and ".debug_abbr" program sections.
* <p> * <p>
@ -67,17 +63,17 @@ public class DWARFProgram implements Closeable {
* original binary file on the native filesystem. (ie. outside of Ghidra). See the DSymSectionProvider * original binary file on the native filesystem. (ie. outside of Ghidra). See the DSymSectionProvider
* for more info. * for more info.
* <p> * <p>
* @param program * @param program {@link Program} to test
* @param monitor * @return boolean true if program has DWARF info, false if not
* @return
*/ */
public static boolean isDWARF(Program program, TaskMonitor monitor) { public static boolean isDWARF(Program program) {
String format = program.getExecutableFormat(); String format = program.getExecutableFormat();
if (ElfLoader.ELF_NAME.equals(format)) { if (ElfLoader.ELF_NAME.equals(format) &&
DWARFSectionProviderFactory.createSectionProviderFor(program) != null) {
return true; return true;
} }
else if (MachoLoader.MACH_O_NAME.equals(format) && if (MachoLoader.MACH_O_NAME.equals(format) &&
DSymSectionProvider.getDSYMForProgram(program) != null) { DSymSectionProvider.getDSYMForProgram(program) != null) {
return true; return true;
} }

View file

@ -15,14 +15,14 @@
*/ */
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider; 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.io.Closeable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
/** /**
* Auto-detects which {@link DWARFSectionProvider} matches a Ghidra program. * Auto-detects which {@link DWARFSectionProvider} matches a Ghidra program.
*/ */
@ -44,7 +44,6 @@ public class DWARFSectionProviderFactory {
static { static {
sectionProviderFactoryFuncs.add(BaseSectionProvider::createSectionProviderFor); sectionProviderFactoryFuncs.add(BaseSectionProvider::createSectionProviderFor);
sectionProviderFactoryFuncs.add(DSymSectionProvider::createSectionProviderFor); sectionProviderFactoryFuncs.add(DSymSectionProvider::createSectionProviderFor);
sectionProviderFactoryFuncs.add(ElfSectionProvider::createSectionProviderFor);
} }
/** /**

View file

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