@Override
	protected RelocationResult relocate(RISCV_ElfRelocationContext elfRelocationContext,
			ElfRelocation relocation, RISCV_ElfRelocationType type, Address relocationAddress,
			ElfSymbol sym, Address symbolAddr, long symbolValue, String symbolName)
			throws MemoryAccessException {

		if (!relocation.hasAddend()) {
			// Implementation only supports Elf_Rela relocations for RISCV
			return RelocationResult.UNSUPPORTED;
		}

		ElfHeader elf = elfRelocationContext.getElfHeader();
		Program program = elfRelocationContext.getProgram();
		Memory memory = program.getMemory();

		long addend = relocation.getAddend();
		long offset = relocationAddress.getOffset();
		long base = elfRelocationContext.getImageBaseWordAdjustmentOffset();

		int symbolIndex = relocation.getSymbolIndex();
		
		long value64 = 0;
		int value32 = 0;
		short value16 = 0;
		byte value8 = 0;
		int target = 0;

		int byteLength = 4; // most relocations affect 4-bytes (change if different)
		
		// Handle relative relocations that do not require symbolAddr or symbolValue 
		switch (type) {

			case R_RISCV_RELATIVE:
				// Runtime relocation word32,64 = B + A
				if (elf.is32Bit()) {
					value32 = (int) (base + addend);
					memory.setInt(relocationAddress, value32);
				}
				else {
					value64 = base + addend;
					memory.setLong(relocationAddress, value64);
					byteLength = 8;
				}
				return new RelocationResult(Status.APPLIED, byteLength);
				
			case R_RISCV_PCREL_LO12_S:
				// PC-relative reference %pcrel_lo(symbol) (S-Type)
				//    S-type immediates split the 12 bit value into separate 7 bit and 5 bit fields.
				// Warning: untested!
				target = getSymbolValueIndirect(elfRelocationContext, sym,
					relocationAddress.getOffset() - relocation.getOffset());
				if (target == 0) {
					markAsError(program, relocationAddress, type, symbolName, symbolIndex,
						"Failed to locate HI20 relocation", elfRelocationContext.getLog());
					return RelocationResult.FAILURE;
				}
				value32 = ((target & 0x000007f) << 25) | (target & 0x00000f80) |
					(memory.getInt(relocationAddress) & 0x1fff07f);
				memory.setInt(relocationAddress, value32);
				return new RelocationResult(Status.APPLIED, byteLength);
				
			case R_RISCV_PCREL_LO12_I:
				// PC-relative reference %pcrel_lo(symbol) (I-Type), relative to the cited pc_rel_hi20
				target = getSymbolValueIndirect(elfRelocationContext, sym,
					relocationAddress.getOffset() - relocation.getOffset());
				if (target == 0) {
					markAsError(program, relocationAddress, type, symbolName, symbolIndex,
						"Failed to locate HI20 relocation", elfRelocationContext.getLog());
					return RelocationResult.FAILURE;
				}
				value32 =
					((target & 0x00000fff) << 20) | (memory.getInt(relocationAddress) & 0xfffff);
				memory.setInt(relocationAddress, value32);
				return new RelocationResult(Status.APPLIED, byteLength);
				
			case R_RISCV_COPY:
				// Runtime relocation must be in executable. not allowed in shared library
				markAsUnsupportedCopy(program, relocationAddress, type, symbolName, symbolIndex,
					sym.getSize(), elfRelocationContext.getLog());
				return RelocationResult.UNSUPPORTED;
			
			default:
				break;
		}
		
		// Check for unresolved symbolAddr and symbolValue required by remaining relocation types handled below
		if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) {
			return RelocationResult.FAILURE;
		}	

		switch (type) {
			case R_RISCV_32:
				// Runtime relocation word32 = S + A
				value32 = (int) (symbolValue + addend);
				memory.setInt(relocationAddress, value32);
				if (symbolIndex != 0 && addend != 0 && !sym.isSection()) {
					warnExternalOffsetRelocation(program, relocationAddress, symbolAddr, symbolName,
						addend, elfRelocationContext.getLog());
					if (elf.is32Bit()) {
						applyComponentOffsetPointer(program, relocationAddress, addend);
					}
				}
				break;

			case R_RISCV_64:
				// Runtime relocation word64 = S + A
				value64 = symbolValue + addend;
				memory.setLong(relocationAddress, value64);
				byteLength = 8;
				if (symbolIndex != 0 && addend != 0 && !sym.isSection()) {
					warnExternalOffsetRelocation(program, relocationAddress, symbolAddr, symbolName,
						addend, elfRelocationContext.getLog());
					if (elf.is64Bit()) {
						applyComponentOffsetPointer(program, relocationAddress, addend);
					}
				}
				break;

			case R_RISCV_JUMP_SLOT:
				// Runtime relocation word32,64 = S ;handled by PLT unless LD_BIND_NOW
				if (elf.is32Bit()) {
					value32 = (int) (symbolValue);
					memory.setInt(relocationAddress, value32);
				}
				else {
					value64 = symbolValue;
					memory.setLong(relocationAddress, value64);
					byteLength = 8;
				}
				break;

//			case R_RISCV_TLS_DTPMOD32: // TLS relocation word32 = S->TLSINDEX
//
//			case R_RISCV_TLS_DTPMOD64: // TLS relocation word64 = S->TLSINDEX
//
//			case R_RISCV_TLS_DTPREL32: // TLS relocation word32 = TLS + S + A - TLS_TP_OFFSET
//
//			case R_RISCV_TLS_DTPREL64: // TLS relocation word64 = TLS + S + A - TLS_TP_OFFSET
//
//			case R_RISCV_TLS_TPREL32:  // TLS relocation word32 = TLS + S + A + S_TLS_OFFSET - TLS_DTV_OFFSET
//
//			case R_RISCV_TLS_TPREL64:  // TLS relocation word64 = TLS + S + A + S_TLS_OFFSET - TLS_DTV_OFFSET

			case R_RISCV_BRANCH:
				// PC-relative branch (SB-Type)
				target = (int) (addend + symbolValue - offset);
				value32 = encodeSBTypeImm(target) |
					(memory.getInt(relocationAddress) & ~encodeSBTypeImm(-1));
				memory.setInt(relocationAddress, value32);
				break;

			case R_RISCV_JAL:
				// PC-relative jump (UJ-Type)
				target = (int) (addend + symbolValue - offset);
				value32 = encodeUJTypeImm(target) |
					(memory.getInt(relocationAddress) & ~encodeUJTypeImm(-1));
				memory.setInt(relocationAddress, value32);
				break;

			case R_RISCV_CALL:
				// PC-relative call (PLT) MACRO call,tail (auipc+jalr pair) PIC
				// Identical processing in Ghidra as the following

			case R_RISCV_CALL_PLT:
				// PC-relative call MACRO call,tail (auipc+jalr pair)
				target = (int) (addend + symbolValue - offset);
				memory.setInt(relocationAddress,
					getHi20(target) | (memory.getInt(relocationAddress) & 0xfff));
				memory.setInt(relocationAddress.add(4),
					(getLo12(target) << 20) | (memory.getInt(relocationAddress.add(4)) & 0xfffff));
				byteLength = 8;
				break;

//			case R_RISCV_TLS_GOT_HI20: // PC-relative TLS IE GOT offset MACRO la.tls.ie
//				
//			case R_RISCV_TLS_GD_HI20:  // PC-relative TLS GD reference MACRO la.tls.gd

			case R_RISCV_GOT_HI20:
				// PC-relative GOT reference MACRO la
			case R_RISCV_PCREL_HI20:
				// PC-relative, not tested on 32 bit objects
				target = (int) (addend + symbolValue - offset);
				memory.setInt(relocationAddress,
					getHi20(target) | (memory.getInt(relocationAddress) & 0xfff));
				break;

			case R_RISCV_HI20:
				// Absolute address %hi(symbol) (U-Type)
				value32 = (int) ((symbolValue + 0x800) & 0xfffff000) |
					(memory.getInt(relocationAddress) & 0xfff);
				memory.setInt(relocationAddress, value32);
				break;

			case R_RISCV_LO12_I:
				// Absolute address %lo(symbol) (I-Type)
				value32 = ((int) (symbolValue & 0x00000fff) << 20) |
					(memory.getInt(relocationAddress) & 0xfffff);
				memory.setInt(relocationAddress, value32);
				break;

			case R_RISCV_LO12_S:
				// Absolute address %lo(symbol) (S-Type)
				value32 = (int) (symbolValue & 0x00000fff);
				value32 = ((value32 & 0x1f) << 7) | ((value32 & 0xfe0) << 20) |
					(memory.getInt(relocationAddress) & 0x1fff07f);
				memory.setInt(relocationAddress, value32);
				break;

//			case R_RISCV_TPREL_HI20: // TLS LE thread offset %tprel_hi(symbol) (U-Type)
//
//			case R_RISCV_TPREL_LO12_I: // TLS LE thread offset %tprel_lo(symbol) (I-Type)
//
//			case R_RISCV_TPREL_LO12_S: // TLS LE thread offset %tprel_lo(symbol) (S-Type)
//
//			case R_RISCV_TPREL_ADD: // TLS LE thread usage %tprel_add(symbol)

			case R_RISCV_ADD8:
				// 8-bit label addition word8 = old + S + A
				value8 = memory.getByte(relocationAddress);
				value8 += (byte) (symbolValue + addend);
				memory.setByte(relocationAddress, value8);
				byteLength = 1;
				break;

			case R_RISCV_ADD16:
				// 16-bit label addition word16 = old + S + A
				value16 = memory.getShort(relocationAddress);
				value16 += (short) (symbolValue + addend);
				memory.setShort(relocationAddress, value16);
				byteLength = 2;
				break;

			case R_RISCV_ADD32:
				// 32-bit label addition word32 = old + S + A
				value32 = memory.getInt(relocationAddress);
				value32 += (int) (symbolValue + addend);
				memory.setInt(relocationAddress, value32);
				break;

			case R_RISCV_ADD64:
				// 64-bit label addition word64 = old + S + A
				value64 = memory.getLong(relocationAddress);
				value64 += (symbolValue + addend);
				memory.setLong(relocationAddress, value64);
				byteLength = 8;
				break;

			case R_RISCV_SUB8:
				// 8-bit label subtraction word8 = old - S - A
				value8 = memory.getByte(relocationAddress);
				value8 -= (byte) (symbolValue + addend);
				memory.setByte(relocationAddress, value8);
				byteLength = 1;
				break;

			case R_RISCV_SUB16:
				// 16-bit label subtraction word16 = old - S - A
				value16 = memory.getShort(relocationAddress);
				value16 -= (short) (symbolValue + addend);
				memory.setShort(relocationAddress, value16);
				byteLength = 2;
				break;

			case R_RISCV_SUB32:
				// 32-bit label subtraction word32 = old - S - A
				value32 = memory.getInt(relocationAddress);
				value32 -= (int) (symbolValue + addend);
				memory.setInt(relocationAddress, value32);
				break;

			case R_RISCV_SUB64:
				// 64-bit label subtraction word64 = old - S - A
				value64 = memory.getLong(relocationAddress);
				value64 -= (symbolValue + addend);
				memory.setLong(relocationAddress, value64);
				byteLength = 8;
				break;

//			case R_RISCV_GNU_VTINHERIT: // GNU C++ vtable hierarchy 
//
//			case R_RISCV_GNU_VTENTRY: // GNU C++ vtable member usage 
//
//			case R_RISCV_ALIGN: // Alignment statement 

			case R_RISCV_RVC_BRANCH: {
				// PC-relative branch offset (CB-Type)
				short target_s = (short) (addend + symbolValue - offset);
				value16 = (short) (encodeCBTypeImm(target_s) |
					(memory.getShort(relocationAddress) & ~encodeCBTypeImm(-1)));
				memory.setShort(relocationAddress, value16);
				byteLength = 2;
				break;
			}

			case R_RISCV_RVC_JUMP: {
				// PC-relative jump offset (CJ-Type)
				short target_s = (short) (addend + symbolValue - offset);
				value16 = (short) (encodeCJTypeImm(target_s) |
					(memory.getShort(relocationAddress) & ~encodeCJTypeImm(-1)));
				memory.setShort(relocationAddress, value16);
				byteLength = 2;
				break;
			}

//			case R_RISCV_RVC_LUI: // Absolute address (CI-Type)
//
//			case R_RISCV_GPREL_I: // GP-relative reference (I-Type)
//
//			case R_RISCV_GPREL_S: // GP-relative reference (S-Type)
//
//			case R_RISCV_TPREL_I: // TP-relative TLS LE load (I-Type)
//
//			case R_RISCV_TPREL_S: // TP-relative TLS LE store (S-Type)

			case R_RISCV_RELAX:
				// Instruction pair can be relaxed by the linker/loader- ignore
				return RelocationResult.SKIPPED;

			case R_RISCV_SUB6:
				int loc8 = memory.getByte(relocationAddress);
				value8 = (byte) (symbolValue + addend);
				value8 = (byte) ((loc8 & 0xc0) | (((loc8 & 0x3f) - value8) & 0x3f));
				memory.setByte(relocationAddress, value8);
				byteLength = 1;
				break;

			case R_RISCV_SET6:
				loc8 = memory.getByte(relocationAddress);
				value8 = (byte) (symbolValue + addend);
				value8 = (byte) ((loc8 & 0xc0) | (value8 & 0x3f));
				memory.setByte(relocationAddress, value8);
				byteLength = 1;
				break;

			case R_RISCV_SET8:
				value8 = (byte) (symbolValue + addend);
				memory.setByte(relocationAddress, value8);
				byteLength = 1;
				break;

			case R_RISCV_SET16:
				value16 = (short) (symbolValue + addend);
				memory.setShort(relocationAddress, value8);
				byteLength = 2;
				break;

			case R_RISCV_32_PCREL:
			case R_RISCV_SET32:
				value32 = (int) (symbolValue + addend);
				memory.setInt(relocationAddress, value8);
				break;

			default:
				// 58-191 Reserved Reserved for future standard use
				// 192-255 Reserved Reserved for nonstandard ABI extensions
				markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
					elfRelocationContext.getLog());
				return RelocationResult.UNSUPPORTED;
		}
		return new RelocationResult(Status.APPLIED, byteLength);
	}
PC relative relocations like R_RISCV_PCREL_LO12_I find their target indirectly,
using their symbolValue to locate the address of the matching R_RISCV_PCREL_HI20
or R_RISCV_GOT_HI20.  The HI20 relocation's symbol identifies the actual target.
@param elfRelocationContext ELF relocation context for RISCV
@param hi20Symbol symbol which identifies a HI20 relocation offset
@param relocAddrOffsetAdj relocation offset adjustment to be used when computing
actual address offet within the target program.
@return the 32-bit HI20 relative offset value or 0 if HI20 relocation not found
Ghidra/Processors/RISCV/src/main/java/ghidra/app/util/bin/format/elf/relocation/RISCV_ElfRelocationHandler.java