mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
GP-5570 copy symbols found in external debug files
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -19,8 +19,7 @@ import java.io.IOException;
|
||||
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.bin.format.dwarf.*;
|
||||
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider;
|
||||
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory;
|
||||
import ghidra.app.util.bin.format.dwarf.sectionprovider.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
@@ -34,7 +33,8 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
private static final String DWARF_LOADED_OPTION_NAME = "DWARF Loaded";
|
||||
private static final String DWARF_ANALYZER_NAME = "DWARF";
|
||||
private static final String DWARF_ANALYZER_DESCRIPTION =
|
||||
"Automatically extracts DWARF info from ELF/MachO/PE files.";
|
||||
"Automatically extracts DWARF info from ELF/MachO/PE files.\n" +
|
||||
"Copies symbols from external debug files into the program.";
|
||||
|
||||
|
||||
/**
|
||||
@@ -92,6 +92,15 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if (importOptions.isCopyExternalDebugFileSymbols() &&
|
||||
dsp instanceof ExternalDebugFileSectionProvider extDSP) {
|
||||
|
||||
ExternalDebugFileSymbolImporter extDFSI = new ExternalDebugFileSymbolImporter(
|
||||
program, extDSP.getExternalProgram(), monitor);
|
||||
extDFSI.importSymbols(log);
|
||||
}
|
||||
|
||||
try (DWARFProgram prog = new DWARFProgram(program, importOptions, monitor, dsp)) {
|
||||
if (prog.getRegisterMappings() == null && importOptions.isImportFuncs()) {
|
||||
log.appendMsg("No DWARF to Ghidra register mappings found for this program's " +
|
||||
@@ -119,6 +128,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated(forRemoval = true, since = "10.0")
|
||||
private static boolean oldCheckIfDWARFImported(Program prog) {
|
||||
// this was the old way of checking if the DWARF analyzer had already been run. Keep
|
||||
|
||||
@@ -82,6 +82,12 @@ public class DWARFImportOptions {
|
||||
private static final String OPTION_MAX_SOURCE_ENTRY_LENGTH_DESC =
|
||||
"Maximum length for a source map entry. Longer lengths will be replaced with 0";
|
||||
|
||||
private static final String OPTION_COPY_EXTERNAL_DEBUG_FILE_SYMBOLS =
|
||||
"Copy external debug file symbols";
|
||||
private static final String OPTION_COPY_EXTERNAL_DEBUG_FILE_SYMBOLS_DESC =
|
||||
"Copies symbols (which will typically be mangled) from a found external debug file into " +
|
||||
"the main program";
|
||||
|
||||
//==================================================================================================
|
||||
// Old Option Names - Should stick around for multiple major versions after 10.2
|
||||
//==================================================================================================
|
||||
@@ -119,6 +125,7 @@ public class DWARFImportOptions {
|
||||
private boolean ignoreParamStorage = false;
|
||||
private String defaultCC = "";
|
||||
private long maxSourceMapEntryLength = 2000;
|
||||
private boolean copyExternalDebugFileSymbols = true;
|
||||
|
||||
/**
|
||||
* Create new instance
|
||||
@@ -438,6 +445,14 @@ public class DWARFImportOptions {
|
||||
maxSourceMapEntryLength = maxLength;
|
||||
}
|
||||
|
||||
public boolean isCopyExternalDebugFileSymbols() {
|
||||
return copyExternalDebugFileSymbols;
|
||||
}
|
||||
|
||||
public void setCopyExternalDebugFileSymbols(boolean b) {
|
||||
copyExternalDebugFileSymbols = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Analyzer#registerOptions(Options, ghidra.program.model.listing.Program)}
|
||||
*
|
||||
@@ -480,6 +495,9 @@ public class DWARFImportOptions {
|
||||
options.registerOption(OPTION_DEFAULT_CC, getDefaultCC(), null, OPTION_DEFAULT_CC_DESC);
|
||||
options.registerOption(OPTION_MAX_SOURCE_ENTRY_LENGTH, maxSourceMapEntryLength, null,
|
||||
OPTION_MAX_SOURCE_ENTRY_LENGTH_DESC);
|
||||
|
||||
options.registerOption(OPTION_COPY_EXTERNAL_DEBUG_FILE_SYMBOLS,
|
||||
isCopyExternalDebugFileSymbols(), null, OPTION_COPY_EXTERNAL_DEBUG_FILE_SYMBOLS_DESC);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -509,6 +527,8 @@ public class DWARFImportOptions {
|
||||
setDefaultCC(options.getString(OPTION_DEFAULT_CC, getDefaultCC()));
|
||||
setMaxSourceMapEntryLength(
|
||||
options.getLong(OPTION_MAX_SOURCE_ENTRY_LENGTH, getMaxSourceMapEntryLength()));
|
||||
setCopyExternalDebugFileSymbols(options.getBoolean(OPTION_COPY_EXTERNAL_DEBUG_FILE_SYMBOLS,
|
||||
isCopyExternalDebugFileSymbols()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/* ###
|
||||
* 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 ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.database.function.OverlappingFunctionException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.DataUtilities.ClearDataMode;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Imports symbols from an external debug program (typically created via a reverse strip) into
|
||||
* the program that contains the executable code that the symbols will be applied to.
|
||||
*/
|
||||
public class ExternalDebugFileSymbolImporter {
|
||||
|
||||
private Program program;
|
||||
private Program externalDebugProgram;
|
||||
private SymbolTable symTable;
|
||||
private FunctionManager funcMgr;
|
||||
private SymbolTable extSymTable;
|
||||
|
||||
private TaskMonitor monitor;
|
||||
|
||||
private int funcSymbolsCopied;
|
||||
private int dataSymbolsCopied;
|
||||
private int symbolsSkipped;
|
||||
private int totalSymbolCount;
|
||||
|
||||
public ExternalDebugFileSymbolImporter(Program program, Program externalDebugProgram,
|
||||
TaskMonitor monitor) {
|
||||
this.program = program;
|
||||
this.externalDebugProgram = externalDebugProgram;
|
||||
this.monitor = monitor;
|
||||
|
||||
this.symTable = program.getSymbolTable();
|
||||
this.funcMgr = program.getFunctionManager();
|
||||
|
||||
this.extSymTable = externalDebugProgram.getSymbolTable();
|
||||
}
|
||||
|
||||
public void importSymbols(MessageLog log) throws CancelledException {
|
||||
if (!isSameMemmap()) {
|
||||
Msg.warn(this,
|
||||
"Unable to copy external symbols from external debug file, memory map does not match");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
monitor.setIndeterminate(false);
|
||||
monitor.initialize(extSymTable.getNumSymbols(), "External debug file symbols");
|
||||
for (Symbol extSym : extSymTable.getPrimarySymbolIterator(true)) {
|
||||
monitor.increment();
|
||||
totalSymbolCount++;
|
||||
if (shouldCopyExtSymbol(extSym)) {
|
||||
copyExtSymbol(extSym);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException | CodeUnitInsertionException
|
||||
| OverlappingFunctionException e) {
|
||||
log.appendMsg("Error while copying external debug file symbols");
|
||||
log.appendException(e);
|
||||
}
|
||||
finally {
|
||||
|
||||
Msg.info(this, "Copied %d/%d of %d func/data/total symbols from external debug file"
|
||||
.formatted(funcSymbolsCopied, dataSymbolsCopied, totalSymbolCount));
|
||||
}
|
||||
}
|
||||
|
||||
private void copyExtSymbol(Symbol extSym)
|
||||
throws InvalidInputException, OverlappingFunctionException, CodeUnitInsertionException {
|
||||
SymbolType symType = extSym.getSymbolType();
|
||||
String name = extSym.getName();
|
||||
Address addr = extSym.getAddress();
|
||||
if (symType == SymbolType.FUNCTION && extSym.getObject() instanceof Function extFunc &&
|
||||
!extFunc.isThunk()) {
|
||||
Function existingFunction = funcMgr.getFunctionAt(addr);
|
||||
if (existingFunction == null) {
|
||||
existingFunction =
|
||||
funcMgr.createFunction(name, addr, new AddressSet(addr), SourceType.IMPORTED);
|
||||
}
|
||||
else if (!name.equals(existingFunction.getName())) {
|
||||
addLabelIfNeeded(name, addr);
|
||||
}
|
||||
funcSymbolsCopied++;
|
||||
}
|
||||
else if (symType == SymbolType.LABEL && extSym.getObject() instanceof Data extData) {
|
||||
if (Undefined.isUndefined(extData.getDataType()) &&
|
||||
DataUtilities.isUndefinedRange(program, addr, addr.add(extData.getLength()))) {
|
||||
DataType undefined = Undefined.getUndefinedDataType(extData.getLength());
|
||||
DataUtilities.createData(program, addr, undefined, -1,
|
||||
ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA);
|
||||
}
|
||||
addLabelIfNeeded(name, addr);
|
||||
dataSymbolsCopied++;
|
||||
}
|
||||
else {
|
||||
symbolsSkipped++;
|
||||
}
|
||||
}
|
||||
|
||||
private void addLabelIfNeeded(String name, Address addr) throws InvalidInputException {
|
||||
for (Symbol sym : symTable.getSymbolsAsIterator(addr)) {
|
||||
if (sym.getName().equals(name)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
symTable.createLabel(addr, name, SourceType.IMPORTED);
|
||||
}
|
||||
|
||||
private boolean shouldCopyExtSymbol(Symbol extSym) {
|
||||
return !extSym.getParentNamespace().isLibrary();
|
||||
}
|
||||
|
||||
private boolean isCommonMemblock(MemoryBlock memBlk) {
|
||||
return memBlk.isExecute();
|
||||
}
|
||||
|
||||
private boolean isSameMemmap() {
|
||||
for (MemoryBlock p1MemBlock : program.getMemory().getBlocks()) {
|
||||
if (!isCommonMemblock(p1MemBlock)) {
|
||||
continue;
|
||||
}
|
||||
MemoryBlock p2MemBlock =
|
||||
externalDebugProgram.getMemory().getBlock(p1MemBlock.getStart());
|
||||
if (p2MemBlock == null || !p2MemBlock.getName().equals(p1MemBlock.getName()) ||
|
||||
p2MemBlock.getSize() != p1MemBlock.getSize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
* 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.
|
||||
@@ -19,12 +19,14 @@ import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.format.dwarf.external.*;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.plugin.importer.ImporterUtilities;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.lang.CompilerSpec;
|
||||
import ghidra.program.model.lang.Language;
|
||||
@@ -69,17 +71,27 @@ public class ExternalDebugFileSectionProvider extends BaseSectionProvider {
|
||||
fsService.getByteProvider(refdDebugFile.file.getFSRL(), false, monitor);) {
|
||||
Object consumer = new Object();
|
||||
Language lang = program.getLanguage();
|
||||
CompilerSpec compSpec =
|
||||
lang.getCompilerSpecByID(program.getCompilerSpec().getCompilerSpecID());
|
||||
LoadSpec origLoadSpec = ImporterUtilities.getLoadSpec(program);
|
||||
if (origLoadSpec == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CompilerSpec compSpec = origLoadSpec.getLanguageCompilerSpec().getCompilerSpec();
|
||||
|
||||
Program debugProgram =
|
||||
new ProgramDB("temp external debug info for " + program.getName(), lang,
|
||||
compSpec, consumer);
|
||||
|
||||
Loader origLoader = origLoadSpec.getLoader();
|
||||
List<Option> defaultOptions = origLoader.getDefaultOptions(debugFileByteProvider,
|
||||
origLoadSpec, debugProgram, false);
|
||||
|
||||
ElfLoader elfLoader = new ElfLoader();
|
||||
elfLoader.load(debugFileByteProvider, null, List.of(), debugProgram, monitor,
|
||||
elfLoader.load(debugFileByteProvider, null, defaultOptions, debugProgram, monitor,
|
||||
new MessageLog());
|
||||
ExternalDebugFileSectionProvider result =
|
||||
new ExternalDebugFileSectionProvider(debugProgram,
|
||||
debugFileByteProvider.getFSRL());
|
||||
|
||||
ExternalDebugFileSectionProvider result = new ExternalDebugFileSectionProvider(
|
||||
debugProgram, debugFileByteProvider.getFSRL());
|
||||
debugProgram.release(consumer);
|
||||
return result;
|
||||
}
|
||||
@@ -106,6 +118,8 @@ public class ExternalDebugFileSectionProvider extends BaseSectionProvider {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// we close the parent class'es program instance here because we repurposed it from its
|
||||
// normal use-case of referring to the main program
|
||||
if (program != null) {
|
||||
program.release(this);
|
||||
}
|
||||
@@ -114,6 +128,10 @@ public class ExternalDebugFileSectionProvider extends BaseSectionProvider {
|
||||
program = null;
|
||||
}
|
||||
|
||||
public Program getExternalProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgramInfo(Program program) {
|
||||
Options options = program.getOptions(Program.PROGRAM_INFO);
|
||||
|
||||
@@ -565,4 +565,20 @@ public class ImporterUtilities {
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the {@link LoadSpec} that was used to import the given {@link Program}
|
||||
*
|
||||
* @param program The {@link Program}
|
||||
* @return The {@link LoadSpec} that was used to import the given {@link Program}, or null if
|
||||
* it could not be determined
|
||||
*/
|
||||
public static LoadSpec getLoadSpec(Program program) {
|
||||
ByteProvider provider;
|
||||
if (program == null || (provider = getProvider(program)) == null) {
|
||||
return null;
|
||||
}
|
||||
return getLoadSpec(provider, program);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user