diff --git a/modules/loader/include/loader/relocation_types.h b/modules/loader/include/loader/relocation_types.h index 4a89dc3e9f..e9930f001a 100644 --- a/modules/loader/include/loader/relocation_types.h +++ b/modules/loader/include/loader/relocation_types.h @@ -472,6 +472,7 @@ enum Type : uint32_t { R_RISCV_NONE = 0, R_RISCV_64 = 2, R_RISCV_BRANCH = 16, + R_RISCV_JAL = 17, R_RISCV_CALL = 18, R_RISCV_CALL_PLT = 19, R_RISCV_PCREL_HI20 = 23, @@ -482,6 +483,7 @@ enum Type : uint32_t { R_RISCV_ADD32 = 35, R_RISCV_SUB32 = 39, R_RISCV_ALIGN = 43, + R_RISCV_RVC_BRANCH = 44, R_RISCV_RVC_JUMP = 45, }; } // namespace RISCV diff --git a/modules/loader/source/relocations.cpp b/modules/loader/source/relocations.cpp index a300532e77..cad18ef035 100644 --- a/modules/loader/source/relocations.cpp +++ b/modules/loader/source/relocations.cpp @@ -878,6 +878,39 @@ bool resolveRISCV(const loader::Relocation &r, loader::ElfMap &map, break; } + case R_RISCV_JAL: { + // R_RISCV_JAL immediate is for J-Type instructions and is split + // across 20 bits spread across the instruction and skips the bottom bit + // of the input relative offset. See conditional branches in the isa spec. + + // Check that the value, when sign-extended back up to a 64-bit number + // will not lose bits. + if (signExtendN(relative_value, 21) != + static_cast(relative_value & (~1))) { +#ifndef NDEBUG + auto name = + map.getSymbolName(r.symbol_index).value_or(""); + (void)fprintf(stderr, + "Error: relocation R_RISCV_BRANCH branch > 13 bits or " + "not even for symbol '%.*s'\n", + (int)name.size(), name.data()); +#endif + return false; + } + uint32_t value; + const uint32_t trunc_value = static_cast(relative_value); + cargo::read_little_endian(&value, relocation_address); + // imm bits 1-10 at bit 21 + value = setBitRange(value, trunc_value >> 1, 21, 30); + // imm bit 11 at bit 22 + value = setBitRange(value, trunc_value >> 11, 22, 22); + // imm bits 12-19 at bit 12 + value = setBitRange(value, trunc_value >> 12, 12, 19); + // imm bit 20 at bit 31 + value = setBitRange(value, trunc_value >> 20, 31, 31); + cargo::write_little_endian(value, relocation_address); + break; + } case R_RISCV_RVC_JUMP: { // R_RISCV_RVC_JUMP is for compressed instructions and fits in bits 2-12 // of a 16 bit value and skips the bottom bit of the input. See CJ format @@ -905,6 +938,34 @@ bool resolveRISCV(const loader::Relocation &r, loader::ElfMap &map, cargo::write_little_endian(value, relocation_address); break; } + case R_RISCV_RVC_BRANCH: { + // R_RISCV_RVC_BRANCH is for compressed instructions and fits in bits 2-6 + // (low) and 10-12 of a 16 bit value and skips the bottom bit of the + // input, requiring a total of 8 bits. See CB format in the isa spec. + + // Check that the value, when sign-extended back up to a 64-bit number + // will not lose bits. + if (signExtendN(relative_value, 9) != + static_cast(relative_value & (~1))) { +#ifndef NDEBUG + auto name = + map.getSymbolName(r.symbol_index).value_or(""); + (void)fprintf(stderr, + "Error: relocation R_RISCV_RVC_JUMP > 12 bits or not " + "even for symbol '%.*s'\n", + (int)name.size(), name.data()); +#endif + return false; + } + + uint16_t value; + cargo::read_little_endian(&value, relocation_address); + const uint16_t offset_16 = static_cast(relative_value) >> 1; + value = setBitRange(value, getBitRange(offset_16, 0, 4), 1, 6); + value = setBitRange(value, getBitRange(offset_16, 5, 7), 10, 12); + cargo::write_little_endian(value, relocation_address); + break; + } case R_RISCV_ADD32: { // 32-bit label addition: V + S + A