summaryrefslogtreecommitdiffstats
path: root/core/relocate.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/relocate.c')
-rw-r--r--core/relocate.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/core/relocate.c b/core/relocate.c
new file mode 100644
index 00000000..f6bda372
--- /dev/null
+++ b/core/relocate.c
@@ -0,0 +1,65 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <elf.h>
+
+/* 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.
+ */
+
+/* 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 false;
+
+ /* Check if the offset is consistent */
+ if ((offset + dt_rela) != (uint64_t)rela)
+ return false;
+
+ /* Perform relocations */
+ for (i = 0; i < dt_relacount; i++, rela++) {
+ uint64_t *t;
+
+ if (ELF64_R_TYPE(rela->r_info) != R_PPC64_RELATIVE)
+ return false;
+ t = (uint64_t *)(rela->r_offset + offset);
+ *t = rela->r_addend + offset;
+ }
+
+ return true;
+}
OpenPOWER on IntegriCloud