1 /* 2 * Copyright (C) 2015 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 #pragma once 30 31 #include "linker.h" 32 33 #include <string.h> 34 35 const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1; 36 const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2; 37 const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4; 38 const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8; 39 40 class plain_reloc_iterator { 41 #if defined(USE_RELA) 42 typedef ElfW(Rela) rel_t; 43 #else 44 typedef ElfW(Rel) rel_t; 45 #endif 46 public: plain_reloc_iterator(rel_t * rel_array,size_t count)47 plain_reloc_iterator(rel_t* rel_array, size_t count) 48 : begin_(rel_array), end_(begin_ + count), current_(begin_) {} 49 has_next()50 bool has_next() { 51 return current_ < end_; 52 } 53 next()54 rel_t* next() { 55 return current_++; 56 } 57 private: 58 rel_t* const begin_; 59 rel_t* const end_; 60 rel_t* current_; 61 62 DISALLOW_COPY_AND_ASSIGN(plain_reloc_iterator); 63 }; 64 65 template <typename decoder_t> 66 class packed_reloc_iterator { 67 #if defined(USE_RELA) 68 typedef ElfW(Rela) rel_t; 69 #else 70 typedef ElfW(Rel) rel_t; 71 #endif 72 public: packed_reloc_iterator(decoder_t && decoder)73 explicit packed_reloc_iterator(decoder_t&& decoder) 74 : decoder_(decoder) { 75 // initialize fields 76 memset(&reloc_, 0, sizeof(reloc_)); 77 relocation_count_ = decoder_.pop_front(); 78 reloc_.r_offset = decoder_.pop_front(); 79 relocation_index_ = 0; 80 relocation_group_index_ = 0; 81 group_size_ = 0; 82 } 83 has_next()84 bool has_next() const { 85 return relocation_index_ < relocation_count_; 86 } 87 next()88 rel_t* next() { 89 if (relocation_group_index_ == group_size_) { 90 if (!read_group_fields()) { 91 // Iterator is inconsistent state; it should not be called again 92 // but in case it is let's make sure has_next() returns false. 93 relocation_index_ = relocation_count_ = 0; 94 return nullptr; 95 } 96 } 97 98 if (is_relocation_grouped_by_offset_delta()) { 99 reloc_.r_offset += group_r_offset_delta_; 100 } else { 101 reloc_.r_offset += decoder_.pop_front(); 102 } 103 104 if (!is_relocation_grouped_by_info()) { 105 reloc_.r_info = decoder_.pop_front(); 106 } 107 108 #if defined(USE_RELA) 109 if (is_relocation_group_has_addend() && 110 !is_relocation_grouped_by_addend()) { 111 reloc_.r_addend += decoder_.pop_front(); 112 } 113 #endif 114 115 relocation_index_++; 116 relocation_group_index_++; 117 118 return &reloc_; 119 } 120 private: read_group_fields()121 bool read_group_fields() { 122 group_size_ = decoder_.pop_front(); 123 group_flags_ = decoder_.pop_front(); 124 125 if (is_relocation_grouped_by_offset_delta()) { 126 group_r_offset_delta_ = decoder_.pop_front(); 127 } 128 129 if (is_relocation_grouped_by_info()) { 130 reloc_.r_info = decoder_.pop_front(); 131 } 132 133 if (is_relocation_group_has_addend() && 134 is_relocation_grouped_by_addend()) { 135 #if !defined(USE_RELA) 136 // This platform does not support rela, and yet we have it encoded in android_rel section. 137 DL_ERR("unexpected r_addend in android.rel section"); 138 return false; 139 #else 140 reloc_.r_addend += decoder_.pop_front(); 141 } else if (!is_relocation_group_has_addend()) { 142 reloc_.r_addend = 0; 143 #endif 144 } 145 146 relocation_group_index_ = 0; 147 return true; 148 } 149 is_relocation_grouped_by_info()150 bool is_relocation_grouped_by_info() { 151 return (group_flags_ & RELOCATION_GROUPED_BY_INFO_FLAG) != 0; 152 } 153 is_relocation_grouped_by_offset_delta()154 bool is_relocation_grouped_by_offset_delta() { 155 return (group_flags_ & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0; 156 } 157 is_relocation_grouped_by_addend()158 bool is_relocation_grouped_by_addend() { 159 return (group_flags_ & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0; 160 } 161 is_relocation_group_has_addend()162 bool is_relocation_group_has_addend() { 163 return (group_flags_ & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0; 164 } 165 166 decoder_t decoder_; 167 size_t relocation_count_; 168 size_t group_size_; 169 size_t group_flags_; 170 size_t group_r_offset_delta_; 171 size_t relocation_index_; 172 size_t relocation_group_index_; 173 rel_t reloc_; 174 }; 175