| /* |
| * Copyright (C) 2016 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. |
| * |
| * Header file of an in-memory representation of DEX files. |
| */ |
| |
| #ifndef ART_DEXLAYOUT_DEX_WRITER_H_ |
| #define ART_DEXLAYOUT_DEX_WRITER_H_ |
| |
| #include <functional> |
| #include <memory> // For unique_ptr |
| |
| #include "base/os.h" |
| #include "base/unix_file/fd_file.h" |
| #include "dex/compact_dex_level.h" |
| #include "dex_container.h" |
| #include "dex/dex_file.h" |
| #include "dex_ir.h" |
| |
| #include <queue> |
| |
| namespace art { |
| |
| class DexLayout; |
| class DexLayoutHotnessInfo; |
| |
| struct MapItem { |
| // Not using DexFile::MapItemType since compact dex and standard dex file may have different |
| // sections. |
| MapItem() = default; |
| MapItem(uint32_t type, uint32_t size, size_t offset) |
| : type_(type), size_(size), offset_(offset) { } |
| |
| // Sort by decreasing order since the priority_queue puts largest elements first. |
| bool operator>(const MapItem& other) const { |
| return offset_ > other.offset_; |
| } |
| |
| uint32_t type_ = 0u; |
| uint32_t size_ = 0u; |
| uint32_t offset_ = 0u; |
| }; |
| |
| class MapItemQueue : public |
| std::priority_queue<MapItem, std::vector<MapItem>, std::greater<MapItem>> { |
| public: |
| void AddIfNotEmpty(const MapItem& item); |
| }; |
| |
| class DexWriter { |
| public: |
| static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; |
| static constexpr uint32_t kDexSectionWordAlignment = 4; |
| |
| // Stream that writes into a dex container section. Do not have two streams pointing to the same |
| // backing storage as there may be invalidation of backing storage to resize the section. |
| // Random access stream (consider refactoring). |
| class Stream { |
| public: |
| explicit Stream(DexContainer::Section* section) : section_(section) { |
| SyncWithSection(); |
| } |
| |
| const uint8_t* Begin() const { |
| return data_; |
| } |
| |
| // Functions are not virtual (yet) for speed. |
| size_t Tell() const { |
| return position_; |
| } |
| |
| void Seek(size_t position) { |
| position_ = position; |
| EnsureStorage(0u); |
| } |
| |
| // Does not allow overwriting for bug prevention purposes. |
| ALWAYS_INLINE size_t Write(const void* buffer, size_t length) { |
| EnsureStorage(length); |
| for (size_t i = 0; i < length; ++i) { |
| DCHECK_EQ(data_[position_ + i], 0u); |
| } |
| memcpy(&data_[position_], buffer, length); |
| position_ += length; |
| return length; |
| } |
| |
| ALWAYS_INLINE size_t Overwrite(const void* buffer, size_t length) { |
| EnsureStorage(length); |
| memcpy(&data_[position_], buffer, length); |
| position_ += length; |
| return length; |
| } |
| |
| ALWAYS_INLINE size_t Clear(size_t position, size_t length) { |
| EnsureStorage(length); |
| memset(&data_[position], 0, length); |
| return length; |
| } |
| |
| ALWAYS_INLINE size_t WriteSleb128(int32_t value) { |
| EnsureStorage(8); |
| uint8_t* ptr = &data_[position_]; |
| const size_t len = EncodeSignedLeb128(ptr, value) - ptr; |
| position_ += len; |
| return len; |
| } |
| |
| ALWAYS_INLINE size_t WriteUleb128(uint32_t value) { |
| EnsureStorage(8); |
| uint8_t* ptr = &data_[position_]; |
| const size_t len = EncodeUnsignedLeb128(ptr, value) - ptr; |
| position_ += len; |
| return len; |
| } |
| |
| ALWAYS_INLINE void AlignTo(const size_t alignment) { |
| position_ = RoundUp(position_, alignment); |
| EnsureStorage(0u); |
| } |
| |
| ALWAYS_INLINE void Skip(const size_t count) { |
| position_ += count; |
| EnsureStorage(0u); |
| } |
| |
| class ScopedSeek { |
| public: |
| ScopedSeek(Stream* stream, uint32_t offset) : stream_(stream), offset_(stream->Tell()) { |
| stream->Seek(offset); |
| } |
| |
| ~ScopedSeek() { |
| stream_->Seek(offset_); |
| } |
| |
| private: |
| Stream* const stream_; |
| const uint32_t offset_; |
| }; |
| |
| private: |
| ALWAYS_INLINE void EnsureStorage(size_t length) { |
| size_t end = position_ + length; |
| while (UNLIKELY(end > data_size_)) { |
| section_->Resize(data_size_ * 3 / 2 + 1); |
| SyncWithSection(); |
| } |
| } |
| |
| void SyncWithSection() { |
| data_ = section_->Begin(); |
| data_size_ = section_->Size(); |
| } |
| |
| // Current position of the stream. |
| size_t position_ = 0u; |
| DexContainer::Section* const section_ = nullptr; |
| // Cached Begin() from the container to provide faster accesses. |
| uint8_t* data_ = nullptr; |
| // Cached Size from the container to provide faster accesses. |
| size_t data_size_ = 0u; |
| }; |
| |
| static inline constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { |
| switch (type) { |
| case DexFile::kDexTypeClassDataItem: |
| case DexFile::kDexTypeStringDataItem: |
| case DexFile::kDexTypeDebugInfoItem: |
| case DexFile::kDexTypeAnnotationItem: |
| case DexFile::kDexTypeEncodedArrayItem: |
| return alignof(uint8_t); |
| |
| default: |
| // All other sections are kDexAlignedSection. |
| return DexWriter::kDexSectionWordAlignment; |
| } |
| } |
| |
| class Container : public DexContainer { |
| public: |
| Section* GetMainSection() override { |
| return &main_section_; |
| } |
| |
| Section* GetDataSection() override { |
| return &data_section_; |
| } |
| |
| bool IsCompactDexContainer() const override { |
| return false; |
| } |
| |
| private: |
| VectorSection main_section_; |
| VectorSection data_section_; |
| |
| friend class CompactDexWriter; |
| }; |
| |
| DexWriter(DexLayout* dex_layout, bool compute_offsets); |
| |
| static bool Output(DexLayout* dex_layout, |
| std::unique_ptr<DexContainer>* container, |
| bool compute_offsets, |
| std::string* error_msg) WARN_UNUSED; |
| |
| virtual ~DexWriter() {} |
| |
| protected: |
| virtual bool Write(DexContainer* output, std::string* error_msg); |
| virtual std::unique_ptr<DexContainer> CreateDexContainer() const; |
| |
| void WriteEncodedValue(Stream* stream, dex_ir::EncodedValue* encoded_value); |
| void WriteEncodedValueHeader(Stream* stream, int8_t value_type, size_t value_arg); |
| void WriteEncodedArray(Stream* stream, dex_ir::EncodedValueVector* values); |
| void WriteEncodedAnnotation(Stream* stream, dex_ir::EncodedAnnotation* annotation); |
| void WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields); |
| void WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods); |
| |
| // Header and id section |
| virtual void WriteHeader(Stream* stream); |
| virtual size_t GetHeaderSize() const; |
| // reserve_only means don't write, only reserve space. This is required since the string data |
| // offsets must be assigned. |
| void WriteStringIds(Stream* stream, bool reserve_only); |
| void WriteTypeIds(Stream* stream); |
| void WriteProtoIds(Stream* stream, bool reserve_only); |
| void WriteFieldIds(Stream* stream); |
| void WriteMethodIds(Stream* stream); |
| void WriteClassDefs(Stream* stream, bool reserve_only); |
| void WriteCallSiteIds(Stream* stream, bool reserve_only); |
| |
| void WriteEncodedArrays(Stream* stream); |
| void WriteAnnotations(Stream* stream); |
| void WriteAnnotationSets(Stream* stream); |
| void WriteAnnotationSetRefs(Stream* stream); |
| void WriteAnnotationsDirectories(Stream* stream); |
| |
| // Data section. |
| void WriteDebugInfoItems(Stream* stream); |
| void WriteCodeItems(Stream* stream, bool reserve_only); |
| void WriteTypeLists(Stream* stream); |
| void WriteStringDatas(Stream* stream); |
| void WriteClassDatas(Stream* stream); |
| void WriteMethodHandles(Stream* stream); |
| void WriteMapItems(Stream* stream, MapItemQueue* queue); |
| void GenerateAndWriteMapItems(Stream* stream); |
| |
| virtual void WriteCodeItemPostInstructionData(Stream* stream, |
| dex_ir::CodeItem* item, |
| bool reserve_only); |
| virtual void WriteCodeItem(Stream* stream, dex_ir::CodeItem* item, bool reserve_only); |
| virtual void WriteDebugInfoItem(Stream* stream, dex_ir::DebugInfoItem* debug_info); |
| virtual void WriteStringData(Stream* stream, dex_ir::StringData* string_data); |
| |
| // Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the |
| // existing offset and use that for writing. |
| void ProcessOffset(Stream* stream, dex_ir::Item* item); |
| |
| dex_ir::Header* const header_; |
| DexLayout* const dex_layout_; |
| bool compute_offsets_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DexWriter); |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_DEXLAYOUT_DEX_WRITER_H_ |