| /* |
| * relocate R_PPC_RELATIVE RELA entries. Normally this is done in |
| * assembly code to avoid the risk of using absolute addresses before |
| * they're relocated. We use C, but cautiously (no global references). |
| * |
| * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com> |
| * |
| * This work is licensed under the terms of the GNU LGPL, version 2. |
| */ |
| #define DT_NULL 0 |
| #define DT_RELA 7 |
| #define DT_RELACOUNT 0x6ffffff9 |
| #define R_PPC_RELATIVE 22 |
| |
| struct elf64_dyn { |
| signed long long tag; |
| unsigned long long val; |
| }; |
| |
| #define RELA_GET_TYPE(rela_ptr) ((rela_ptr)->info & 0xffffffff) |
| struct elf64_rela { |
| unsigned long long offset; |
| unsigned long long info; |
| signed long long addend; |
| }; |
| |
| void relocate(unsigned long load_addr, struct elf64_dyn *dyn_table); |
| |
| void relocate(unsigned long load_addr, struct elf64_dyn *dyn_table) |
| { |
| unsigned long long rela_addr = 0, rela_count = 0, *addr; |
| struct elf64_dyn *d = dyn_table; |
| struct elf64_rela *r; |
| |
| while (d && d->tag != DT_NULL) { |
| if (d->tag == DT_RELA) |
| rela_addr = d->val; |
| else if (d->tag == DT_RELACOUNT) |
| rela_count = d->val; |
| if (rela_addr && rela_count) |
| break; |
| ++d; |
| } |
| |
| if (!rela_addr || !rela_count) |
| return; |
| |
| r = (void *)(rela_addr + load_addr); |
| |
| while (rela_count--) { |
| if (RELA_GET_TYPE(r) == R_PPC_RELATIVE) { |
| addr = (void *)(r->offset + load_addr); |
| *addr = r->addend + load_addr; |
| } |
| ++r; |
| } |
| } |