diff options
Diffstat (limited to 'compiler/image_writer.cc')
-rw-r--r-- | compiler/image_writer.cc | 346 |
1 files changed, 193 insertions, 153 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 75be2c9c43..c22f8d6c01 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -75,8 +75,6 @@ bool ImageWriter::Write(const std::string& image_filename, image_begin_ = reinterpret_cast<byte*>(image_begin); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const std::vector<DexCache*>& all_dex_caches = class_linker->GetDexCaches(); - dex_caches_.insert(all_dex_caches.begin(), all_dex_caches.end()); UniquePtr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str())); if (oat_file.get() == NULL) { @@ -121,22 +119,16 @@ bool ImageWriter::Write(const std::string& image_filename, } gc::Heap* heap = Runtime::Current()->GetHeap(); heap->CollectGarbage(false); // Remove garbage. - // Trim size of alloc spaces. - for (const auto& space : heap->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - space->AsDlMallocSpace()->Trim(); - } - } if (!AllocMemory()) { return false; } -#ifndef NDEBUG - { // NOLINT(whitespace/braces) + + if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); CheckNonImageClassesRemoved(); } -#endif + Thread::Current()->TransitionFromSuspendedToRunnable(); size_t oat_loaded_size = 0; size_t oat_data_offset = 0; @@ -144,8 +136,6 @@ bool ImageWriter::Write(const std::string& image_filename, CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset); CopyAndFixupObjects(); PatchOatCodeAndMethods(); - // Record allocations into the image bitmap. - RecordImageAllocations(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); UniquePtr<File> image_file(OS::CreateEmptyFile(image_filename.c_str())); @@ -178,39 +168,82 @@ bool ImageWriter::Write(const std::string& image_filename, return true; } -void ImageWriter::RecordImageAllocations() { - uint64_t start_time = NanoTime(); - CHECK(image_bitmap_.get() != nullptr); - for (const auto& it : offsets_) { - mirror::Object* obj = reinterpret_cast<mirror::Object*>(image_->Begin() + it.second); - DCHECK_ALIGNED(obj, kObjectAlignment); - image_bitmap_->Set(obj); +void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { + DCHECK(object != nullptr); + DCHECK_NE(offset, 0U); + DCHECK(!IsImageOffsetAssigned(object)); + mirror::Object* obj = reinterpret_cast<mirror::Object*>(image_->Begin() + offset); + DCHECK_ALIGNED(obj, kObjectAlignment); + image_bitmap_->Set(obj); + // Before we stomp over the lock word, save the hash code for later. + Monitor::Deflate(Thread::Current(), object);; + LockWord lw(object->GetLockWord()); + switch (lw.GetState()) { + case LockWord::kFatLocked: { + LOG(FATAL) << "Fat locked object " << obj << " found during object copy"; + break; + } + case LockWord::kThinLocked: { + LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; + break; + } + case LockWord::kUnlocked: + // No hash, don't need to save it. + break; + case LockWord::kHashCode: + saved_hashes_.push_back(std::make_pair(obj, lw.GetHashCode())); + break; + default: + LOG(FATAL) << "Unreachable."; + break; } - LOG(INFO) << "RecordImageAllocations took " << PrettyDuration(NanoTime() - start_time); + object->SetLockWord(LockWord::FromForwardingAddress(offset)); + DCHECK(IsImageOffsetAssigned(object)); } -bool ImageWriter::AllocMemory() { - size_t size = 0; - for (const auto& space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - size += space->Size(); - } - } +void ImageWriter::AssignImageOffset(mirror::Object* object) { + DCHECK(object != nullptr); + SetImageOffset(object, image_end_); + image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment + DCHECK_LT(image_end_, image_->Size()); +} - int prot = PROT_READ | PROT_WRITE; - size_t length = RoundUp(size, kPageSize); +bool ImageWriter::IsImageOffsetAssigned(const mirror::Object* object) const { + DCHECK(object != nullptr); + return object->GetLockWord().GetState() == LockWord::kForwardingAddress; +} + +size_t ImageWriter::GetImageOffset(const mirror::Object* object) const { + DCHECK(object != nullptr); + DCHECK(IsImageOffsetAssigned(object)); + LockWord lock_word = object->GetLockWord(); + size_t offset = lock_word.ForwardingAddress(); + DCHECK_LT(offset, image_end_); + return offset; +} + +bool ImageWriter::AllocMemory() { + size_t length = RoundUp(Runtime::Current()->GetHeap()->GetTotalMemory(), kPageSize); std::string error_msg; - image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot, &error_msg)); + image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, PROT_READ | PROT_WRITE, + &error_msg)); if (UNLIKELY(image_.get() == nullptr)) { LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; return false; } + + // Create the image bitmap. + image_bitmap_.reset(gc::accounting::SpaceBitmap::Create("image bitmap", image_->Begin(), + length)); + if (image_bitmap_.get() == nullptr) { + LOG(ERROR) << "Failed to allocate memory for image bitmap"; + return false; + } return true; } void ImageWriter::ComputeLazyFieldsForImageClasses() { - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); class_linker->VisitClassesWithoutClassesLock(ComputeLazyFieldsForClassesVisitor, NULL); } @@ -223,13 +256,12 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { if (!obj->GetClass()->IsStringClass()) { return; } - String* string = obj->AsString(); + mirror::String* string = obj->AsString(); const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); - ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); - for (DexCache* dex_cache : writer->dex_caches_) { + for (DexCache* dex_cache : Runtime::Current()->GetClassLinker()->GetDexCaches()) { const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::StringId* string_id = dex_file.FindStringId(utf16_string); - if (string_id != NULL) { + if (string_id != nullptr) { // This string occurs in this dex file, assign the dex cache entry. uint32_t string_idx = dex_file.GetIndexForStringId(*string_id); if (dex_cache->GetResolvedString(string_idx) == NULL) { @@ -239,13 +271,9 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { } } -void ImageWriter::ComputeEagerResolvedStrings() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // TODO: Check image spaces only? - gc::Heap* heap = Runtime::Current()->GetHeap(); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(ComputeEagerResolvedStringsCallback, this); +void ImageWriter::ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this); } bool ImageWriter::IsImageClass(const Class* klass) { @@ -278,7 +306,7 @@ void ImageWriter::PruneNonImageClasses() { // Clear references to removed classes from the DexCaches. ArtMethod* resolution_method = runtime->GetResolutionMethod(); - for (DexCache* dex_cache : dex_caches_) { + for (DexCache* dex_cache : class_linker->GetDexCaches()) { for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { Class* klass = dex_cache->GetResolvedType(i); if (klass != NULL && !IsImageClass(klass)) { @@ -311,31 +339,22 @@ bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { void ImageWriter::CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (compiler_driver_.GetImageClasses() == NULL) { - return; - } - - gc::Heap* heap = Runtime::Current()->GetHeap(); - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); + if (compiler_driver_.GetImageClasses() != nullptr) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + heap->VisitObjects(CheckNonImageClassesRemovedCallback, this); } - - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->GetLiveBitmap()->Walk(CheckNonImageClassesRemovedCallback, this); } void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); - if (!obj->IsClass()) { - return; - } - Class* klass = obj->AsClass(); - if (!image_writer->IsImageClass(klass)) { - image_writer->DumpImageClasses(); - CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() - << " " << PrettyDescriptor(klass); + if (obj->IsClass()) { + Class* klass = obj->AsClass(); + if (!image_writer->IsImageClass(klass)) { + image_writer->DumpImageClasses(); + CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() + << " " << PrettyDescriptor(klass); + } } } @@ -347,53 +366,50 @@ void ImageWriter::DumpImageClasses() { } } -void ImageWriter::CalculateNewObjectOffsetsCallback(Object* obj, void* arg) { +void ImageWriter::CalculateObjectOffsets(Object* obj) { DCHECK(obj != NULL); - DCHECK(arg != NULL); - ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); - // if it is a string, we want to intern it if its not interned. if (obj->GetClass()->IsStringClass()) { // we must be an interned string that was forward referenced and already assigned - if (image_writer->IsImageOffsetAssigned(obj)) { + if (IsImageOffsetAssigned(obj)) { DCHECK_EQ(obj, obj->AsString()->Intern()); return; } - SirtRef<String> interned(Thread::Current(), obj->AsString()->Intern()); - if (obj != interned.get()) { - if (!image_writer->IsImageOffsetAssigned(interned.get())) { + Thread* self = Thread::Current(); + SirtRef<Object> sirt_obj(self, obj); + mirror::String* interned = obj->AsString()->Intern(); + if (sirt_obj.get() != interned) { + if (!IsImageOffsetAssigned(interned)) { // interned obj is after us, allocate its location early - image_writer->AssignImageOffset(interned.get()); + AssignImageOffset(interned); } // point those looking for this object to the interned version. - image_writer->SetImageOffset(obj, image_writer->GetImageOffset(interned.get())); + SetImageOffset(sirt_obj.get(), GetImageOffset(interned)); return; } // else (obj == interned), nothing to do but fall through to the normal case } - image_writer->AssignImageOffset(obj); + AssignImageOffset(obj); } ObjectArray<Object>* ImageWriter::CreateImageRoots() const { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;"); Thread* self = Thread::Current(); + SirtRef<Class> object_array_class(self, class_linker->FindSystemClass("[Ljava/lang/Object;")); // build an Object[] of all the DexCaches used in the source_space_ - ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class, - dex_caches_.size()); + ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class.get(), + class_linker->GetDexCaches().size()); int i = 0; - for (DexCache* dex_cache : dex_caches_) { + for (DexCache* dex_cache : class_linker->GetDexCaches()) { dex_caches->Set(i++, dex_cache); } // build an Object[] of the roots needed to restore the runtime - SirtRef<ObjectArray<Object> > - image_roots(self, - ObjectArray<Object>::Alloc(self, object_array_class, - ImageHeader::kImageRootsMax)); + SirtRef<ObjectArray<Object> > image_roots( + self, ObjectArray<Object>::Alloc(self, object_array_class.get(), ImageHeader::kImageRootsMax)); image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod()); image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt()); @@ -405,24 +421,82 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); image_roots->Set(ImageHeader::kOatLocation, String::AllocFromModifiedUtf8(self, oat_file_->GetLocation().c_str())); - image_roots->Set(ImageHeader::kDexCaches, - dex_caches); - image_roots->Set(ImageHeader::kClassRoots, - class_linker->GetClassRoots()); + image_roots->Set(ImageHeader::kDexCaches, dex_caches); + image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { CHECK(image_roots->Get(i) != NULL); } return image_roots.get(); } +// Walk instance fields of the given Class. Separate function to allow recursion on the super +// class. +void ImageWriter::WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) { + // Visit fields of parent classes first. + SirtRef<mirror::Class> sirt_class(Thread::Current(), klass); + mirror::Class* super = sirt_class->GetSuperClass(); + if (super != nullptr) { + WalkInstanceFields(obj, super); + } + // + size_t num_reference_fields = sirt_class->NumReferenceInstanceFields(); + for (size_t i = 0; i < num_reference_fields; ++i) { + mirror::ArtField* field = sirt_class->GetInstanceField(i); + MemberOffset field_offset = field->GetOffset(); + mirror::Object* value = obj->GetFieldObject<mirror::Object*>(field_offset, false); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } +} + +// For an unvisited object, visit it then all its children found via fields. +void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { + if (!IsImageOffsetAssigned(obj)) { + // Walk instance fields of all objects + Thread* self = Thread::Current(); + SirtRef<mirror::Object> sirt_obj(self, obj); + SirtRef<mirror::Class> klass(self, obj->GetClass()); + // visit the object itself. + CalculateObjectOffsets(sirt_obj.get()); + WalkInstanceFields(sirt_obj.get(), klass.get()); + // Walk static fields of a Class. + if (sirt_obj->IsClass()) { + size_t num_static_fields = klass->NumReferenceStaticFields(); + for (size_t i = 0; i < num_static_fields; ++i) { + mirror::ArtField* field = klass->GetStaticField(i); + MemberOffset field_offset = field->GetOffset(); + mirror::Object* value = sirt_obj->GetFieldObject<mirror::Object*>(field_offset, false); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } + } else if (sirt_obj->IsObjectArray()) { + // Walk elements of an object array. + int32_t length = sirt_obj->AsObjectArray<mirror::Object>()->GetLength(); + for (int32_t i = 0; i < length; i++) { + mirror::ObjectArray<mirror::Object>* obj_array = sirt_obj->AsObjectArray<mirror::Object>(); + mirror::Object* value = obj_array->Get(i); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } + } + } +} + +void ImageWriter::WalkFieldsCallback(mirror::Object* obj, void* arg) { + ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); + DCHECK(writer != nullptr); + writer->WalkFieldsInOrder(obj); +} + void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) { CHECK_NE(0U, oat_loaded_size); Thread* self = Thread::Current(); SirtRef<ObjectArray<Object> > image_roots(self, CreateImageRoots()); gc::Heap* heap = Runtime::Current()->GetHeap(); - const auto& spaces = heap->GetContinuousSpaces(); - DCHECK(!spaces.empty()); DCHECK_EQ(0U, image_end_); // Leave space for the header, but do not write it yet, we need to @@ -431,21 +505,14 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); // TODO: Image spaces only? - // TODO: Add InOrderWalk to heap bitmap. const char* old = self->StartAssertNoThreadSuspension("ImageWriter"); - DCHECK(heap->GetLargeObjectsSpace()->GetLiveObjects()->IsEmpty()); - for (const auto& space : spaces) { - space->GetLiveBitmap()->InOrderWalk(CalculateNewObjectOffsetsCallback, this); - DCHECK_LT(image_end_, image_->Size()); - } + DCHECK_LT(image_end_, image_->Size()); + // Clear any pre-existing monitors which may have been in the monitor words. + heap->VisitObjects(WalkFieldsCallback, this); self->EndAssertNoThreadSuspension(old); } - // Create the image bitmap. - image_bitmap_.reset(gc::accounting::SpaceBitmap::Create("image bitmap", image_->Begin(), - image_end_)); const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize); const byte* oat_file_end = oat_file_begin + oat_loaded_size; oat_data_begin_ = oat_file_begin + oat_data_offset; @@ -456,7 +523,8 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d ImageHeader image_header(reinterpret_cast<uint32_t>(image_begin_), static_cast<uint32_t>(image_end_), RoundUp(image_end_, kPageSize), - image_bitmap_->Size(), + RoundUp(image_end_ / gc::accounting::SpaceBitmap::kAlignment, + sizeof(size_t)), reinterpret_cast<uint32_t>(GetImageAddress(image_roots.get())), oat_file_->GetOatHeader().GetChecksum(), reinterpret_cast<uint32_t>(oat_file_begin), @@ -477,17 +545,19 @@ void ImageWriter::CopyAndFixupObjects() heap->DisableObjectValidation(); // TODO: Image spaces only? WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(CopyAndFixupObjectsCallback, this); + heap->VisitObjects(CopyAndFixupObjectsCallback, this); + // Fix up the object previously had hash codes. + for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) { + hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second)); + } + saved_hashes_.clear(); self->EndAssertNoThreadSuspension(old_cause); } -void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { - DCHECK(object != NULL); +void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { + DCHECK(obj != NULL); DCHECK(arg != NULL); - const Object* obj = object; ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); - // see GetLocalAddress for similar computation size_t offset = image_writer->GetImageOffset(obj); byte* dst = image_writer->image_->Begin() + offset; @@ -498,33 +568,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { Object* copy = reinterpret_cast<Object*>(dst); // Write in a hash code of objects which have inflated monitors or a hash code in their monitor // word. - LockWord lw(copy->GetLockWord()); - switch (lw.GetState()) { - case LockWord::kFatLocked: { - Monitor* monitor = lw.FatLockMonitor(); - CHECK(monitor != nullptr); - CHECK(!monitor->IsLocked()); - if (monitor->HasHashCode()) { - copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); - } else { - copy->SetLockWord(LockWord()); - } - break; - } - case LockWord::kThinLocked: { - LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; - break; - } - case LockWord::kUnlocked: - break; - case LockWord::kHashCode: - // Do nothing since we can just keep the same hash code. - CHECK_NE(lw.GetHashCode(), 0); - break; - default: - LOG(FATAL) << "Unreachable."; - break; - } + copy->SetLockWord(LockWord()); image_writer->FixupObject(obj, copy); } @@ -629,19 +673,13 @@ void ImageWriter::FixupInstanceFields(const Object* orig, Object* copy) { DCHECK(copy != NULL); Class* klass = orig->GetClass(); DCHECK(klass != NULL); - FixupFields(orig, - copy, - klass->GetReferenceInstanceOffsets(), - false); + FixupFields(orig, copy, klass->GetReferenceInstanceOffsets(), false); } void ImageWriter::FixupStaticFields(const Class* orig, Class* copy) { DCHECK(orig != NULL); DCHECK(copy != NULL); - FixupFields(orig, - copy, - orig->GetReferenceStaticOffsets(), - true); + FixupFields(orig, copy, orig->GetReferenceStaticOffsets(), true); } void ImageWriter::FixupFields(const Object* orig, @@ -693,11 +731,13 @@ void ImageWriter::FixupFields(const Object* orig, static ArtMethod* GetTargetMethod(const CompilerDriver::PatchInformation* patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - DexCache* dex_cache = class_linker->FindDexCache(patch->GetDexFile()); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, class_linker->FindDexCache(patch->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader(self, nullptr); ArtMethod* method = class_linker->ResolveMethod(patch->GetDexFile(), patch->GetTargetMethodIdx(), dex_cache, - NULL, + class_loader, NULL, patch->GetTargetInvokeType()); CHECK(method != NULL) @@ -749,15 +789,15 @@ void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch // TODO: make this Thumb2 specific uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uint32_t>(oat_code) & ~0x1); uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset()); -#ifndef NDEBUG - const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); - uint32_t expected = reinterpret_cast<uint32_t>(&id); - uint32_t actual = *patch_location; - CHECK(actual == expected || actual == value) << std::hex - << "actual=" << actual - << "expected=" << expected - << "value=" << value; -#endif + if (kIsDebugBuild) { + const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); + uint32_t expected = reinterpret_cast<uint32_t>(&id); + uint32_t actual = *patch_location; + CHECK(actual == expected || actual == value) << std::hex + << "actual=" << actual + << "expected=" << expected + << "value=" << value; + } *patch_location = value; oat_header.UpdateChecksum(patch_location, sizeof(value)); } |