public static Symbol getMatchingExternalSymbol(Program program, Symbol symbol,
			Program otherProgram, boolean allowInferredMatch, Set<Long> otherRestrictedSymbolIds) {

		// TODO: It is rather confusing to have two sets of methods for finding external
		// locations.  This should be simplified.  getOther... getMatchingExternal...

		if (symbol == null) {
			return null;
		}
		SymbolType type = symbol.getSymbolType();
		if ((type != SymbolType.FUNCTION && type != SymbolType.LABEL) || !symbol.isExternal()) {
			return null;
		}

		if (allowInferredMatch) {
			// Inferred reference-based match only valid for the same program (i.e., multi-user merge)
			if (program.getUniqueProgramID() != otherProgram.getUniqueProgramID()) {
				allowInferredMatch = false;
			}
		}

		ExternalManager extMgr = program.getExternalManager();
		ExternalLocation extLoc = extMgr.getExternalLocation(symbol);

		String targetName = symbol.getSource() != SourceType.DEFAULT ? symbol.getName() : null;
		String targetOrigImportedName = extLoc.getOriginalImportedName();
		String targetNamespace = symbol.getParentNamespace().getName(true);
		if (targetNamespace.startsWith(Library.UNKNOWN)) {
			targetNamespace = null;
		}
		Address targetAddr = extLoc.getAddress();

		SymbolTable otherSymbMgr = otherProgram.getSymbolTable();
		ExternalManager otherExtMgr = otherProgram.getExternalManager();

		HashMap<Address, ExternalReferenceCount> matchesMap = new HashMap<>();

		// Search by name
		if (symbol.getSource() != SourceType.DEFAULT) {

			// TODO: Need to improve support for lookup based upon other fields
			// Need separate Symbol DB indexing of ExternalLocation fields (address,originalImportName)

			Symbol otherParent = getSymbol(symbol.getParentSymbol(), otherProgram);
			if (otherParent != null) {
				SymbolIterator symbols =
					otherProgram.getSymbolTable().getExternalSymbols(symbol.getName());
				for (Symbol otherSym : symbols) {
					ExternalLocation otherExtLoc = otherExtMgr.getExternalLocation(otherSym);
					if (otherExtLoc != null) {
						ExternalMatchType matchType = testExternalMatch(otherExtLoc, extLoc);
						if (matchType == ExternalMatchType.NONE) {
							continue;
						}
						ExternalReferenceCount refMatch =
							new ExternalReferenceCount(otherExtLoc, matchType);
						refMatch.setRelativeRank(targetAddr, targetNamespace, targetName,
							targetOrigImportedName);
						matchesMap.put(otherSym.getAddress(), refMatch);
					}
				}
			}
		}

		if (allowInferredMatch && matchesMap.isEmpty()) {
			// Process references
			ReferenceManager refMgr = program.getReferenceManager();
			ReferenceManager otherRefMgr = otherProgram.getReferenceManager();
			ReferenceIterator refIter = refMgr.getReferencesTo(symbol.getAddress());
			int totalMatchCnt = 0;
			while (refIter.hasNext()) {
				Reference ref = refIter.next();
				Reference otherRef = otherRefMgr.getPrimaryReferenceFrom(ref.getFromAddress(),
					ref.getOperandIndex());
				if (otherRef == null || !otherRef.isExternalReference()) {
					continue;
				}
				Address otherExtAddr = otherRef.getToAddress();
				ExternalReferenceCount refMatch = matchesMap.get(otherExtAddr);
				if (refMatch == null) {
					Symbol otherSym = otherSymbMgr.getPrimarySymbol(otherExtAddr);
					if (otherRestrictedSymbolIds != null &&
						!otherRestrictedSymbolIds.contains(otherSym.getID())) {
						continue;
					}
					ExternalLocation otherExtLoc = otherExtMgr.getExternalLocation(otherSym);
					ExternalMatchType matchType = testExternalMatch(otherExtLoc, extLoc);
					refMatch = new ExternalReferenceCount(otherExtLoc, matchType);
					if (matchType != ExternalMatchType.NONE) {
						refMatch.setRelativeRank(targetAddr, targetNamespace, targetName,
							targetOrigImportedName);
					}
					matchesMap.put(otherExtAddr, refMatch);
				}
				else {
					++refMatch.refCount;
				}
				if (++totalMatchCnt == 20) {
					break;
				}
			}

			// Process thunk-references (include all)
			if (extLoc.isFunction()) {
				Address[] thunkAddrs = extLoc.getFunction().getFunctionThunkAddresses();
				if (thunkAddrs != null) {
					for (Address thunkAddr : thunkAddrs) {
						Symbol otherThunkSym = otherSymbMgr.getPrimarySymbol(thunkAddr);
						if (otherThunkSym == null ||
							otherThunkSym.getSymbolType() != SymbolType.FUNCTION) {
							continue;
						}
						Function otherFunc = (Function) otherThunkSym.getObject();
						Function otherThunkedFunc = otherFunc.getThunkedFunction(false);
						if (otherThunkedFunc == null || !otherThunkedFunc.isExternal()) {
							continue;
						}
						ExternalReferenceCount refMatch =
							matchesMap.get(otherThunkedFunc.getEntryPoint());
						if (refMatch == null) {
							if (otherRestrictedSymbolIds != null &&
								!otherRestrictedSymbolIds.contains(otherThunkedFunc.getID())) {
								continue;
							}
							ExternalLocation otherExtLoc =
								otherExtMgr.getExternalLocation(otherThunkedFunc.getSymbol());
							ExternalMatchType matchType = testExternalMatch(otherExtLoc, extLoc);
							refMatch = new ExternalReferenceCount(otherExtLoc, matchType);
							if (matchType != ExternalMatchType.NONE) {
								refMatch.setRelativeRank(targetAddr, targetNamespace, targetName,
									targetOrigImportedName);
							}
							matchesMap.put(otherThunkedFunc.getEntryPoint(), refMatch);
						}
						else {
							++refMatch.refCount;
						}
					}
				}
			}
		}

		if (matchesMap.isEmpty()) {
			// Brute force search for match candidates using address/name
			// This will occur anytime an external add occurs on program and not otherProgram
			for (Symbol otherSym : otherSymbMgr.getExternalSymbols()) {
				if (otherRestrictedSymbolIds != null &&
					!otherRestrictedSymbolIds.contains(otherSym.getID())) {
					continue;
				}
				ExternalLocation otherExtLoc = otherExtMgr.getExternalLocation(otherSym);
				if (otherExtLoc != null) {
					ExternalMatchType matchType = testExternalMatch(otherExtLoc, extLoc);
					if (matchType == ExternalMatchType.NONE) {
						continue;
					}
					ExternalReferenceCount refMatch =
						new ExternalReferenceCount(otherExtLoc, matchType);
					refMatch.setRelativeRank(targetAddr, targetNamespace, targetName,
						targetOrigImportedName);
					matchesMap.put(otherSym.getAddress(), refMatch);
				}
			}
			if (matchesMap.isEmpty()) {
				return null;
			}
		}

		ExternalReferenceCount[] matches =
			matchesMap.values().toArray(new ExternalReferenceCount[matchesMap.size()]);
		if (matches.length == 1) {
			// If only one match candidate - rank of 0 is OK
			return matches[0].rank >= 0 ? matches[0].getSymbol() : null;
		}

		// If multiple matches, rank > 0 required (i.e., must match on name and/or addr)
		Arrays.sort(matches);
		return matches[0].rank > 0 ? matches[0].getSymbol() : null;
	}
Given an external symbol for a specified program, get the corresponding symbol,
which has the same name and path,  from the specified otherProgram.<br>
Note: In The type of the returned symbol may be different than the type of the symbol
(i.e., Function vs Label).
@param program program which contains the specified symbol instance
@param symbol symbol to look for
@param otherProgram other program
@param allowInferredMatch if true an inferred match may be performed using reference
correlation (NOTE: reference correlation is only possible if the exact same binary
is in use).  This option is ignored if the two programs do not have the same 
original binary hash. 
@param otherRestrictedSymbolIds an optional set of symbol ID's from the other program
which will be treated as the exclusive set of candidate symbols to consider.
@return corresponding external symbol for otherProgram or null if no such symbol exists.
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/SimpleDiffUtility.java