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