aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/relocate.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/relocate.c')
-rw-r--r--roms/skiboot/core/relocate.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/roms/skiboot/core/relocate.c b/roms/skiboot/core/relocate.c
new file mode 100644
index 000000000..6295927e2
--- /dev/null
+++ b/roms/skiboot/core/relocate.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Relocate ourselves
+ *
+ * WARNING: This code is used to self-relocate, it cannot have any
+ * global reference nor TOC reference. It's also called before BSS
+ * is cleared.
+ *
+ * Copyright 2013-2015 IBM Corp.
+ */
+
+#include <stdbool.h>
+#include <elf.h>
+
+/* Called from head.S, thus no header. */
+int relocate(uint64_t offset, struct elf64_dyn *dyn, struct elf64_rela *rela);
+
+/* Note: This code is simplified according to the assumptions
+ * that our link address is 0 and we are running at the
+ * target address already.
+ */
+int relocate(uint64_t offset, struct elf64_dyn *dyn, struct elf64_rela *rela)
+{
+ uint64_t dt_rela = 0;
+ uint64_t dt_relacount = 0;
+ unsigned int i;
+
+ /* Look for relocation table */
+ for (; dyn->d_tag != DT_NULL; dyn++) {
+ if (dyn->d_tag == DT_RELA)
+ dt_rela = dyn->d_val;
+ else if (dyn->d_tag == DT_RELACOUNT)
+ dt_relacount = dyn->d_val;
+ }
+
+ /* If we miss either rela or relacount, bail */
+ if (!dt_rela || !dt_relacount)
+ return -1;
+
+ /* Check if the offset is consistent */
+ if ((offset + dt_rela) != (uint64_t)rela)
+ return -2;
+
+ /* Perform relocations */
+ for (i = 0; i < dt_relacount; i++, rela++) {
+ uint64_t *t;
+
+ if (ELF64_R_TYPE(rela->r_info) != R_PPC64_RELATIVE)
+ return -3;
+ t = (uint64_t *)(rela->r_offset + offset);
+ *t = rela->r_addend + offset;
+ }
+
+ return 0;
+}