mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
commit
cc483f08ab
11 changed files with 502 additions and 133 deletions
|
@ -268,11 +268,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
|
|
||||||
private void processSourceArchiveChanges() throws CancelledException {
|
private void processSourceArchiveChanges() throws CancelledException {
|
||||||
conflictOption = OPTION_MY;
|
conflictOption = OPTION_MY;
|
||||||
for (int i = 0; i < myArchiveChangeList.size(); i++) {
|
for (Long element : myArchiveChangeList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long id = myArchiveChangeList.get(i).longValue();
|
long id = element.longValue();
|
||||||
|
|
||||||
updateSourceArchive(id);
|
updateSourceArchive(id);
|
||||||
}
|
}
|
||||||
|
@ -318,11 +318,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processSourceArchiveAdditions() throws CancelledException {
|
private void processSourceArchiveAdditions() throws CancelledException {
|
||||||
for (int i = 0; i < myArchiveAddedList.size(); i++) {
|
for (Long element : myArchiveAddedList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long id = myArchiveAddedList.get(i).longValue();
|
long id = element.longValue();
|
||||||
|
|
||||||
UniversalID universalID = new UniversalID(id);
|
UniversalID universalID = new UniversalID(id);
|
||||||
SourceArchive mySourceArchive = dtms[MY].getSourceArchive(universalID);
|
SourceArchive mySourceArchive = dtms[MY].getSourceArchive(universalID);
|
||||||
|
@ -381,11 +381,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
|
|
||||||
private void processSourceArchiveConflicts() throws CancelledException {
|
private void processSourceArchiveConflicts() throws CancelledException {
|
||||||
|
|
||||||
for (int i = 0; i < archiveConflictList.size(); i++) {
|
for (Long element : archiveConflictList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long sourceArchiveID = archiveConflictList.get(i).longValue();
|
long sourceArchiveID = element.longValue();
|
||||||
|
|
||||||
++currentConflictIndex;
|
++currentConflictIndex;
|
||||||
handleSourceArchiveConflict(sourceArchiveID, currentConflictIndex);
|
handleSourceArchiveConflict(sourceArchiveID, currentConflictIndex);
|
||||||
|
@ -412,11 +412,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
* @throws CancelledException if task cancelled
|
* @throws CancelledException if task cancelled
|
||||||
*/
|
*/
|
||||||
private void processCategoriesAdded() throws CancelledException {
|
private void processCategoriesAdded() throws CancelledException {
|
||||||
for (int i = 0; i < myCatAddedList.size(); i++) {
|
for (Long element : myCatAddedList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long id = myCatAddedList.get(i).longValue();
|
long id = element.longValue();
|
||||||
Category myCat = dtms[MY].getCategory(id);
|
Category myCat = dtms[MY].getCategory(id);
|
||||||
CategoryPath myPath = myCat.getCategoryPath();
|
CategoryPath myPath = myCat.getCategoryPath();
|
||||||
if (dtms[RESULT].containsCategory(myPath)) {
|
if (dtms[RESULT].containsCategory(myPath)) {
|
||||||
|
@ -432,11 +432,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
*/
|
*/
|
||||||
private void processCategoryConflicts() throws CancelledException {
|
private void processCategoryConflicts() throws CancelledException {
|
||||||
|
|
||||||
for (int i = 0; i < catConflictList.size(); i++) {
|
for (Long element : catConflictList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long id = catConflictList.get(i).longValue();
|
long id = element.longValue();
|
||||||
|
|
||||||
++currentConflictIndex;
|
++currentConflictIndex;
|
||||||
handleCategoryConflict(id, currentConflictIndex);
|
handleCategoryConflict(id, currentConflictIndex);
|
||||||
|
@ -460,11 +460,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processCategoryChanges() throws CancelledException {
|
private void processCategoryChanges() throws CancelledException {
|
||||||
for (int i = 0; i < myCatChangeList.size(); i++) {
|
for (Long element : myCatChangeList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long id = myCatChangeList.get(i).longValue();
|
long id = element.longValue();
|
||||||
|
|
||||||
processCategoryRenamed(id);
|
processCategoryRenamed(id);
|
||||||
processCategoryMoved(id);
|
processCategoryMoved(id);
|
||||||
|
@ -473,10 +473,10 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processCategoriesDeleted() throws CancelledException {
|
private void processCategoriesDeleted() throws CancelledException {
|
||||||
for (int i = 0; i < myCatChangeList.size(); i++) {
|
for (Long element : myCatChangeList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
|
|
||||||
long id = myCatChangeList.get(i).longValue();
|
long id = element.longValue();
|
||||||
processCategoryDeleted(id);
|
processCategoryDeleted(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -499,8 +499,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
if (fixUpList.size() > 0) {
|
if (fixUpList.size() > 0) {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
sb.append("The following data types are unresolved:\n");
|
sb.append("The following data types are unresolved:\n");
|
||||||
for (int i = 0; i < fixUpList.size(); i++) {
|
for (FixUpInfo info : fixUpList) {
|
||||||
FixUpInfo info = fixUpList.get(i);
|
|
||||||
DataTypeManager dtm = info.getDataTypeManager();
|
DataTypeManager dtm = info.getDataTypeManager();
|
||||||
DataType dt = dtm.getDataType(info.id);
|
DataType dt = dtm.getDataType(info.id);
|
||||||
DataType compDt = dtm.getDataType(info.compID);
|
DataType compDt = dtm.getDataType(info.compID);
|
||||||
|
@ -1128,26 +1127,22 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID, Integer.MAX_VALUE,
|
fixUpList.add(new FixUpInfo(sourceDtID, sourceComponentID, Integer.MAX_VALUE,
|
||||||
resolvedDataTypes));
|
resolvedDataTypes));
|
||||||
fixUpIDSet.add(sourceDtID);
|
fixUpIDSet.add(sourceDtID);
|
||||||
|
|
||||||
|
// substitute datatype to preserve component name for subsequent fixup
|
||||||
|
resultCompDt = Undefined1DataType.dataType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (resultCompDt != null) {
|
// Apply resultCompDt as flex array
|
||||||
// There is a matching component data type in the result.
|
try {
|
||||||
try {
|
destStruct.setFlexibleArrayComponent(resultCompDt, flexDtc.getFieldName(), comment);
|
||||||
// If I have compDt, it should now be from result DTM.
|
|
||||||
destStruct.setFlexibleArrayComponent(resultCompDt, flexDtc.getFieldName(),
|
|
||||||
comment);
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException e) {
|
|
||||||
displayError(destStruct, e);
|
|
||||||
DataType badDt = Undefined1DataType.dataType;
|
|
||||||
comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " +
|
|
||||||
e.getMessage() + " " + ((comment != null) ? (" " + comment) : "");
|
|
||||||
destStruct.setFlexibleArrayComponent(badDt, flexDtc.getFieldName(), comment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
catch (IllegalArgumentException e) {
|
||||||
destStruct.clearFlexibleArrayComponent();
|
displayError(destStruct, e);
|
||||||
|
DataType badDt = Undefined1DataType.dataType;
|
||||||
|
comment = "Couldn't add " + resultCompDt.getDisplayName() + " here. " +
|
||||||
|
e.getMessage() + " " + ((comment != null) ? (" " + comment) : "");
|
||||||
|
destStruct.setFlexibleArrayComponent(badDt, flexDtc.getFieldName(), comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
|
@ -1216,7 +1211,9 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// must have been deleted in LATEST
|
// must have been deleted in LATEST
|
||||||
// put an entry in the fixup list if this is a conflict
|
// put an entry in the fixup list if this is a conflict.
|
||||||
|
// NOTE: This may also be caused by a replaced datatype but
|
||||||
|
// we have no indication as to what the replacment was
|
||||||
deletedInLatest = true;
|
deletedInLatest = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1599,11 +1596,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
*/
|
*/
|
||||||
private void processDataTypeChanges() throws CancelledException {
|
private void processDataTypeChanges() throws CancelledException {
|
||||||
|
|
||||||
for (int i = 0; i < myDtChangeList.size(); i++) {
|
for (Long element : myDtChangeList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long id = myDtChangeList.get(i).longValue();
|
long id = element.longValue();
|
||||||
|
|
||||||
DataType dt = dtms[MY].getDataType(id);
|
DataType dt = dtms[MY].getDataType(id);
|
||||||
if ((dt instanceof Pointer) || (dt instanceof Array) || (dt instanceof BuiltIn)) {
|
if ((dt instanceof Pointer) || (dt instanceof Array) || (dt instanceof BuiltIn)) {
|
||||||
|
@ -1681,8 +1678,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isParent(CategoryPath catPath) {
|
private boolean isParent(CategoryPath catPath) {
|
||||||
for (int i = 0; i < myDtAddedList.size(); i++) {
|
for (Long id : myDtAddedList) {
|
||||||
Long id = myDtAddedList.get(i);
|
|
||||||
DataType dt = dtms[MY].getDataType(id.longValue());
|
DataType dt = dtms[MY].getDataType(id.longValue());
|
||||||
if (catPath.equals(dt.getCategoryPath())) {
|
if (catPath.equals(dt.getCategoryPath())) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -2225,10 +2221,10 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processDataTypesDeleted() throws CancelledException {
|
private void processDataTypesDeleted() throws CancelledException {
|
||||||
for (int i = 0; i < myDtChangeList.size(); i++) {
|
for (Long element : myDtChangeList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
|
|
||||||
long id = myDtChangeList.get(i).longValue();
|
long id = element.longValue();
|
||||||
processDataTypeDeleted(id);
|
processDataTypeDeleted(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2237,11 +2233,11 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private void processDataTypesAdded() throws CancelledException {
|
private void processDataTypesAdded() throws CancelledException {
|
||||||
for (int i = 0; i < myDtAddedList.size(); i++) {
|
for (Long element : myDtAddedList) {
|
||||||
currentMonitor.checkCanceled();
|
currentMonitor.checkCanceled();
|
||||||
currentMonitor.setProgress(++progressIndex);
|
currentMonitor.setProgress(++progressIndex);
|
||||||
|
|
||||||
long myDtKey = myDtAddedList.get(i).longValue();
|
long myDtKey = element.longValue();
|
||||||
DataType myDt = dtms[MY].getDataType(myDtKey);
|
DataType myDt = dtms[MY].getDataType(myDtKey);
|
||||||
|
|
||||||
if (equivalentDataTypeFound(myDtKey, myDt)) {
|
if (equivalentDataTypeFound(myDtKey, myDt)) {
|
||||||
|
@ -3360,7 +3356,7 @@ public class DataTypeMergeManager implements MergeResolver {
|
||||||
if (nextOrdinal > maxOrdinal) {
|
if (nextOrdinal > maxOrdinal) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
struct.getComponent(nextOrdinal);
|
dtc = struct.getComponent(nextOrdinal);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.felix.framework.FrameworkFactory;
|
import org.apache.felix.framework.FrameworkFactory;
|
||||||
|
@ -63,8 +62,7 @@ public class BundleHost {
|
||||||
private static final String SAVE_STATE_TAG_ACTIVE = "BundleHost_ACTIVE";
|
private static final String SAVE_STATE_TAG_ACTIVE = "BundleHost_ACTIVE";
|
||||||
private static final String SAVE_STATE_TAG_SYSTEM = "BundleHost_SYSTEM";
|
private static final String SAVE_STATE_TAG_SYSTEM = "BundleHost_SYSTEM";
|
||||||
|
|
||||||
Map<ResourceFile, GhidraBundle> fileToBundleMap = new HashMap<>();
|
private final BundleMap bundleMap = new BundleMap();
|
||||||
Map<String, GhidraBundle> bundleLocationToBundleMap = new HashMap<>();
|
|
||||||
|
|
||||||
BundleContext frameworkBundleContext;
|
BundleContext frameworkBundleContext;
|
||||||
Framework felixFramework;
|
Framework felixFramework;
|
||||||
|
@ -93,7 +91,7 @@ public class BundleHost {
|
||||||
* @return false if the bundle was already enabled
|
* @return false if the bundle was already enabled
|
||||||
*/
|
*/
|
||||||
public boolean enable(ResourceFile bundleFile) {
|
public boolean enable(ResourceFile bundleFile) {
|
||||||
GhidraBundle bundle = fileToBundleMap.get(bundleFile);
|
GhidraBundle bundle = bundleMap.get(bundleFile);
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
bundle = add(bundleFile, true, false);
|
bundle = add(bundleFile, true, false);
|
||||||
return true;
|
return true;
|
||||||
|
@ -139,7 +137,7 @@ public class BundleHost {
|
||||||
* @return a {@link GhidraBundle} or {@code null}
|
* @return a {@link GhidraBundle} or {@code null}
|
||||||
*/
|
*/
|
||||||
public GhidraBundle getExistingGhidraBundle(ResourceFile bundleFile) {
|
public GhidraBundle getExistingGhidraBundle(ResourceFile bundleFile) {
|
||||||
GhidraBundle bundle = fileToBundleMap.get(bundleFile);
|
GhidraBundle bundle = bundleMap.get(bundleFile);
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
Msg.showError(this, null, "ghidra bundle cache",
|
Msg.showError(this, null, "ghidra bundle cache",
|
||||||
"getExistingGhidraBundle expected a GhidraBundle created at " + bundleFile +
|
"getExistingGhidraBundle expected a GhidraBundle created at " + bundleFile +
|
||||||
|
@ -156,7 +154,7 @@ public class BundleHost {
|
||||||
* @return a {@link GhidraBundle} or {@code null}
|
* @return a {@link GhidraBundle} or {@code null}
|
||||||
*/
|
*/
|
||||||
public GhidraBundle getGhidraBundle(ResourceFile bundleFile) {
|
public GhidraBundle getGhidraBundle(ResourceFile bundleFile) {
|
||||||
return fileToBundleMap.get(bundleFile);
|
return bundleMap.get(bundleFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GhidraBundle createGhidraBundle(BundleHost bundleHost, ResourceFile bundleFile,
|
private static GhidraBundle createGhidraBundle(BundleHost bundleHost, ResourceFile bundleFile,
|
||||||
|
@ -189,25 +187,11 @@ public class BundleHost {
|
||||||
*/
|
*/
|
||||||
public GhidraBundle add(ResourceFile bundleFile, boolean enabled, boolean systemBundle) {
|
public GhidraBundle add(ResourceFile bundleFile, boolean enabled, boolean systemBundle) {
|
||||||
GhidraBundle bundle = createGhidraBundle(this, bundleFile, enabled, systemBundle);
|
GhidraBundle bundle = createGhidraBundle(this, bundleFile, enabled, systemBundle);
|
||||||
fileToBundleMap.put(bundleFile, bundle);
|
bundleMap.add(bundle);
|
||||||
bundleLocationToBundleMap.put(bundle.getLocationIdentifier(), bundle);
|
|
||||||
fireBundleAdded(bundle);
|
fireBundleAdded(bundle);
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<ResourceFile> dedupeBundleFiles(List<ResourceFile> bundleFiles) {
|
|
||||||
Set<ResourceFile> dedupedBundleFiles = new HashSet<>(bundleFiles);
|
|
||||||
Iterator<ResourceFile> bundleFileIterator = dedupedBundleFiles.iterator();
|
|
||||||
while (bundleFileIterator.hasNext()) {
|
|
||||||
ResourceFile bundleFile = bundleFileIterator.next();
|
|
||||||
if (fileToBundleMap.containsKey(bundleFile)) {
|
|
||||||
bundleFileIterator.remove();
|
|
||||||
Msg.warn(this, "adding an already managed bundle: " + bundleFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dedupedBundleFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new GhidraBundles and add to the list of managed bundles. All GhidraBundles created
|
* Create new GhidraBundles and add to the list of managed bundles. All GhidraBundles created
|
||||||
* with the same {@code enabled} and {@code systemBundle} values.
|
* with the same {@code enabled} and {@code systemBundle} values.
|
||||||
|
@ -219,17 +203,8 @@ public class BundleHost {
|
||||||
*/
|
*/
|
||||||
public Collection<GhidraBundle> add(List<ResourceFile> bundleFiles, boolean enabled,
|
public Collection<GhidraBundle> add(List<ResourceFile> bundleFiles, boolean enabled,
|
||||||
boolean systemBundle) {
|
boolean systemBundle) {
|
||||||
Set<ResourceFile> dedupedBundleFiles = dedupeBundleFiles(bundleFiles);
|
Collection<GhidraBundle> newBundles = bundleMap.computeAllIfAbsent(bundleFiles,
|
||||||
Map<ResourceFile, GhidraBundle> newBundleMap = dedupedBundleFiles.stream()
|
bundleFile -> createGhidraBundle(BundleHost.this, bundleFile, enabled, systemBundle));
|
||||||
.collect(Collectors.toUnmodifiableMap(Function.identity(),
|
|
||||||
bundleFile -> createGhidraBundle(BundleHost.this, bundleFile, enabled,
|
|
||||||
systemBundle)));
|
|
||||||
fileToBundleMap.putAll(newBundleMap);
|
|
||||||
bundleLocationToBundleMap.putAll(newBundleMap.values()
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toUnmodifiableMap(GhidraBundle::getLocationIdentifier,
|
|
||||||
Function.identity())));
|
|
||||||
Collection<GhidraBundle> newBundles = newBundleMap.values();
|
|
||||||
fireBundlesAdded(newBundles);
|
fireBundlesAdded(newBundles);
|
||||||
return newBundles;
|
return newBundles;
|
||||||
}
|
}
|
||||||
|
@ -240,10 +215,7 @@ public class BundleHost {
|
||||||
* @param bundles the bundles to add
|
* @param bundles the bundles to add
|
||||||
*/
|
*/
|
||||||
private void add(List<GhidraBundle> bundles) {
|
private void add(List<GhidraBundle> bundles) {
|
||||||
for (GhidraBundle bundle : bundles) {
|
bundleMap.addAll(bundles);
|
||||||
fileToBundleMap.put(bundle.getFile(), bundle);
|
|
||||||
bundleLocationToBundleMap.put(bundle.getLocationIdentifier(), bundle);
|
|
||||||
}
|
|
||||||
fireBundlesAdded(bundles);
|
fireBundlesAdded(bundles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,8 +225,7 @@ public class BundleHost {
|
||||||
* @param bundleFile the file of the bundle to remove
|
* @param bundleFile the file of the bundle to remove
|
||||||
*/
|
*/
|
||||||
public void remove(ResourceFile bundleFile) {
|
public void remove(ResourceFile bundleFile) {
|
||||||
GhidraBundle bundle = fileToBundleMap.remove(bundleFile);
|
GhidraBundle bundle = bundleMap.remove(bundleFile);
|
||||||
bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
|
|
||||||
fireBundleRemoved(bundle);
|
fireBundleRemoved(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,8 +235,7 @@ public class BundleHost {
|
||||||
* @param bundleLocation the location id of the bundle to remove
|
* @param bundleLocation the location id of the bundle to remove
|
||||||
*/
|
*/
|
||||||
public void remove(String bundleLocation) {
|
public void remove(String bundleLocation) {
|
||||||
GhidraBundle bundle = bundleLocationToBundleMap.remove(bundleLocation);
|
GhidraBundle bundle = bundleMap.remove(bundleLocation);
|
||||||
fileToBundleMap.remove(bundle.getFile());
|
|
||||||
fireBundleRemoved(bundle);
|
fireBundleRemoved(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,8 +245,7 @@ public class BundleHost {
|
||||||
* @param bundle the bundle to remove
|
* @param bundle the bundle to remove
|
||||||
*/
|
*/
|
||||||
public void remove(GhidraBundle bundle) {
|
public void remove(GhidraBundle bundle) {
|
||||||
fileToBundleMap.remove(bundle.getFile());
|
bundleMap.remove(bundle);
|
||||||
bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
|
|
||||||
fireBundleRemoved(bundle);
|
fireBundleRemoved(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,10 +255,7 @@ public class BundleHost {
|
||||||
* @param bundles the bundles to remove
|
* @param bundles the bundles to remove
|
||||||
*/
|
*/
|
||||||
public void remove(Collection<GhidraBundle> bundles) {
|
public void remove(Collection<GhidraBundle> bundles) {
|
||||||
for (GhidraBundle bundle : bundles) {
|
bundleMap.removeAll(bundles);
|
||||||
fileToBundleMap.remove(bundle.getFile());
|
|
||||||
bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
|
|
||||||
}
|
|
||||||
fireBundlesRemoved(bundles);
|
fireBundlesRemoved(bundles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +304,7 @@ public class BundleHost {
|
||||||
* @return all the bundles
|
* @return all the bundles
|
||||||
*/
|
*/
|
||||||
public Collection<GhidraBundle> getGhidraBundles() {
|
public Collection<GhidraBundle> getGhidraBundles() {
|
||||||
return fileToBundleMap.values();
|
return bundleMap.getGhidraBundles();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -347,7 +313,7 @@ public class BundleHost {
|
||||||
* @return all the bundle files
|
* @return all the bundle files
|
||||||
*/
|
*/
|
||||||
public Collection<ResourceFile> getBundleFiles() {
|
public Collection<ResourceFile> getBundleFiles() {
|
||||||
return fileToBundleMap.keySet();
|
return bundleMap.getBundleFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
void dumpLoadedBundles() {
|
void dumpLoadedBundles() {
|
||||||
|
@ -861,7 +827,7 @@ public class BundleHost {
|
||||||
boolean isEnabled = bundleIsEnabled[i];
|
boolean isEnabled = bundleIsEnabled[i];
|
||||||
boolean isActive = bundleIsActive[i];
|
boolean isActive = bundleIsActive[i];
|
||||||
boolean isSystem = bundleIsSystem[i];
|
boolean isSystem = bundleIsSystem[i];
|
||||||
GhidraBundle bundle = fileToBundleMap.get(bundleFile);
|
GhidraBundle bundle = bundleMap.get(bundleFile);
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
if (isEnabled != bundle.isEnabled()) {
|
if (isEnabled != bundle.isEnabled()) {
|
||||||
bundle.setEnabled(isEnabled);
|
bundle.setEnabled(isEnabled);
|
||||||
|
@ -900,14 +866,15 @@ public class BundleHost {
|
||||||
* @param saveState the state object
|
* @param saveState the state object
|
||||||
*/
|
*/
|
||||||
public void saveManagedBundleState(SaveState saveState) {
|
public void saveManagedBundleState(SaveState saveState) {
|
||||||
int numBundles = fileToBundleMap.size();
|
Collection<GhidraBundle> bundles = bundleMap.getGhidraBundles();
|
||||||
|
int numBundles = bundles.size();
|
||||||
String[] bundleFiles = new String[numBundles];
|
String[] bundleFiles = new String[numBundles];
|
||||||
boolean[] bundleIsEnabled = new boolean[numBundles];
|
boolean[] bundleIsEnabled = new boolean[numBundles];
|
||||||
boolean[] bundleIsActive = new boolean[numBundles];
|
boolean[] bundleIsActive = new boolean[numBundles];
|
||||||
boolean[] bundleIsSystem = new boolean[numBundles];
|
boolean[] bundleIsSystem = new boolean[numBundles];
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (GhidraBundle bundle : fileToBundleMap.values()) {
|
for (GhidraBundle bundle : bundles) {
|
||||||
bundleFiles[index] = generic.util.Path.toPathString(bundle.getFile());
|
bundleFiles[index] = generic.util.Path.toPathString(bundle.getFile());
|
||||||
bundleIsEnabled[index] = bundle.isEnabled();
|
bundleIsEnabled[index] = bundle.isEnabled();
|
||||||
bundleIsActive[index] = bundle.isActive();
|
bundleIsActive[index] = bundle.isActive();
|
||||||
|
@ -1081,7 +1048,7 @@ public class BundleHost {
|
||||||
GhidraBundle bundle;
|
GhidraBundle bundle;
|
||||||
switch (event.getType()) {
|
switch (event.getType()) {
|
||||||
case BundleEvent.STARTED:
|
case BundleEvent.STARTED:
|
||||||
bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
|
bundle = bundleMap.getBundleAtLocation(osgiBundle.getLocation());
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
fireBundleActivationChange(bundle, true);
|
fireBundleActivationChange(bundle, true);
|
||||||
}
|
}
|
||||||
|
@ -1093,7 +1060,7 @@ public class BundleHost {
|
||||||
break;
|
break;
|
||||||
// force "inactive" updates for all other states
|
// force "inactive" updates for all other states
|
||||||
default:
|
default:
|
||||||
bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
|
bundle = bundleMap.getBundleAtLocation(osgiBundle.getLocation());
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
fireBundleActivationChange(bundle, false);
|
fireBundleActivationChange(bundle, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
/* ###
|
||||||
|
* 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.plugin.core.osgi;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.locks.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A thread-safe container that maps {@link GhidraBundle}s by file and bundle location.
|
||||||
|
*/
|
||||||
|
public class BundleMap {
|
||||||
|
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
|
private final Lock readLock = lock.readLock();
|
||||||
|
private final Lock writeLock = lock.writeLock();
|
||||||
|
|
||||||
|
private final Map<ResourceFile, GhidraBundle> bundlesByFile = new HashMap<>();
|
||||||
|
private final Map<String, GhidraBundle> bundlesByLocation = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps associations between a bundle, its file, and its bundle location.
|
||||||
|
*
|
||||||
|
* @param bundle a GhidraBundle object
|
||||||
|
*/
|
||||||
|
public void add(GhidraBundle bundle) {
|
||||||
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
bundlesByFile.put(bundle.getFile(), bundle);
|
||||||
|
bundlesByLocation.put(bundle.getLocationIdentifier(), bundle);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps bundles in a collection.
|
||||||
|
*
|
||||||
|
* <p>This is the same as calling {@link BundleMap#add(GhidraBundle)} for each bundle in {@code bundles}.
|
||||||
|
*
|
||||||
|
* @param bundles a collection of GhidraBundle objects
|
||||||
|
*/
|
||||||
|
public void addAll(Collection<GhidraBundle> bundles) {
|
||||||
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
for (GhidraBundle bundle : bundles) {
|
||||||
|
bundlesByFile.put(bundle.getFile(), bundle);
|
||||||
|
bundlesByLocation.put(bundle.getLocationIdentifier(), bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writeLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the mappings of a bundle.
|
||||||
|
*
|
||||||
|
* @param bundle a GhidraBundle object
|
||||||
|
*/
|
||||||
|
public void remove(GhidraBundle bundle) {
|
||||||
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
bundlesByFile.remove(bundle.getFile());
|
||||||
|
bundlesByLocation.remove(bundle.getLocationIdentifier());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writeLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all mappings of each bundle from a collection.
|
||||||
|
*
|
||||||
|
* This is the same as calling {@link #remove(GhidraBundle)} for each bundle in {@code bundles}.
|
||||||
|
*
|
||||||
|
* @param bundles a collection of GhidraBundle objects
|
||||||
|
*/
|
||||||
|
public void removeAll(Collection<GhidraBundle> bundles) {
|
||||||
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
for (GhidraBundle bundle : bundles) {
|
||||||
|
bundlesByFile.remove(bundle.getFile());
|
||||||
|
bundlesByLocation.remove(bundle.getLocationIdentifier());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writeLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the mapping for a bundle with a given bundle location.
|
||||||
|
*
|
||||||
|
* @param bundleLocation a bundle location
|
||||||
|
* @return the bundle removed
|
||||||
|
*/
|
||||||
|
public GhidraBundle remove(String bundleLocation) {
|
||||||
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
GhidraBundle bundle = bundlesByLocation.remove(bundleLocation);
|
||||||
|
bundlesByFile.remove(bundle.getFile());
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writeLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the mapping for a bundle with a given file.
|
||||||
|
|
||||||
|
* @param bundleFile a bundle file
|
||||||
|
* @return the bundle removed
|
||||||
|
*/
|
||||||
|
public GhidraBundle remove(ResourceFile bundleFile) {
|
||||||
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
GhidraBundle bundle = bundlesByFile.remove(bundleFile);
|
||||||
|
bundlesByLocation.remove(bundle.getLocationIdentifier());
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writeLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and maps bundles from files in a collection that aren't already mapped.
|
||||||
|
*
|
||||||
|
* @param bundleFiles a collection of bundle files
|
||||||
|
* @param ctor a constructor for a GhidraBundle given a bundle file
|
||||||
|
* @return the newly created GhidraBundle objects
|
||||||
|
*/
|
||||||
|
public Collection<GhidraBundle> computeAllIfAbsent(Collection<ResourceFile> bundleFiles,
|
||||||
|
Function<ResourceFile, GhidraBundle> ctor) {
|
||||||
|
writeLock.lock();
|
||||||
|
try {
|
||||||
|
Set<ResourceFile> newBundleFiles = new HashSet<>(bundleFiles);
|
||||||
|
newBundleFiles.removeAll(bundlesByFile.keySet());
|
||||||
|
List<GhidraBundle> newBundles =
|
||||||
|
newBundleFiles.stream().map(ctor).collect(Collectors.toList());
|
||||||
|
addAll(newBundles);
|
||||||
|
return newBundles;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writeLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bundle with the given location.
|
||||||
|
*
|
||||||
|
* @param location a bundle location
|
||||||
|
* @return the bundle found or null
|
||||||
|
*/
|
||||||
|
public GhidraBundle getBundleAtLocation(String location) {
|
||||||
|
readLock.lock();
|
||||||
|
try {
|
||||||
|
return bundlesByLocation.get(location);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
readLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bundle with the given file.
|
||||||
|
*
|
||||||
|
* @param bundleFile a bundle file
|
||||||
|
* @return the bundle found or null
|
||||||
|
*/
|
||||||
|
public GhidraBundle get(ResourceFile bundleFile) {
|
||||||
|
readLock.lock();
|
||||||
|
try {
|
||||||
|
return bundlesByFile.get(bundleFile);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
readLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the currently mapped bundles
|
||||||
|
*/
|
||||||
|
public Collection<GhidraBundle> getGhidraBundles() {
|
||||||
|
readLock.lock();
|
||||||
|
try {
|
||||||
|
return new ArrayList<>(bundlesByFile.values());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
readLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the currently mapped bundle files
|
||||||
|
*/
|
||||||
|
public Collection<ResourceFile> getBundleFiles() {
|
||||||
|
readLock.lock();
|
||||||
|
try {
|
||||||
|
return new ArrayList<>(bundlesByFile.keySet());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
readLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -133,4 +133,19 @@ public abstract class GhidraScriptProvider
|
||||||
protected void writeBody(PrintWriter writer) {
|
protected void writeBody(PrintWriter writer) {
|
||||||
writer.println(getCommentCharacter() + "TODO Add User Code Here");
|
writer.println(getCommentCharacter() + "TODO Add User Code Here");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixup a script name for searching in script directories.
|
||||||
|
*
|
||||||
|
* <p>This method is part of a poorly specified behavior that is due for future amendment,
|
||||||
|
* see {@link GhidraScriptUtil#fixupName(String)}.
|
||||||
|
*
|
||||||
|
* @param scriptName the name of the script, must end with this provider's extension
|
||||||
|
* @return a (relative) file path to the corresponding script
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
protected String fixupName(String scriptName) {
|
||||||
|
return scriptName;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import generic.jar.ResourceFile;
|
||||||
import ghidra.app.plugin.core.osgi.BundleHost;
|
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||||
import ghidra.app.plugin.core.osgi.OSGiException;
|
import ghidra.app.plugin.core.osgi.OSGiException;
|
||||||
import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin;
|
import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin;
|
||||||
|
import ghidra.app.util.headless.HeadlessAnalyzer;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.classfinder.ClassSearcher;
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
|
@ -139,8 +140,7 @@ public class GhidraScriptUtil {
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
Msg.error(GhidraScriptUtil.class,
|
Msg.error(GhidraScriptUtil.class,
|
||||||
"Failed to find script in any script directory: " + sourceFile.toString(),
|
"Failed to find script in any script directory: " + sourceFile.toString(), e);
|
||||||
e);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -293,14 +293,7 @@ public class GhidraScriptUtil {
|
||||||
* @return the Ghidra script provider
|
* @return the Ghidra script provider
|
||||||
*/
|
*/
|
||||||
public static GhidraScriptProvider getProvider(ResourceFile scriptFile) {
|
public static GhidraScriptProvider getProvider(ResourceFile scriptFile) {
|
||||||
String scriptFileName = scriptFile.getName().toLowerCase();
|
return findProvider(scriptFile.getName());
|
||||||
|
|
||||||
for (GhidraScriptProvider provider : getProviders()) {
|
|
||||||
if (scriptFileName.endsWith(provider.getExtension().toLowerCase())) {
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -310,13 +303,23 @@ public class GhidraScriptUtil {
|
||||||
* @return true if a provider exists that can process the specified file
|
* @return true if a provider exists that can process the specified file
|
||||||
*/
|
*/
|
||||||
public static boolean hasScriptProvider(ResourceFile scriptFile) {
|
public static boolean hasScriptProvider(ResourceFile scriptFile) {
|
||||||
String scriptFileName = scriptFile.getName().toLowerCase();
|
return findProvider(scriptFile.getName()) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the provider whose extension matches the given filename extension.
|
||||||
|
*
|
||||||
|
* @param fileName name of script file
|
||||||
|
* @return the first matching provider or null if no provider matches
|
||||||
|
*/
|
||||||
|
private static GhidraScriptProvider findProvider(String fileName) {
|
||||||
|
fileName = fileName.toLowerCase();
|
||||||
for (GhidraScriptProvider provider : getProviders()) {
|
for (GhidraScriptProvider provider : getProviders()) {
|
||||||
if (scriptFileName.endsWith(provider.getExtension().toLowerCase())) {
|
if (fileName.endsWith(provider.getExtension().toLowerCase())) {
|
||||||
return true;
|
return provider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,31 +365,35 @@ public class GhidraScriptUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fixup name issues, such as package parts in the name and inner class names.
|
* Fix script name issues for searching in script directories.
|
||||||
|
* If no provider can be identified, Java is assumed.
|
||||||
*
|
*
|
||||||
* <p>This method can handle names with or without '.java' at the end; names with
|
* <p>This method is part of a poorly specified behavior that is due for future amendment.
|
||||||
* '$' (inner classes) and names with '.' characters for package separators
|
*
|
||||||
|
* <p>It is used by {@link GhidraScript#runScript(String)} methods,
|
||||||
|
* {@link #createNewScript(String, String, ResourceFile, List)}, and by {@link HeadlessAnalyzer} for
|
||||||
|
* {@code preScript} and {@code postScript}. The intent was to allow some freedom in how a user specifies
|
||||||
|
* a script in two ways: 1) if the extension is omitted ".java" is assumed and 2) if a Java class name is
|
||||||
|
* given it's converted to a relative path.
|
||||||
*
|
*
|
||||||
* @param name the name of the script
|
* @param name the name of the script
|
||||||
* @return the name as a '.java' file path (with '/'s and not '.'s)
|
* @return the name as a file path
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
static String fixupName(String name) {
|
static String fixupName(String name) {
|
||||||
if (name.endsWith(".java")) {
|
GhidraScriptProvider provider = findProvider(name);
|
||||||
name = name.substring(0, name.length() - 5);
|
// assume Java if no provider matched
|
||||||
|
if (provider == null) {
|
||||||
|
name = name + ".java";
|
||||||
|
provider = findProvider(".java");
|
||||||
}
|
}
|
||||||
|
return provider.fixupName(name);
|
||||||
String path = name.replace('.', '/');
|
|
||||||
int innerClassIndex = path.indexOf('$');
|
|
||||||
if (innerClassIndex != -1) {
|
|
||||||
path = path.substring(0, innerClassIndex);
|
|
||||||
}
|
|
||||||
return path + ".java";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ResourceFile findScriptFileInPaths(Collection<ResourceFile> scriptDirectories,
|
static ResourceFile findScriptFileInPaths(Collection<ResourceFile> scriptDirectories,
|
||||||
String filename) {
|
String name) {
|
||||||
|
|
||||||
String validatedName = fixupName(filename);
|
String validatedName = fixupName(name);
|
||||||
|
|
||||||
for (ResourceFile resourceFile : scriptDirectories) {
|
for (ResourceFile resourceFile : scriptDirectories) {
|
||||||
if (resourceFile.isDirectory()) {
|
if (resourceFile.isDirectory()) {
|
||||||
|
|
|
@ -164,4 +164,29 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||||
return "//";
|
return "//";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Fix script name for search in script directories, such as Java package parts in the name and inner class names.
|
||||||
|
*
|
||||||
|
* <p>This method can handle names with '$' (inner classes) and names with '.'
|
||||||
|
* characters for package separators
|
||||||
|
*
|
||||||
|
* <p>It is part of a poorly specified behavior that is due for future amendment,
|
||||||
|
* see {@link GhidraScriptUtil#fixupName(String)}.
|
||||||
|
*
|
||||||
|
* @param scriptName the name of the script
|
||||||
|
* @return the name as a '.java' file path (with '/'s and not '.'s)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String fixupName(String scriptName) {
|
||||||
|
scriptName = scriptName.substring(0, scriptName.length() - 5);
|
||||||
|
|
||||||
|
String path = scriptName.replace('.', '/');
|
||||||
|
int innerClassIndex = path.indexOf('$');
|
||||||
|
if (innerClassIndex != -1) {
|
||||||
|
path = path.substring(0, innerClassIndex);
|
||||||
|
}
|
||||||
|
return path + ".java";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,7 @@ package ghidra.app.merge.datatypes;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.*;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import ghidra.program.database.*;
|
import ghidra.program.database.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
|
@ -1247,6 +1246,122 @@ public class DataTypeMerge3Test extends AbstractDataTypeMergeTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testEditStructureWithReplacementAndRemoval() throws Exception {
|
||||||
|
|
||||||
|
// See GP-585 for design issue preventing this test from passing
|
||||||
|
|
||||||
|
mtf.initialize("notepad", new OriginalProgramModifierListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyPrivate(ProgramDB program) throws Exception {
|
||||||
|
boolean commit = false;
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
Structure s = (Structure) dtm.getDataType("/Category5/Test");
|
||||||
|
DataType dt = dtm.getDataType("/MISC/FooTypedef");
|
||||||
|
s.setFlexibleArrayComponent(dt, "foo", "");
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyLatest(ProgramDB program) throws Exception {
|
||||||
|
boolean commit = false;
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
TypeDef td = (TypeDef) dtm.getDataType("/BF");
|
||||||
|
//
|
||||||
|
// NOTE: Merge does not handle datatype replacements as one might hope
|
||||||
|
// If latest version has defined data/components based upon a type which has
|
||||||
|
// been replaced in private, the replaced datatype will be treated as removed
|
||||||
|
//
|
||||||
|
dtm.replaceDataType(td, new TypedefDataType("NewBF", IntegerDataType.dataType),
|
||||||
|
true);
|
||||||
|
DataType dt = dtm.getDataType("/MISC/FooTypedef");
|
||||||
|
dtm.remove(dt, TaskMonitor.DUMMY);
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, commit);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType dt1 = dtm.getDataType("/BF");
|
||||||
|
assertNull(dt1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void modifyOriginal(ProgramDB program) throws Exception {
|
||||||
|
boolean commit = false;
|
||||||
|
DataTypeManager dtm = program.getDataTypeManager();
|
||||||
|
int transactionID = program.startTransaction("test");
|
||||||
|
try {
|
||||||
|
TypeDef td = new TypedefDataType("BF", IntegerDataType.dataType);
|
||||||
|
|
||||||
|
Structure struct = new StructureDataType(new CategoryPath("/Category5"), "Test",
|
||||||
|
0, program.getDataTypeManager());
|
||||||
|
struct.add(td);
|
||||||
|
struct.insertBitFieldAt(3, 2, 6, td, 2, "bf1", null);
|
||||||
|
struct.insertBitFieldAt(3, 2, 4, td, 2, "bf2", null);
|
||||||
|
struct.add(new WordDataType());
|
||||||
|
struct.add(new QWordDataType());
|
||||||
|
|
||||||
|
struct.setFlexibleArrayComponent(td, "flex", "my flex");
|
||||||
|
|
||||||
|
dtm.addDataType(struct, null);
|
||||||
|
commit = true;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Assert.fail(e.toString());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
program.endTransaction(transactionID, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executeMerge(DataTypeMergeManager.OPTION_MY);
|
||||||
|
|
||||||
|
DataTypeManager dtm = resultProgram.getDataTypeManager();
|
||||||
|
|
||||||
|
DataType dt = dtm.getDataType("/Category5/Test");
|
||||||
|
assertTrue(dt instanceof Structure);
|
||||||
|
Structure s = (Structure) dt;
|
||||||
|
/** Current Result for /Category5/Test
|
||||||
|
*
|
||||||
|
Unaligned
|
||||||
|
Structure Test {
|
||||||
|
4 int:2(6) 1 bf1 ""
|
||||||
|
4 int:2(4) 1 bf2 ""
|
||||||
|
5 word 2 null ""
|
||||||
|
7 qword 8 null ""
|
||||||
|
}
|
||||||
|
Size = 15 Actual Alignment = 1
|
||||||
|
*
|
||||||
|
* See assertion below for preferred result
|
||||||
|
*/
|
||||||
|
//@formatter:off
|
||||||
|
assertEquals("/Category5/Test\n" +
|
||||||
|
"Unaligned\n" +
|
||||||
|
"Structure Test {\n" +
|
||||||
|
" 0 NewBF 4 null \"\"\n" +
|
||||||
|
" 4 NewBF:2(6) 1 bf1 \"\"\n" +
|
||||||
|
" 4 NewBF:2(4) 1 bf2 \"\"\n" +
|
||||||
|
" 5 word 2 null \"\"\n" +
|
||||||
|
" 7 qword 8 null \"\"\n" +
|
||||||
|
" Undefined1[0] 0 foo \"\"\n" + // reflects removal of /MISC/FooTypedef
|
||||||
|
"}\n" +
|
||||||
|
"Size = 15 Actual Alignment = 1\n", s.toString());
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEditUnions() throws Exception {
|
public void testEditUnions() throws Exception {
|
||||||
|
|
||||||
|
|
|
@ -669,6 +669,8 @@ public class DefaultGraphDisplay implements GraphDisplay {
|
||||||
// always get the current predicate from the main view and test with it,
|
// always get the current predicate from the main view and test with it,
|
||||||
satellite.getRenderContext()
|
satellite.getRenderContext()
|
||||||
.setVertexIncludePredicate(v -> viewer.getRenderContext().getVertexIncludePredicate().test(v));
|
.setVertexIncludePredicate(v -> viewer.getRenderContext().getVertexIncludePredicate().test(v));
|
||||||
|
satellite.getRenderContext()
|
||||||
|
.setEdgeIncludePredicate(e -> viewer.getRenderContext().getEdgeIncludePredicate().test(e));
|
||||||
satellite.getComponent().setBorder(BorderFactory.createEtchedBorder());
|
satellite.getComponent().setBorder(BorderFactory.createEtchedBorder());
|
||||||
parentViewer.getComponent().addComponentListener(new ComponentAdapter() {
|
parentViewer.getComponent().addComponentListener(new ComponentAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1473,11 +1473,12 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
|
||||||
public DataType getDataType(String dataTypePath) {
|
public DataType getDataType(String dataTypePath) {
|
||||||
// Category path now has sourceID followed by ":" followed by path under that source.
|
// Category path now has sourceID followed by ":" followed by path under that source.
|
||||||
String name = getName();
|
String name = getName();
|
||||||
if (dataTypePath.startsWith(name)) {
|
int nameLen = name.length();
|
||||||
dataTypePath = dataTypePath.substring(name.length());
|
if (dataTypePath.length() > nameLen && dataTypePath.charAt(nameLen) == '/' &&
|
||||||
|
dataTypePath.startsWith(name)) {
|
||||||
|
dataTypePath = dataTypePath.substring(nameLen);
|
||||||
}
|
}
|
||||||
|
else if (!dataTypePath.startsWith("/")) {
|
||||||
if (!dataTypePath.startsWith("/")) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ set OS_DIR=build\os
|
||||||
:continue
|
:continue
|
||||||
|
|
||||||
REM create absolute path
|
REM create absolute path
|
||||||
for /f %%i in ("%GHIDRA_DIR%") do set GHIDRA_DIR=%%~fi
|
for /f "delims=" %%i in ("%GHIDRA_DIR%") do set GHIDRA_DIR=%%~fi
|
||||||
|
|
||||||
REM Determine if 64-bit or 32-bit
|
REM Determine if 64-bit or 32-bit
|
||||||
if exist "%PROGRAMFILES(X86)%" (
|
if exist "%PROGRAMFILES(X86)%" (
|
||||||
|
@ -64,7 +64,7 @@ for /f "tokens=* delims=" %%a in ('dir %arg1% /s /b') do (
|
||||||
setlocal enableDelayedExpansion
|
setlocal enableDelayedExpansion
|
||||||
(
|
(
|
||||||
echo "Processing file: %%a"
|
echo "Processing file: %%a"
|
||||||
START /B /WAIT "" "%PDB_EXE%" %%a > "%%a.xml"
|
START /B /WAIT "" "%PDB_EXE%" "%%a" > "%%a.xml"
|
||||||
|
|
||||||
REM Exit if executable returned non-zero error code (signifies that there is a problem).
|
REM Exit if executable returned non-zero error code (signifies that there is a problem).
|
||||||
if !errorlevel! neq 0 (
|
if !errorlevel! neq 0 (
|
||||||
|
|
|
@ -15,14 +15,23 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.script;
|
package ghidra.app.script;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import generic.test.AbstractGenericTest;
|
import generic.test.AbstractGenericTest;
|
||||||
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.ConsoleTaskMonitor;
|
||||||
|
|
||||||
public class GhidraScriptUtilTest extends AbstractGenericTest {
|
public class GhidraScriptUtilTest extends AbstractGenericTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws CancelledException {
|
||||||
|
ClassSearcher.search(false, new ConsoleTaskMonitor());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fixupName_WithExtension() {
|
public void fixupName_WithExtension() {
|
||||||
String input = "Bob.java";
|
String input = "Bob.java";
|
||||||
|
@ -58,4 +67,11 @@ public class GhidraScriptUtilTest extends AbstractGenericTest {
|
||||||
String input = "a.b.c.Bob$InnerClass";
|
String input = "a.b.c.Bob$InnerClass";
|
||||||
assertEquals(GhidraScriptUtil.fixupName(input), "a/b/c/Bob.java");
|
assertEquals(GhidraScriptUtil.fixupName(input), "a/b/c/Bob.java");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fixupName_Python() {
|
||||||
|
String input = "Bob.py";
|
||||||
|
assertEquals(GhidraScriptUtil.fixupName(input), "Bob.py");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue