From 0646d0a48f7cc36e0e641017f410881c21136420 Mon Sep 17 00:00:00 2001 From: kobalicek Date: Fri, 7 Aug 2020 23:44:44 +0200 Subject: [PATCH] [Bug] Added a possibility to order sections so the address table section can stay last even when user creates sections after it has been created (#293) --- src/asmjit/core.h | 3 +- src/asmjit/core/codeholder.cpp | 90 +++++++++++++++++++++++++++---- src/asmjit/core/codeholder.h | 13 +++-- src/asmjit/core/constpool.cpp | 4 +- src/asmjit/core/zonevector.h | 16 ++++-- test/asmjit_test_x86_sections.cpp | 4 +- 6 files changed, 110 insertions(+), 20 deletions(-) diff --git a/src/asmjit/core.h b/src/asmjit/core.h index 530ae90..b83b152 100644 --- a/src/asmjit/core.h +++ b/src/asmjit/core.h @@ -870,7 +870,8 @@ namespace asmjit { //! ".data", // Section name //! SIZE_MAX, // Name length if the name is not null terminated (or SIZE_MAX). //! 0, // Section flags, see Section::Flags. -//! 8); // Section alignment, must be power of 2. +//! 8, // Section alignment, must be power of 2. +//! 0); // Section order value (optional, default 0). //! //! // When you switch sections in Assembler, Builder, or Compiler the cursor //! // will always move to the end of that section. When you create an Assembler diff --git a/src/asmjit/core/codeholder.cpp b/src/asmjit/core/codeholder.cpp index 69032e8..2a53b20 100644 --- a/src/asmjit/core/codeholder.cpp +++ b/src/asmjit/core/codeholder.cpp @@ -113,6 +113,7 @@ static void CodeHolder_resetInternal(CodeHolder* self, uint32_t resetPolicy) noe self->_relocations.reset(); self->_labelEntries.reset(); self->_sections.reset(); + self->_sectionsByOrder.reset(); self->_unresolvedLinkCount = 0; self->_addressTableSection = nullptr; @@ -169,13 +170,15 @@ Error CodeHolder::init(const Environment& environment, uint64_t baseAddress) noe ASMJIT_ASSERT(_emitters.empty()); // Create a default section and insert it to the `_sections` array. - Error err = _sections.willGrow(&_allocator); + Error err = _sections.willGrow(&_allocator) | + _sectionsByOrder.willGrow(&_allocator); if (err == kErrorOk) { Section* section = _allocator.allocZeroedT
(); if (ASMJIT_LIKELY(section)) { section->_flags = Section::kFlagExec | Section::kFlagConst; CodeHolder_setSectionDefaultName(section, '.', 't', 'e', 'x', 't'); _sections.appendUnsafe(section); + _sectionsByOrder.appendUnsafe(section); } else { err = DebugUtils::errored(kErrorOutOfMemory); @@ -365,7 +368,26 @@ Error CodeHolder::reserveBuffer(CodeBuffer* cb, size_t n) noexcept { // [asmjit::CodeHolder - Sections] // ============================================================================ -Error CodeHolder::newSection(Section** sectionOut, const char* name, size_t nameSize, uint32_t flags, uint32_t alignment) noexcept { +template +static inline size_t binarySearchClosestFirst(const T* array, size_t size, const T& value, const Compar& compar) noexcept { + if (!size) + return 0; + + const T* base = array; + while (size_t half = size / 2u) { + const T* middle = base + half; + size -= half; + if (compar(middle[0], value) < 0) + base = middle; + } + + if (compar(base[0], value) < 0) + base++; + + return size_t(base - array); +} + +Error CodeHolder::newSection(Section** sectionOut, const char* name, size_t nameSize, uint32_t flags, uint32_t alignment, int32_t order) noexcept { *sectionOut = nullptr; if (nameSize == SIZE_MAX) @@ -385,16 +407,25 @@ Error CodeHolder::newSection(Section** sectionOut, const char* name, size_t name return DebugUtils::errored(kErrorTooManySections); ASMJIT_PROPAGATE(_sections.willGrow(&_allocator)); - Section* section = _allocator.allocZeroedT
(); + ASMJIT_PROPAGATE(_sectionsByOrder.willGrow(&_allocator)); + Section* section = _allocator.allocZeroedT
(); if (ASMJIT_UNLIKELY(!section)) return DebugUtils::errored(kErrorOutOfMemory); section->_id = sectionId; section->_flags = flags; section->_alignment = alignment; + section->_order = order; memcpy(section->_name.str, name, nameSize); + + size_t n = binarySearchClosestFirst(_sectionsByOrder.data(), _sectionsByOrder.size(), section, [](const Section* a, const Section* b) -> int64_t { + return a->order() == b->order() ? int64_t(a->id()) - int64_t(b->id()) + : int64_t(a->order()) - int64_t(b->order()); + }); + _sections.appendUnsafe(section); + _sectionsByOrder.insertUnsafe(n, section); *sectionOut = section; return kErrorOk; @@ -420,7 +451,7 @@ Section* CodeHolder::ensureAddressTableSection() noexcept { if (_addressTableSection) return _addressTableSection; - newSection(&_addressTableSection, CodeHolder_addrTabName, sizeof(CodeHolder_addrTabName) - 1, 0, _environment.registerSize()); + newSection(&_addressTableSection, CodeHolder_addrTabName, sizeof(CodeHolder_addrTabName) - 1, 0, _environment.registerSize(), std::numeric_limits::max()); return _addressTableSection; } @@ -842,7 +873,7 @@ static Error CodeHolder_evaluateExpression(CodeHolder* self, Expression* exp, ui Error CodeHolder::flatten() noexcept { uint64_t offset = 0; - for (Section* section : _sections) { + for (Section* section : _sectionsByOrder) { uint64_t realSize = section->realSize(); if (realSize) { uint64_t alignedOffset = Support::alignUp(offset, section->alignment()); @@ -860,7 +891,7 @@ Error CodeHolder::flatten() noexcept { // Now we know that we can assign offsets of all sections properly. Section* prev = nullptr; offset = 0; - for (Section* section : _sections) { + for (Section* section : _sectionsByOrder) { uint64_t realSize = section->realSize(); if (realSize) offset = Support::alignUp(offset, section->alignment()); @@ -881,7 +912,7 @@ size_t CodeHolder::codeSize() const noexcept { Support::FastUInt8 of = 0; uint64_t offset = 0; - for (Section* section : _sections) { + for (Section* section : _sectionsByOrder) { uint64_t realSize = section->realSize(); if (realSize) { @@ -1047,7 +1078,7 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept { } // Fixup the virtual size of the address table if it's the last section. - if (_sections.last() == addressTableSection) { + if (_sectionsByOrder.last() == addressTableSection) { size_t addressTableSize = addressTableEntryCount * addressSize; addressTableSection->_buffer._size = addressTableSize; addressTableSection->_virtualSize = addressTableSize; @@ -1078,7 +1109,7 @@ Error CodeHolder::copySectionData(void* dst, size_t dstSize, uint32_t sectionId, Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t copyOptions) noexcept { size_t end = 0; - for (Section* section : _sections) { + for (Section* section : _sectionsByOrder) { if (section->offset() > dstSize) return DebugUtils::errored(kErrorInvalidArgument); @@ -1107,4 +1138,45 @@ Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t copyOpti return kErrorOk; } +// ============================================================================ +// [asmjit::CodeHolder - Unit] +// ============================================================================ + +#if defined(ASMJIT_TEST) +UNIT(code_holder) { + CodeHolder code; + + INFO("Verifying CodeHolder::init()"); + Environment env; + env.init(Environment::kArchX86); + + code.init(env); + EXPECT(code.arch() == Environment::kArchX86); + + INFO("Verifying named labels"); + LabelEntry* le; + EXPECT(code.newNamedLabelEntry(&le, "NamedLabel", SIZE_MAX, Label::kTypeGlobal) == kErrorOk); + EXPECT(strcmp(le->name(), "NamedLabel") == 0); + EXPECT(code.labelIdByName("NamedLabel") == le->id()); + + INFO("Verifying section ordering"); + Section* section1; + EXPECT(code.newSection(§ion1, "high-priority", SIZE_MAX, 0, 1, -1) == kErrorOk); + EXPECT(code.sections()[1] == section1); + EXPECT(code.sectionsByOrder()[0] == section1); + + Section* section0; + EXPECT(code.newSection(§ion0, "higher-priority", SIZE_MAX, 0, 1, -2) == kErrorOk); + EXPECT(code.sections()[2] == section0); + EXPECT(code.sectionsByOrder()[0] == section0); + EXPECT(code.sectionsByOrder()[1] == section1); + + Section* section3; + EXPECT(code.newSection(§ion3, "low-priority", SIZE_MAX, 0, 1, 2) == kErrorOk); + EXPECT(code.sections()[3] == section3); + EXPECT(code.sectionsByOrder()[3] == section3); + +} +#endif + ASMJIT_END_NAMESPACE diff --git a/src/asmjit/core/codeholder.h b/src/asmjit/core/codeholder.h index 2351273..f1719ac 100644 --- a/src/asmjit/core/codeholder.h +++ b/src/asmjit/core/codeholder.h @@ -81,8 +81,8 @@ public: uint32_t _flags; //! Section alignment requirements (0 if no requirements). uint32_t _alignment; - //! Reserved for future use (padding). - uint32_t _reserved; + //! Order (lower value means higher priority). + int32_t _order; //! Offset of this section from base-address. uint64_t _offset; //! Virtual size of the section (zero initialized sections). @@ -133,6 +133,9 @@ public: //! Sets the minimum section alignment inline void setAlignment(uint32_t alignment) noexcept { _alignment = alignment; } + //! Returns the section order, which has a higher priority than section id. + inline int32_t order() const noexcept { return _order; } + //! Returns the section offset, relative to base. inline uint64_t offset() const noexcept { return _offset; } //! Set the section offset. @@ -519,6 +522,8 @@ public: ZoneVector _emitters; //! Section entries. ZoneVector _sections; + //! Section entries sorted by section order and then section id. + ZoneVector _sectionsByOrder; //! Label entries. ZoneVector _labelEntries; //! Relocation entries. @@ -667,6 +672,8 @@ public: //! Returns an array of `Section*` records. inline const ZoneVector& sections() const noexcept { return _sections; } + //! Returns an array of `Section*` records sorted according to section order first, then section id. + inline const ZoneVector& sectionsByOrder() const noexcept { return _sectionsByOrder; } //! Returns the number of sections. inline uint32_t sectionCount() const noexcept { return _sections.size(); } @@ -676,7 +683,7 @@ public: //! Creates a new section and return its pointer in `sectionOut`. //! //! Returns `Error`, does not report a possible error to `ErrorHandler`. - ASMJIT_API Error newSection(Section** sectionOut, const char* name, size_t nameSize = SIZE_MAX, uint32_t flags = 0, uint32_t alignment = 1) noexcept; + ASMJIT_API Error newSection(Section** sectionOut, const char* name, size_t nameSize = SIZE_MAX, uint32_t flags = 0, uint32_t alignment = 1, int32_t order = 0) noexcept; //! Returns a section entry of the given index. inline Section* sectionById(uint32_t sectionId) const noexcept { return _sections[sectionId]; } diff --git a/src/asmjit/core/constpool.cpp b/src/asmjit/core/constpool.cpp index 4db68e2..65c995b 100644 --- a/src/asmjit/core/constpool.cpp +++ b/src/asmjit/core/constpool.cpp @@ -258,7 +258,7 @@ UNIT(const_pool) { uint32_t i; uint32_t kCount = BrokenAPI::hasArg("--quick") ? 1000 : 1000000; - INFO("Adding %u constants to the pool.", kCount); + INFO("Adding %u constants to the pool", kCount); { size_t prevOffset; size_t curOffset; @@ -278,7 +278,7 @@ UNIT(const_pool) { EXPECT(pool.alignment() == 8); } - INFO("Retrieving %u constants from the pool.", kCount); + INFO("Retrieving %u constants from the pool", kCount); { uint64_t c = 0x0101010101010101u; diff --git a/src/asmjit/core/zonevector.h b/src/asmjit/core/zonevector.h index 770543b..b7288b0 100644 --- a/src/asmjit/core/zonevector.h +++ b/src/asmjit/core/zonevector.h @@ -231,7 +231,7 @@ public: } //! Inserts an `item` at the specified `index`. - inline Error insert(ZoneAllocator* allocator, uint32_t index, const T& item) noexcept { + inline Error insert(ZoneAllocator* allocator, size_t index, const T& item) noexcept { ASMJIT_ASSERT(index <= _size); if (ASMJIT_UNLIKELY(_size == _capacity)) @@ -298,6 +298,16 @@ public: _size++; } + //! Inserts an `item` at the specified `index` (unsafe case). + inline void insertUnsafe(size_t index, const T& item) noexcept { + ASMJIT_ASSERT(_size < _capacity); + ASMJIT_ASSERT(index <= _size); + + T* dst = static_cast(_data) + index; + ::memmove(dst + 1, dst, size_t(_size - index) * sizeof(T)); + memcpy(dst, &item, sizeof(T)); + _size++; + } //! Concatenates all items of `other` at the end of the vector. inline void concatUnsafe(const ZoneVector& other) noexcept { uint32_t size = other._size; @@ -326,11 +336,11 @@ public: } //! Removes item at index `i`. - inline void removeAt(uint32_t i) noexcept { + inline void removeAt(size_t i) noexcept { ASMJIT_ASSERT(i < _size); T* data = static_cast(_data) + i; - uint32_t size = --_size - i; + size_t size = --_size - i; if (size) ::memmove(data, data + 1, size_t(size) * sizeof(T)); diff --git a/test/asmjit_test_x86_sections.cpp b/test/asmjit_test_x86_sections.cpp index de81fca..599fa57 100644 --- a/test/asmjit_test_x86_sections.cpp +++ b/test/asmjit_test_x86_sections.cpp @@ -118,7 +118,7 @@ int main() { // how to do it explicitly. printf("\nCalculating section offsets:\n"); uint64_t offset = 0; - for (Section* section : code.sections()) { + for (Section* section : code.sectionsByOrder()) { offset = Support::alignUp(offset, section->alignment()); section->setOffset(offset); offset += section->realSize(); @@ -157,7 +157,7 @@ int main() { // Copy the flattened code into `mem.rw`. There are two ways. You can either copy // everything manually by iterating over all sections or use `copyFlattenedData`. // This code is similar to what `copyFlattenedData(p, codeSize, 0)` would do: - for (Section* section : code.sections()) + for (Section* section : code.sectionsByOrder()) memcpy(static_cast(rwPtr) + size_t(section->offset()), section->data(), section->bufferSize()); // Execute the function and test whether it works.