mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00

remove access to dwarfline file count because it encourages callers to try to iterate via index, which is not something they should do.
214 lines
7.6 KiB
Java
214 lines
7.6 KiB
Java
/* ###
|
|
* 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.
|
|
*/
|
|
// Adds DWARF source file info to the current program.
|
|
// A source file that is relative after path normalization will have all leading "."
|
|
// and "/../" entries stripped and then be placed under an artificial directory.
|
|
// Note that you can run this script on a program that has already been analyzed by the
|
|
// DWARF analyzer.
|
|
//@category DWARF
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.app.util.bin.BinaryReader;
|
|
import ghidra.app.util.bin.format.dwarf.*;
|
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine;
|
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine.SourceFileAddr;
|
|
import ghidra.app.util.bin.format.dwarf.line.DWARFLine.SourceFileInfo;
|
|
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProvider;
|
|
import ghidra.app.util.bin.format.dwarf.sectionprovider.DWARFSectionProviderFactory;
|
|
import ghidra.framework.store.LockException;
|
|
import ghidra.program.database.sourcemap.SourceFile;
|
|
import ghidra.program.database.sourcemap.SourceFileIdType;
|
|
import ghidra.program.model.address.*;
|
|
import ghidra.program.model.sourcemap.SourceFileManager;
|
|
import ghidra.util.Msg;
|
|
import ghidra.util.SourceFileUtils;
|
|
import ghidra.util.exception.CancelledException;
|
|
|
|
public class DWARFLineInfoSourceMapScript extends GhidraScript {
|
|
public static final int ENTRY_MAX_LENGTH = 1000;
|
|
private static final int MAX_ERROR_MSGS_TO_DISPLAY = 25;
|
|
private static final int MAX_WARNING_MSGS_TO_DISPLAY = 25;
|
|
private static final String COMPILATION_ROOT_DIRECTORY = DWARFImporter.DEFAULT_COMPILATION_DIR;
|
|
private int numErrors;
|
|
private int numWarnings;
|
|
|
|
@Override
|
|
protected void run() throws Exception {
|
|
|
|
if (!currentProgram.hasExclusiveAccess()) {
|
|
Msg.showError(this, null, "Exclusive Access Required",
|
|
"Must have exclusive access to a program to add source map info");
|
|
return;
|
|
}
|
|
DWARFSectionProvider dsp =
|
|
DWARFSectionProviderFactory.createSectionProviderFor(currentProgram, monitor);
|
|
if (dsp == null) {
|
|
printerr("Unable to find DWARF information");
|
|
return;
|
|
}
|
|
|
|
DWARFImportOptions importOptions = new DWARFImportOptions();
|
|
try (DWARFProgram dprog = new DWARFProgram(currentProgram, importOptions, monitor, dsp)) {
|
|
dprog.init(monitor);
|
|
addSourceLineInfo(dprog);
|
|
}
|
|
}
|
|
|
|
private void addSourceLineInfo(DWARFProgram dprog)
|
|
throws CancelledException, IOException, LockException, AddressOverflowException {
|
|
BinaryReader reader = dprog.getDebugLineBR();
|
|
if (reader == null) {
|
|
popup("Unable to get reader for debug line info");
|
|
return;
|
|
}
|
|
int entryCount = 0;
|
|
List<DWARFCompilationUnit> compUnits = dprog.getCompilationUnits();
|
|
SourceFileManager sourceManager = currentProgram.getSourceFileManager();
|
|
|
|
monitor.initialize(compUnits.size(), "DWARF: Reading Source Information");
|
|
// add all the source files and read the source map entries
|
|
Map<SourceFileInfo, SourceFile> sourceFileInfoToSourceFile = new HashMap<>();
|
|
Set<SourceFileInfo> badSourceFileInfo = new HashSet<>();
|
|
List<SourceFileAddr> sourceInfo = new ArrayList<>();
|
|
for (DWARFCompilationUnit cu : compUnits) {
|
|
DWARFLine dLine = cu.getLine();
|
|
monitor.increment();
|
|
for (SourceFileInfo sfi : dLine.getAllSourceFileInfos()) {
|
|
if (sourceFileInfoToSourceFile.containsKey(sfi)) {
|
|
continue;
|
|
}
|
|
if (badSourceFileInfo.contains(sfi)) {
|
|
continue;
|
|
}
|
|
try {
|
|
String path = SourceFileUtils.normalizeDwarfPath(sfi.filePath(),
|
|
COMPILATION_ROOT_DIRECTORY);
|
|
SourceFileIdType type =
|
|
sfi.md5() == null ? SourceFileIdType.NONE : SourceFileIdType.MD5;
|
|
SourceFile sFile = new SourceFile(path, type, sfi.md5());
|
|
sourceManager.addSourceFile(sFile);
|
|
sourceFileInfoToSourceFile.put(sfi, sFile);
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
if (numErrors++ < MAX_ERROR_MSGS_TO_DISPLAY) {
|
|
printerr("Exception creating source file %s".formatted(e.getMessage()));
|
|
}
|
|
badSourceFileInfo.add(sfi);
|
|
continue;
|
|
}
|
|
}
|
|
sourceInfo.addAll(cu.getLine().getAllSourceFileAddrInfo(cu, reader));
|
|
}
|
|
|
|
monitor.setIndeterminate(true);
|
|
monitor.setMessage("Sorting " + sourceInfo.size() + " entries");
|
|
sourceInfo.sort((i, j) -> Long.compareUnsigned(i.address(), j.address()));
|
|
monitor.setIndeterminate(false);
|
|
monitor.initialize(sourceInfo.size(), "DWARF: Applying Source Map Info");
|
|
|
|
AddressSet warnedAddresses = new AddressSet();
|
|
for (int i = 0; i < sourceInfo.size(); i++) {
|
|
monitor.increment(1);
|
|
SourceFileAddr sourceFileAddr = sourceInfo.get(i);
|
|
if (sourceFileAddr.isEndSequence()) {
|
|
continue;
|
|
}
|
|
if (sourceFileAddr.fileName() == null) {
|
|
continue;
|
|
}
|
|
SourceFileInfo sfi =
|
|
new SourceFileInfo(sourceFileAddr.fileName(), sourceFileAddr.md5());
|
|
if (badSourceFileInfo.contains(sfi)) {
|
|
continue;
|
|
}
|
|
|
|
Address addr = dprog.getCodeAddress(sourceFileAddr.address());
|
|
if (warnedAddresses.contains(addr)) {
|
|
continue; // only warn once per address
|
|
}
|
|
|
|
if (!currentProgram.getMemory().getExecuteSet().contains(addr)) {
|
|
if (numWarnings++ < MAX_WARNING_MSGS_TO_DISPLAY) {
|
|
printerr(
|
|
"entry for non-executable address; skipping: file %s line %d address: %s %x"
|
|
.formatted(sourceFileAddr.fileName(), sourceFileAddr.lineNum(),
|
|
addr.toString(), sourceFileAddr.address()));
|
|
}
|
|
warnedAddresses.add(addr);
|
|
continue;
|
|
}
|
|
|
|
long length = getLength(i, sourceInfo);
|
|
if (length < 0) {
|
|
if (numWarnings++ < MAX_WARNING_MSGS_TO_DISPLAY) {
|
|
println(
|
|
"Error computing entry length for file %s line %d address %s %x; replacing" +
|
|
" with length 0 entry".formatted(sourceFileAddr.fileName(),
|
|
sourceFileAddr.lineNum(), addr.toString(),
|
|
sourceFileAddr.address()));
|
|
}
|
|
}
|
|
if (length > ENTRY_MAX_LENGTH) {
|
|
if (numWarnings++ < MAX_WARNING_MSGS_TO_DISPLAY) {
|
|
println(
|
|
("entry for file %s line %d address: %s %x length %d too large, replacing " +
|
|
"with length 0 entry").formatted(sourceFileAddr.fileName(),
|
|
sourceFileAddr.lineNum(), addr.toString(), sourceFileAddr.address(),
|
|
length));
|
|
}
|
|
}
|
|
|
|
|
|
SourceFile source = sourceFileInfoToSourceFile.get(sfi);
|
|
try {
|
|
sourceManager.addSourceMapEntry(source, sourceFileAddr.lineNum(), addr, length);
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
if (numErrors++ < MAX_ERROR_MSGS_TO_DISPLAY) {
|
|
printerr(e.getMessage());
|
|
}
|
|
continue;
|
|
}
|
|
entryCount++;
|
|
}
|
|
if (numWarnings >= MAX_WARNING_MSGS_TO_DISPLAY) {
|
|
println("Additional warning messages suppressed");
|
|
}
|
|
if (numErrors >= MAX_ERROR_MSGS_TO_DISPLAY) {
|
|
println("Additional error messages suppressed");
|
|
}
|
|
println("Added " + entryCount + " source map entries");
|
|
printf("There were %d errors and %d warnings\n", numErrors,numWarnings);
|
|
}
|
|
|
|
private long getLength(int i, List<SourceFileAddr> allSFA) {
|
|
SourceFileAddr iAddr = allSFA.get(i);
|
|
long iOffset = iAddr.address();
|
|
for (int j = i + 1; j < allSFA.size(); j++) {
|
|
SourceFileAddr current = allSFA.get(j);
|
|
long currentAddr = current.address();
|
|
if (current.isEndSequence()) {
|
|
return currentAddr + 1 - iOffset;
|
|
}
|
|
if (currentAddr != iOffset) {
|
|
return currentAddr - iOffset;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
}
|