1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /* Find the load bias (difference between address and p_vaddr) of an
30 * executable or shared object loaded by the kernel. The ELF file's
31 * PHDR table must have a PT_PHDR entry. A VDSO doesn't have a PT_PHDR
32 * entry in its PHDR table.
33 */
34 static inline ElfW(Addr)
get_elf_load_bias_from_phdr(const ElfW (Phdr)* phdr_table,size_t phdr_count)35 get_elf_load_bias_from_phdr(const ElfW(Phdr) * phdr_table, size_t phdr_count) {
36 for (size_t i = 0; i < phdr_count; ++i) {
37 if (phdr_table[i].p_type == PT_PHDR) {
38 return reinterpret_cast<ElfW(Addr)>(phdr_table) - phdr_table[i].p_vaddr;
39 }
40 }
41 return 0;
42 }
43
44 /* Copy the phdr to a new location. Update the PT_PHDR section to point to the
45 * new location.
46 */
copy_phdr(ElfW (Phdr)* phdr_from,ElfW (Phdr)* phdr_to,size_t count,ElfW (Addr)load_bias)47 static inline void copy_phdr(ElfW(Phdr) * phdr_from, ElfW(Phdr) * phdr_to, size_t count,
48 ElfW(Addr) load_bias) {
49 ElfW(Phdr)* pt_phdr = nullptr; // The phdr entry with type PT_PHDR.
50 ElfW(Phdr)* phdr_to_phdr = nullptr; // The phdr entry for the load segment that contains phdr_to.
51 ElfW(Phdr)* p = phdr_to;
52
53 // The ELF vaddr of phdr_to.
54 ElfW(Addr) phdr_to_vaddr = reinterpret_cast<ElfW(Addr)>(phdr_to) - load_bias;
55
56 for (size_t i = 0; i < count; ++i, ++p) {
57 // Assign each member to avoid the struct assignment being turned into a memcpy.
58 p->p_type = phdr_from[i].p_type;
59 p->p_offset = phdr_from[i].p_offset;
60 p->p_vaddr = phdr_from[i].p_vaddr;
61 p->p_paddr = phdr_from[i].p_paddr;
62 p->p_filesz = phdr_from[i].p_filesz;
63 p->p_memsz = phdr_from[i].p_memsz;
64 p->p_flags = phdr_from[i].p_flags;
65 p->p_align = phdr_from[i].p_align;
66
67 if (p->p_type == PT_PHDR) pt_phdr = p;
68 if (p->p_vaddr <= phdr_to_vaddr && p->p_vaddr + p->p_memsz > phdr_to_vaddr) phdr_to_phdr = p;
69 }
70
71 if (pt_phdr != nullptr && phdr_to_phdr != nullptr) {
72 pt_phdr->p_vaddr = reinterpret_cast<ElfW(Addr)>(phdr_to) - load_bias;
73 pt_phdr->p_paddr = pt_phdr->p_vaddr;
74 pt_phdr->p_offset = phdr_to_phdr->p_offset + (pt_phdr->p_vaddr - phdr_to_phdr->p_vaddr);
75 }
76 }
77
78 /* Trim a section to the given start and end.
79 */
phdr_trim_segment(ElfW (Phdr)* phdr,ElfW (Addr)start,ElfW (Addr)end)80 static inline void phdr_trim_segment(ElfW(Phdr) * phdr, ElfW(Addr) start, ElfW(Addr) end) {
81 const ElfW(Addr) shift = start - phdr->p_vaddr;
82 phdr->p_vaddr = start;
83 phdr->p_paddr = start;
84 phdr->p_memsz = end - start;
85 if (shift > 0) {
86 phdr->p_offset += shift;
87 phdr->p_filesz = (shift > phdr->p_filesz) ? 0 : (phdr->p_filesz - shift);
88 }
89 if (phdr->p_filesz > end - start) {
90 phdr->p_filesz = end - start;
91 }
92 }
93
94 /* Trim load sections that overlap with the embedded linker, and replace load sections
95 * that are entirely contained within the embedded linker with PT_NULL.
96 */
phdr_trim_embedded_linker(ElfW (Phdr)* phdr,size_t phdr_count,ElfW (Off)linker_start,ElfW (Off)linker_end)97 static inline void phdr_trim_embedded_linker(ElfW(Phdr) * phdr, size_t phdr_count,
98 ElfW(Off) linker_start, ElfW(Off) linker_end) {
99 for (size_t i = 0; i < phdr_count; ++i, ++phdr) {
100 if (phdr->p_type != PT_LOAD) continue;
101
102 ElfW(Addr) start = phdr->p_vaddr;
103 ElfW(Addr) end = phdr->p_vaddr + phdr->p_memsz;
104
105 // A segment that surrounds an embedded linker segment is not supported;
106 if (start < linker_start && end > linker_end) __builtin_trap();
107
108 // Handle a segment that overlaps the beginning of the embedded linker;
109 if (start < linker_start && end > linker_start) end = linker_start;
110
111 // Handle a segment that overlaps the end of the embedded linker;
112 if (start < linker_end && end > linker_end) start = linker_end;
113
114 if (start < end && (start < linker_start || end > linker_end)) {
115 // The segment is still needed, trim it.
116 phdr_trim_segment(phdr, start, end);
117 } else {
118 // The segment is not needed, replace it with PT_NULL to avoid having
119 // to move the following segments in the phdr.
120 phdr->p_type = PT_NULL;
121 }
122 }
123 }
124