/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_PATCHOAT_PATCHOAT_H_ #define ART_PATCHOAT_PATCHOAT_H_ #include "arch/instruction_set.h" #include "base/macros.h" #include "base/mutex.h" #include "elf_file.h" #include "elf_utils.h" #include "gc/accounting/space_bitmap.h" #include "gc/space/image_space.h" #include "gc/heap.h" #include "os.h" #include "runtime.h" namespace art { class ArtMethod; class ImageHeader; class OatHeader; namespace mirror { class Object; class PointerArray; class Reference; class Class; } // namespace mirror class PatchOat { public: // Patch only the oat file static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings, bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? bool new_oat_out); // Output oat was a new file created by us? // Patch only the image (art file) static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa, TimingLogger* timings); // Patch both the image and the oat file static bool Patch(const std::string& art_location, off_t delta, const std::string& output_directory, InstructionSet isa, TimingLogger* timings); ~PatchOat() {} PatchOat(PatchOat&&) = default; private: // Takes ownership only of the ElfFile. All other pointers are only borrowed. PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings) : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta), isa_(kNone), space_map_(nullptr), timings_(timings) {} PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, TimingLogger* timings) : image_(image), bitmap_(bitmap), heap_(heap), delta_(delta), isa_(isa), space_map_(nullptr), timings_(timings) {} PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings) : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap), delta_(delta), isa_(isa), space_map_(map), timings_(timings) {} // Was the .art image at image_path made with --compile-pic ? static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path); enum MaybePic { NOT_PIC, // Code not pic. Patch as usual. PIC, // Code was pic. Create symlink; skip OAT patching. ERROR_OAT_FILE, // Failed to symlink oat file ERROR_FIRST = ERROR_OAT_FILE, }; // Was the .oat image at oat_in made with --compile-pic ? static MaybePic IsOatPic(const ElfFile* oat_in); // Attempt to replace the file with a symlink // Returns false if it fails static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename, const std::string& output_oat_filename, bool output_oat_opened_from_fd, bool new_oat_out); // Output oat was newly created? static void BitmapCallback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) { reinterpret_cast<PatchOat*>(arg)->VisitObject(obj); } void VisitObject(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_); void FixupMethod(ArtMethod* object, ArtMethod* copy) SHARED_REQUIRES(Locks::mutator_lock_); // Patches oat in place, modifying the oat_file given to the constructor. bool PatchElf(); template <typename ElfFileImpl> bool PatchElf(ElfFileImpl* oat_file); template <typename ElfFileImpl> bool PatchOatHeader(ElfFileImpl* oat_file); bool PatchImage(bool primary_image) SHARED_REQUIRES(Locks::mutator_lock_); void PatchArtFields(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_); void PatchArtMethods(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_); void PatchImtConflictTables(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_); void PatchInternedStrings(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_); void PatchClassTable(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_); void PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots) SHARED_REQUIRES(Locks::mutator_lock_); bool WriteElf(File* out); bool WriteImage(File* out); template <typename T> T* RelocatedCopyOf(T* obj) const { if (obj == nullptr) { return nullptr; } DCHECK_GT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->Begin())); DCHECK_LT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->End())); uintptr_t heap_off = reinterpret_cast<uintptr_t>(obj) - reinterpret_cast<uintptr_t>(heap_->Begin()); DCHECK_LT(heap_off, image_->Size()); return reinterpret_cast<T*>(image_->Begin() + heap_off); } template <typename T> T* RelocatedCopyOfFollowImages(T* obj) const { if (obj == nullptr) { return nullptr; } // Find ImageSpace this belongs to. auto image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); for (gc::space::ImageSpace* image_space : image_spaces) { if (image_space->Contains(obj)) { uintptr_t heap_off = reinterpret_cast<uintptr_t>(obj) - reinterpret_cast<uintptr_t>(image_space->GetMemMap()->Begin()); return reinterpret_cast<T*>(space_map_->find(image_space)->second->Begin() + heap_off); } } LOG(FATAL) << "Did not find object in boot image space " << obj; UNREACHABLE(); } template <typename T> T* RelocatedAddressOfPointer(T* obj) const { if (obj == nullptr) { return obj; } auto ret = reinterpret_cast<uintptr_t>(obj) + delta_; // Trim off high bits in case negative relocation with 64 bit patchoat. if (InstructionSetPointerSize(isa_) == sizeof(uint32_t)) { ret = static_cast<uintptr_t>(static_cast<uint32_t>(ret)); } return reinterpret_cast<T*>(ret); } template <typename T> T RelocatedAddressOfIntPointer(T obj) const { if (obj == 0) { return obj; } T ret = obj + delta_; // Trim off high bits in case negative relocation with 64 bit patchoat. if (InstructionSetPointerSize(isa_) == 4) { ret = static_cast<T>(static_cast<uint32_t>(ret)); } return ret; } // Walks through the old image and patches the mmap'd copy of it to the new offset. It does not // change the heap. class PatchVisitor { public: PatchVisitor(PatchOat* patcher, mirror::Object* copy) : patcher_(patcher), copy_(copy) {} ~PatchVisitor() {} void operator() (mirror::Object* obj, MemberOffset off, bool b) const REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); // For reference classes. void operator() (mirror::Class* cls, mirror::Reference* ref) const REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_); // TODO: Consider using these for updating native class roots? void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} private: PatchOat* const patcher_; mirror::Object* const copy_; }; // The elf file we are patching. std::unique_ptr<ElfFile> oat_file_; // A mmap of the image we are patching. This is modified. const MemMap* const image_; // The bitmap over the image within the heap we are patching. This is not modified. gc::accounting::ContinuousSpaceBitmap* const bitmap_; // The heap we are patching. This is not modified. const MemMap* const heap_; // The amount we are changing the offset by. const off_t delta_; // Active instruction set, used to know the entrypoint size. const InstructionSet isa_; const std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* space_map_; TimingLogger* timings_; friend class FixupRootVisitor; friend class RelocatedPointerVisitor; friend class PatchOatArtFieldVisitor; friend class PatchOatArtMethodVisitor; DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat); }; } // namespace art #endif // ART_PATCHOAT_PATCHOAT_H_