1 /*
2 * Copyright (C) 2011 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 #include "oat.h"
18
19 #include <string.h>
20 #include <zlib.h>
21
22 #include "android-base/logging.h"
23 #include "android-base/stringprintf.h"
24 #include "arch/instruction_set.h"
25 #include "arch/instruction_set_features.h"
26 #include "base/bit_utils.h"
27 #include "base/strlcpy.h"
28
29 namespace art HIDDEN {
30
31 using android::base::StringPrintf;
32
ComputeOatHeaderSize(const SafeMap<std::string,std::string> * variable_data)33 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
34 size_t estimate = 0U;
35 if (variable_data != nullptr) {
36 SafeMap<std::string, std::string>::const_iterator it = variable_data->begin();
37 SafeMap<std::string, std::string>::const_iterator end = variable_data->end();
38 for ( ; it != end; ++it) {
39 estimate += it->first.length() + 1;
40 estimate += it->second.length() + 1;
41
42 size_t non_deterministic_field_length = OatHeader::GetNonDeterministicFieldLength(it->first);
43 if (non_deterministic_field_length > 0u) {
44 DCHECK_LE(it->second.length(), non_deterministic_field_length);
45 size_t padding = non_deterministic_field_length - it->second.length();
46 estimate += padding;
47 }
48 }
49 }
50 return sizeof(OatHeader) + estimate;
51 }
52
Create(InstructionSet instruction_set,const InstructionSetFeatures * instruction_set_features,uint32_t dex_file_count,const SafeMap<std::string,std::string> * variable_data,uint32_t base_oat_offset)53 OatHeader* OatHeader::Create(InstructionSet instruction_set,
54 const InstructionSetFeatures* instruction_set_features,
55 uint32_t dex_file_count,
56 const SafeMap<std::string, std::string>* variable_data,
57 uint32_t base_oat_offset) {
58 // Estimate size of optional data.
59 size_t needed_size = ComputeOatHeaderSize(variable_data);
60
61 // Reserve enough memory.
62 void* memory = operator new (needed_size);
63
64 // Create the OatHeader in-place.
65 return new (memory) OatHeader(instruction_set,
66 instruction_set_features,
67 dex_file_count,
68 variable_data,
69 base_oat_offset);
70 }
71
Delete(OatHeader * header)72 void OatHeader::Delete(OatHeader* header) {
73 if (header != nullptr) {
74 size_t size = header->GetHeaderSize();
75 header->~OatHeader();
76 operator delete(header, size);
77 }
78 }
79
OatHeader(InstructionSet instruction_set,const InstructionSetFeatures * instruction_set_features,uint32_t dex_file_count,const SafeMap<std::string,std::string> * variable_data,uint32_t base_oat_offset)80 OatHeader::OatHeader(InstructionSet instruction_set,
81 const InstructionSetFeatures* instruction_set_features,
82 uint32_t dex_file_count,
83 const SafeMap<std::string, std::string>* variable_data,
84 uint32_t base_oat_offset)
85 : oat_checksum_(0u),
86 instruction_set_(instruction_set),
87 instruction_set_features_bitmap_(instruction_set_features->AsBitmap()),
88 dex_file_count_(dex_file_count),
89 oat_dex_files_offset_(0),
90 bcp_bss_info_offset_(0),
91 base_oat_offset_(base_oat_offset),
92 executable_offset_(0),
93 jni_dlsym_lookup_trampoline_offset_(0),
94 jni_dlsym_lookup_critical_trampoline_offset_(0),
95 quick_generic_jni_trampoline_offset_(0),
96 quick_imt_conflict_trampoline_offset_(0),
97 quick_resolution_trampoline_offset_(0),
98 quick_to_interpreter_bridge_offset_(0),
99 nterp_trampoline_offset_(0) {
100 // Don't want asserts in header as they would be checked in each file that includes it. But the
101 // fields are private, so we check inside a method.
102 static_assert(decltype(magic_)().size() == kOatMagic.size(),
103 "Oat magic and magic_ have different lengths.");
104 static_assert(decltype(version_)().size() == kOatVersion.size(),
105 "Oat version and version_ have different lengths.");
106
107 magic_ = kOatMagic;
108 version_ = kOatVersion;
109
110 CHECK_NE(instruction_set, InstructionSet::kNone);
111
112 // Flatten the map. Will also update variable_size_data_size_.
113 Flatten(variable_data);
114 }
115
IsValid() const116 bool OatHeader::IsValid() const {
117 if (magic_ != kOatMagic) {
118 return false;
119 }
120 if (version_ != kOatVersion) {
121 return false;
122 }
123 // Only check the offset is valid after it has been set.
124 if (executable_offset_ != 0u &&
125 !IsAligned<kElfSegmentAlignment>(executable_offset_ + base_oat_offset_)) {
126 return false;
127 }
128 if (!IsValidInstructionSet(instruction_set_)) {
129 return false;
130 }
131 return true;
132 }
133
GetValidationErrorMessage() const134 std::string OatHeader::GetValidationErrorMessage() const {
135 if (magic_ != kOatMagic) {
136 static_assert(kOatMagic.size() == 4, "kOatMagic has unexpected length");
137 return StringPrintf("Invalid oat magic, expected 0x%02x%02x%02x%02x, got 0x%02x%02x%02x%02x.",
138 kOatMagic[0], kOatMagic[1], kOatMagic[2], kOatMagic[3],
139 magic_[0], magic_[1], magic_[2], magic_[3]);
140 }
141 if (version_ != kOatVersion) {
142 static_assert(kOatVersion.size() == 4, "kOatVersion has unexpected length");
143 return StringPrintf("Invalid oat version, expected 0x%02x%02x%02x%02x, got 0x%02x%02x%02x%02x.",
144 kOatVersion[0], kOatVersion[1], kOatVersion[2], kOatVersion[3],
145 version_[0], version_[1], version_[2], version_[3]);
146 }
147 // Only check the offset is valid after it has been set.
148 if (executable_offset_ != 0u &&
149 !IsAligned<kElfSegmentAlignment>(executable_offset_ + base_oat_offset_)) {
150 return "Executable offset not properly aligned.";
151 }
152 if (!IsValidInstructionSet(instruction_set_)) {
153 return StringPrintf("Invalid instruction set, %d.", static_cast<int>(instruction_set_));
154 }
155 return "";
156 }
157
158 // Do not move this into the header. The method must be compiled in the runtime library,
159 // so that we can check that the compile-time oat version matches the version in the caller.
CheckOatVersion(std::array<uint8_t,4> version)160 void OatHeader::CheckOatVersion(std::array<uint8_t, 4> version) {
161 constexpr std::array<uint8_t, 4> expected = kOatVersion; // Runtime oat version.
162 if (version != kOatVersion) {
163 LOG(FATAL) << StringPrintf("Invalid oat version, expected 0x%02x%02x%02x%02x, "
164 "got 0x%02x%02x%02x%02x.",
165 expected[0], expected[1], expected[2], expected[3],
166 version[0], version[1], version[2], version[3]);
167 }
168 }
169
GetMagic() const170 const char* OatHeader::GetMagic() const {
171 CHECK(IsValid());
172 return reinterpret_cast<const char*>(magic_.data());
173 }
174
GetChecksum() const175 uint32_t OatHeader::GetChecksum() const {
176 CHECK(IsValid());
177 return oat_checksum_;
178 }
179
SetChecksum(uint32_t oat_checksum)180 void OatHeader::SetChecksum(uint32_t oat_checksum) {
181 oat_checksum_ = oat_checksum;
182 }
183
GetInstructionSet() const184 InstructionSet OatHeader::GetInstructionSet() const {
185 CHECK(IsValid());
186 return instruction_set_;
187 }
188
GetInstructionSetFeaturesBitmap() const189 uint32_t OatHeader::GetInstructionSetFeaturesBitmap() const {
190 CHECK(IsValid());
191 return instruction_set_features_bitmap_;
192 }
193
GetOatDexFilesOffset() const194 uint32_t OatHeader::GetOatDexFilesOffset() const {
195 DCHECK(IsValid());
196 DCHECK_GT(oat_dex_files_offset_, sizeof(OatHeader));
197 return oat_dex_files_offset_;
198 }
199
SetOatDexFilesOffset(uint32_t oat_dex_files_offset)200 void OatHeader::SetOatDexFilesOffset(uint32_t oat_dex_files_offset) {
201 DCHECK_GT(oat_dex_files_offset, sizeof(OatHeader));
202 DCHECK(IsValid());
203 DCHECK_EQ(oat_dex_files_offset_, 0u);
204
205 oat_dex_files_offset_ = oat_dex_files_offset;
206 }
207
GetBcpBssInfoOffset() const208 uint32_t OatHeader::GetBcpBssInfoOffset() const {
209 DCHECK(IsValid());
210 DCHECK(bcp_bss_info_offset_ == 0u || bcp_bss_info_offset_ > sizeof(OatHeader))
211 << "bcp_bss_info_offset_: " << bcp_bss_info_offset_
212 << "sizeof(OatHeader): " << sizeof(OatHeader);
213 return bcp_bss_info_offset_;
214 }
215
SetBcpBssInfoOffset(uint32_t bcp_info_offset)216 void OatHeader::SetBcpBssInfoOffset(uint32_t bcp_info_offset) {
217 DCHECK_GT(bcp_info_offset, sizeof(OatHeader));
218 DCHECK(IsValid());
219 DCHECK_EQ(bcp_bss_info_offset_, 0u);
220
221 bcp_bss_info_offset_ = bcp_info_offset;
222 }
223
GetExecutableOffset() const224 uint32_t OatHeader::GetExecutableOffset() const {
225 DCHECK(IsValid());
226 DCHECK_ALIGNED(executable_offset_ + base_oat_offset_, kElfSegmentAlignment);
227 CHECK_GT(executable_offset_, sizeof(OatHeader));
228 return executable_offset_;
229 }
230
SetExecutableOffset(uint32_t executable_offset)231 void OatHeader::SetExecutableOffset(uint32_t executable_offset) {
232 DCHECK_ALIGNED(executable_offset + base_oat_offset_, kElfSegmentAlignment);
233 CHECK_GT(executable_offset, sizeof(OatHeader));
234 DCHECK(IsValid());
235 DCHECK_EQ(executable_offset_, 0U);
236
237 executable_offset_ = executable_offset;
238 }
239
GetTrampoline(const OatHeader & header,uint32_t offset)240 static const void* GetTrampoline(const OatHeader& header, uint32_t offset) {
241 return (offset != 0u) ? reinterpret_cast<const uint8_t*>(&header) + offset : nullptr;
242 }
243
GetJniDlsymLookupTrampoline() const244 const void* OatHeader::GetJniDlsymLookupTrampoline() const {
245 return GetTrampoline(*this, GetJniDlsymLookupTrampolineOffset());
246 }
247
GetJniDlsymLookupTrampolineOffset() const248 uint32_t OatHeader::GetJniDlsymLookupTrampolineOffset() const {
249 DCHECK(IsValid());
250 return jni_dlsym_lookup_trampoline_offset_;
251 }
252
SetJniDlsymLookupTrampolineOffset(uint32_t offset)253 void OatHeader::SetJniDlsymLookupTrampolineOffset(uint32_t offset) {
254 DCHECK(IsValid());
255 DCHECK_EQ(jni_dlsym_lookup_trampoline_offset_, 0U) << offset;
256
257 jni_dlsym_lookup_trampoline_offset_ = offset;
258 }
259
GetJniDlsymLookupCriticalTrampoline() const260 const void* OatHeader::GetJniDlsymLookupCriticalTrampoline() const {
261 return GetTrampoline(*this, GetJniDlsymLookupCriticalTrampolineOffset());
262 }
263
GetJniDlsymLookupCriticalTrampolineOffset() const264 uint32_t OatHeader::GetJniDlsymLookupCriticalTrampolineOffset() const {
265 DCHECK(IsValid());
266 return jni_dlsym_lookup_critical_trampoline_offset_;
267 }
268
SetJniDlsymLookupCriticalTrampolineOffset(uint32_t offset)269 void OatHeader::SetJniDlsymLookupCriticalTrampolineOffset(uint32_t offset) {
270 DCHECK(IsValid());
271 DCHECK_EQ(jni_dlsym_lookup_critical_trampoline_offset_, 0U) << offset;
272
273 jni_dlsym_lookup_critical_trampoline_offset_ = offset;
274 }
275
GetQuickGenericJniTrampoline() const276 const void* OatHeader::GetQuickGenericJniTrampoline() const {
277 return GetTrampoline(*this, GetQuickGenericJniTrampolineOffset());
278 }
279
GetQuickGenericJniTrampolineOffset() const280 uint32_t OatHeader::GetQuickGenericJniTrampolineOffset() const {
281 DCHECK(IsValid());
282 CHECK_GE(quick_generic_jni_trampoline_offset_, jni_dlsym_lookup_trampoline_offset_);
283 return quick_generic_jni_trampoline_offset_;
284 }
285
SetQuickGenericJniTrampolineOffset(uint32_t offset)286 void OatHeader::SetQuickGenericJniTrampolineOffset(uint32_t offset) {
287 CHECK(offset == 0 || offset >= jni_dlsym_lookup_trampoline_offset_);
288 DCHECK(IsValid());
289 DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
290
291 quick_generic_jni_trampoline_offset_ = offset;
292 }
293
GetQuickImtConflictTrampoline() const294 const void* OatHeader::GetQuickImtConflictTrampoline() const {
295 return GetTrampoline(*this, GetQuickImtConflictTrampolineOffset());
296 }
297
GetQuickImtConflictTrampolineOffset() const298 uint32_t OatHeader::GetQuickImtConflictTrampolineOffset() const {
299 DCHECK(IsValid());
300 CHECK_GE(quick_imt_conflict_trampoline_offset_, quick_generic_jni_trampoline_offset_);
301 return quick_imt_conflict_trampoline_offset_;
302 }
303
SetQuickImtConflictTrampolineOffset(uint32_t offset)304 void OatHeader::SetQuickImtConflictTrampolineOffset(uint32_t offset) {
305 CHECK(offset == 0 || offset >= quick_generic_jni_trampoline_offset_);
306 DCHECK(IsValid());
307 DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset;
308
309 quick_imt_conflict_trampoline_offset_ = offset;
310 }
311
GetQuickResolutionTrampoline() const312 const void* OatHeader::GetQuickResolutionTrampoline() const {
313 return GetTrampoline(*this, GetQuickResolutionTrampolineOffset());
314 }
315
GetQuickResolutionTrampolineOffset() const316 uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const {
317 DCHECK(IsValid());
318 CHECK_GE(quick_resolution_trampoline_offset_, quick_imt_conflict_trampoline_offset_);
319 return quick_resolution_trampoline_offset_;
320 }
321
SetQuickResolutionTrampolineOffset(uint32_t offset)322 void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) {
323 CHECK(offset == 0 || offset >= quick_imt_conflict_trampoline_offset_);
324 DCHECK(IsValid());
325 DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
326
327 quick_resolution_trampoline_offset_ = offset;
328 }
329
GetQuickToInterpreterBridge() const330 const void* OatHeader::GetQuickToInterpreterBridge() const {
331 return GetTrampoline(*this, GetQuickToInterpreterBridgeOffset());
332 }
333
GetQuickToInterpreterBridgeOffset() const334 uint32_t OatHeader::GetQuickToInterpreterBridgeOffset() const {
335 DCHECK(IsValid());
336 CHECK_GE(quick_to_interpreter_bridge_offset_, quick_resolution_trampoline_offset_);
337 return quick_to_interpreter_bridge_offset_;
338 }
339
SetQuickToInterpreterBridgeOffset(uint32_t offset)340 void OatHeader::SetQuickToInterpreterBridgeOffset(uint32_t offset) {
341 CHECK(offset == 0 || offset >= quick_resolution_trampoline_offset_);
342 DCHECK(IsValid());
343 DCHECK_EQ(quick_to_interpreter_bridge_offset_, 0U) << offset;
344
345 quick_to_interpreter_bridge_offset_ = offset;
346 }
347
GetNterpTrampoline() const348 const void* OatHeader::GetNterpTrampoline() const {
349 return GetTrampoline(*this, GetNterpTrampolineOffset());
350 }
351
GetNterpTrampolineOffset() const352 uint32_t OatHeader::GetNterpTrampolineOffset() const {
353 DCHECK(IsValid());
354 CHECK_GE(nterp_trampoline_offset_, quick_to_interpreter_bridge_offset_);
355 return nterp_trampoline_offset_;
356 }
357
SetNterpTrampolineOffset(uint32_t offset)358 void OatHeader::SetNterpTrampolineOffset(uint32_t offset) {
359 CHECK(offset == 0 || offset >= quick_to_interpreter_bridge_offset_);
360 DCHECK(IsValid());
361 DCHECK_EQ(nterp_trampoline_offset_, 0U) << offset;
362
363 nterp_trampoline_offset_ = offset;
364 }
365
GetKeyValueStoreSize() const366 uint32_t OatHeader::GetKeyValueStoreSize() const {
367 CHECK(IsValid());
368 return key_value_store_size_;
369 }
370
GetKeyValueStore() const371 const uint8_t* OatHeader::GetKeyValueStore() const {
372 CHECK(IsValid());
373 return key_value_store_;
374 }
375
GetStoreValueByKeyUnsafe(const char * key) const376 const char* OatHeader::GetStoreValueByKeyUnsafe(const char* key) const {
377 std::string_view key_view(key);
378
379 uint32_t offset = 0;
380 const char* current_key;
381 const char* value;
382 while (GetNextStoreKeyValuePair(&offset, ¤t_key, &value)) {
383 if (key_view == current_key) {
384 // Same as key.
385 return value;
386 }
387 }
388
389 // Not found.
390 return nullptr;
391 }
392
GetNextStoreKeyValuePair(uint32_t * offset,const char ** key,const char ** value) const393 bool OatHeader::GetNextStoreKeyValuePair(/*inout*/ uint32_t* offset,
394 /*out*/ const char** key,
395 /*out*/ const char** value) const {
396 if (*offset >= key_value_store_size_) {
397 return false;
398 }
399
400 const char* start = reinterpret_cast<const char*>(&key_value_store_);
401 const char* ptr = start + *offset;
402 const char* end = start + key_value_store_size_;
403
404 // Scan for a closing zero.
405 const char* str_end = reinterpret_cast<const char*>(memchr(ptr, 0, end - ptr));
406 if (UNLIKELY(str_end == nullptr)) {
407 LOG(WARNING) << "OatHeader: Unterminated key in key value store.";
408 return false;
409 }
410 const char* value_start = str_end + 1;
411 const char* value_end = reinterpret_cast<const char*>(memchr(value_start, 0, end - value_start));
412 if (UNLIKELY(value_end == nullptr)) {
413 LOG(WARNING) << "OatHeader: Unterminated value in key value store.";
414 return false;
415 }
416
417 *key = ptr;
418 *value = value_start;
419
420 // Advance over the value.
421 size_t key_len = str_end - ptr;
422 size_t value_len = value_end - value_start;
423 size_t non_deterministic_field_length = GetNonDeterministicFieldLength(*key);
424 if (non_deterministic_field_length > 0u) {
425 if (UNLIKELY(value_len > non_deterministic_field_length)) {
426 LOG(WARNING) << "OatHeader: Non-deterministic field too long in key value store.";
427 return false;
428 }
429 *offset += key_len + 1 + non_deterministic_field_length + 1;
430 } else {
431 *offset += key_len + 1 + value_len + 1;
432 }
433
434 return true;
435 }
436
ComputeChecksum(uint32_t * checksum) const437 void OatHeader::ComputeChecksum(/*inout*/ uint32_t* checksum) const {
438 *checksum = adler32(*checksum, reinterpret_cast<const uint8_t*>(this), sizeof(OatHeader));
439
440 uint32_t last_offset = 0;
441 uint32_t offset = 0;
442 const char* key;
443 const char* value;
444 while (GetNextStoreKeyValuePair(&offset, &key, &value)) {
445 if (IsDeterministicField(key)) {
446 // Update the checksum.
447 *checksum = adler32(*checksum, GetKeyValueStore() + last_offset, offset - last_offset);
448 }
449 last_offset = offset;
450 }
451 }
452
GetHeaderSize() const453 size_t OatHeader::GetHeaderSize() const {
454 return sizeof(OatHeader) + key_value_store_size_;
455 }
456
IsDebuggable() const457 bool OatHeader::IsDebuggable() const {
458 return IsKeyEnabled(OatHeader::kDebuggableKey);
459 }
460
IsConcurrentCopying() const461 bool OatHeader::IsConcurrentCopying() const {
462 return IsKeyEnabled(OatHeader::kConcurrentCopying);
463 }
464
IsNativeDebuggable() const465 bool OatHeader::IsNativeDebuggable() const {
466 return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
467 }
468
RequiresImage() const469 bool OatHeader::RequiresImage() const {
470 return IsKeyEnabled(OatHeader::kRequiresImage);
471 }
472
GetCompilerFilter() const473 CompilerFilter::Filter OatHeader::GetCompilerFilter() const {
474 CompilerFilter::Filter filter;
475 const char* key_value = GetStoreValueByKey(kCompilerFilter);
476 CHECK(key_value != nullptr) << "compiler-filter not found in oat header";
477 CHECK(CompilerFilter::ParseCompilerFilter(key_value, &filter))
478 << "Invalid compiler-filter in oat header: " << key_value;
479 return filter;
480 }
481
KeyHasValue(const char * key,const char * value,size_t value_size) const482 bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const {
483 const char* key_value = GetStoreValueByKey(key);
484 return (key_value != nullptr && strncmp(key_value, value, value_size) == 0);
485 }
486
IsKeyEnabled(const char * key) const487 bool OatHeader::IsKeyEnabled(const char* key) const {
488 return KeyHasValue(key, kTrueValue, sizeof(kTrueValue));
489 }
490
Flatten(const SafeMap<std::string,std::string> * key_value_store)491 void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
492 char* data_ptr = reinterpret_cast<char*>(&key_value_store_);
493 if (key_value_store != nullptr) {
494 SafeMap<std::string, std::string>::const_iterator it = key_value_store->begin();
495 SafeMap<std::string, std::string>::const_iterator end = key_value_store->end();
496 for ( ; it != end; ++it) {
497 strlcpy(data_ptr, it->first.c_str(), it->first.length() + 1);
498 data_ptr += it->first.length() + 1;
499 strlcpy(data_ptr, it->second.c_str(), it->second.length() + 1);
500 data_ptr += it->second.length() + 1;
501
502 size_t non_deterministic_field_length = GetNonDeterministicFieldLength(it->first);
503 if (non_deterministic_field_length > 0u) {
504 DCHECK_LE(it->second.length(), non_deterministic_field_length);
505 size_t padding = non_deterministic_field_length - it->second.length();
506 memset(data_ptr, 0, padding);
507 data_ptr += padding;
508 }
509 }
510 }
511 key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_);
512 }
513
GetOatAddress(StubType type) const514 const uint8_t* OatHeader::GetOatAddress(StubType type) const {
515 DCHECK_LE(type, StubType::kLast);
516 switch (type) {
517 // TODO: We could maybe clean this up if we stored them in an array in the oat header.
518 case StubType::kQuickGenericJNITrampoline:
519 return static_cast<const uint8_t*>(GetQuickGenericJniTrampoline());
520 case StubType::kJNIDlsymLookupTrampoline:
521 return static_cast<const uint8_t*>(GetJniDlsymLookupTrampoline());
522 case StubType::kJNIDlsymLookupCriticalTrampoline:
523 return static_cast<const uint8_t*>(GetJniDlsymLookupCriticalTrampoline());
524 case StubType::kQuickIMTConflictTrampoline:
525 return static_cast<const uint8_t*>(GetQuickImtConflictTrampoline());
526 case StubType::kQuickResolutionTrampoline:
527 return static_cast<const uint8_t*>(GetQuickResolutionTrampoline());
528 case StubType::kQuickToInterpreterBridge:
529 return static_cast<const uint8_t*>(GetQuickToInterpreterBridge());
530 case StubType::kNterpTrampoline:
531 return static_cast<const uint8_t*>(GetNterpTrampoline());
532 }
533 }
534
535 } // namespace art
536