David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2012 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 | */ |
Elliott Hughes | cbc80ba | 2018-02-13 14:26:29 -0800 | [diff] [blame] | 28 | |
| 29 | #pragma once |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 30 | |
| 31 | /* Declarations related to the ELF program header table and segments. |
| 32 | * |
| 33 | * The design goal is to provide an API that is as close as possible |
| 34 | * to the ELF spec, and does not depend on linker-specific data |
| 35 | * structures (e.g. the exact layout of struct soinfo). |
| 36 | */ |
| 37 | |
| 38 | #include "linker.h" |
Dmitriy Ivanov | cf1cbbe | 2015-10-19 16:57:46 -0700 | [diff] [blame] | 39 | #include "linker_mapped_file_fragment.h" |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 40 | #include "linker_note_gnu_property.h" |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 41 | |
Florian Mayer | 4edc20d | 2024-10-30 14:24:26 -0700 | [diff] [blame] | 42 | #include <list> |
| 43 | |
Kalesh Singh | ce1c3cf | 2024-09-30 13:26:23 -0700 | [diff] [blame] | 44 | #define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0) |
| 45 | #define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ |
| 46 | MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ |
| 47 | MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) |
| 48 | |
| 49 | static constexpr size_t kCompatPageSize = 0x1000; |
| 50 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 51 | class ElfReader { |
| 52 | public: |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 53 | ElfReader(); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 54 | |
Elliott Hughes | 396868c | 2024-04-10 21:52:10 +0000 | [diff] [blame] | 55 | [[nodiscard]] bool Read(const char* name, int fd, off64_t file_offset, off64_t file_size); |
| 56 | [[nodiscard]] bool Load(address_space_params* address_space); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 57 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 58 | const char* name() const { return name_.c_str(); } |
| 59 | size_t phdr_count() const { return phdr_num_; } |
| 60 | ElfW(Addr) load_start() const { return reinterpret_cast<ElfW(Addr)>(load_start_); } |
| 61 | size_t load_size() const { return load_size_; } |
Evgenii Stepanov | e0848bb | 2020-07-14 16:44:57 -0700 | [diff] [blame] | 62 | ElfW(Addr) gap_start() const { return reinterpret_cast<ElfW(Addr)>(gap_start_); } |
| 63 | size_t gap_size() const { return gap_size_; } |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 64 | ElfW(Addr) load_bias() const { return load_bias_; } |
| 65 | const ElfW(Phdr)* loaded_phdr() const { return loaded_phdr_; } |
| 66 | const ElfW(Dyn)* dynamic() const { return dynamic_; } |
| 67 | const char* get_string(ElfW(Word) index) const; |
Dimitry Ivanov | f45b0e9 | 2016-01-15 11:13:35 -0800 | [diff] [blame] | 68 | bool is_mapped_by_caller() const { return mapped_by_caller_; } |
Ryan Prichard | 8f639a4 | 2018-10-01 23:10:05 -0700 | [diff] [blame] | 69 | ElfW(Addr) entry_point() const { return header_.e_entry + load_bias_; } |
Kalesh Singh | 377f0b9 | 2024-01-31 20:23:39 -0800 | [diff] [blame] | 70 | bool should_pad_segments() const { return should_pad_segments_; } |
Kalesh Singh | b23787f | 2024-09-05 08:22:06 +0000 | [diff] [blame] | 71 | bool should_use_16kib_app_compat() const { return should_use_16kib_app_compat_; } |
Kalesh Singh | ce1c3cf | 2024-09-30 13:26:23 -0700 | [diff] [blame] | 72 | ElfW(Addr) compat_relro_start() const { return compat_relro_start_; } |
| 73 | ElfW(Addr) compat_relro_size() const { return compat_relro_size_; } |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 74 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 75 | private: |
Elliott Hughes | 396868c | 2024-04-10 21:52:10 +0000 | [diff] [blame] | 76 | [[nodiscard]] bool ReadElfHeader(); |
| 77 | [[nodiscard]] bool VerifyElfHeader(); |
| 78 | [[nodiscard]] bool ReadProgramHeaders(); |
| 79 | [[nodiscard]] bool ReadSectionHeaders(); |
| 80 | [[nodiscard]] bool ReadDynamicSection(); |
| 81 | [[nodiscard]] bool ReadPadSegmentNote(); |
| 82 | [[nodiscard]] bool ReserveAddressSpace(address_space_params* address_space); |
Kalesh Singh | 86e04f6 | 2024-09-05 06:24:14 +0000 | [diff] [blame] | 83 | [[nodiscard]] bool MapSegment(size_t seg_idx, size_t len); |
Kalesh Singh | ce1c3cf | 2024-09-30 13:26:23 -0700 | [diff] [blame] | 84 | [[nodiscard]] bool CompatMapSegment(size_t seg_idx, size_t len); |
Kalesh Singh | 37bcaea | 2024-09-05 06:32:07 +0000 | [diff] [blame] | 85 | void ZeroFillSegment(const ElfW(Phdr)* phdr); |
Kalesh Singh | e0f4a37 | 2024-09-05 07:07:21 +0000 | [diff] [blame] | 86 | void DropPaddingPages(const ElfW(Phdr)* phdr, uint64_t seg_file_end); |
Kalesh Singh | 138a955 | 2024-09-05 08:05:56 +0000 | [diff] [blame] | 87 | [[nodiscard]] bool MapBssSection(const ElfW(Phdr)* phdr, ElfW(Addr) seg_page_end, |
| 88 | ElfW(Addr) seg_file_end); |
Kalesh Singh | ce1c3cf | 2024-09-30 13:26:23 -0700 | [diff] [blame] | 89 | [[nodiscard]] bool IsEligibleFor16KiBAppCompat(ElfW(Addr)* vaddr); |
| 90 | [[nodiscard]] bool HasAtMostOneRelroSegment(const ElfW(Phdr)** relro_phdr); |
| 91 | [[nodiscard]] bool Setup16KiBAppCompat(); |
Elliott Hughes | 396868c | 2024-04-10 21:52:10 +0000 | [diff] [blame] | 92 | [[nodiscard]] bool LoadSegments(); |
| 93 | [[nodiscard]] bool FindPhdr(); |
| 94 | [[nodiscard]] bool FindGnuPropertySection(); |
| 95 | [[nodiscard]] bool CheckPhdr(ElfW(Addr)); |
| 96 | [[nodiscard]] bool CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 97 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 98 | bool did_read_; |
| 99 | bool did_load_; |
| 100 | std::string name_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 101 | int fd_; |
Dmitriy Ivanov | 07e5bc1 | 2014-10-03 17:52:44 -0700 | [diff] [blame] | 102 | off64_t file_offset_; |
Dmitriy Ivanov | 3f987f5 | 2015-06-25 15:51:41 -0700 | [diff] [blame] | 103 | off64_t file_size_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 104 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 105 | ElfW(Ehdr) header_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 106 | size_t phdr_num_; |
| 107 | |
Dmitriy Ivanov | cf1cbbe | 2015-10-19 16:57:46 -0700 | [diff] [blame] | 108 | MappedFileFragment phdr_fragment_; |
| 109 | const ElfW(Phdr)* phdr_table_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 110 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 111 | MappedFileFragment shdr_fragment_; |
| 112 | const ElfW(Shdr)* shdr_table_; |
| 113 | size_t shdr_num_; |
| 114 | |
| 115 | MappedFileFragment dynamic_fragment_; |
| 116 | const ElfW(Dyn)* dynamic_; |
| 117 | |
| 118 | MappedFileFragment strtab_fragment_; |
| 119 | const char* strtab_; |
| 120 | size_t strtab_size_; |
| 121 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 122 | // First page of reserved address space. |
| 123 | void* load_start_; |
| 124 | // Size in bytes of reserved address space. |
Elliott Hughes | c00f2cb | 2013-10-04 17:01:33 -0700 | [diff] [blame] | 125 | size_t load_size_; |
Evgenii Stepanov | e0848bb | 2020-07-14 16:44:57 -0700 | [diff] [blame] | 126 | // First page of inaccessible gap mapping reserved for this DSO. |
| 127 | void* gap_start_; |
| 128 | // Size in bytes of the gap mapping. |
| 129 | size_t gap_size_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 130 | // Load bias. |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 131 | ElfW(Addr) load_bias_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 132 | |
| 133 | // Loaded phdr. |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 134 | const ElfW(Phdr)* loaded_phdr_; |
Dimitry Ivanov | f45b0e9 | 2016-01-15 11:13:35 -0800 | [diff] [blame] | 135 | |
| 136 | // Is map owned by the caller |
| 137 | bool mapped_by_caller_; |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 138 | |
Kalesh Singh | 377f0b9 | 2024-01-31 20:23:39 -0800 | [diff] [blame] | 139 | // Pad gaps between segments when memory mapping? |
| 140 | bool should_pad_segments_ = false; |
| 141 | |
Kalesh Singh | b23787f | 2024-09-05 08:22:06 +0000 | [diff] [blame] | 142 | // Use app compat mode when loading 4KiB max-page-size ELFs on 16KiB page-size devices? |
| 143 | bool should_use_16kib_app_compat_ = false; |
| 144 | |
Kalesh Singh | ce1c3cf | 2024-09-30 13:26:23 -0700 | [diff] [blame] | 145 | // RELRO region for 16KiB compat loading |
| 146 | ElfW(Addr) compat_relro_start_ = 0; |
| 147 | ElfW(Addr) compat_relro_size_ = 0; |
| 148 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 149 | // Only used by AArch64 at the moment. |
| 150 | GnuPropertySection note_gnu_property_ __unused; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 151 | }; |
| 152 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 153 | size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Dmitriy Ivanov | 851135b | 2014-08-29 12:02:36 -0700 | [diff] [blame] | 154 | ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 155 | |
Collin Fijalkovich | 47d27aa | 2021-03-24 10:17:39 -0700 | [diff] [blame] | 156 | size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count); |
Steven Moreland | fc89c8a | 2024-08-01 21:20:33 +0000 | [diff] [blame] | 157 | size_t phdr_table_get_minimum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count); |
Collin Fijalkovich | 47d27aa | 2021-03-24 10:17:39 -0700 | [diff] [blame] | 158 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 159 | int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Kalesh Singh | 4084b55 | 2024-03-13 13:35:49 -0700 | [diff] [blame] | 160 | ElfW(Addr) load_bias, bool should_pad_segments, |
Kalesh Singh | b23787f | 2024-09-05 08:22:06 +0000 | [diff] [blame] | 161 | bool should_use_16kib_app_compat, |
Kalesh Singh | 4084b55 | 2024-03-13 13:35:49 -0700 | [diff] [blame] | 162 | const GnuPropertySection* prop = nullptr); |
Dimitry Ivanov | 56be6ed | 2015-04-01 21:18:48 +0000 | [diff] [blame] | 163 | |
| 164 | int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Kalesh Singh | b23787f | 2024-09-05 08:22:06 +0000 | [diff] [blame] | 165 | ElfW(Addr) load_bias, bool should_pad_segments, |
| 166 | bool should_use_16kib_app_compat); |
Dimitry Ivanov | 56be6ed | 2015-04-01 21:18:48 +0000 | [diff] [blame] | 167 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 168 | int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Kalesh Singh | b23787f | 2024-09-05 08:22:06 +0000 | [diff] [blame] | 169 | ElfW(Addr) load_bias, bool should_pad_segments, |
| 170 | bool should_use_16kib_app_compat); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 171 | |
Kalesh Singh | ce1c3cf | 2024-09-30 13:26:23 -0700 | [diff] [blame] | 172 | int phdr_table_protect_gnu_relro_16kib_compat(ElfW(Addr) start, ElfW(Addr) size); |
| 173 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 174 | int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Torne (Richard Coles) | fa9f7f2 | 2019-04-02 17:04:42 -0400 | [diff] [blame] | 175 | ElfW(Addr) load_bias, int fd, size_t* file_offset); |
Torne (Richard Coles) | 183ad9d | 2014-02-27 13:18:00 +0000 | [diff] [blame] | 176 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 177 | int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Torne (Richard Coles) | efbe9a5 | 2018-10-17 15:59:38 -0400 | [diff] [blame] | 178 | ElfW(Addr) load_bias, int fd, size_t* file_offset); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 179 | |
Elliott Hughes | 4eeb1f1 | 2013-10-25 17:38:02 -0700 | [diff] [blame] | 180 | #if defined(__arm__) |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 181 | int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias, |
Dmitriy Ivanov | 1649e7e | 2015-01-22 16:04:25 -0800 | [diff] [blame] | 182 | ElfW(Addr)** arm_exidx, size_t* arm_exidix_count); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 183 | #endif |
| 184 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 185 | void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Ningsheng Jian | e93be99 | 2014-09-16 15:22:10 +0800 | [diff] [blame] | 186 | ElfW(Addr) load_bias, ElfW(Dyn)** dynamic, |
| 187 | ElfW(Word)* dynamic_flags); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 188 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 189 | const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Evgenii Stepanov | d640b22 | 2015-07-10 17:54:01 -0700 | [diff] [blame] | 190 | ElfW(Addr) load_bias); |
Kalesh Singh | c5c1d19 | 2024-04-09 16:27:56 -0700 | [diff] [blame] | 191 | |
| 192 | bool page_size_migration_supported(); |
Florian Mayer | 4edc20d | 2024-10-30 14:24:26 -0700 | [diff] [blame] | 193 | |
| 194 | int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count, |
| 195 | ElfW(Addr) load_bias); |
| 196 | |
| 197 | void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count, |
| 198 | ElfW(Addr) load_bias); |
| 199 | |
| 200 | void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count, |
| 201 | ElfW(Addr) load_bias, const char* soname, |
| 202 | std::list<std::string>* vma_names); |