Skip to content

Commit a414919

Browse files
committed
Merge remote-tracking branch 'origin/GP-3883_James_add_source_map_manager_v2'
2 parents 0d490ef + 9aeeaa4 commit a414919

File tree

52 files changed

+8430
-304
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+8430
-304
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/* ###
2+
* IP: GHIDRA
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
// Adds a SourceFile with a user-defined path and name to the program.
17+
//@category SourceMapping
18+
import java.util.HexFormat;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
22+
import ghidra.app.script.GhidraScript;
23+
import ghidra.features.base.values.GhidraValuesMap;
24+
import ghidra.program.database.sourcemap.SourceFile;
25+
import ghidra.program.database.sourcemap.SourceFileIdType;
26+
import ghidra.util.MessageType;
27+
28+
public class AddSourceFileScript extends GhidraScript {
29+
30+
private static final String PATH = "Source File Path";
31+
private static final String ID_TYPE = "Id Type";
32+
private static final String IDENTIFIER = "Identifier";
33+
34+
@Override
35+
protected void run() throws Exception {
36+
if (isRunningHeadless()) {
37+
println("This script must be run through the Ghidra GUI");
38+
return;
39+
}
40+
if (currentProgram == null) {
41+
popup("This script requires an open program");
42+
return;
43+
}
44+
if (!currentProgram.hasExclusiveAccess()) {
45+
popup("This script requires exclusive access to the program");
46+
return;
47+
}
48+
49+
GhidraValuesMap values = new GhidraValuesMap();
50+
values.defineString(PATH, "/");
51+
SourceFileIdType[] idTypes = SourceFileIdType.values();
52+
String[] enumNames = new String[idTypes.length];
53+
for (int i = 0; i < enumNames.length; ++i) {
54+
enumNames[i] = idTypes[i].name();
55+
}
56+
values.defineChoice(ID_TYPE, SourceFileIdType.NONE.name(), enumNames);
57+
values.defineString(IDENTIFIER, StringUtils.EMPTY);
58+
59+
values.setValidator((valueMap, status) -> {
60+
String path = valueMap.getString(PATH);
61+
SourceFileIdType idType = SourceFileIdType.valueOf(values.getChoice(ID_TYPE));
62+
byte[] identifier = null;
63+
if (idType != SourceFileIdType.NONE) {
64+
identifier = HexFormat.of().parseHex(values.getString(IDENTIFIER));
65+
}
66+
try {
67+
SourceFile srcFile = new SourceFile(path, idType, identifier);
68+
if (currentProgram.getSourceFileManager().containsSourceFile(srcFile)) {
69+
status.setStatusText("SourceFile " + srcFile + " already exists",
70+
MessageType.ERROR);
71+
return false;
72+
}
73+
}
74+
catch (IllegalArgumentException e) {
75+
status.setStatusText(e.getMessage(), MessageType.ERROR);
76+
return false;
77+
}
78+
return true;
79+
});
80+
askValues("Enter (Absolute) Source File URI Path",
81+
"e.g.: /usr/bin/echo, /C:/Programs/file.exe", values);
82+
String absolutePath = values.getString(PATH);
83+
SourceFileIdType idType = SourceFileIdType.valueOf(values.getChoice(ID_TYPE));
84+
byte[] identifier = null;
85+
if (idType != SourceFileIdType.NONE) {
86+
identifier = HexFormat.of().parseHex(values.getString(IDENTIFIER));
87+
}
88+
SourceFile srcFile = new SourceFile(absolutePath, idType, identifier);
89+
currentProgram.getSourceFileManager().addSourceFile(srcFile);
90+
printf("Successfully added source file %s%n", srcFile.toString());
91+
}
92+
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/* ###
2+
* IP: GHIDRA
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
// Add a source map entry for the current selection.
17+
// The current selection must consist of a single address range.
18+
// If there is no selection, a length 0 entry will be added at the current address.
19+
//@category SourceMapping
20+
import java.util.*;
21+
22+
import ghidra.app.script.GhidraScript;
23+
import ghidra.features.base.values.GhidraValuesMap;
24+
import ghidra.program.database.sourcemap.SourceFile;
25+
import ghidra.program.model.address.Address;
26+
import ghidra.program.model.address.AddressRange;
27+
import ghidra.program.model.sourcemap.*;
28+
import ghidra.util.MessageType;
29+
30+
public class AddSourceMapEntryScript extends GhidraScript {
31+
32+
private static final String LINE_NUM = "Line Number";
33+
private static final String SOURCE_FILE = "Source File";
34+
35+
@Override
36+
protected void run() throws Exception {
37+
if (isRunningHeadless()) {
38+
println("This script must be run through the Ghidra GUI");
39+
return;
40+
}
41+
if (currentProgram == null) {
42+
popup("This script requires an open program");
43+
return;
44+
}
45+
if (!currentProgram.hasExclusiveAccess()) {
46+
popup("This script requires exclusive access to the program");
47+
return;
48+
}
49+
50+
SourceFileManager sourceManager = currentProgram.getSourceFileManager();
51+
List<SourceFile> sourceFiles = sourceManager.getAllSourceFiles();
52+
if (sourceFiles.isEmpty()) {
53+
popup("You must first add at least one source file to the program");
54+
return;
55+
}
56+
57+
boolean valid = isCurrentSelectionValid();
58+
if (!valid) {
59+
return; // checkCurrentSelection will tell the user what the problem is
60+
}
61+
62+
Address baseAddr =
63+
currentSelection == null ? currentAddress : currentSelection.getMinAddress();
64+
long length = currentSelection == null ? 0 : currentSelection.getNumAddresses();
65+
66+
AddressRange currentRange =
67+
currentSelection == null ? null : currentSelection.getAddressRanges().next();
68+
69+
Map<String, SourceFile> stringsToSourceFiles = new HashMap<>();
70+
sourceFiles.forEach(sf -> stringsToSourceFiles.put(sf.toString(), sf));
71+
72+
GhidraValuesMap values = new GhidraValuesMap();
73+
values.defineInt(LINE_NUM, 1);
74+
String[] sourceFileArray = stringsToSourceFiles.keySet().toArray(new String[0]);
75+
Arrays.sort(sourceFileArray);
76+
values.defineChoice(SOURCE_FILE, sourceFileArray[0], sourceFileArray);
77+
78+
values.setValidator((valueMap, status) -> {
79+
int lineNum = values.getInt(LINE_NUM);
80+
if (lineNum < 0) {
81+
status.setStatusText("Line number cannot be negative", MessageType.ERROR);
82+
return false;
83+
}
84+
if (currentRange == null) {
85+
return true; // length 0 entry, nothing else to check
86+
}
87+
SourceFile source = new SourceFile(values.getChoice(SOURCE_FILE));
88+
89+
for (SourceMapEntry entry : sourceManager.getSourceMapEntries(source, lineNum)) {
90+
// because checkCurrentSelection returned true, if the entries intersect
91+
// they must be equal
92+
if (entry.getLength() != 0 && entry.getRange().intersects(currentRange)) {
93+
status.setStatusText(currentSelection + " is already mapped to " +
94+
source.getPath() + ":" + lineNum);
95+
return false;
96+
}
97+
}
98+
return true;
99+
});
100+
101+
askValues("Source Map Info", "Enter Source Map Info (length = " + length + ")", values);
102+
int lineNum = values.getInt(LINE_NUM);
103+
String fileString = values.getChoice(SOURCE_FILE);
104+
SourceFile source = stringsToSourceFiles.get(fileString);
105+
sourceManager.addSourceMapEntry(source, lineNum, baseAddr, length);
106+
}
107+
108+
// check that the selected range doesn't already intersect a SourceMapEntry with a
109+
// conflicting range.
110+
private boolean isCurrentSelectionValid() {
111+
if (currentSelection == null) {
112+
return true;
113+
}
114+
if (currentSelection.getNumAddressRanges() != 1) {
115+
popup("This script requires the current selection to be a single address range");
116+
return false;
117+
}
118+
AddressRange range = currentSelection.getFirstRange();
119+
Address end = range.getMaxAddress();
120+
SourceMapEntryIterator iter =
121+
currentProgram.getSourceFileManager().getSourceMapEntryIterator(end, false);
122+
while (iter.hasNext()) {
123+
SourceMapEntry entry = iter.next();
124+
if (!entry.getBaseAddress().getAddressSpace().equals(range.getAddressSpace())) {
125+
return true; // iterator has exhausted entries in the address space, no conflicts
126+
// are possible
127+
}
128+
if (entry.getLength() == 0) {
129+
continue; // length 0 entries can't conflict
130+
}
131+
AddressRange entryRange = entry.getRange();
132+
if (entryRange.equals(range)) {
133+
return true; // range is the same as a range already in the db, so no problems
134+
}
135+
if (entryRange.intersects(range)) {
136+
popup("Selection conflicts with existing entry " + entry.toString());
137+
return false;
138+
}
139+
if (entryRange.getMaxAddress().compareTo(range.getMinAddress()) < 0) {
140+
return true; // no conflicting entries exists
141+
}
142+
}
143+
return true;
144+
}
145+
146+
}

Ghidra/Features/Base/ghidra_scripts/DWARFLineInfoScript.java renamed to Ghidra/Features/Base/ghidra_scripts/DWARFLineInfoCommentScript.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
*
88
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
9+
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
// Adds DWARF source file line number info to the current binary
16+
// Adds DWARF source file line number info to the current binary as EOL comments.
17+
// Note that you can run this script on a program that has already been analyzed by the
18+
// DWARF analyzer.
1719
//@category DWARF
1820
import java.io.IOException;
1921
import java.util.List;
@@ -29,7 +31,7 @@
2931
import ghidra.util.Msg;
3032
import ghidra.util.exception.CancelledException;
3133

32-
public class DWARFLineInfoScript extends GhidraScript {
34+
public class DWARFLineInfoCommentScript extends GhidraScript {
3335
@Override
3436
protected void run() throws Exception {
3537
DWARFSectionProvider dsp =

0 commit comments

Comments
 (0)