mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
GP-3883 added source file manager
This commit is contained in:
93
Ghidra/Features/Base/ghidra_scripts/AddSourceFileScript.java
Normal file
93
Ghidra/Features/Base/ghidra_scripts/AddSourceFileScript.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/* ###
|
||||
* 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 a SourceFile with a user-defined path and name to the program.
|
||||
//@category SourceMapping
|
||||
import java.util.HexFormat;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.features.base.values.GhidraValuesMap;
|
||||
import ghidra.program.database.sourcemap.SourceFile;
|
||||
import ghidra.program.database.sourcemap.SourceFileIdType;
|
||||
import ghidra.util.MessageType;
|
||||
|
||||
public class AddSourceFileScript extends GhidraScript {
|
||||
|
||||
private static final String PATH = "Source File Path";
|
||||
private static final String ID_TYPE = "Id Type";
|
||||
private static final String IDENTIFIER = "Identifier";
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
if (isRunningHeadless()) {
|
||||
println("This script must be run through the Ghidra GUI");
|
||||
return;
|
||||
}
|
||||
if (currentProgram == null) {
|
||||
popup("This script requires an open program");
|
||||
return;
|
||||
}
|
||||
if (!currentProgram.hasExclusiveAccess()) {
|
||||
popup("This script requires exclusive access to the program");
|
||||
return;
|
||||
}
|
||||
|
||||
GhidraValuesMap values = new GhidraValuesMap();
|
||||
values.defineString(PATH, "/");
|
||||
SourceFileIdType[] idTypes = SourceFileIdType.values();
|
||||
String[] enumNames = new String[idTypes.length];
|
||||
for (int i = 0; i < enumNames.length; ++i) {
|
||||
enumNames[i] = idTypes[i].name();
|
||||
}
|
||||
values.defineChoice(ID_TYPE, SourceFileIdType.NONE.name(), enumNames);
|
||||
values.defineString(IDENTIFIER, StringUtils.EMPTY);
|
||||
|
||||
values.setValidator((valueMap, status) -> {
|
||||
String path = valueMap.getString(PATH);
|
||||
SourceFileIdType idType = SourceFileIdType.valueOf(values.getChoice(ID_TYPE));
|
||||
byte[] identifier = null;
|
||||
if (idType != SourceFileIdType.NONE) {
|
||||
identifier = HexFormat.of().parseHex(values.getString(IDENTIFIER));
|
||||
}
|
||||
try {
|
||||
SourceFile srcFile = new SourceFile(path, idType, identifier);
|
||||
if (currentProgram.getSourceFileManager().containsSourceFile(srcFile)) {
|
||||
status.setStatusText("SourceFile " + srcFile + " already exists",
|
||||
MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
status.setStatusText(e.getMessage(), MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
askValues("Enter (Absolute) Source File URI Path",
|
||||
"e.g.: /usr/bin/echo, /C:/Programs/file.exe", values);
|
||||
String absolutePath = values.getString(PATH);
|
||||
SourceFileIdType idType = SourceFileIdType.valueOf(values.getChoice(ID_TYPE));
|
||||
byte[] identifier = null;
|
||||
if (idType != SourceFileIdType.NONE) {
|
||||
identifier = HexFormat.of().parseHex(values.getString(IDENTIFIER));
|
||||
}
|
||||
SourceFile srcFile = new SourceFile(absolutePath, idType, identifier);
|
||||
currentProgram.getSourceFileManager().addSourceFile(srcFile);
|
||||
printf("Successfully added source file %s%n", srcFile.toString());
|
||||
}
|
||||
|
||||
}
|
||||
146
Ghidra/Features/Base/ghidra_scripts/AddSourceMapEntryScript.java
Normal file
146
Ghidra/Features/Base/ghidra_scripts/AddSourceMapEntryScript.java
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// Add a source map entry for the current selection.
|
||||
// The current selection must consist of a single address range.
|
||||
// If there is no selection, a length 0 entry will be added at the current address.
|
||||
//@category SourceMapping
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.features.base.values.GhidraValuesMap;
|
||||
import ghidra.program.database.sourcemap.SourceFile;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressRange;
|
||||
import ghidra.program.model.sourcemap.*;
|
||||
import ghidra.util.MessageType;
|
||||
|
||||
public class AddSourceMapEntryScript extends GhidraScript {
|
||||
|
||||
private static final String LINE_NUM = "Line Number";
|
||||
private static final String SOURCE_FILE = "Source File";
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
if (isRunningHeadless()) {
|
||||
println("This script must be run through the Ghidra GUI");
|
||||
return;
|
||||
}
|
||||
if (currentProgram == null) {
|
||||
popup("This script requires an open program");
|
||||
return;
|
||||
}
|
||||
if (!currentProgram.hasExclusiveAccess()) {
|
||||
popup("This script requires exclusive access to the program");
|
||||
return;
|
||||
}
|
||||
|
||||
SourceFileManager sourceManager = currentProgram.getSourceFileManager();
|
||||
List<SourceFile> sourceFiles = sourceManager.getAllSourceFiles();
|
||||
if (sourceFiles.isEmpty()) {
|
||||
popup("You must first add at least one source file to the program");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean valid = isCurrentSelectionValid();
|
||||
if (!valid) {
|
||||
return; // checkCurrentSelection will tell the user what the problem is
|
||||
}
|
||||
|
||||
Address baseAddr =
|
||||
currentSelection == null ? currentAddress : currentSelection.getMinAddress();
|
||||
long length = currentSelection == null ? 0 : currentSelection.getNumAddresses();
|
||||
|
||||
AddressRange currentRange =
|
||||
currentSelection == null ? null : currentSelection.getAddressRanges().next();
|
||||
|
||||
Map<String, SourceFile> stringsToSourceFiles = new HashMap<>();
|
||||
sourceFiles.forEach(sf -> stringsToSourceFiles.put(sf.toString(), sf));
|
||||
|
||||
GhidraValuesMap values = new GhidraValuesMap();
|
||||
values.defineInt(LINE_NUM, 1);
|
||||
String[] sourceFileArray = stringsToSourceFiles.keySet().toArray(new String[0]);
|
||||
Arrays.sort(sourceFileArray);
|
||||
values.defineChoice(SOURCE_FILE, sourceFileArray[0], sourceFileArray);
|
||||
|
||||
values.setValidator((valueMap, status) -> {
|
||||
int lineNum = values.getInt(LINE_NUM);
|
||||
if (lineNum < 0) {
|
||||
status.setStatusText("Line number cannot be negative", MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
if (currentRange == null) {
|
||||
return true; // length 0 entry, nothing else to check
|
||||
}
|
||||
SourceFile source = new SourceFile(values.getChoice(SOURCE_FILE));
|
||||
|
||||
for (SourceMapEntry entry : sourceManager.getSourceMapEntries(source, lineNum)) {
|
||||
// because checkCurrentSelection returned true, if the entries intersect
|
||||
// they must be equal
|
||||
if (entry.getLength() != 0 && entry.getRange().intersects(currentRange)) {
|
||||
status.setStatusText(currentSelection + " is already mapped to " +
|
||||
source.getPath() + ":" + lineNum);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
askValues("Source Map Info", "Enter Source Map Info (length = " + length + ")", values);
|
||||
int lineNum = values.getInt(LINE_NUM);
|
||||
String fileString = values.getChoice(SOURCE_FILE);
|
||||
SourceFile source = stringsToSourceFiles.get(fileString);
|
||||
sourceManager.addSourceMapEntry(source, lineNum, baseAddr, length);
|
||||
}
|
||||
|
||||
// check that the selected range doesn't already intersect a SourceMapEntry with a
|
||||
// conflicting range.
|
||||
private boolean isCurrentSelectionValid() {
|
||||
if (currentSelection == null) {
|
||||
return true;
|
||||
}
|
||||
if (currentSelection.getNumAddressRanges() != 1) {
|
||||
popup("This script requires the current selection to be a single address range");
|
||||
return false;
|
||||
}
|
||||
AddressRange range = currentSelection.getFirstRange();
|
||||
Address end = range.getMaxAddress();
|
||||
SourceMapEntryIterator iter =
|
||||
currentProgram.getSourceFileManager().getSourceMapEntryIterator(end, false);
|
||||
while (iter.hasNext()) {
|
||||
SourceMapEntry entry = iter.next();
|
||||
if (!entry.getBaseAddress().getAddressSpace().equals(range.getAddressSpace())) {
|
||||
return true; // iterator has exhausted entries in the address space, no conflicts
|
||||
// are possible
|
||||
}
|
||||
if (entry.getLength() == 0) {
|
||||
continue; // length 0 entries can't conflict
|
||||
}
|
||||
AddressRange entryRange = entry.getRange();
|
||||
if (entryRange.equals(range)) {
|
||||
return true; // range is the same as a range already in the db, so no problems
|
||||
}
|
||||
if (entryRange.intersects(range)) {
|
||||
popup("Selection conflicts with existing entry " + entry.toString());
|
||||
return false;
|
||||
}
|
||||
if (entryRange.getMaxAddress().compareTo(range.getMinAddress()) < 0) {
|
||||
return true; // no conflicting entries exists
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,16 +4,18 @@
|
||||
* 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 line number info to the current binary
|
||||
// Adds DWARF source file line number info to the current binary as EOL comments.
|
||||
// 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.List;
|
||||
@@ -29,7 +31,7 @@ import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class DWARFLineInfoScript extends GhidraScript {
|
||||
public class DWARFLineInfoCommentScript extends GhidraScript {
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
DWARFSectionProvider dsp =
|
||||
@@ -0,0 +1,154 @@
|
||||
/* ###
|
||||
* 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 line number info to the current program as source map entries.
|
||||
// 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.SourceFileAddr;
|
||||
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.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.sourcemap.SourceFileManager;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class DWARFLineInfoSourceMapScript extends GhidraScript {
|
||||
public static final int ENTRY_MAX_LENGTH = 1000;
|
||||
|
||||
@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;
|
||||
monitor.initialize(reader.length(), "DWARF Source Map Info");
|
||||
List<DWARFCompilationUnit> compUnits = dprog.getCompilationUnits();
|
||||
SourceFileManager sourceManager = currentProgram.getSourceFileManager();
|
||||
List<SourceFileAddr> sourceInfo = new ArrayList<>();
|
||||
for (DWARFCompilationUnit cu : compUnits) {
|
||||
sourceInfo.addAll(cu.getLine().getAllSourceFileAddrInfo(cu, reader));
|
||||
}
|
||||
sourceInfo.sort((i, j) -> Long.compareUnsigned(i.address(), j.address()));
|
||||
monitor.initialize(sourceInfo.size());
|
||||
for (int i = 0; i < sourceInfo.size(); i++) {
|
||||
monitor.checkCancelled();
|
||||
monitor.increment(1);
|
||||
SourceFileAddr sourceFileAddr = sourceInfo.get(i);
|
||||
if (sourceFileAddr.isEndSequence()) {
|
||||
continue;
|
||||
}
|
||||
Address addr = dprog.getCodeAddress(sourceFileAddr.address());
|
||||
if (!currentProgram.getMemory().getExecuteSet().contains(addr)) {
|
||||
printerr(
|
||||
"entry for non-executable address; skipping: file %s line %d address: %s %x"
|
||||
.formatted(sourceFileAddr.fileName(), sourceFileAddr.lineNum(),
|
||||
addr.toString(), sourceFileAddr.address()));
|
||||
continue;
|
||||
}
|
||||
|
||||
long length = getLength(i, sourceInfo);
|
||||
if (length < 0) {
|
||||
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()));
|
||||
length = 0;
|
||||
}
|
||||
if (length > ENTRY_MAX_LENGTH) {
|
||||
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));
|
||||
length = 0;
|
||||
}
|
||||
if (sourceFileAddr.fileName() == null) {
|
||||
continue;
|
||||
}
|
||||
SourceFile source = null;
|
||||
try {
|
||||
SourceFileIdType type =
|
||||
sourceFileAddr.md5() == null ? SourceFileIdType.NONE : SourceFileIdType.MD5;
|
||||
source = new SourceFile(sourceFileAddr.fileName(), type, sourceFileAddr.md5());
|
||||
sourceManager.addSourceFile(source);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
printerr("Exception creating source file %s".formatted(e.getMessage()));
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
sourceManager.addSourceMapEntry(source, sourceFileAddr.lineNum(), addr, length);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
printerr(e.getMessage());
|
||||
continue;
|
||||
}
|
||||
entryCount++;
|
||||
}
|
||||
println("Added " + entryCount + " source map entries");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// Select and remove a source map entry at the current address.
|
||||
//@category SourceMapping
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.features.base.values.GhidraValuesMap;
|
||||
import ghidra.program.model.sourcemap.SourceMapEntry;
|
||||
|
||||
public class RemoveSourceMapEntryScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
if (isRunningHeadless()) {
|
||||
println("This script must be run through the Ghidra GUI");
|
||||
return;
|
||||
}
|
||||
if (currentProgram == null) {
|
||||
popup("This script requires an open program");
|
||||
return;
|
||||
}
|
||||
if (!currentProgram.hasExclusiveAccess()) {
|
||||
popup("Modifying the source map requires exclusive access to the program");
|
||||
return;
|
||||
}
|
||||
List<SourceMapEntry> entries =
|
||||
currentProgram.getSourceFileManager().getSourceMapEntries(currentAddress);
|
||||
if (entries.isEmpty()) {
|
||||
popup("No source map entries at " + currentAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, SourceMapEntry> stringToEntry = new HashMap<>();
|
||||
List<String> choices = new ArrayList<>();
|
||||
for (SourceMapEntry entry : entries) {
|
||||
String entryString = entry.toString();
|
||||
stringToEntry.put(entryString, entry);
|
||||
choices.add(entryString);
|
||||
}
|
||||
GhidraValuesMap values = new GhidraValuesMap();
|
||||
String[] choiceArray = choices.toArray(new String[] {});
|
||||
values.defineChoice("Entry", choiceArray[0], choiceArray);
|
||||
askValues("Select SourceMapEntry to remove", null, values);
|
||||
String selectedString = values.getChoice("Entry");
|
||||
SourceMapEntry toDelete = stringToEntry.get(selectedString);
|
||||
currentProgram.getSourceFileManager().removeSourceMapEntry(toDelete);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// Sets the current selection based on source file and line number parameters
|
||||
//@category SourceMapping
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.features.base.values.GhidraValuesMap;
|
||||
import ghidra.program.database.sourcemap.SourceFile;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.sourcemap.SourceFileManager;
|
||||
import ghidra.program.model.sourcemap.SourceMapEntry;
|
||||
import ghidra.util.MessageType;
|
||||
import ghidra.util.SourceFileUtils;
|
||||
import ghidra.util.SourceFileUtils.SourceLineBounds;
|
||||
|
||||
public class SelectAddressesMappedToSourceFileScript extends GhidraScript {
|
||||
|
||||
private static final String SOURCE_FILE = "Source File";
|
||||
private static final String MIN_LINE = "Minimum Source Line";
|
||||
private static final String MAX_LINE = " Maximum Source Line";
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
if (isRunningHeadless()) {
|
||||
println("This script must be run through the Ghidra GUI");
|
||||
return;
|
||||
}
|
||||
if (currentProgram == null) {
|
||||
popup("This script requires an open program");
|
||||
return;
|
||||
}
|
||||
|
||||
SourceFileManager sourceManager = currentProgram.getSourceFileManager();
|
||||
|
||||
List<SourceFile> sourceFiles = sourceManager.getMappedSourceFiles();
|
||||
if (sourceFiles.isEmpty()) {
|
||||
popup(currentProgram.getName() + " contains no mapped source files");
|
||||
return;
|
||||
}
|
||||
|
||||
GhidraValuesMap sourceFileValue = new GhidraValuesMap();
|
||||
|
||||
Map<String, SourceFile> stringsToSourceFiles = new HashMap<>();
|
||||
sourceFiles.forEach(sf -> stringsToSourceFiles.put(sf.toString(), sf));
|
||||
String[] sourceFileArray = stringsToSourceFiles.keySet().toArray(new String[0]);
|
||||
Arrays.sort(sourceFileArray);
|
||||
|
||||
sourceFileValue.defineChoice(SOURCE_FILE, sourceFileArray[0], sourceFileArray);
|
||||
askValues("Select Source File", "Source File", sourceFileValue);
|
||||
String sfString = sourceFileValue.getChoice(SOURCE_FILE);
|
||||
|
||||
SourceFile mappedSourceFile = stringsToSourceFiles.get(sfString);
|
||||
SourceLineBounds bounds =
|
||||
SourceFileUtils.getSourceLineBounds(currentProgram, mappedSourceFile);
|
||||
GhidraValuesMap boundValues = new GhidraValuesMap();
|
||||
boundValues.defineInt(MIN_LINE, bounds.min());
|
||||
boundValues.setInt(MIN_LINE, bounds.min());
|
||||
boundValues.defineInt(MAX_LINE, bounds.max());
|
||||
boundValues.setInt(MAX_LINE, bounds.max());
|
||||
|
||||
boundValues.setValidator((valueMap, status) -> {
|
||||
int minLine = boundValues.getInt(MIN_LINE);
|
||||
if (minLine < 0) {
|
||||
status.setStatusText("Line number cannot be negative", MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
int maxLine = boundValues.getInt(MAX_LINE);
|
||||
if (maxLine < minLine) {
|
||||
status.setStatusText("Max line cannot be less than min line", MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
setReusePreviousChoices(false);
|
||||
askValues("Select Line Bounds", "Bounds for " + mappedSourceFile.getFilename(),
|
||||
boundValues);
|
||||
setReusePreviousChoices(true);
|
||||
|
||||
int minLine = boundValues.getInt(MIN_LINE);
|
||||
int maxLine = boundValues.getInt(MAX_LINE);
|
||||
AddressSet selection = new AddressSet();
|
||||
List<SourceMapEntry> entries =
|
||||
sourceManager.getSourceMapEntries(mappedSourceFile, minLine, maxLine);
|
||||
for (SourceMapEntry entry : entries) {
|
||||
if (entry.getLength() == 0) {
|
||||
selection.add(entry.getBaseAddress());
|
||||
continue;
|
||||
}
|
||||
selection.add(entry.getRange());
|
||||
}
|
||||
if (selection.isEmpty()) {
|
||||
popup("No addresses for the selected file and range");
|
||||
return;
|
||||
}
|
||||
setCurrentSelection(selection);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user