Merge remote-tracking branch 'origin/patch'

This commit is contained in:
ghidra1 2021-01-11 18:50:07 -05:00
commit cc483f08ab
11 changed files with 502 additions and 133 deletions

View file

@ -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,15 +1127,15 @@ 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 {
// If I have compDt, it should now be from result DTM. destStruct.setFlexibleArrayComponent(resultCompDt, flexDtc.getFieldName(), comment);
destStruct.setFlexibleArrayComponent(resultCompDt, flexDtc.getFieldName(),
comment);
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
displayError(destStruct, e); displayError(destStruct, e);
@ -1146,10 +1145,6 @@ public class DataTypeMergeManager implements MergeResolver {
destStruct.setFlexibleArrayComponent(badDt, flexDtc.getFieldName(), comment); destStruct.setFlexibleArrayComponent(badDt, flexDtc.getFieldName(), comment);
} }
} }
else {
destStruct.clearFlexibleArrayComponent();
}
}
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
displayError(destStruct, e); displayError(destStruct, 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;
} }

View file

@ -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);
} }

View file

@ -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();
}
}
}

View file

@ -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;
}
} }

View file

@ -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()) {

View file

@ -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";
}
} }

View file

@ -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 {

View file

@ -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

View file

@ -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;
} }

View file

@ -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 (

View file

@ -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");
}
} }