• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current_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