public static BidiMap<MachoPrelinkMap, Long> matchPrelinkToMachoHeaderOffsets(ByteProvider provider,
			List<MachoPrelinkMap> prelinkList, List<Long> machoHeaderOffsets, TaskMonitor monitor)
			throws MachException, IOException {

		monitor.setMessage("Matching PRELINK to Mach-O headers...");
		monitor.initialize(prelinkList.size());

		BidiMap<MachoPrelinkMap, Long> map = new DualHashBidiMap<>();

		// For pre-iOS 12, we can use the PrelinkExecutableLoadAddr field to match PrelinkMap
		// entries to Mach-O offsets.  For iOS 12, PrelinkExecutableLoadAddr is gone so we use
		// the new ModuleIndex field instead. 
		long maxModuleIndex =
			prelinkList.stream().mapToLong(info -> info.getPrelinkModuleIndex()).max().orElse(-1);
		if (maxModuleIndex >= 0) {
			Msg.debug(MachoPrelinkUtils.class, String.format(
				"Using ModuleIndex to find Mach-O offsets (%d module indexes, %d indexed modules found)",
				maxModuleIndex + 1, machoHeaderOffsets.size()));
			if (maxModuleIndex + 1 != machoHeaderOffsets.size()) {
				Msg.warn(MachoPrelinkUtils.class,
					String.format(
						"Maximum ModuleIndex is not consistent with # of modules found! (%d vs %d)",
						maxModuleIndex + 1, machoHeaderOffsets.size()));
			}

			for (MachoPrelinkMap info : prelinkList) {
				if (monitor.isCancelled()) {
					break;
				}
				monitor.incrementProgress(1);

				int machoOffsetIndex = (int) info.getPrelinkModuleIndex();
				if (machoOffsetIndex != -1 && machoOffsetIndex < machoHeaderOffsets.size()) {
					long machoHeaderOffset = machoHeaderOffsets.get(machoOffsetIndex);
					map.put(info, machoHeaderOffset);
				}
			}

		}
		else {
			Msg.debug(MachoPrelinkUtils.class,
				String.format(
					"Using PrelinkExecutableLoadAddr to find Mach-O offsets (%d modules found)",
					machoHeaderOffsets.size()));

			MachHeader machoHeader = new MachHeader(provider, 0, true);
			machoHeader.parse();
			long prelinkStart = MachoPrelinkUtils.getPrelinkStartAddr(machoHeader);
			for (MachoPrelinkMap info : prelinkList) {
				if (monitor.isCancelled()) {
					break;
				}
				monitor.incrementProgress(1);

				map.put(info,
					info.getPrelinkExecutableLoadAddr() - prelinkStart + machoHeaderOffsets.get(0));

			}
		}

		return map;
	}
Forms a bidirectional mapping of PRELINK XML to Mach-O header offset in the given provider.

@param provider The PRELINK Mach-O provider.
@param prelinkList A list of {@link MachoPrelinkMap}s.
@param machoHeaderOffsets A list of provider offsets where PRELINK Mach-O headers start (not 
  including the "System" Mach-O at offset 0).
@param monitor A monitor
@return A bidirectional mapping of PRELINK XML to Mach-O header offset in the given provider.
@throws MachException If there was a problem parsing a Mach-O header.
@throws IOException If there was an IO-related issue mapping PRELINK XML to Mach-O headers.
Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoPrelinkUtils.java