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
public void run() throws Exception {
if (!DWARFProgram.isDWARF(currentProgram, monitor)) {
if (!DWARFProgram.isDWARF(currentProgram)) {
popup("Unable to find DWARF information, aborting");
return;
}

View file

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

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 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>
* If the program is an Elf binary, it must have (at least) ".debug_info" and ".debug_abbr" program sections.
* <p>
@ -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.
* <p>
* @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;
}

View file

@ -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);
}
/**

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