1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef __LINKER_RELOC_ITERATORS_H 18 #define __LINKER_RELOC_ITERATORS_H 19 20 #include "linker.h" 21 22 #include <string.h> 23 24 const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1; 25 const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2; 26 const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4; 27 const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8; 28 29 class plain_reloc_iterator { 30 #if defined(USE_RELA) 31 typedef ElfW(Rela) rel_t; 32 #else 33 typedef ElfW(Rel) rel_t; 34 #endif 35 public: plain_reloc_iterator(rel_t * rel_array,size_t count)36 plain_reloc_iterator(rel_t* rel_array, size_t count) 37 : begin_(rel_array), end_(begin_ + count), current_(begin_) {} 38 has_next()39 bool has_next() { 40 return current_ < end_; 41 } 42 next()43 rel_t* next() { 44 return current_++; 45 } 46 private: 47 rel_t* const begin_; 48 rel_t* const end_; 49 rel_t* current_; 50 51 DISALLOW_COPY_AND_ASSIGN(plain_reloc_iterator); 52 }; 53 54 template <typename decoder_t> 55 class packed_reloc_iterator { 56 #if defined(USE_RELA) 57 typedef ElfW(Rela) rel_t; 58 #else 59 typedef ElfW(Rel) rel_t; 60 #endif 61 public: packed_reloc_iterator(decoder_t && decoder)62 explicit packed_reloc_iterator(decoder_t&& decoder) 63 : decoder_(decoder) { 64 // initialize fields 65 memset(&reloc_, 0, sizeof(reloc_)); 66 relocation_count_ = decoder_.pop_front(); 67 reloc_.r_offset = decoder_.pop_front(); 68 relocation_index_ = 0; 69 relocation_group_index_ = 0; 70 group_size_ = 0; 71 } 72 has_next()73 bool has_next() const { 74 return relocation_index_ < relocation_count_; 75 } 76 next()77 rel_t* next() { 78 if (relocation_group_index_ == group_size_) { 79 if (!read_group_fields()) { 80 // Iterator is inconsistent state; it should not be called again 81 // but in case it is let's make sure has_next() returns false. 82 relocation_index_ = relocation_count_ = 0; 83 return nullptr; 84 } 85 } 86 87 if (is_relocation_grouped_by_offset_delta()) { 88 reloc_.r_offset += group_r_offset_delta_; 89 } else { 90 reloc_.r_offset += decoder_.pop_front(); 91 } 92 93 if (!is_relocation_grouped_by_info()) { 94 reloc_.r_info = decoder_.pop_front(); 95 } 96 97 #if defined(USE_RELA) 98 if (is_relocation_group_has_addend() && 99 !is_relocation_grouped_by_addend()) { 100 reloc_.r_addend += decoder_.pop_front(); 101 } 102 #endif 103 104 relocation_index_++; 105 relocation_group_index_++; 106 107 return &reloc_; 108 } 109 private: read_group_fields()110 bool read_group_fields() { 111 group_size_ = decoder_.pop_front(); 112 group_flags_ = decoder_.pop_front(); 113 114 if (is_relocation_grouped_by_offset_delta()) { 115 group_r_offset_delta_ = decoder_.pop_front(); 116 } 117 118 if (is_relocation_grouped_by_info()) { 119 reloc_.r_info = decoder_.pop_front(); 120 } 121 122 if (is_relocation_group_has_addend() && 123 is_relocation_grouped_by_addend()) { 124 #if !defined(USE_RELA) 125 // This platform does not support rela, and yet we have it encoded in android_rel section. 126 DL_ERR("unexpected r_addend in android.rel section"); 127 return false; 128 #else 129 reloc_.r_addend += decoder_.pop_front(); 130 } else if (!is_relocation_group_has_addend()) { 131 reloc_.r_addend = 0; 132 #endif 133 } 134 135 relocation_group_index_ = 0; 136 return true; 137 } 138 is_relocation_grouped_by_info()139 bool is_relocation_grouped_by_info() { 140 return (group_flags_ & RELOCATION_GROUPED_BY_INFO_FLAG) != 0; 141 } 142 is_relocation_grouped_by_offset_delta()143 bool is_relocation_grouped_by_offset_delta() { 144 return (group_flags_ & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0; 145 } 146 is_relocation_grouped_by_addend()147 bool is_relocation_grouped_by_addend() { 148 return (group_flags_ & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0; 149 } 150 is_relocation_group_has_addend()151 bool is_relocation_group_has_addend() { 152 return (group_flags_ & RELOCATION_GROUP_HAS_ADDEND_FLAG) != 0; 153 } 154 155 decoder_t decoder_; 156 size_t relocation_count_; 157 size_t group_size_; 158 size_t group_flags_; 159 size_t group_r_offset_delta_; 160 size_t relocation_index_; 161 size_t relocation_group_index_; 162 rel_t reloc_; 163 }; 164 165 #endif // __LINKER_RELOC_ITERATORS_H 166