mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
GP-695 Improve DWARF analyzer's runOnce, remove parsing of raw Elf
This commit is contained in:
parent
45927bb9c3
commit
33435048de
5 changed files with 34 additions and 121 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue