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 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 42 | class ElfReader { |
| 43 | public: |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 44 | ElfReader(); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 45 | |
Elliott Hughes | 396868c | 2024-04-10 21:52:10 +0000 | [diff] [blame^] | 46 | [[nodiscard]] bool Read(const char* name, int fd, off64_t file_offset, off64_t file_size); |
| 47 | [[nodiscard]] bool Load(address_space_params* address_space); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 48 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 49 | const char* name() const { return name_.c_str(); } |
| 50 | size_t phdr_count() const { return phdr_num_; } |
| 51 | ElfW(Addr) load_start() const { return reinterpret_cast<ElfW(Addr)>(load_start_); } |
| 52 | size_t load_size() const { return load_size_; } |
Evgenii Stepanov | e0848bb | 2020-07-14 16:44:57 -0700 | [diff] [blame] | 53 | ElfW(Addr) gap_start() const { return reinterpret_cast<ElfW(Addr)>(gap_start_); } |
| 54 | size_t gap_size() const { return gap_size_; } |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 55 | ElfW(Addr) load_bias() const { return load_bias_; } |
| 56 | const ElfW(Phdr)* loaded_phdr() const { return loaded_phdr_; } |
| 57 | const ElfW(Dyn)* dynamic() const { return dynamic_; } |
| 58 | const char* get_string(ElfW(Word) index) const; |
Dimitry Ivanov | f45b0e9 | 2016-01-15 11:13:35 -0800 | [diff] [blame] | 59 | bool is_mapped_by_caller() const { return mapped_by_caller_; } |
Ryan Prichard | 8f639a4 | 2018-10-01 23:10:05 -0700 | [diff] [blame] | 60 | ElfW(Addr) entry_point() const { return header_.e_entry + load_bias_; } |
Kalesh Singh | 377f0b9 | 2024-01-31 20:23:39 -0800 | [diff] [blame] | 61 | bool should_pad_segments() const { return should_pad_segments_; } |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 62 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 63 | private: |
Elliott Hughes | 396868c | 2024-04-10 21:52:10 +0000 | [diff] [blame^] | 64 | [[nodiscard]] bool ReadElfHeader(); |
| 65 | [[nodiscard]] bool VerifyElfHeader(); |
| 66 | [[nodiscard]] bool ReadProgramHeaders(); |
| 67 | [[nodiscard]] bool ReadSectionHeaders(); |
| 68 | [[nodiscard]] bool ReadDynamicSection(); |
| 69 | [[nodiscard]] bool ReadPadSegmentNote(); |
| 70 | [[nodiscard]] bool ReserveAddressSpace(address_space_params* address_space); |
| 71 | [[nodiscard]] bool LoadSegments(); |
| 72 | [[nodiscard]] bool FindPhdr(); |
| 73 | [[nodiscard]] bool FindGnuPropertySection(); |
| 74 | [[nodiscard]] bool CheckPhdr(ElfW(Addr)); |
| 75 | [[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] | 76 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 77 | bool did_read_; |
| 78 | bool did_load_; |
| 79 | std::string name_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 80 | int fd_; |
Dmitriy Ivanov | 07e5bc1 | 2014-10-03 17:52:44 -0700 | [diff] [blame] | 81 | off64_t file_offset_; |
Dmitriy Ivanov | 3f987f5 | 2015-06-25 15:51:41 -0700 | [diff] [blame] | 82 | off64_t file_size_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 83 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 84 | ElfW(Ehdr) header_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 85 | size_t phdr_num_; |
| 86 | |
Dmitriy Ivanov | cf1cbbe | 2015-10-19 16:57:46 -0700 | [diff] [blame] | 87 | MappedFileFragment phdr_fragment_; |
| 88 | const ElfW(Phdr)* phdr_table_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 89 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 90 | MappedFileFragment shdr_fragment_; |
| 91 | const ElfW(Shdr)* shdr_table_; |
| 92 | size_t shdr_num_; |
| 93 | |
| 94 | MappedFileFragment dynamic_fragment_; |
| 95 | const ElfW(Dyn)* dynamic_; |
| 96 | |
| 97 | MappedFileFragment strtab_fragment_; |
| 98 | const char* strtab_; |
| 99 | size_t strtab_size_; |
| 100 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 101 | // First page of reserved address space. |
| 102 | void* load_start_; |
| 103 | // Size in bytes of reserved address space. |
Elliott Hughes | c00f2cb | 2013-10-04 17:01:33 -0700 | [diff] [blame] | 104 | size_t load_size_; |
Evgenii Stepanov | e0848bb | 2020-07-14 16:44:57 -0700 | [diff] [blame] | 105 | // First page of inaccessible gap mapping reserved for this DSO. |
| 106 | void* gap_start_; |
| 107 | // Size in bytes of the gap mapping. |
| 108 | size_t gap_size_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 109 | // Load bias. |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 110 | ElfW(Addr) load_bias_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 111 | |
| 112 | // Loaded phdr. |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 113 | const ElfW(Phdr)* loaded_phdr_; |
Dimitry Ivanov | f45b0e9 | 2016-01-15 11:13:35 -0800 | [diff] [blame] | 114 | |
| 115 | // Is map owned by the caller |
| 116 | bool mapped_by_caller_; |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 117 | |
Kalesh Singh | 377f0b9 | 2024-01-31 20:23:39 -0800 | [diff] [blame] | 118 | // Pad gaps between segments when memory mapping? |
| 119 | bool should_pad_segments_ = false; |
| 120 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 121 | // Only used by AArch64 at the moment. |
| 122 | GnuPropertySection note_gnu_property_ __unused; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 123 | }; |
| 124 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 125 | 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] | 126 | ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 127 | |
Collin Fijalkovich | 47d27aa | 2021-03-24 10:17:39 -0700 | [diff] [blame] | 128 | size_t phdr_table_get_maximum_alignment(const ElfW(Phdr)* phdr_table, size_t phdr_count); |
| 129 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 130 | 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] | 131 | ElfW(Addr) load_bias, bool should_pad_segments, |
| 132 | const GnuPropertySection* prop = nullptr); |
Dimitry Ivanov | 56be6ed | 2015-04-01 21:18:48 +0000 | [diff] [blame] | 133 | |
| 134 | int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Kalesh Singh | 4084b55 | 2024-03-13 13:35:49 -0700 | [diff] [blame] | 135 | ElfW(Addr) load_bias, bool should_pad_segments); |
Dimitry Ivanov | 56be6ed | 2015-04-01 21:18:48 +0000 | [diff] [blame] | 136 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 137 | int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
Kalesh Singh | 702d9b0 | 2024-03-13 13:38:04 -0700 | [diff] [blame] | 138 | ElfW(Addr) load_bias, bool should_pad_segments); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 139 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 140 | 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] | 141 | ElfW(Addr) load_bias, int fd, size_t* file_offset); |
Torne (Richard Coles) | 183ad9d | 2014-02-27 13:18:00 +0000 | [diff] [blame] | 142 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 143 | 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] | 144 | ElfW(Addr) load_bias, int fd, size_t* file_offset); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 145 | |
Elliott Hughes | 4eeb1f1 | 2013-10-25 17:38:02 -0700 | [diff] [blame] | 146 | #if defined(__arm__) |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 147 | 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] | 148 | ElfW(Addr)** arm_exidx, size_t* arm_exidix_count); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 149 | #endif |
| 150 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 151 | 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] | 152 | ElfW(Addr) load_bias, ElfW(Dyn)** dynamic, |
| 153 | ElfW(Word)* dynamic_flags); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 154 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 155 | 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] | 156 | ElfW(Addr) load_bias); |