| // 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; |
| } |