private long fixupOPDSymbolValue(ElfRelocationContext<?> elfRelocationContext, ElfSymbol sym)
			throws MemoryAccessException {
		Address addr = elfRelocationContext.getSymbolAddress(sym);
		if (addr == null) {
			return 0;
		}
		Program program = elfRelocationContext.getProgram();
		MemoryBlock block = program.getMemory().getBlock(addr);
		if (block == null || !".opd".equals(block.getName())) {
			return addr.getOffset();
		}
		// .opd symbols will get moved to the real function by the extension (see processFunctionDescriptors)
		// Call stubs should always use the .opd symbol value and not the function address so we can - this
		// distinction can only be made using the relocation type.
		byte[] bytes = new byte[8];
		block.getBytes(addr, bytes);
		boolean bigEndian = elfRelocationContext.getElfHeader().isBigEndian();
		DataConverter dataConverter =
			bigEndian ? BigEndianDataConverter.INSTANCE : LittleEndianDataConverter.INSTANCE;
		return dataConverter.getLong(bytes);
	}
This method generates a symbol value with possible substitution for those
symbols residing within the .opd to refer to the real function instead.
Care must be taken not to invoke this method for relocations which may be
applied to call stubs. It is also important that relocations have already
been applied to the .opd section since we will be using its data for
locating the real function.
@param elfRelocationContext ELF relocation context
@param sym ELF relocation symbol
@return symbol value
@throws MemoryAccessException if memory access error occurs
Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/relocation/PowerPC64_ElfRelocationHandler.java