• 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 "arch/instruction_set_features.h"
23 #include "base/bit_utils.h"
24 #include "base/stringprintf.h"
25 
26 namespace art {
27 
28 constexpr uint8_t OatHeader::kOatMagic[4];
29 constexpr uint8_t OatHeader::kOatVersion[4];
30 constexpr const char OatHeader::kTrueValue[];
31 constexpr const char OatHeader::kFalseValue[];
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   }
43   return sizeof(OatHeader) + estimate;
44 }
45 
Create(InstructionSet instruction_set,const InstructionSetFeatures * instruction_set_features,uint32_t dex_file_count,const SafeMap<std::string,std::string> * variable_data)46 OatHeader* OatHeader::Create(InstructionSet instruction_set,
47                              const InstructionSetFeatures* instruction_set_features,
48                              uint32_t dex_file_count,
49                              const SafeMap<std::string, std::string>* variable_data) {
50   // Estimate size of optional data.
51   size_t needed_size = ComputeOatHeaderSize(variable_data);
52 
53   // Reserve enough memory.
54   void* memory = operator new (needed_size);
55 
56   // Create the OatHeader in-place.
57   return new (memory) OatHeader(instruction_set,
58                                 instruction_set_features,
59                                 dex_file_count,
60                                 variable_data);
61 }
62 
OatHeader(InstructionSet instruction_set,const InstructionSetFeatures * instruction_set_features,uint32_t dex_file_count,const SafeMap<std::string,std::string> * variable_data)63 OatHeader::OatHeader(InstructionSet instruction_set,
64                      const InstructionSetFeatures* instruction_set_features,
65                      uint32_t dex_file_count,
66                      const SafeMap<std::string, std::string>* variable_data)
67     : adler32_checksum_(adler32(0L, Z_NULL, 0)),
68       instruction_set_(instruction_set),
69       instruction_set_features_bitmap_(instruction_set_features->AsBitmap()),
70       dex_file_count_(dex_file_count),
71       executable_offset_(0),
72       interpreter_to_interpreter_bridge_offset_(0),
73       interpreter_to_compiled_code_bridge_offset_(0),
74       jni_dlsym_lookup_offset_(0),
75       quick_generic_jni_trampoline_offset_(0),
76       quick_imt_conflict_trampoline_offset_(0),
77       quick_resolution_trampoline_offset_(0),
78       quick_to_interpreter_bridge_offset_(0),
79       image_patch_delta_(0),
80       image_file_location_oat_checksum_(0),
81       image_file_location_oat_data_begin_(0) {
82   // Don't want asserts in header as they would be checked in each file that includes it. But the
83   // fields are private, so we check inside a method.
84   static_assert(sizeof(magic_) == sizeof(kOatMagic),
85                 "Oat magic and magic_ have different lengths.");
86   static_assert(sizeof(version_) == sizeof(kOatVersion),
87                 "Oat version and version_ have different lengths.");
88 
89   memcpy(magic_, kOatMagic, sizeof(kOatMagic));
90   memcpy(version_, kOatVersion, sizeof(kOatVersion));
91 
92   CHECK_NE(instruction_set, kNone);
93 
94   // Flatten the map. Will also update variable_size_data_size_.
95   Flatten(variable_data);
96 }
97 
IsValid() const98 bool OatHeader::IsValid() const {
99   if (memcmp(magic_, kOatMagic, sizeof(kOatMagic)) != 0) {
100     return false;
101   }
102   if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
103     return false;
104   }
105   if (!IsAligned<kPageSize>(executable_offset_)) {
106     return false;
107   }
108   if (!IsAligned<kPageSize>(image_patch_delta_)) {
109     return false;
110   }
111   if (!IsValidInstructionSet(instruction_set_)) {
112     return false;
113   }
114   return true;
115 }
116 
GetValidationErrorMessage() const117 std::string OatHeader::GetValidationErrorMessage() const {
118   if (memcmp(magic_, kOatMagic, sizeof(kOatMagic)) != 0) {
119     static_assert(sizeof(kOatMagic) == 4, "kOatMagic has unexpected length");
120     return StringPrintf("Invalid oat magic, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
121                         kOatMagic[0], kOatMagic[1], kOatMagic[2], kOatMagic[3],
122                         magic_[0], magic_[1], magic_[2], magic_[3]);
123   }
124   if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
125     static_assert(sizeof(kOatVersion) == 4, "kOatVersion has unexpected length");
126     return StringPrintf("Invalid oat version, expected 0x%x%x%x%x, got 0x%x%x%x%x.",
127                         kOatVersion[0], kOatVersion[1], kOatVersion[2], kOatVersion[3],
128                         version_[0], version_[1], version_[2], version_[3]);
129   }
130   if (!IsAligned<kPageSize>(executable_offset_)) {
131     return "Executable offset not page-aligned.";
132   }
133   if (!IsAligned<kPageSize>(image_patch_delta_)) {
134     return "Image patch delta not page-aligned.";
135   }
136   if (!IsValidInstructionSet(instruction_set_)) {
137     return StringPrintf("Invalid instruction set, %d.", static_cast<int>(instruction_set_));
138   }
139   return "";
140 }
141 
GetMagic() const142 const char* OatHeader::GetMagic() const {
143   CHECK(IsValid());
144   return reinterpret_cast<const char*>(magic_);
145 }
146 
GetChecksum() const147 uint32_t OatHeader::GetChecksum() const {
148   CHECK(IsValid());
149   return adler32_checksum_;
150 }
151 
UpdateChecksumWithHeaderData()152 void OatHeader::UpdateChecksumWithHeaderData() {
153   UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
154   UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_));
155   UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
156   UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_));
157   UpdateChecksum(&image_file_location_oat_data_begin_, sizeof(image_file_location_oat_data_begin_));
158 
159   // Update checksum for variable data size.
160   UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_));
161 
162   // Update for data, if existing.
163   if (key_value_store_size_ > 0U) {
164     UpdateChecksum(&key_value_store_, key_value_store_size_);
165   }
166 
167   UpdateChecksum(&executable_offset_, sizeof(executable_offset_));
168   UpdateChecksum(&interpreter_to_interpreter_bridge_offset_,
169                  sizeof(interpreter_to_interpreter_bridge_offset_));
170   UpdateChecksum(&interpreter_to_compiled_code_bridge_offset_,
171                  sizeof(interpreter_to_compiled_code_bridge_offset_));
172   UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(jni_dlsym_lookup_offset_));
173   UpdateChecksum(&quick_generic_jni_trampoline_offset_,
174                  sizeof(quick_generic_jni_trampoline_offset_));
175   UpdateChecksum(&quick_imt_conflict_trampoline_offset_,
176                  sizeof(quick_imt_conflict_trampoline_offset_));
177   UpdateChecksum(&quick_resolution_trampoline_offset_,
178                  sizeof(quick_resolution_trampoline_offset_));
179   UpdateChecksum(&quick_to_interpreter_bridge_offset_,
180                  sizeof(quick_to_interpreter_bridge_offset_));
181 }
182 
UpdateChecksum(const void * data,size_t length)183 void OatHeader::UpdateChecksum(const void* data, size_t length) {
184   DCHECK(IsValid());
185   if (data != nullptr) {
186     const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
187     adler32_checksum_ = adler32(adler32_checksum_, bytes, length);
188   } else {
189     DCHECK_EQ(0U, length);
190   }
191 }
192 
GetInstructionSet() const193 InstructionSet OatHeader::GetInstructionSet() const {
194   CHECK(IsValid());
195   return instruction_set_;
196 }
197 
GetInstructionSetFeaturesBitmap() const198 uint32_t OatHeader::GetInstructionSetFeaturesBitmap() const {
199   CHECK(IsValid());
200   return instruction_set_features_bitmap_;
201 }
202 
GetExecutableOffset() const203 uint32_t OatHeader::GetExecutableOffset() const {
204   DCHECK(IsValid());
205   DCHECK_ALIGNED(executable_offset_, kPageSize);
206   CHECK_GT(executable_offset_, sizeof(OatHeader));
207   return executable_offset_;
208 }
209 
SetExecutableOffset(uint32_t executable_offset)210 void OatHeader::SetExecutableOffset(uint32_t executable_offset) {
211   DCHECK_ALIGNED(executable_offset, kPageSize);
212   CHECK_GT(executable_offset, sizeof(OatHeader));
213   DCHECK(IsValid());
214   DCHECK_EQ(executable_offset_, 0U);
215 
216   executable_offset_ = executable_offset;
217 }
218 
GetInterpreterToInterpreterBridge() const219 const void* OatHeader::GetInterpreterToInterpreterBridge() const {
220   return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToInterpreterBridgeOffset();
221 }
222 
GetInterpreterToInterpreterBridgeOffset() const223 uint32_t OatHeader::GetInterpreterToInterpreterBridgeOffset() const {
224   DCHECK(IsValid());
225   CHECK(interpreter_to_interpreter_bridge_offset_ == 0 ||
226         interpreter_to_interpreter_bridge_offset_ >= executable_offset_);
227   return interpreter_to_interpreter_bridge_offset_;
228 }
229 
SetInterpreterToInterpreterBridgeOffset(uint32_t offset)230 void OatHeader::SetInterpreterToInterpreterBridgeOffset(uint32_t offset) {
231   CHECK(offset == 0 || offset >= executable_offset_);
232   DCHECK(IsValid());
233   DCHECK_EQ(interpreter_to_interpreter_bridge_offset_, 0U) << offset;
234 
235   interpreter_to_interpreter_bridge_offset_ = offset;
236 }
237 
GetInterpreterToCompiledCodeBridge() const238 const void* OatHeader::GetInterpreterToCompiledCodeBridge() const {
239   return reinterpret_cast<const uint8_t*>(this) + GetInterpreterToCompiledCodeBridgeOffset();
240 }
241 
GetInterpreterToCompiledCodeBridgeOffset() const242 uint32_t OatHeader::GetInterpreterToCompiledCodeBridgeOffset() const {
243   DCHECK(IsValid());
244   CHECK_GE(interpreter_to_compiled_code_bridge_offset_, interpreter_to_interpreter_bridge_offset_);
245   return interpreter_to_compiled_code_bridge_offset_;
246 }
247 
SetInterpreterToCompiledCodeBridgeOffset(uint32_t offset)248 void OatHeader::SetInterpreterToCompiledCodeBridgeOffset(uint32_t offset) {
249   CHECK(offset == 0 || offset >= interpreter_to_interpreter_bridge_offset_);
250   DCHECK(IsValid());
251   DCHECK_EQ(interpreter_to_compiled_code_bridge_offset_, 0U) << offset;
252 
253   interpreter_to_compiled_code_bridge_offset_ = offset;
254 }
255 
GetJniDlsymLookup() const256 const void* OatHeader::GetJniDlsymLookup() const {
257   return reinterpret_cast<const uint8_t*>(this) + GetJniDlsymLookupOffset();
258 }
259 
GetJniDlsymLookupOffset() const260 uint32_t OatHeader::GetJniDlsymLookupOffset() const {
261   DCHECK(IsValid());
262   CHECK_GE(jni_dlsym_lookup_offset_, interpreter_to_compiled_code_bridge_offset_);
263   return jni_dlsym_lookup_offset_;
264 }
265 
SetJniDlsymLookupOffset(uint32_t offset)266 void OatHeader::SetJniDlsymLookupOffset(uint32_t offset) {
267   CHECK(offset == 0 || offset >= interpreter_to_compiled_code_bridge_offset_);
268   DCHECK(IsValid());
269   DCHECK_EQ(jni_dlsym_lookup_offset_, 0U) << offset;
270 
271   jni_dlsym_lookup_offset_ = offset;
272 }
273 
GetQuickGenericJniTrampoline() const274 const void* OatHeader::GetQuickGenericJniTrampoline() const {
275   return reinterpret_cast<const uint8_t*>(this) + GetQuickGenericJniTrampolineOffset();
276 }
277 
GetQuickGenericJniTrampolineOffset() const278 uint32_t OatHeader::GetQuickGenericJniTrampolineOffset() const {
279   DCHECK(IsValid());
280   CHECK_GE(quick_generic_jni_trampoline_offset_, jni_dlsym_lookup_offset_);
281   return quick_generic_jni_trampoline_offset_;
282 }
283 
SetQuickGenericJniTrampolineOffset(uint32_t offset)284 void OatHeader::SetQuickGenericJniTrampolineOffset(uint32_t offset) {
285   CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
286   DCHECK(IsValid());
287   DCHECK_EQ(quick_generic_jni_trampoline_offset_, 0U) << offset;
288 
289   quick_generic_jni_trampoline_offset_ = offset;
290 }
291 
GetQuickImtConflictTrampoline() const292 const void* OatHeader::GetQuickImtConflictTrampoline() const {
293   return reinterpret_cast<const uint8_t*>(this) + GetQuickImtConflictTrampolineOffset();
294 }
295 
GetQuickImtConflictTrampolineOffset() const296 uint32_t OatHeader::GetQuickImtConflictTrampolineOffset() const {
297   DCHECK(IsValid());
298   CHECK_GE(quick_imt_conflict_trampoline_offset_, quick_generic_jni_trampoline_offset_);
299   return quick_imt_conflict_trampoline_offset_;
300 }
301 
SetQuickImtConflictTrampolineOffset(uint32_t offset)302 void OatHeader::SetQuickImtConflictTrampolineOffset(uint32_t offset) {
303   CHECK(offset == 0 || offset >= quick_generic_jni_trampoline_offset_);
304   DCHECK(IsValid());
305   DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset;
306 
307   quick_imt_conflict_trampoline_offset_ = offset;
308 }
309 
GetQuickResolutionTrampoline() const310 const void* OatHeader::GetQuickResolutionTrampoline() const {
311   return reinterpret_cast<const uint8_t*>(this) + GetQuickResolutionTrampolineOffset();
312 }
313 
GetQuickResolutionTrampolineOffset() const314 uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const {
315   DCHECK(IsValid());
316   CHECK_GE(quick_resolution_trampoline_offset_, quick_imt_conflict_trampoline_offset_);
317   return quick_resolution_trampoline_offset_;
318 }
319 
SetQuickResolutionTrampolineOffset(uint32_t offset)320 void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) {
321   CHECK(offset == 0 || offset >= quick_imt_conflict_trampoline_offset_);
322   DCHECK(IsValid());
323   DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
324 
325   quick_resolution_trampoline_offset_ = offset;
326 }
327 
GetQuickToInterpreterBridge() const328 const void* OatHeader::GetQuickToInterpreterBridge() const {
329   return reinterpret_cast<const uint8_t*>(this) + GetQuickToInterpreterBridgeOffset();
330 }
331 
GetQuickToInterpreterBridgeOffset() const332 uint32_t OatHeader::GetQuickToInterpreterBridgeOffset() const {
333   DCHECK(IsValid());
334   CHECK_GE(quick_to_interpreter_bridge_offset_, quick_resolution_trampoline_offset_);
335   return quick_to_interpreter_bridge_offset_;
336 }
337 
SetQuickToInterpreterBridgeOffset(uint32_t offset)338 void OatHeader::SetQuickToInterpreterBridgeOffset(uint32_t offset) {
339   CHECK(offset == 0 || offset >= quick_resolution_trampoline_offset_);
340   DCHECK(IsValid());
341   DCHECK_EQ(quick_to_interpreter_bridge_offset_, 0U) << offset;
342 
343   quick_to_interpreter_bridge_offset_ = offset;
344 }
345 
GetImagePatchDelta() const346 int32_t OatHeader::GetImagePatchDelta() const {
347   CHECK(IsValid());
348   return image_patch_delta_;
349 }
350 
RelocateOat(off_t delta)351 void OatHeader::RelocateOat(off_t delta) {
352   CHECK(IsValid());
353   CHECK_ALIGNED(delta, kPageSize);
354   image_patch_delta_ += delta;
355   if (image_file_location_oat_data_begin_ != 0) {
356     image_file_location_oat_data_begin_ += delta;
357   }
358 }
359 
SetImagePatchDelta(int32_t off)360 void OatHeader::SetImagePatchDelta(int32_t off) {
361   CHECK(IsValid());
362   CHECK_ALIGNED(off, kPageSize);
363   image_patch_delta_ = off;
364 }
365 
GetImageFileLocationOatChecksum() const366 uint32_t OatHeader::GetImageFileLocationOatChecksum() const {
367   CHECK(IsValid());
368   return image_file_location_oat_checksum_;
369 }
370 
SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum)371 void OatHeader::SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum) {
372   CHECK(IsValid());
373   image_file_location_oat_checksum_ = image_file_location_oat_checksum;
374 }
375 
GetImageFileLocationOatDataBegin() const376 uint32_t OatHeader::GetImageFileLocationOatDataBegin() const {
377   CHECK(IsValid());
378   return image_file_location_oat_data_begin_;
379 }
380 
SetImageFileLocationOatDataBegin(uint32_t image_file_location_oat_data_begin)381 void OatHeader::SetImageFileLocationOatDataBegin(uint32_t image_file_location_oat_data_begin) {
382   CHECK(IsValid());
383   CHECK_ALIGNED(image_file_location_oat_data_begin, kPageSize);
384   image_file_location_oat_data_begin_ = image_file_location_oat_data_begin;
385 }
386 
GetKeyValueStoreSize() const387 uint32_t OatHeader::GetKeyValueStoreSize() const {
388   CHECK(IsValid());
389   return key_value_store_size_;
390 }
391 
GetKeyValueStore() const392 const uint8_t* OatHeader::GetKeyValueStore() const {
393   CHECK(IsValid());
394   return key_value_store_;
395 }
396 
397 // Advance start until it is either end or \0.
ParseString(const char * start,const char * end)398 static const char* ParseString(const char* start, const char* end) {
399   while (start < end && *start != 0) {
400     start++;
401   }
402   return start;
403 }
404 
GetStoreValueByKey(const char * key) const405 const char* OatHeader::GetStoreValueByKey(const char* key) const {
406   const char* ptr = reinterpret_cast<const char*>(&key_value_store_);
407   const char* end = ptr + key_value_store_size_;
408 
409   while (ptr < end) {
410     // Scan for a closing zero.
411     const char* str_end = ParseString(ptr, end);
412     if (str_end < end) {
413       if (strcmp(key, ptr) == 0) {
414         // Same as key. Check if value is OK.
415         if (ParseString(str_end + 1, end) < end) {
416           return str_end + 1;
417         }
418       } else {
419         // Different from key. Advance over the value.
420         ptr = ParseString(str_end + 1, end) + 1;
421       }
422     } else {
423       break;
424     }
425   }
426   // Not found.
427   return nullptr;
428 }
429 
GetStoreKeyValuePairByIndex(size_t index,const char ** key,const char ** value) const430 bool OatHeader::GetStoreKeyValuePairByIndex(size_t index, const char** key,
431                                             const char** value) const {
432   const char* ptr = reinterpret_cast<const char*>(&key_value_store_);
433   const char* end = ptr + key_value_store_size_;
434   ssize_t counter = static_cast<ssize_t>(index);
435 
436   while (ptr < end && counter >= 0) {
437     // Scan for a closing zero.
438     const char* str_end = ParseString(ptr, end);
439     if (str_end < end) {
440       const char* maybe_key = ptr;
441       ptr = ParseString(str_end + 1, end) + 1;
442       if (ptr <= end) {
443         if (counter == 0) {
444           *key = maybe_key;
445           *value = str_end + 1;
446           return true;
447         } else {
448           counter--;
449         }
450       } else {
451         return false;
452       }
453     } else {
454       break;
455     }
456   }
457   // Not found.
458   return false;
459 }
460 
GetHeaderSize() const461 size_t OatHeader::GetHeaderSize() const {
462   return sizeof(OatHeader) + key_value_store_size_;
463 }
464 
IsPic() const465 bool OatHeader::IsPic() const {
466   return IsKeyEnabled(OatHeader::kPicKey);
467 }
468 
HasPatchInfo() const469 bool OatHeader::HasPatchInfo() const {
470   return IsKeyEnabled(OatHeader::kHasPatchInfoKey);
471 }
472 
IsDebuggable() const473 bool OatHeader::IsDebuggable() const {
474   return IsKeyEnabled(OatHeader::kDebuggableKey);
475 }
476 
IsNativeDebuggable() const477 bool OatHeader::IsNativeDebuggable() const {
478   return IsKeyEnabled(OatHeader::kNativeDebuggableKey);
479 }
480 
GetCompilerFilter() const481 CompilerFilter::Filter OatHeader::GetCompilerFilter() const {
482   CompilerFilter::Filter filter;
483   const char* key_value = GetStoreValueByKey(kCompilerFilter);
484   CHECK(key_value != nullptr) << "compiler-filter not found in oat header";
485   CHECK(CompilerFilter::ParseCompilerFilter(key_value, &filter))
486       << "Invalid compiler-filter in oat header: " << key_value;
487   return filter;
488 }
489 
KeyHasValue(const char * key,const char * value,size_t value_size) const490 bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const {
491   const char* key_value = GetStoreValueByKey(key);
492   return (key_value != nullptr && strncmp(key_value, value, value_size) == 0);
493 }
494 
IsKeyEnabled(const char * key) const495 bool OatHeader::IsKeyEnabled(const char* key) const {
496   return KeyHasValue(key, kTrueValue, sizeof(kTrueValue));
497 }
498 
Flatten(const SafeMap<std::string,std::string> * key_value_store)499 void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
500   char* data_ptr = reinterpret_cast<char*>(&key_value_store_);
501   if (key_value_store != nullptr) {
502     SafeMap<std::string, std::string>::const_iterator it = key_value_store->begin();
503     SafeMap<std::string, std::string>::const_iterator end = key_value_store->end();
504     for ( ; it != end; ++it) {
505       strcpy(data_ptr, it->first.c_str());
506       data_ptr += it->first.length() + 1;
507       strcpy(data_ptr, it->second.c_str());
508       data_ptr += it->second.length() + 1;
509     }
510   }
511   key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_);
512 }
513 
OatMethodOffsets(uint32_t code_offset)514 OatMethodOffsets::OatMethodOffsets(uint32_t code_offset) : code_offset_(code_offset) {
515 }
516 
~OatMethodOffsets()517 OatMethodOffsets::~OatMethodOffsets() {}
518 
519 }  // namespace art
520