[Git][ghc/ghc][wip/supersven/riscv64-ncg] Runtime linker LLVM style
Sven Tennie (@supersven)
gitlab at gitlab.haskell.org
Thu Feb 8 11:14:48 UTC 2024
Sven Tennie pushed to branch wip/supersven/riscv64-ncg at Glasgow Haskell Compiler / GHC
Commits:
512524a7 by Sven Tennie at 2024-02-08T12:13:55+01:00
Runtime linker LLVM style
- - - - -
4 changed files:
- + rts/linker/elf_plt_riscv64.c
- + rts/linker/elf_plt_riscv64.h
- + rts/linker/elf_reloc_riscv64.c
- + rts/linker/elf_reloc_riscv64.h
Changes:
=====================================
rts/linker/elf_plt_riscv64.c
=====================================
@@ -0,0 +1,57 @@
+#include "Rts.h"
+#include "elf_compat.h"
+#include "elf_plt_riscv64.h"
+
+#include <stdlib.h>
+
+#if defined(riscv64_HOST_ARCH)
+
+#if defined(OBJFORMAT_ELF)
+
+const size_t instSizeRISCV64 = 2;
+const size_t stubSizeRISCV64 = 5 * instSizeRISCV64;
+
+bool needStubForRelRISCV64(Elf_Rel * rel) {
+ switch(ELF64_R_TYPE(rel->r_info)) {
+ case R_RISCV_CALL_PLT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool needStubForRelaIRISCV64(Elf_Rela * rela) {
+ switch(ELF64_R_TYPE(rela->r_info)) {
+ case R_RISCV_CALL_PLT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// The stub is just a long jump to the target address.
+bool makeStubRISCV64(Stub * s) {
+ uint32_t *P = (uint32_t*)s->addr;
+
+ /* target address */
+ uint64_t addr = (uint64_t)s->target;
+
+ // LUI ip, %hi(addr)
+ uint32_t luiInst = 0x37; // opcode
+ luiInst |= 0x1f << 7; // rd = ip (x31)
+ luiInst |= ((addr >> 12) & 0xfffff) << 12; // imm[31:12]
+
+ // JALR x0, ip, %lo(addr)
+ uint32_t jalrInst = 0x67; // opcode
+ jalrInst |= 0x00 << 7; // rd = x0
+ jalrInst |= 0x1f << 15; // rs1 = ip (x31)
+ jalrInst |= (addr & 0xfff) << 20; // imm[11:0]
+
+ P[0] = luiInst;
+ P[1] = jalrInst;
+
+ return EXIT_SUCCESS;
+}
+#endif // OBJECTFORMAT_ELF
+
+#endif // riscv64_HOST_ARCH
=====================================
rts/linker/elf_plt_riscv64.h
=====================================
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "LinkerInternals.h"
+
+#if defined(OBJFORMAT_ELF)
+
+extern const size_t stubSizeRISCV64;
+bool needStubForRelRISCV64(Elf_Rel * rel);
+bool needStubForRelaRISCV64(Elf_Rela * rel);
+bool makeStubRISCV64(Stub * s);
+
+#endif
=====================================
rts/linker/elf_reloc_riscv64.c
=====================================
@@ -0,0 +1,349 @@
+#include "Rts.h"
+#include "elf.h"
+#include "elf_plt.h"
+#include "elf_reloc_riscv64.h"
+#include "elf_util.h"
+#include "rts/Messages.h"
+#include "util.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#if defined(riscv64_HOST_ARCH)
+
+#if defined(OBJFORMAT_ELF)
+
+#define Page(x) ((x) & ~0xFFF)
+
+typedef uint64_t addr_t;
+
+int64_t decodeAddendRISCV64(Section *section, Elf_Rel *rel) STG_NORETURN;
+bool encodeAddendRISCV64(Section *section, Elf_Rel *rel, int64_t addend);
+
+/* regular instructions are 32bit */
+typedef uint32_t inst_t;
+
+/* compressed instructions are 16bit */
+typedef uint16_t cinst_t;
+
+// TODO: Decide which functions should be static and/or inlined.
+int64_t decodeAddendRISCV64(Section *section STG_UNUSED,
+ Elf_Rel *rel STG_UNUSED) {
+ abort(/* we don't support Rel locations yet. */);
+}
+
+// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
+// Requires 0 < B <= 64.
+int64_t SignExtend64(uint64_t X, unsigned B) {
+ assert(B > 0 && "Bit width can't be 0.");
+ assert(B <= 64 && "Bit width out of range.");
+ return (int64_t)(X << (64 - B)) >> (64 - B);
+}
+
+// Make sure that V can be represented as an N bit signed integer.
+void checkInt(inst_t *loc, int64_t v, int n) {
+ if (v != SignExtend64(v, n))
+ debugBelch("Relocation at %x is out of range: 0x%lx - 0x%x", *loc, v, n);
+}
+// RISCV is little-endian by definition.
+void write8le(uint8_t *p, uint8_t v) { *p = v; }
+
+// RISCV is little-endian by definition.
+uint8_t read8le(const uint8_t *P) { return *P; }
+
+// RISCV is little-endian by definition.
+void write16le(cinst_t *p, uint16_t v) { *p = v; }
+
+// RISCV is little-endian by definition.
+uint16_t read16le(const cinst_t *P) { return *P; }
+
+// RISCV is little-endian by definition.
+uint32_t read32le(const inst_t *P) { return *P; }
+
+// RISCV is little-endian by definition.
+void write32le(inst_t *p, uint32_t v) { *p = v; }
+
+// RISCV is little-endian by definition.
+uint64_t read64le(const uint64_t *P) { return *P; }
+
+// RISCV is little-endian by definition.
+void write64le(uint64_t *p, uint64_t v) { *p = v; }
+
+uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
+ return (v & ((1ULL << (begin + 1)) - 1)) >> end;
+}
+
+uint32_t setLO12_I(uint32_t insn, uint32_t imm) {
+ return (insn & 0xfffff) | (imm << 20);
+}
+
+void setUType(inst_t *loc, uint32_t val) {
+ const unsigned bits = 64;
+ uint64_t hi = val + 0x800;
+ checkInt(loc, SignExtend64(hi, bits) >> 12, 20);
+ write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000));
+}
+
+void setIType(inst_t *loc, uint32_t val) {
+ uint64_t hi = (val + 0x800) >> 12;
+ uint64_t lo = val - (hi << 12);
+ write32le(loc, setLO12_I(read32le(loc), lo & 0xfff));
+}
+
+void setJType(inst_t *loc, uint32_t val) {
+ checkInt(loc, val, 21);
+
+ uint32_t insn = read32le(loc) & 0xFFF;
+ uint32_t imm20 = extractBits(val, 20, 20) << 31;
+ uint32_t imm10_1 = extractBits(val, 10, 1) << 21;
+ uint32_t imm11 = extractBits(val, 11, 11) << 20;
+ uint32_t imm19_12 = extractBits(val, 19, 12) << 12;
+ insn |= imm20 | imm10_1 | imm11 | imm19_12;
+
+ write32le(loc, insn);
+}
+
+void setBType(inst_t *loc, uint32_t val) {
+ checkInt(loc, val, 13);
+
+ uint32_t insn = read32le(loc) & 0x1FFF07F;
+ uint32_t imm12 = extractBits(val, 12, 12) << 31;
+ uint32_t imm10_5 = extractBits(val, 10, 5) << 25;
+ uint32_t imm4_1 = extractBits(val, 4, 1) << 8;
+ uint32_t imm11 = extractBits(val, 11, 11) << 7;
+ insn |= imm12 | imm10_5 | imm4_1 | imm11;
+
+ write32le(loc, insn);
+}
+
+void setCBType(cinst_t *loc, uint32_t val) {
+ checkInt((inst_t *)loc, val, 9);
+ uint16_t insn = read16le(loc) & 0xE383;
+ uint16_t imm8 = extractBits(val, 8, 8) << 12;
+ uint16_t imm4_3 = extractBits(val, 4, 3) << 10;
+ uint16_t imm7_6 = extractBits(val, 7, 6) << 5;
+ uint16_t imm2_1 = extractBits(val, 2, 1) << 3;
+ uint16_t imm5 = extractBits(val, 5, 5) << 2;
+ insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5;
+
+ write16le(loc, insn);
+}
+
+void setCJType(cinst_t *loc, uint32_t val) {
+ checkInt((inst_t *)loc, val, 12);
+ uint16_t insn = read16le(loc) & 0xE003;
+ uint16_t imm11 = extractBits(val, 11, 11) << 12;
+ uint16_t imm4 = extractBits(val, 4, 4) << 11;
+ uint16_t imm9_8 = extractBits(val, 9, 8) << 9;
+ uint16_t imm10 = extractBits(val, 10, 10) << 8;
+ uint16_t imm6 = extractBits(val, 6, 6) << 7;
+ uint16_t imm7 = extractBits(val, 7, 7) << 6;
+ uint16_t imm3_1 = extractBits(val, 3, 1) << 3;
+ uint16_t imm5 = extractBits(val, 5, 5) << 2;
+ insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5;
+
+ write16le(loc, insn);
+}
+
+bool encodeAddendRISCV64(Section *section, Elf_Rel *rel, int64_t addend) {
+ addr_t P = (addr_t)((uint8_t *)section->start + rel->r_offset);
+ int exp_shift = -1;
+ switch (ELF64_R_TYPE(rel->r_info)) {
+ case R_RISCV_GOT_HI20:
+ case R_RISCV_PCREL_HI20:
+ case R_RISCV_TLS_GD_HI20:
+ case R_RISCV_TLS_GOT_HI20:
+ case R_RISCV_TPREL_HI20:
+ case R_RISCV_HI20: {
+ setUType((inst_t *)P, addend);
+ break;
+ }
+ case R_RISCV_PCREL_LO12_I:
+ case R_RISCV_TPREL_LO12_I:
+ case R_RISCV_LO12_I: {
+ setIType((inst_t *)P, addend);
+ break;
+ }
+ case R_RISCV_RVC_JUMP: {
+ setCJType((cinst_t *)P, addend);
+ break;
+ }
+ case R_RISCV_RVC_BRANCH: {
+ setCBType((cinst_t *)P, addend);
+ break;
+ }
+ case R_RISCV_BRANCH: {
+ setBType((inst_t *)P, addend);
+ break;
+ }
+ case R_RISCV_CALL_PLT: {
+ setUType((inst_t *)P, addend);
+ break;
+ }
+ case R_RISCV_ADD8:
+ write8le((uint8_t *)P, read8le((uint8_t *)P) + addend);
+ break;
+ case R_RISCV_ADD16:
+ write16le((cinst_t *)P, read16le((cinst_t *)P) + addend);
+ break;
+ case R_RISCV_ADD32:
+ write32le((inst_t *)P, read32le((inst_t *)P) + addend);
+ break;
+ case R_RISCV_ADD64:
+ write64le((uint64_t *)P, read64le((uint64_t *)P) + addend);
+ break;
+ case R_RISCV_SUB6: {
+ uint8_t keep = *((uint8_t *)P) & 0xc0;
+ uint8_t imm = (((*(uint8_t *)P) & 0x3f) - addend) & 0x3f;
+
+ write8le((uint8_t *)P, keep | imm);
+ break;
+ }
+ case R_RISCV_SUB8:
+ write8le((uint8_t *)P, read8le((uint8_t *)P) - addend);
+ break;
+ case R_RISCV_SUB16:
+ write16le((cinst_t *)P, read16le((cinst_t *)P) - addend);
+ break;
+ case R_RISCV_SUB32:
+ write32le((inst_t *)P, read32le((inst_t *)P) - addend);
+ break;
+ case R_RISCV_SUB64:
+ write64le((uint64_t *)P, read64le((uint64_t *)P) - addend);
+ break;
+ case R_RISCV_RELAX:
+ case R_RISCV_ALIGN:
+ // I guess we don't need to implement these relaxations (optimizations).
+ break;
+ default:
+ abort();
+ }
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Compute the *new* addend for a relocation, given a pre-existing addend.
+ * @param section The section the relocation is in.
+ * @param rel The Relocation struct.
+ * @param symbol The target symbol.
+ * @param addend The existing addend. Either explicit or implicit.
+ * @return The new computed addend.
+ */
+static int64_t computeAddend(Section *section, Elf_Rel *rel, ElfSymbol *symbol,
+ int64_t addend) {
+
+ /* Position where something is relocated */
+ addr_t P = (addr_t)((uint8_t *)section->start + rel->r_offset);
+
+ CHECK(0x0 != P);
+ CHECK((uint64_t)section->start <= P);
+ CHECK(P <= (uint64_t)section->start + section->size);
+ /* Address of the symbol */
+ addr_t S = (addr_t)symbol->addr;
+ CHECK(0x0 != S);
+ /* GOT slot for the symbol */
+ addr_t GOT_S = (addr_t)symbol->got_addr;
+
+ int64_t A = addend;
+ switch (ELF64_R_TYPE(rel->r_info)) {
+ case R_RISCV_32:
+ return S + A;
+ case R_RISCV_64:
+ return S + A;
+ case R_RISCV_HI20:
+ return S + A;
+ case R_RISCV_JUMP_SLOT:
+ return S;
+ case R_RISCV_JAL:
+ return S + A - P;
+ case R_RISCV_PCREL_HI20:
+ return S + A - P;
+ case R_RISCV_LO12_I:
+ return S + A;
+ case R_RISCV_PCREL_LO12_I:
+ return S - P;
+ case R_RISCV_RVC_JUMP:
+ return S + A - P;
+ case R_RISCV_RVC_BRANCH:
+ return S + A - P;
+ case R_RISCV_BRANCH:
+ return S + A - P;
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT:
+ return S + A - P;
+ case R_RISCV_ALIGN:
+ // I guess we don't need to implement this relaxation. Otherwise, this
+ // should return the number of blank bytes to insert via NOPs.
+ return 0;
+ case R_RISCV_ADD8:
+ case R_RISCV_ADD16:
+ case R_RISCV_ADD32:
+ case R_RISCV_ADD64:
+ return S + A; // Add V when the value is set
+ case R_RISCV_SUB8:
+ case R_RISCV_SUB16:
+ case R_RISCV_SUB32:
+ case R_RISCV_SUB64:
+ return S + A; // Subtract from V when value is set
+ default:
+ abort(/* unhandled rel */);
+ }
+}
+
+// TODO: This is duplicated from elf_reloc_aarch64.c
+bool relocateObjectCodeRISCV64(ObjectCode *oc) {
+ for (ElfRelocationTable *relTab = oc->info->relTable; relTab != NULL;
+ relTab = relTab->next) {
+ /* only relocate interesting sections */
+ if (SECTIONKIND_OTHER == oc->sections[relTab->targetSectionIndex].kind)
+ continue;
+
+ Section *targetSection = &oc->sections[relTab->targetSectionIndex];
+
+ for (unsigned i = 0; i < relTab->n_relocations; i++) {
+ Elf_Rel *rel = &relTab->relocations[i];
+
+ ElfSymbol *symbol = findSymbol(oc, relTab->sectionHeader->sh_link,
+ ELF64_R_SYM((Elf64_Xword)rel->r_info));
+
+ CHECK(0x0 != symbol);
+
+ // TODO: This always fails, because we don't support Rel locations: Do we
+ // need this case?
+ /* decode implicit addend */
+ int64_t addend = decodeAddendRISCV64(targetSection, rel);
+
+ addend = computeAddend(targetSection, rel, symbol, addend);
+ encodeAddendRISCV64(targetSection, rel, addend);
+ }
+ }
+ for (ElfRelocationATable *relaTab = oc->info->relaTable; relaTab != NULL;
+ relaTab = relaTab->next) {
+ /* only relocate interesting sections */
+ if (SECTIONKIND_OTHER == oc->sections[relaTab->targetSectionIndex].kind)
+ continue;
+
+ Section *targetSection = &oc->sections[relaTab->targetSectionIndex];
+
+ for (unsigned i = 0; i < relaTab->n_relocations; i++) {
+
+ Elf_Rela *rel = &relaTab->relocations[i];
+
+ ElfSymbol *symbol = findSymbol(oc, relaTab->sectionHeader->sh_link,
+ ELF64_R_SYM((Elf64_Xword)rel->r_info));
+
+ CHECK(0x0 != symbol);
+ CHECK(0x0 != symbol->addr);
+
+ /* take explicit addend */
+ int64_t addend = rel->r_addend;
+
+ addend = computeAddend(targetSection, (Elf_Rel *)rel, symbol, addend);
+ encodeAddendRISCV64(targetSection, (Elf_Rel *)rel, addend);
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+#endif /* OBJECTFORMAT_ELF */
+#endif /* riscv64_HOST_ARCH */
=====================================
rts/linker/elf_reloc_riscv64.h
=====================================
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "LinkerInternals.h"
+
+#if defined(OBJFORMAT_ELF)
+
+bool
+relocateObjectCodeRISCV64(ObjectCode * oc);
+
+#endif /* OBJETFORMAT_ELF */
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/512524a7cf4e26b13744772664054551c3bbcba8
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/512524a7cf4e26b13744772664054551c3bbcba8
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20240208/1cd5637e/attachment-0001.html>
More information about the ghc-commits
mailing list