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_; } |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 61 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 62 | private: |
| 63 | bool ReadElfHeader(); |
| 64 | bool VerifyElfHeader(); |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 65 | bool ReadProgramHeaders(); |
| 66 | bool ReadSectionHeaders(); |
| 67 | bool ReadDynamicSection(); |
Torne (Richard Coles) | efbe9a5 | 2018-10-17 15:59:38 -0400 | [diff] [blame] | 68 | bool ReserveAddressSpace(address_space_params* address_space); |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 69 | bool LoadSegments(); |
| 70 | bool FindPhdr(); |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame^] | 71 | bool FindGnuPropertySection(); |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 72 | bool CheckPhdr(ElfW(Addr)); |
Dimitry Ivanov | 7e2d49a | 2016-08-08 17:12:18 -0700 | [diff] [blame] | 73 | bool CheckFileRange(ElfW(Addr) offset, size_t size, size_t alignment); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 74 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 75 | bool did_read_; |
| 76 | bool did_load_; |
| 77 | std::string name_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 78 | int fd_; |
Dmitriy Ivanov | 07e5bc1 | 2014-10-03 17:52:44 -0700 | [diff] [blame] | 79 | off64_t file_offset_; |
Dmitriy Ivanov | 3f987f5 | 2015-06-25 15:51:41 -0700 | [diff] [blame] | 80 | off64_t file_size_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 81 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 82 | ElfW(Ehdr) header_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 83 | size_t phdr_num_; |
| 84 | |
Dmitriy Ivanov | cf1cbbe | 2015-10-19 16:57:46 -0700 | [diff] [blame] | 85 | MappedFileFragment phdr_fragment_; |
| 86 | const ElfW(Phdr)* phdr_table_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 87 | |
Dmitriy Ivanov | 4f7a7ad | 2015-10-15 12:07:25 -0700 | [diff] [blame] | 88 | MappedFileFragment shdr_fragment_; |
| 89 | const ElfW(Shdr)* shdr_table_; |
| 90 | size_t shdr_num_; |
| 91 | |
| 92 | MappedFileFragment dynamic_fragment_; |
| 93 | const ElfW(Dyn)* dynamic_; |
| 94 | |
| 95 | MappedFileFragment strtab_fragment_; |
| 96 | const char* strtab_; |
| 97 | size_t strtab_size_; |
| 98 | |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 99 | // First page of reserved address space. |
| 100 | void* load_start_; |
| 101 | // Size in bytes of reserved address space. |
Elliott Hughes | c00f2cb | 2013-10-04 17:01:33 -0700 | [diff] [blame] | 102 | size_t load_size_; |
Evgenii Stepanov | e0848bb | 2020-07-14 16:44:57 -0700 | [diff] [blame] | 103 | // First page of inaccessible gap mapping reserved for this DSO. |
| 104 | void* gap_start_; |
| 105 | // Size in bytes of the gap mapping. |
| 106 | size_t gap_size_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 107 | // Load bias. |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 108 | ElfW(Addr) load_bias_; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 109 | |
| 110 | // Loaded phdr. |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 111 | const ElfW(Phdr)* loaded_phdr_; |
Dimitry Ivanov | f45b0e9 | 2016-01-15 11:13:35 -0800 | [diff] [blame] | 112 | |
| 113 | // Is map owned by the caller |
| 114 | bool mapped_by_caller_; |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame^] | 115 | |
| 116 | // Only used by AArch64 at the moment. |
| 117 | GnuPropertySection note_gnu_property_ __unused; |
Elliott Hughes | 650be4e | 2013-03-05 18:47:58 -0800 | [diff] [blame] | 118 | }; |
| 119 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 120 | 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] | 121 | ElfW(Addr)* min_vaddr = nullptr, ElfW(Addr)* max_vaddr = nullptr); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 122 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame^] | 123 | int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
| 124 | ElfW(Addr) load_bias, const GnuPropertySection* prop = nullptr); |
Dimitry Ivanov | 56be6ed | 2015-04-01 21:18:48 +0000 | [diff] [blame] | 125 | |
| 126 | int phdr_table_unprotect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
| 127 | ElfW(Addr) load_bias); |
| 128 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 129 | int phdr_table_protect_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count, |
| 130 | ElfW(Addr) load_bias); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 131 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 132 | 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] | 133 | ElfW(Addr) load_bias, int fd, size_t* file_offset); |
Torne (Richard Coles) | 183ad9d | 2014-02-27 13:18:00 +0000 | [diff] [blame] | 134 | |
Dmitriy Ivanov | 20d89cb | 2015-03-30 18:43:38 -0700 | [diff] [blame] | 135 | 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] | 136 | ElfW(Addr) load_bias, int fd, size_t* file_offset); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 137 | |
Elliott Hughes | 4eeb1f1 | 2013-10-25 17:38:02 -0700 | [diff] [blame] | 138 | #if defined(__arm__) |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 139 | 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] | 140 | ElfW(Addr)** arm_exidx, size_t* arm_exidix_count); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 141 | #endif |
| 142 | |
Elliott Hughes | 0266ae5 | 2014-02-10 17:46:57 -0800 | [diff] [blame] | 143 | 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] | 144 | ElfW(Addr) load_bias, ElfW(Dyn)** dynamic, |
| 145 | ElfW(Word)* dynamic_flags); |
David 'Digit' Turner | c1bd559 | 2012-06-19 11:21:29 +0200 | [diff] [blame] | 146 | |
Tamas Petz | 8d55d18 | 2020-02-24 14:15:25 +0100 | [diff] [blame^] | 147 | 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] | 148 | ElfW(Addr) load_bias); |