[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