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
24const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
25const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
26const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
27const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
28
29class 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:
36  plain_reloc_iterator(rel_t* rel_array, size_t count)
37      : begin_(rel_array), end_(begin_ + count), current_(begin_) {}
38
39  bool has_next() {
40    return current_ < end_;
41  }
42
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
54template <typename decoder_t>
55class 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:
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
73  bool has_next() const {
74    return relocation_index_ < relocation_count_;
75  }
76
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:
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
139  bool is_relocation_grouped_by_info() {
140    return (group_flags_ & RELOCATION_GROUPED_BY_INFO_FLAG) != 0;
141  }
142
143  bool is_relocation_grouped_by_offset_delta() {
144    return (group_flags_ & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) != 0;
145  }
146
147  bool is_relocation_grouped_by_addend() {
148    return (group_flags_ & RELOCATION_GROUPED_BY_ADDEND_FLAG) != 0;
149  }
150
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