| 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 |  | 
| Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 46 |   bool Read(const char* name, int fd, off64_t file_offset, off64_t file_size); | 
| Torne (Richard Coles) | efbe9a5 | 2018-10-17 15:59:38 -0400 | [diff] [blame] | 47 |   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: | 
 | 64 |   bool ReadElfHeader(); | 
 | 65 |   bool VerifyElfHeader(); | 
| Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 66 |   bool ReadProgramHeaders(); | 
 | 67 |   bool ReadSectionHeaders(); | 
 | 68 |   bool ReadDynamicSection(); | 
| Kalesh Singh | 377f0b9 | 2024-01-31 20:23:39 -0800 | [diff] [blame] | 69 |   bool ReadPadSegmentNote(); | 
| Torne (Richard Coles) | efbe9a5 | 2018-10-17 15:59:38 -0400 | [diff] [blame] | 70 |   bool ReserveAddressSpace(address_space_params* address_space); | 
| Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 71 |   bool LoadSegments(); | 
 | 72 |   bool FindPhdr(); | 
| Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame] | 73 |   bool FindGnuPropertySection(); | 
| Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 74 |   bool CheckPhdr(ElfW(Addr)); | 
| Dimitry Ivanov | 7e2d49a | 2016-08-08 17:12:18 -0700 | [diff] [blame] | 75 |   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 | 944164c | 2023-10-18 22:19:41 -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 | 944164c | 2023-10-18 22:19:41 -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 | 41b8863 | 2023-10-24 12:47:16 -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); |