private void processDynamicPLTGOT(ElfDynamicType pltGotType, ElfDynamicType pltGotRelType,
			TaskMonitor monitor) throws CancelledException {

		ElfDynamicTable dynamicTable = elf.getDynamicTable();
		if (dynamicTable == null || !dynamicTable.containsDynamicValue(pltGotType) ||
			!dynamicTable.containsDynamicValue(pltGotRelType)) {
			return;
		}

		AddressSpace defaultSpace = program.getAddressFactory().getDefaultAddressSpace();
		long imageBaseAdj = elfLoadHelper.getImageBaseWordAdjustmentOffset();

		try {
			long relocTableAddr =
				elf.adjustAddressForPrelink(dynamicTable.getDynamicValue(pltGotRelType));

			ElfProgramHeader relocTableLoadHeader =
				elf.getProgramLoadHeaderContaining(relocTableAddr);
			if (relocTableLoadHeader == null || relocTableLoadHeader.isInvalidOffset()) {
				return;
			}
			long relocTableOffset = relocTableLoadHeader.getOffset(relocTableAddr);
			ElfRelocationTable relocationTable = elf.getRelocationTableAtOffset(relocTableOffset);
			if (relocationTable == null) {
				return;
			}

			// External dynamic symbol entries in the GOT, if any, will be placed
			// after any local symbol entries.  Local entries are assumed to have original 
			// bytes of zero, whereas non-local entries will refer to the PLT

			// While the dynamic value for pltGotType (e.g., DT_PLTGOT) identifies the start of 
			// dynamic GOT table it does not specify its length.  The associated relocation
			// table, identified by the dynamic value for pltGotRelType, will have a relocation
			// record for each PLT entry linked via the GOT.  The number of relocations matches
			// the number of PLT entries and the one with the greatest offset correspionds
			// to the last GOT entry.  Unfortuntely, the length of each PLT entry and initial
			// PLT head is unknown.  If the binary has not placed external symbols within the PLT
			// processing and disassembly of the PLT may be skipped.

			long pltgot = elf.adjustAddressForPrelink(
				dynamicTable.getDynamicValue(pltGotType));
			Address gotStart = defaultSpace.getAddress(pltgot + imageBaseAdj);

			ElfRelocation[] relocations = relocationTable.getRelocations();
			ElfSymbolTable associatedSymbolTable = relocationTable.getAssociatedSymbolTable();
			if (associatedSymbolTable == null) {
				return;
			}

			// Create ordered list of PLTGOT symbols based upon offset with GOT.
			// It assumed that the PLT entry sequence will match this list.
			ElfSymbol[] symbols = associatedSymbolTable.getSymbols();
			List<PltGotSymbol> pltGotSymbols = new ArrayList<>();
			for (ElfRelocation reloc : relocations) {
				pltGotSymbols
						.add(new PltGotSymbol(symbols[reloc.getSymbolIndex()], reloc.getOffset()));
			}
			Collections.sort(pltGotSymbols);

			// Identify end of GOT table based upon relocation offsets
			long maxGotOffset = pltGotSymbols.get(pltGotSymbols.size() - 1).offset;
			Address gotEnd = defaultSpace.getAddress(maxGotOffset + imageBaseAdj);

			processGOT(gotStart, gotEnd, monitor);

			//
			// Examine the first two GOT entries which correspond to the relocations (i.e., pltGotSymbols).
			// An adjusted address from the original bytes is computed.  These will point into the PLT.  
			// These two pointers will either refer to the same address (i.e., PLT head) or different 
			// addresses which correspond to the first two PLT entries.  While likely offcut into each PLT 
			// entry, the differing PLT addresses can be used to identify the PLT entry size/spacing but 
			// not the top of PLT.  If symbols are present within the PLT for each entry, they may 
			// be used to identify the PLT entry size/spacing and will be converted to external symbols.
			// 

			long pltEntryCount = pltGotSymbols.size();

			// Get original bytes, converted to addresses, for first two PLT/GOT symbols
			Address pltAddr1 = null;
			Address pltAddr2 = null;
			for (PltGotSymbol pltGotSym : pltGotSymbols) {
				Address gotEntryAddr = defaultSpace.getAddress(pltGotSym.offset + imageBaseAdj);
				long originalGotEntry = elfLoadHelper.getOriginalValue(gotEntryAddr, true);
				if (originalGotEntry == 0) {
					return; // unexpected original bytes for PLTGOT entry - skip PLT processing
				}
				if (pltAddr1 == null) {
					pltAddr1 = defaultSpace.getAddress(originalGotEntry + imageBaseAdj);
				}
				else {
					pltAddr2 = defaultSpace.getAddress(originalGotEntry + imageBaseAdj);
					break;
				}
			}
			if (pltAddr2 == null) {
				return; // unable to find two GOT entries which refer to PLT - skip PLT processing
			}

			// NOTE: This approach assumes that all PLT entries have the same structure (i.e., instruction sequence)
			long pltSpacing = pltAddr2.subtract(pltAddr1);
			if (pltSpacing < 0 || pltSpacing > MAX_SUPPORTED_PLT_ENTRY_SIZE ||
				(pltSpacing % 2) != 0) {
				return; // unsupported PLT entry size - skip PLT processing
			}

			Address minSymbolSearchAddress;
			long symbolSearchSpacing; // nominal PLT entry size for computing maxSymbolSearchAddress

			Address firstPltEntryAddr = null; // may be offcut within first PLT entry

			if (pltSpacing == 0) { // Entries have same original bytes which refer to PLT head
				Function pltHeadFunc = elfLoadHelper.createOneByteFunction(null, pltAddr1, false);
				if (pltHeadFunc.getSymbol().getSource() == SourceType.DEFAULT) {
					try {
						pltHeadFunc.setName(PLT_HEAD_SYMBOL_NAME, SourceType.ANALYSIS);
					}
					catch (DuplicateNameException | InvalidInputException e) {
						// Ignore - unexpected
					}
				}

				// PLT spacing is not known.  pltAddr1 is PLT head
				minSymbolSearchAddress = pltAddr1.next();

				// Use conservative PLT entry size when computing address limit for PLT symbol search.
				// For a PLT with an actual entry size of 16 this will reduce the scan to less than half 
				// of the PLT.  This should only present an issue for very small PLTs or those 
				// with sparsely placed symbols.
				symbolSearchSpacing = MIN_SUPPORTED_PLT_ENTRY_SIZE;
			}
			else {
				// PLT spacing is known, but start of entry and head is not known.  pltAddr1 points to middle of first PLT entry (not head).
				firstPltEntryAddr = pltAddr1;
				minSymbolSearchAddress = pltAddr1.subtract(pltSpacing - 1); // try to avoid picking up symbol which may be at head
				symbolSearchSpacing = pltSpacing;
			}

			// Attempt to find symbols located within the PLT.
			Address maxSymbolSearchAddress =
				minSymbolSearchAddress.add(pltEntryCount * symbolSearchSpacing);

			// Scan symbols within PLT; helps to identify start of first entry and PLT entry size/spacing if unknown
			Symbol firstSymbol = null;
			Symbol lastSymbol = null;
			long discoveredPltSpacing = Long.MAX_VALUE;
			Map<Long, Integer> spacingCounts = new HashMap<>();
			for (Symbol sym : elfLoadHelper.getProgram()
					.getSymbolTable()
					.getSymbolIterator(minSymbolSearchAddress, true)) {
				if (sym.getSource() == SourceType.DEFAULT) {
					continue;
				}
				Address addr = sym.getAddress();
				if (addr.compareTo(maxSymbolSearchAddress) > 0) {
					break;
				}
				if (firstSymbol == null) {
					firstSymbol = sym;
				}
				if (pltSpacing == 0) {
					// Collect spacing samples if PLT spacing is unknown
					if (lastSymbol != null) {
						long spacing = addr.subtract(lastSymbol.getAddress());
						if (spacing > MAX_SUPPORTED_PLT_ENTRY_SIZE) {
							lastSymbol = null; // reset on large symbol spacing
							continue;
						}
						int count =
							spacingCounts.compute(spacing, (k, v) -> (v == null) ? 1 : v + 1);
						discoveredPltSpacing = Math.min(discoveredPltSpacing, spacing);
						if (count == PLT_SYMBOL_SAMPLE_COUNT_THRESHOLD) {
							break; // stop on 10 occurances of the same spacing (rather arbitrary sample limit)
						}
					}
					lastSymbol = sym;
				}
			}

			if (pltSpacing == 0) {
				if (discoveredPltSpacing == Long.MAX_VALUE ||
					spacingCounts.get(discoveredPltSpacing) == 1) { // NOTE: required number of symbol-spacing samples could be increased from 1
					return; // PLT spacing not determined / too large or insufficient PLT symbols - skip PLT processing
				}
				pltSpacing = discoveredPltSpacing;
			}

			if (firstSymbol != null) {
				// use PLT symbol if found to identify start of first PLT entry
				int firstSymbolEntryIndex = -1;
				Address firstSymbolAddr = firstSymbol.getAddress();
				int entryIndex = 0;
				for (PltGotSymbol entrySymbol : pltGotSymbols) {
					if (firstSymbolAddr
							.equals(elfLoadHelper.getElfSymbolAddress(entrySymbol.elfSymbol))) {
						firstSymbolEntryIndex = entryIndex;
						break;
					}
					++entryIndex;
				}
				if (firstSymbolEntryIndex >= 0) {
					firstPltEntryAddr = firstSymbolAddr;
					if (firstSymbolEntryIndex > 0) {
						firstPltEntryAddr =
							firstPltEntryAddr.subtract(firstSymbolEntryIndex * pltSpacing);
					}
				}
			}

			if (firstPltEntryAddr == null) {
				return; // failed to identify first PLT entry - skip PLT processing
			}

			Address pltEnd = firstPltEntryAddr.add(pltSpacing * (pltEntryCount - 1));
			processLinkageTable("PLT", firstPltEntryAddr, pltEnd, monitor);
		}
		catch (Exception e) {
			String msg = "Failed to process " + pltGotType + ": " + e.getMessage();
			log(msg);
			Msg.error(this, msg, e);
		}
	}
Process GOT and associated PLT based upon specified dynamic table entries.
The primary goal is to identify the bounds of the GOT and PLT and process
any external symbols which may be defined within the PLT.  Processing of PLT
is only critical if it contains external symbols which must be processed, otherwise
they will likely resolve adequately during subsequent analysis.
@param pltGotType dynamic type for dynamic PLTGOT lookup (identifies dynamic PLTGOT)
@param pltGotRelType dynamic type for associated dynamic JMPREL lookup (identifies dynamic PLTGOT relocation table)
@param monitor task monitor
@throws CancelledException thrown if task cancelled
Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java