diff options
| -rw-r--r-- | na/arch/riscv/relocate.cc | 100 | ||||
| -rw-r--r-- | na/include/lume/relocate.h | 53 |
2 files changed, 153 insertions, 0 deletions
diff --git a/na/arch/riscv/relocate.cc b/na/arch/riscv/relocate.cc new file mode 100644 index 0000000..0216827 --- /dev/null +++ b/na/arch/riscv/relocate.cc @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#include <lume/relocate.h> + +static inline bool range_ok(uint64 offset, uint64 size, uint64 image_size) +{ + if (offset > image_size) { + return false; + } + if (size > image_size - offset) { + return false; + } + return true; +} + +RelocStatus relocate_apply(uint64 load_base, uint64 image_size, + uint64 link_base, const void* reloc_blob, + uint64 reloc_size) +{ + if (!reloc_blob || reloc_size == 0) { + return RELOC_OK; + } + if (reloc_size < sizeof(RelocTableHeader)) { + return RELOC_ERR_SIZE; + } + + const auto* hdr = reinterpret_cast<const RelocTableHeader*>(reloc_blob); + if (hdr->magic != kRelocTableMagic) { + return RELOC_ERR_MAGIC; + } + if (hdr->version != kRelocTableVersion) { + return RELOC_ERR_VERSION; + } + if (hdr->arch != RELOC_ARCH_RISCV) { + return RELOC_ERR_ARCH; + } + if (hdr->entry_size < sizeof(RelocEntry) || (hdr->entry_size % 8) != 0) { + return RELOC_ERR_SIZE; + } + + uint64 max_entries = (reloc_size - sizeof(RelocTableHeader)) / + static_cast<uint64>(hdr->entry_size); + if (hdr->entry_count > max_entries) { + return RELOC_ERR_SIZE; + } + + if (image_size < sizeof(uint64)) { + return RELOC_ERR_RANGE; + } + + const uint64 delta = load_base - link_base; + const uint8* p = reinterpret_cast<const uint8*>(reloc_blob) + + sizeof(RelocTableHeader); + + for (uint32 i = 0; i < hdr->entry_count; ++i) { + const auto* ent = reinterpret_cast<const RelocEntry*>(p); + if ((ent->offset & 0x7) != 0 || + !range_ok(ent->offset, sizeof(uint64), image_size)) { + return RELOC_ERR_RANGE; + } + + uint64* target = reinterpret_cast<uint64*>(load_base + ent->offset); + switch (ent->type) { + case RELOC_RISCV_RELATIVE: + *target = load_base + ent->addend; + break; + case RELOC_RISCV_64: + *target = ent->addend + delta; + break; + default: + return RELOC_ERR_UNSUPPORTED; + } + p += hdr->entry_size; + } + + return RELOC_OK; +} + +const char* reloc_status_str(RelocStatus st) +{ + switch (st) { + case RELOC_OK: + return "ok"; + case RELOC_ERR_MAGIC: + return "bad magic"; + case RELOC_ERR_VERSION: + return "bad version"; + case RELOC_ERR_ARCH: + return "arch mismatch"; + case RELOC_ERR_SIZE: + return "size mismatch"; + case RELOC_ERR_RANGE: + return "range"; + case RELOC_ERR_UNSUPPORTED: + return "unsupported"; + default: + return "unknown"; + } +} diff --git a/na/include/lume/relocate.h b/na/include/lume/relocate.h new file mode 100644 index 0000000..120824f --- /dev/null +++ b/na/include/lume/relocate.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +#pragma once + +#include <lume/types.h> + +inline constexpr uint64 kRelocTableMagic = 0x4C554D45524C4F43ULL; /* LUMERLOC */ +inline constexpr uint16 kRelocTableVersion = 1; + +enum RelocArch : uint16 { + RELOC_ARCH_RISCV = 1, + RELOC_ARCH_AARCH64 = 2, + RELOC_ARCH_X86_64 = 3, +}; + +enum RelocType : uint32 { + RELOC_RISCV_RELATIVE = 1, + RELOC_RISCV_64 = 2, +}; + +struct RelocTableHeader { + uint64 magic; + uint16 version; + uint16 arch; + uint32 entry_size; + uint32 entry_count; + uint32 reserved; +}; + +struct RelocEntry { + uint64 offset; + uint64 addend; + uint32 type; + uint32 reserved; +}; + +enum RelocStatus : uint32 { + RELOC_OK = 0, + RELOC_ERR_MAGIC, + RELOC_ERR_VERSION, + RELOC_ERR_ARCH, + RELOC_ERR_SIZE, + RELOC_ERR_RANGE, + RELOC_ERR_UNSUPPORTED, +}; + +/* Apply relocations to the image loaded at load_base. */ +RelocStatus relocate_apply(uint64 load_base, uint64 image_size, + uint64 link_base, const void* reloc_blob, + uint64 reloc_size); + +const char* reloc_status_str(RelocStatus st); |
