1 /*
2 * Copyright (c) 2022 Google Inc. All rights reserved
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <kernel/vm.h>
25 #include <sys/types.h>
26
update_relocation_entries(uintptr_t * relr_start,uintptr_t * relr_end,uintptr_t reloc_delta)27 void update_relocation_entries(uintptr_t* relr_start, uintptr_t* relr_end,
28 uintptr_t reloc_delta) {
29 ASSERT(!(reloc_delta & 1));
30 uintptr_t* relr_addr;
31 for (relr_addr = relr_start; relr_addr < relr_end; relr_addr++) {
32 uintptr_t entry = *relr_addr;
33 if (!(entry & 1)) {
34 *relr_addr -= reloc_delta;
35 }
36 }
37 }
38
arch_relocate_relative(uintptr_t * ptr,uintptr_t old_base,uintptr_t new_base)39 __WEAK void arch_relocate_relative(uintptr_t* ptr, uintptr_t old_base,
40 uintptr_t new_base) {
41 uintptr_t offset = *ptr - old_base;
42 *ptr = new_base + offset;
43 }
44
relocate_kernel(uintptr_t * relr_start,uintptr_t * relr_end,uintptr_t old_base,uintptr_t new_base)45 void relocate_kernel(uintptr_t* relr_start, uintptr_t* relr_end,
46 uintptr_t old_base, uintptr_t new_base) {
47 if (new_base == old_base) {
48 return;
49 }
50
51 /*
52 * The RELR format is a compact encoding for all the R_AARCH64_RELATIVE
53 * dynamic relocations that apply to an ELF binary. It consists of an array
54 * of 64-bit entry words (sorted by relocation address) with the following
55 * semantics:
56 * * Even entries (LSB is clear) encode an absolute 64-bit pointer to the
57 * next relocation in the file.
58 * * Odd entries (LSB is set) encode a bitmap that specifies which of the
59 * next 63 file words following the last relocation also have relative
60 * relocations. The bits of the bitmap are mapped to file words in little
61 * endian order. Each odd entry covers a consecutive interval of
62 * 63 * 8 = 504 bytes in the ELF file.
63 *
64 * For more details, see the original proposal at
65 * https://groups.google.com/g/generic-abi/c/bX460iggiKg
66 */
67 uintptr_t* relr_addr;
68 uintptr_t base = 0;
69 for (relr_addr = relr_start; relr_addr < relr_end; relr_addr++) {
70 uintptr_t entry = *relr_addr;
71 if (!(entry & 1)) {
72 arch_relocate_relative((uintptr_t*)entry, old_base, new_base);
73 base = entry + sizeof(uintptr_t);
74 } else {
75 uintptr_t* offset = (uintptr_t*)base;
76 while (entry) {
77 entry >>= 1;
78 if (entry & 1) {
79 arch_relocate_relative(offset, old_base, new_base);
80 }
81 offset++;
82 }
83
84 const size_t length =
85 (8 * sizeof(uintptr_t) - 1) * sizeof(uintptr_t);
86 base += length;
87 }
88 }
89 }
90