1 /*
2  * Copyright (C) 2016 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 ART_RUNTIME_IMT_CONFLICT_TABLE_H_
18 #define ART_RUNTIME_IMT_CONFLICT_TABLE_H_
19 
20 #include <cstddef>
21 
22 #include "base/casts.h"
23 #include "base/enums.h"
24 #include "base/macros.h"
25 
26 namespace art {
27 
28 class ArtMethod;
29 
30 // Table to resolve IMT conflicts at runtime. The table is attached to
31 // the jni entrypoint of IMT conflict ArtMethods.
32 // The table contains a list of pairs of { interface_method, implementation_method }
33 // with the last entry being null to make an assembly implementation of a lookup
34 // faster.
35 class ImtConflictTable {
36   enum MethodIndex {
37     kMethodInterface,
38     kMethodImplementation,
39     kMethodCount,  // Number of elements in enum.
40   };
41 
42  public:
43   // Build a new table copying `other` and adding the new entry formed of
44   // the pair { `interface_method`, `implementation_method` }
ImtConflictTable(ImtConflictTable * other,ArtMethod * interface_method,ArtMethod * implementation_method,PointerSize pointer_size)45   ImtConflictTable(ImtConflictTable* other,
46                    ArtMethod* interface_method,
47                    ArtMethod* implementation_method,
48                    PointerSize pointer_size) {
49     const size_t count = other->NumEntries(pointer_size);
50     for (size_t i = 0; i < count; ++i) {
51       SetInterfaceMethod(i, pointer_size, other->GetInterfaceMethod(i, pointer_size));
52       SetImplementationMethod(i, pointer_size, other->GetImplementationMethod(i, pointer_size));
53     }
54     SetInterfaceMethod(count, pointer_size, interface_method);
55     SetImplementationMethod(count, pointer_size, implementation_method);
56     // Add the null marker.
57     SetInterfaceMethod(count + 1, pointer_size, nullptr);
58     SetImplementationMethod(count + 1, pointer_size, nullptr);
59   }
60 
61   // num_entries excludes the header.
ImtConflictTable(size_t num_entries,PointerSize pointer_size)62   ImtConflictTable(size_t num_entries, PointerSize pointer_size) {
63     SetInterfaceMethod(num_entries, pointer_size, nullptr);
64     SetImplementationMethod(num_entries, pointer_size, nullptr);
65   }
66 
67   // Set an entry at an index.
SetInterfaceMethod(size_t index,PointerSize pointer_size,ArtMethod * method)68   void SetInterfaceMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
69     SetMethod(index * kMethodCount + kMethodInterface, pointer_size, method);
70   }
71 
SetImplementationMethod(size_t index,PointerSize pointer_size,ArtMethod * method)72   void SetImplementationMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
73     SetMethod(index * kMethodCount + kMethodImplementation, pointer_size, method);
74   }
75 
GetInterfaceMethod(size_t index,PointerSize pointer_size)76   ArtMethod* GetInterfaceMethod(size_t index, PointerSize pointer_size) const {
77     return GetMethod(index * kMethodCount + kMethodInterface, pointer_size);
78   }
79 
GetImplementationMethod(size_t index,PointerSize pointer_size)80   ArtMethod* GetImplementationMethod(size_t index, PointerSize pointer_size) const {
81     return GetMethod(index * kMethodCount + kMethodImplementation, pointer_size);
82   }
83 
AddressOfInterfaceMethod(size_t index,PointerSize pointer_size)84   void** AddressOfInterfaceMethod(size_t index, PointerSize pointer_size) {
85     return AddressOfMethod(index * kMethodCount + kMethodInterface, pointer_size);
86   }
87 
AddressOfImplementationMethod(size_t index,PointerSize pointer_size)88   void** AddressOfImplementationMethod(size_t index, PointerSize pointer_size) {
89     return AddressOfMethod(index * kMethodCount + kMethodImplementation, pointer_size);
90   }
91 
92   // Return true if two conflict tables are the same.
Equals(ImtConflictTable * other,PointerSize pointer_size)93   bool Equals(ImtConflictTable* other, PointerSize pointer_size) const {
94     size_t num = NumEntries(pointer_size);
95     if (num != other->NumEntries(pointer_size)) {
96       return false;
97     }
98     for (size_t i = 0; i < num; ++i) {
99       if (GetInterfaceMethod(i, pointer_size) != other->GetInterfaceMethod(i, pointer_size) ||
100           GetImplementationMethod(i, pointer_size) !=
101               other->GetImplementationMethod(i, pointer_size)) {
102         return false;
103       }
104     }
105     return true;
106   }
107 
108   // Visit all of the entries.
109   // NO_THREAD_SAFETY_ANALYSIS for calling with held locks. Visitor is passed a pair of ArtMethod*
110   // and also returns one. The order is <interface, implementation>.
111   template<typename Visitor>
Visit(const Visitor & visitor,PointerSize pointer_size)112   void Visit(const Visitor& visitor, PointerSize pointer_size) NO_THREAD_SAFETY_ANALYSIS {
113     uint32_t table_index = 0;
114     for (;;) {
115       ArtMethod* interface_method = GetInterfaceMethod(table_index, pointer_size);
116       if (interface_method == nullptr) {
117         break;
118       }
119       ArtMethod* implementation_method = GetImplementationMethod(table_index, pointer_size);
120       auto input = std::make_pair(interface_method, implementation_method);
121       std::pair<ArtMethod*, ArtMethod*> updated = visitor(input);
122       if (input.first != updated.first) {
123         SetInterfaceMethod(table_index, pointer_size, updated.first);
124       }
125       if (input.second != updated.second) {
126         SetImplementationMethod(table_index, pointer_size, updated.second);
127       }
128       ++table_index;
129     }
130   }
131 
132   // Lookup the implementation ArtMethod associated to `interface_method`. Return null
133   // if not found.
Lookup(ArtMethod * interface_method,PointerSize pointer_size)134   ArtMethod* Lookup(ArtMethod* interface_method, PointerSize pointer_size) const {
135     uint32_t table_index = 0;
136     for (;;) {
137       ArtMethod* current_interface_method = GetInterfaceMethod(table_index, pointer_size);
138       if (current_interface_method == nullptr) {
139         break;
140       }
141       if (current_interface_method == interface_method) {
142         return GetImplementationMethod(table_index, pointer_size);
143       }
144       ++table_index;
145     }
146     return nullptr;
147   }
148 
149   // Compute the number of entries in this table.
NumEntries(PointerSize pointer_size)150   size_t NumEntries(PointerSize pointer_size) const {
151     uint32_t table_index = 0;
152     while (GetInterfaceMethod(table_index, pointer_size) != nullptr) {
153       ++table_index;
154     }
155     return table_index;
156   }
157 
158   // Compute the size in bytes taken by this table.
ComputeSize(PointerSize pointer_size)159   size_t ComputeSize(PointerSize pointer_size) const {
160     // Add the end marker.
161     return ComputeSize(NumEntries(pointer_size), pointer_size);
162   }
163 
164   // Compute the size in bytes needed for copying the given `table` and add
165   // one more entry.
ComputeSizeWithOneMoreEntry(ImtConflictTable * table,PointerSize pointer_size)166   static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table, PointerSize pointer_size) {
167     return table->ComputeSize(pointer_size) + EntrySize(pointer_size);
168   }
169 
170   // Compute size with a fixed number of entries.
ComputeSize(size_t num_entries,PointerSize pointer_size)171   static size_t ComputeSize(size_t num_entries, PointerSize pointer_size) {
172     return (num_entries + 1) * EntrySize(pointer_size);  // Add one for null terminator.
173   }
174 
EntrySize(PointerSize pointer_size)175   static size_t EntrySize(PointerSize pointer_size) {
176     return static_cast<size_t>(pointer_size) * static_cast<size_t>(kMethodCount);
177   }
178 
179  private:
AddressOfMethod(size_t index,PointerSize pointer_size)180   void** AddressOfMethod(size_t index, PointerSize pointer_size) {
181     if (pointer_size == PointerSize::k64) {
182       return reinterpret_cast<void**>(&data64_[index]);
183     } else {
184       return reinterpret_cast<void**>(&data32_[index]);
185     }
186   }
187 
GetMethod(size_t index,PointerSize pointer_size)188   ArtMethod* GetMethod(size_t index, PointerSize pointer_size) const {
189     if (pointer_size == PointerSize::k64) {
190       return reinterpret_cast64<ArtMethod*>(data64_[index]);
191     } else {
192       return reinterpret_cast32<ArtMethod*>(data32_[index]);
193     }
194   }
195 
SetMethod(size_t index,PointerSize pointer_size,ArtMethod * method)196   void SetMethod(size_t index, PointerSize pointer_size, ArtMethod* method) {
197     if (pointer_size == PointerSize::k64) {
198       data64_[index] = reinterpret_cast64<uint64_t>(method);
199     } else {
200       data32_[index] = reinterpret_cast32<uint32_t>(method);
201     }
202   }
203 
204   // Array of entries that the assembly stubs will iterate over. Note that this is
205   // not fixed size, and we allocate data prior to calling the constructor
206   // of ImtConflictTable.
207   union {
208     uint32_t data32_[0];
209     uint64_t data64_[0];
210   };
211 
212   DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
213 };
214 
215 }  // namespace art
216 
217 #endif  // ART_RUNTIME_IMT_CONFLICT_TABLE_H_
218