aboutsummaryrefslogtreecommitdiff
path: root/client/asmjit/core/codeholder.cpp
diff options
context:
space:
mode:
authorauth12 <[email protected]>2020-07-19 11:45:43 -0700
committerauth12 <[email protected]>2020-07-19 11:45:43 -0700
commit4e6a09d486ed462ee4cf38c3735a12d530dc09d4 (patch)
treea67ccac41fef7a412b4357fbe54582cc4b692863 /client/asmjit/core/codeholder.cpp
parentDeleted asmjit submodule (diff)
downloadloader-4e6a09d486ed462ee4cf38c3735a12d530dc09d4.tar.xz
loader-4e6a09d486ed462ee4cf38c3735a12d530dc09d4.zip
Added asmjit.
Fixed solution file.
Diffstat (limited to 'client/asmjit/core/codeholder.cpp')
-rw-r--r--client/asmjit/core/codeholder.cpp1110
1 files changed, 1110 insertions, 0 deletions
diff --git a/client/asmjit/core/codeholder.cpp b/client/asmjit/core/codeholder.cpp
new file mode 100644
index 0000000..69032e8
--- /dev/null
+++ b/client/asmjit/core/codeholder.cpp
@@ -0,0 +1,1110 @@
+// AsmJit - Machine code generation for C++
+//
+// * Official AsmJit Home Page: https://asmjit.com
+// * Official Github Repository: https://github.com/asmjit/asmjit
+//
+// Copyright (c) 2008-2020 The AsmJit Authors
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+#include "../core/api-build_p.h"
+#include "../core/assembler.h"
+#include "../core/logger.h"
+#include "../core/support.h"
+
+ASMJIT_BEGIN_NAMESPACE
+
+// ============================================================================
+// [Globals]
+// ============================================================================
+
+static const char CodeHolder_addrTabName[] = ".addrtab";
+
+//! Encode MOD byte.
+static inline uint32_t x86EncodeMod(uint32_t m, uint32_t o, uint32_t rm) noexcept {
+ return (m << 6) | (o << 3) | rm;
+}
+
+// ============================================================================
+// [asmjit::LabelLinkIterator]
+// ============================================================================
+
+class LabelLinkIterator {
+public:
+ ASMJIT_INLINE LabelLinkIterator(LabelEntry* le) noexcept { reset(le); }
+
+ ASMJIT_INLINE explicit operator bool() const noexcept { return isValid(); }
+ ASMJIT_INLINE bool isValid() const noexcept { return _link != nullptr; }
+
+ ASMJIT_INLINE LabelLink* link() const noexcept { return _link; }
+ ASMJIT_INLINE LabelLink* operator->() const noexcept { return _link; }
+
+ ASMJIT_INLINE void reset(LabelEntry* le) noexcept {
+ _pPrev = &le->_links;
+ _link = *_pPrev;
+ }
+
+ ASMJIT_INLINE void next() noexcept {
+ _pPrev = &_link->next;
+ _link = *_pPrev;
+ }
+
+ ASMJIT_INLINE void resolveAndNext(CodeHolder* code) noexcept {
+ LabelLink* linkToDelete = _link;
+
+ _link = _link->next;
+ *_pPrev = _link;
+
+ code->_unresolvedLinkCount--;
+ code->_allocator.release(linkToDelete, sizeof(LabelLink));
+ }
+
+ LabelLink** _pPrev;
+ LabelLink* _link;
+};
+
+// ============================================================================
+// [asmjit::CodeHolder - Utilities]
+// ============================================================================
+
+static void CodeHolder_resetInternal(CodeHolder* self, uint32_t resetPolicy) noexcept {
+ uint32_t i;
+ const ZoneVector<BaseEmitter*>& emitters = self->emitters();
+
+ i = emitters.size();
+ while (i)
+ self->detach(emitters[--i]);
+
+ // Reset everything into its construction state.
+ self->_environment.reset();
+ self->_baseAddress = Globals::kNoBaseAddress;
+ self->_logger = nullptr;
+ self->_errorHandler = nullptr;
+
+ // Reset all sections.
+ uint32_t numSections = self->_sections.size();
+ for (i = 0; i < numSections; i++) {
+ Section* section = self->_sections[i];
+ if (section->_buffer.data() && !section->_buffer.isExternal())
+ ::free(section->_buffer._data);
+ section->_buffer._data = nullptr;
+ section->_buffer._capacity = 0;
+ }
+
+ // Reset zone allocator and all containers using it.
+ ZoneAllocator* allocator = self->allocator();
+
+ self->_emitters.reset();
+ self->_namedLabels.reset();
+ self->_relocations.reset();
+ self->_labelEntries.reset();
+ self->_sections.reset();
+
+ self->_unresolvedLinkCount = 0;
+ self->_addressTableSection = nullptr;
+ self->_addressTableEntries.reset();
+
+ allocator->reset(&self->_zone);
+ self->_zone.reset(resetPolicy);
+}
+
+static void CodeHolder_onSettingsUpdated(CodeHolder* self) noexcept {
+ // Notify all attached emitters about a settings update.
+ for (BaseEmitter* emitter : self->emitters()) {
+ emitter->onSettingsUpdated();
+ }
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Construction / Destruction]
+// ============================================================================
+
+CodeHolder::CodeHolder() noexcept
+ : _environment(),
+ _baseAddress(Globals::kNoBaseAddress),
+ _logger(nullptr),
+ _errorHandler(nullptr),
+ _zone(16384 - Zone::kBlockOverhead),
+ _allocator(&_zone),
+ _unresolvedLinkCount(0),
+ _addressTableSection(nullptr) {}
+
+CodeHolder::~CodeHolder() noexcept {
+ CodeHolder_resetInternal(this, Globals::kResetHard);
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Init / Reset]
+// ============================================================================
+
+inline void CodeHolder_setSectionDefaultName(
+ Section* section,
+ char c0 = 0, char c1 = 0, char c2 = 0, char c3 = 0,
+ char c4 = 0, char c5 = 0, char c6 = 0, char c7 = 0) noexcept {
+
+ section->_name.u32[0] = Support::bytepack32_4x8(uint8_t(c0), uint8_t(c1), uint8_t(c2), uint8_t(c3));
+ section->_name.u32[1] = Support::bytepack32_4x8(uint8_t(c4), uint8_t(c5), uint8_t(c6), uint8_t(c7));
+}
+
+Error CodeHolder::init(const Environment& environment, uint64_t baseAddress) noexcept {
+ // Cannot reinitialize if it's locked or there is one or more emitter attached.
+ if (isInitialized())
+ return DebugUtils::errored(kErrorAlreadyInitialized);
+
+ // If we are just initializing there should be no emitters attached.
+ ASMJIT_ASSERT(_emitters.empty());
+
+ // Create a default section and insert it to the `_sections` array.
+ Error err = _sections.willGrow(&_allocator);
+ if (err == kErrorOk) {
+ Section* section = _allocator.allocZeroedT<Section>();
+ if (ASMJIT_LIKELY(section)) {
+ section->_flags = Section::kFlagExec | Section::kFlagConst;
+ CodeHolder_setSectionDefaultName(section, '.', 't', 'e', 'x', 't');
+ _sections.appendUnsafe(section);
+ }
+ else {
+ err = DebugUtils::errored(kErrorOutOfMemory);
+ }
+ }
+
+ if (ASMJIT_UNLIKELY(err)) {
+ _zone.reset();
+ return err;
+ }
+ else {
+ _environment = environment;
+ _baseAddress = baseAddress;
+ return kErrorOk;
+ }
+}
+
+void CodeHolder::reset(uint32_t resetPolicy) noexcept {
+ CodeHolder_resetInternal(this, resetPolicy);
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Attach / Detach]
+// ============================================================================
+
+Error CodeHolder::attach(BaseEmitter* emitter) noexcept {
+ // Catch a possible misuse of the API.
+ if (ASMJIT_UNLIKELY(!emitter))
+ return DebugUtils::errored(kErrorInvalidArgument);
+
+ // Invalid emitter, this should not be possible.
+ uint32_t type = emitter->emitterType();
+ if (ASMJIT_UNLIKELY(type == BaseEmitter::kTypeNone || type >= BaseEmitter::kTypeCount))
+ return DebugUtils::errored(kErrorInvalidState);
+
+ // This is suspicious, but don't fail if `emitter` is already attached
+ // to this code holder. This is not error, but it's not recommended.
+ if (emitter->_code != nullptr) {
+ if (emitter->_code == this)
+ return kErrorOk;
+ return DebugUtils::errored(kErrorInvalidState);
+ }
+
+ // Reserve the space now as we cannot fail after `onAttach()` succeeded.
+ ASMJIT_PROPAGATE(_emitters.willGrow(&_allocator, 1));
+ ASMJIT_PROPAGATE(emitter->onAttach(this));
+
+ // Connect CodeHolder <-> BaseEmitter.
+ ASMJIT_ASSERT(emitter->_code == this);
+ _emitters.appendUnsafe(emitter);
+
+ return kErrorOk;
+}
+
+Error CodeHolder::detach(BaseEmitter* emitter) noexcept {
+ if (ASMJIT_UNLIKELY(!emitter))
+ return DebugUtils::errored(kErrorInvalidArgument);
+
+ if (ASMJIT_UNLIKELY(emitter->_code != this))
+ return DebugUtils::errored(kErrorInvalidState);
+
+ // NOTE: We always detach if we were asked to, if error happens during
+ // `emitter->onDetach()` we just propagate it, but the BaseEmitter will
+ // be detached.
+ Error err = kErrorOk;
+ if (!emitter->isDestroyed())
+ err = emitter->onDetach(this);
+
+ // Disconnect CodeHolder <-> BaseEmitter.
+ uint32_t index = _emitters.indexOf(emitter);
+ ASMJIT_ASSERT(index != Globals::kNotFound);
+
+ _emitters.removeAt(index);
+ emitter->_code = nullptr;
+
+ return err;
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Logging]
+// ============================================================================
+
+void CodeHolder::setLogger(Logger* logger) noexcept {
+#ifndef ASMJIT_NO_LOGGING
+ _logger = logger;
+ CodeHolder_onSettingsUpdated(this);
+#else
+ DebugUtils::unused(logger);
+#endif
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Error Handling]
+// ============================================================================
+
+void CodeHolder::setErrorHandler(ErrorHandler* errorHandler) noexcept {
+ _errorHandler = errorHandler;
+ CodeHolder_onSettingsUpdated(this);
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Code Buffer]
+// ============================================================================
+
+static Error CodeHolder_reserveInternal(CodeHolder* self, CodeBuffer* cb, size_t n) noexcept {
+ uint8_t* oldData = cb->_data;
+ uint8_t* newData;
+
+ if (oldData && !cb->isExternal())
+ newData = static_cast<uint8_t*>(::realloc(oldData, n));
+ else
+ newData = static_cast<uint8_t*>(::malloc(n));
+
+ if (ASMJIT_UNLIKELY(!newData))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ cb->_data = newData;
+ cb->_capacity = n;
+
+ // Update pointers used by assemblers, if attached.
+ for (BaseEmitter* emitter : self->emitters()) {
+ if (emitter->isAssembler()) {
+ BaseAssembler* a = static_cast<BaseAssembler*>(emitter);
+ if (&a->_section->_buffer == cb) {
+ size_t offset = a->offset();
+
+ a->_bufferData = newData;
+ a->_bufferEnd = newData + n;
+ a->_bufferPtr = newData + offset;
+ }
+ }
+ }
+
+ return kErrorOk;
+}
+
+Error CodeHolder::growBuffer(CodeBuffer* cb, size_t n) noexcept {
+ // The size of the section must be valid.
+ size_t size = cb->size();
+ if (ASMJIT_UNLIKELY(n > std::numeric_limits<uintptr_t>::max() - size))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ // We can now check if growing the buffer is really necessary. It's unlikely
+ // that this function is called while there is still room for `n` bytes.
+ size_t capacity = cb->capacity();
+ size_t required = cb->size() + n;
+ if (ASMJIT_UNLIKELY(required <= capacity))
+ return kErrorOk;
+
+ if (cb->isFixed())
+ return DebugUtils::errored(kErrorTooLarge);
+
+ size_t kInitialCapacity = 8096;
+ if (capacity < kInitialCapacity)
+ capacity = kInitialCapacity;
+ else
+ capacity += Globals::kAllocOverhead;
+
+ do {
+ size_t old = capacity;
+ if (capacity < Globals::kGrowThreshold)
+ capacity *= 2;
+ else
+ capacity += Globals::kGrowThreshold;
+
+ // Overflow.
+ if (ASMJIT_UNLIKELY(old > capacity))
+ return DebugUtils::errored(kErrorOutOfMemory);
+ } while (capacity - Globals::kAllocOverhead < required);
+
+ return CodeHolder_reserveInternal(this, cb, capacity - Globals::kAllocOverhead);
+}
+
+Error CodeHolder::reserveBuffer(CodeBuffer* cb, size_t n) noexcept {
+ size_t capacity = cb->capacity();
+
+ if (n <= capacity)
+ return kErrorOk;
+
+ if (cb->isFixed())
+ return DebugUtils::errored(kErrorTooLarge);
+
+ return CodeHolder_reserveInternal(this, cb, n);
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Sections]
+// ============================================================================
+
+Error CodeHolder::newSection(Section** sectionOut, const char* name, size_t nameSize, uint32_t flags, uint32_t alignment) noexcept {
+ *sectionOut = nullptr;
+
+ if (nameSize == SIZE_MAX)
+ nameSize = strlen(name);
+
+ if (alignment == 0)
+ alignment = 1;
+
+ if (ASMJIT_UNLIKELY(!Support::isPowerOf2(alignment)))
+ return DebugUtils::errored(kErrorInvalidArgument);
+
+ if (ASMJIT_UNLIKELY(nameSize > Globals::kMaxSectionNameSize))
+ return DebugUtils::errored(kErrorInvalidSectionName);
+
+ uint32_t sectionId = _sections.size();
+ if (ASMJIT_UNLIKELY(sectionId == Globals::kInvalidId))
+ return DebugUtils::errored(kErrorTooManySections);
+
+ ASMJIT_PROPAGATE(_sections.willGrow(&_allocator));
+ Section* section = _allocator.allocZeroedT<Section>();
+
+ if (ASMJIT_UNLIKELY(!section))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ section->_id = sectionId;
+ section->_flags = flags;
+ section->_alignment = alignment;
+ memcpy(section->_name.str, name, nameSize);
+ _sections.appendUnsafe(section);
+
+ *sectionOut = section;
+ return kErrorOk;
+}
+
+Section* CodeHolder::sectionByName(const char* name, size_t nameSize) const noexcept {
+ if (nameSize == SIZE_MAX)
+ nameSize = strlen(name);
+
+ // This could be also put in a hash-table similarly like we do with labels,
+ // however it's questionable as the number of sections should be pretty low
+ // in general. Create an issue if this becomes a problem.
+ if (nameSize <= Globals::kMaxSectionNameSize) {
+ for (Section* section : _sections)
+ if (memcmp(section->_name.str, name, nameSize) == 0 && section->_name.str[nameSize] == '\0')
+ return section;
+ }
+
+ return nullptr;
+}
+
+Section* CodeHolder::ensureAddressTableSection() noexcept {
+ if (_addressTableSection)
+ return _addressTableSection;
+
+ newSection(&_addressTableSection, CodeHolder_addrTabName, sizeof(CodeHolder_addrTabName) - 1, 0, _environment.registerSize());
+ return _addressTableSection;
+}
+
+Error CodeHolder::addAddressToAddressTable(uint64_t address) noexcept {
+ AddressTableEntry* entry = _addressTableEntries.get(address);
+ if (entry)
+ return kErrorOk;
+
+ Section* section = ensureAddressTableSection();
+ if (ASMJIT_UNLIKELY(!section))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ entry = _zone.newT<AddressTableEntry>(address);
+ if (ASMJIT_UNLIKELY(!entry))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ _addressTableEntries.insert(entry);
+ section->_virtualSize += _environment.registerSize();
+
+ return kErrorOk;
+}
+
+// ============================================================================
+// [asmjit::CodeHolder - Labels / Symbols]
+// ============================================================================
+
+//! Only used to lookup a label from `_namedLabels`.
+class LabelByName {
+public:
+ inline LabelByName(const char* key, size_t keySize, uint32_t hashCode, uint32_t parentId) noexcept
+ : _key(key),
+ _keySize(uint32_t(keySize)),
+ _hashCode(hashCode),
+ _parentId(parentId) {}
+
+ inline uint32_t hashCode() const noexcept { return _hashCode; }
+
+ inline bool matches(const LabelEntry* entry) const noexcept {
+ return entry->nameSize() == _keySize &&
+ entry->parentId() == _parentId &&
+ ::memcmp(entry->name(), _key, _keySize) == 0;
+ }
+
+ const char* _key;
+ uint32_t _keySize;
+ uint32_t _hashCode;
+ uint32_t _parentId;
+};
+
+// Returns a hash of `name` and fixes `nameSize` if it's `SIZE_MAX`.
+static uint32_t CodeHolder_hashNameAndGetSize(const char* name, size_t& nameSize) noexcept {
+ uint32_t hashCode = 0;
+ if (nameSize == SIZE_MAX) {
+ size_t i = 0;
+ for (;;) {
+ uint8_t c = uint8_t(name[i]);
+ if (!c) break;
+ hashCode = Support::hashRound(hashCode, c);
+ i++;
+ }
+ nameSize = i;
+ }
+ else {
+ for (size_t i = 0; i < nameSize; i++) {
+ uint8_t c = uint8_t(name[i]);
+ if (ASMJIT_UNLIKELY(!c)) return DebugUtils::errored(kErrorInvalidLabelName);
+ hashCode = Support::hashRound(hashCode, c);
+ }
+ }
+ return hashCode;
+}
+
+static bool CodeHolder_writeDisplacement(void* dst, int64_t displacement, uint32_t displacementSize) {
+ if (displacementSize == 4 && Support::isInt32(displacement)) {
+ Support::writeI32uLE(dst, int32_t(displacement));
+ return true;
+ }
+ else if (displacementSize == 1 && Support::isInt8(displacement)) {
+ Support::writeI8(dst, int8_t(displacement));
+ return true;
+ }
+
+ return false;
+}
+
+LabelLink* CodeHolder::newLabelLink(LabelEntry* le, uint32_t sectionId, size_t offset, intptr_t rel) noexcept {
+ LabelLink* link = _allocator.allocT<LabelLink>();
+ if (ASMJIT_UNLIKELY(!link)) return nullptr;
+
+ link->next = le->_links;
+ le->_links = link;
+
+ link->sectionId = sectionId;
+ link->relocId = Globals::kInvalidId;
+ link->offset = offset;
+ link->rel = rel;
+
+ _unresolvedLinkCount++;
+ return link;
+}
+
+Error CodeHolder::newLabelEntry(LabelEntry** entryOut) noexcept {
+ *entryOut = nullptr;
+
+ uint32_t labelId = _labelEntries.size();
+ if (ASMJIT_UNLIKELY(labelId == Globals::kInvalidId))
+ return DebugUtils::errored(kErrorTooManyLabels);
+
+ ASMJIT_PROPAGATE(_labelEntries.willGrow(&_allocator));
+ LabelEntry* le = _allocator.allocZeroedT<LabelEntry>();
+
+ if (ASMJIT_UNLIKELY(!le))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ le->_setId(labelId);
+ le->_parentId = Globals::kInvalidId;
+ le->_offset = 0;
+ _labelEntries.appendUnsafe(le);
+
+ *entryOut = le;
+ return kErrorOk;
+}
+
+Error CodeHolder::newNamedLabelEntry(LabelEntry** entryOut, const char* name, size_t nameSize, uint32_t type, uint32_t parentId) noexcept {
+ *entryOut = nullptr;
+ uint32_t hashCode = CodeHolder_hashNameAndGetSize(name, nameSize);
+
+ if (ASMJIT_UNLIKELY(nameSize == 0))
+ return DebugUtils::errored(kErrorInvalidLabelName);
+
+ if (ASMJIT_UNLIKELY(nameSize > Globals::kMaxLabelNameSize))
+ return DebugUtils::errored(kErrorLabelNameTooLong);
+
+ switch (type) {
+ case Label::kTypeLocal:
+ if (ASMJIT_UNLIKELY(parentId >= _labelEntries.size()))
+ return DebugUtils::errored(kErrorInvalidParentLabel);
+
+ hashCode ^= parentId;
+ break;
+
+ case Label::kTypeGlobal:
+ if (ASMJIT_UNLIKELY(parentId != Globals::kInvalidId))
+ return DebugUtils::errored(kErrorNonLocalLabelCannotHaveParent);
+
+ break;
+
+ default:
+ return DebugUtils::errored(kErrorInvalidArgument);
+ }
+
+ // Don't allow to insert duplicates. Local labels allow duplicates that have
+ // different id, this is already accomplished by having a different hashes
+ // between the same label names having different parent labels.
+ LabelEntry* le = _namedLabels.get(LabelByName(name, nameSize, hashCode, parentId));
+ if (ASMJIT_UNLIKELY(le))
+ return DebugUtils::errored(kErrorLabelAlreadyDefined);
+
+ Error err = kErrorOk;
+ uint32_t labelId = _labelEntries.size();
+
+ if (ASMJIT_UNLIKELY(labelId == Globals::kInvalidId))
+ return DebugUtils::errored(kErrorTooManyLabels);
+
+ ASMJIT_PROPAGATE(_labelEntries.willGrow(&_allocator));
+ le = _allocator.allocZeroedT<LabelEntry>();
+
+ if (ASMJIT_UNLIKELY(!le))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ le->_hashCode = hashCode;
+ le->_setId(labelId);
+ le->_type = uint8_t(type);
+ le->_parentId = parentId;
+ le->_offset = 0;
+ ASMJIT_PROPAGATE(le->_name.setData(&_zone, name, nameSize));
+
+ _labelEntries.appendUnsafe(le);
+ _namedLabels.insert(allocator(), le);
+
+ *entryOut = le;
+ return err;
+}
+
+uint32_t CodeHolder::labelIdByName(const char* name, size_t nameSize, uint32_t parentId) noexcept {
+ uint32_t hashCode = CodeHolder_hashNameAndGetSize(name, nameSize);
+ if (ASMJIT_UNLIKELY(!nameSize))
+ return 0;
+
+ if (parentId != Globals::kInvalidId)
+ hashCode ^= parentId;
+
+ LabelEntry* le = _namedLabels.get(LabelByName(name, nameSize, hashCode, parentId));
+ return le ? le->id() : uint32_t(Globals::kInvalidId);
+}
+
+ASMJIT_API Error CodeHolder::resolveUnresolvedLinks() noexcept {
+ if (!hasUnresolvedLinks())
+ return kErrorOk;
+
+ Error err = kErrorOk;
+ for (LabelEntry* le : labelEntries()) {
+ if (!le->isBound())
+ continue;
+
+ LabelLinkIterator link(le);
+ if (link) {
+ Support::FastUInt8 of = 0;
+ Section* toSection = le->section();
+ uint64_t toOffset = Support::addOverflow(toSection->offset(), le->offset(), &of);
+
+ do {
+ uint32_t linkSectionId = link->sectionId;
+ if (link->relocId == Globals::kInvalidId) {
+ Section* fromSection = sectionById(linkSectionId);
+ size_t linkOffset = link->offset;
+
+ CodeBuffer& buf = _sections[linkSectionId]->buffer();
+ ASMJIT_ASSERT(linkOffset < buf.size());
+
+ // Calculate the offset relative to the start of the virtual base.
+ uint64_t fromOffset = Support::addOverflow<uint64_t>(fromSection->offset(), linkOffset, &of);
+ int64_t displacement = int64_t(toOffset - fromOffset + uint64_t(int64_t(link->rel)));
+
+ if (!of) {
+ ASMJIT_ASSERT(size_t(linkOffset) < buf.size());
+
+ // Size of the value we are going to patch. Only BYTE/DWORD is allowed.
+ uint32_t displacementSize = buf._data[linkOffset];
+ ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize);
+
+ // Overwrite a real displacement in the CodeBuffer.
+ if (CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) {
+ link.resolveAndNext(this);
+ continue;
+ }
+ }
+
+ err = DebugUtils::errored(kErrorInvalidDisplacement);
+ // Falls through to `link.next()`.
+ }
+
+ link.next();
+ } while (link);
+ }
+ }
+
+ return err;
+}
+
+ASMJIT_API Error CodeHolder::bindLabel(const Label& label, uint32_t toSectionId, uint64_t toOffset) noexcept {
+ LabelEntry* le = labelEntry(label);
+ if (ASMJIT_UNLIKELY(!le))
+ return DebugUtils::errored(kErrorInvalidLabel);
+
+ if (ASMJIT_UNLIKELY(toSectionId > _sections.size()))
+ return DebugUtils::errored(kErrorInvalidSection);
+
+ // Label can be bound only once.
+ if (ASMJIT_UNLIKELY(le->isBound()))
+ return DebugUtils::errored(kErrorLabelAlreadyBound);
+
+ // Bind the label.
+ Section* section = _sections[toSectionId];
+ le->_section = section;
+ le->_offset = toOffset;
+
+ Error err = kErrorOk;
+ CodeBuffer& buf = section->buffer();
+
+ // Fix all links to this label we have collected so far if they are within
+ // the same section. We ignore any inter-section links as these have to be
+ // fixed later.
+ LabelLinkIterator link(le);
+ while (link) {
+ uint32_t linkSectionId = link->sectionId;
+ size_t linkOffset = link->offset;
+
+ uint32_t relocId = link->relocId;
+ if (relocId != Globals::kInvalidId) {
+ // Adjust relocation data only.
+ RelocEntry* re = _relocations[relocId];
+ re->_payload += toOffset;
+ re->_targetSectionId = toSectionId;
+ }
+ else {
+ if (linkSectionId != toSectionId) {
+ link.next();
+ continue;
+ }
+
+ ASMJIT_ASSERT(linkOffset < buf.size());
+ int64_t displacement = int64_t(toOffset - uint64_t(linkOffset) + uint64_t(int64_t(link->rel)));
+
+ // Size of the value we are going to patch. Only BYTE/DWORD is allowed.
+ uint32_t displacementSize = buf._data[linkOffset];
+ ASMJIT_ASSERT(buf.size() - size_t(linkOffset) >= displacementSize);
+
+ // Overwrite a real displacement in the CodeBuffer.
+ if (!CodeHolder_writeDisplacement(buf._data + linkOffset, displacement, displacementSize)) {
+ err = DebugUtils::errored(kErrorInvalidDisplacement);
+ link.next();
+ continue;
+ }
+ }
+
+ link.resolveAndNext(this);
+ }
+
+ return err;
+}
+
+// ============================================================================
+// [asmjit::BaseEmitter - Relocations]
+// ============================================================================
+
+Error CodeHolder::newRelocEntry(RelocEntry** dst, uint32_t relocType, uint32_t valueSize) noexcept {
+ ASMJIT_PROPAGATE(_relocations.willGrow(&_allocator));
+
+ uint32_t relocId = _relocations.size();
+ if (ASMJIT_UNLIKELY(relocId == Globals::kInvalidId))
+ return DebugUtils::errored(kErrorTooManyRelocations);
+
+ RelocEntry* re = _allocator.allocZeroedT<RelocEntry>();
+ if (ASMJIT_UNLIKELY(!re))
+ return DebugUtils::errored(kErrorOutOfMemory);
+
+ re->_id = relocId;
+ re->_relocType = uint8_t(relocType);
+ re->_valueSize = uint8_t(valueSize);
+ re->_sourceSectionId = Globals::kInvalidId;
+ re->_targetSectionId = Globals::kInvalidId;
+ _relocations.appendUnsafe(re);
+
+ *dst = re;
+ return kErrorOk;
+}
+
+// ============================================================================
+// [asmjit::BaseEmitter - Expression Evaluation]
+// ============================================================================
+
+static Error CodeHolder_evaluateExpression(CodeHolder* self, Expression* exp, uint64_t* out) noexcept {
+ uint64_t value[2];
+ for (size_t i = 0; i < 2; i++) {
+ uint64_t v;
+ switch (exp->valueType[i]) {
+ case Expression::kValueNone: {
+ v = 0;
+ break;
+ }
+
+ case Expression::kValueConstant: {
+ v = exp->value[i].constant;
+ break;
+ }
+
+ case Expression::kValueLabel: {
+ LabelEntry* le = exp->value[i].label;
+ if (!le->isBound())
+ return DebugUtils::errored(kErrorExpressionLabelNotBound);
+ v = le->section()->offset() + le->offset();
+ break;
+ }
+
+ case Expression::kValueExpression: {
+ Expression* nested = exp->value[i].expression;
+ ASMJIT_PROPAGATE(CodeHolder_evaluateExpression(self, nested, &v));
+ break;
+ }
+
+ default:
+ return DebugUtils::errored(kErrorInvalidState);
+ }
+
+ value[i] = v;
+ }
+
+ uint64_t result;
+ uint64_t& a = value[0];
+ uint64_t& b = value[1];
+
+ switch (exp->opType) {
+ case Expression::kOpAdd:
+ result = a + b;
+ break;
+
+ case Expression::kOpSub:
+ result = a - b;
+ break;
+
+ case Expression::kOpMul:
+ result = a * b;
+ break;
+
+ case Expression::kOpSll:
+ result = (b > 63) ? uint64_t(0) : uint64_t(a << b);
+ break;
+
+ case Expression::kOpSrl:
+ result = (b > 63) ? uint64_t(0) : uint64_t(a >> b);
+ break;
+
+ case Expression::kOpSra:
+ result = Support::sar(a, Support::min<uint64_t>(b, 63));
+ break;
+
+ default:
+ return DebugUtils::errored(kErrorInvalidState);
+ }
+
+ *out = result;
+ return kErrorOk;
+}
+
+// ============================================================================
+// [asmjit::BaseEmitter - Utilities]
+// ============================================================================
+
+Error CodeHolder::flatten() noexcept {
+ uint64_t offset = 0;
+ for (Section* section : _sections) {
+ uint64_t realSize = section->realSize();
+ if (realSize) {
+ uint64_t alignedOffset = Support::alignUp(offset, section->alignment());
+ if (ASMJIT_UNLIKELY(alignedOffset < offset))
+ return DebugUtils::errored(kErrorTooLarge);
+
+ Support::FastUInt8 of = 0;
+ offset = Support::addOverflow(alignedOffset, realSize, &of);
+
+ if (ASMJIT_UNLIKELY(of))
+ return DebugUtils::errored(kErrorTooLarge);
+ }
+ }
+
+ // Now we know that we can assign offsets of all sections properly.
+ Section* prev = nullptr;
+ offset = 0;
+ for (Section* section : _sections) {
+ uint64_t realSize = section->realSize();
+ if (realSize)
+ offset = Support::alignUp(offset, section->alignment());
+ section->_offset = offset;
+
+ // Make sure the previous section extends a bit to cover the alignment.
+ if (prev)
+ prev->_virtualSize = offset - prev->_offset;
+
+ prev = section;
+ offset += realSize;
+ }
+
+ return kErrorOk;
+}
+
+size_t CodeHolder::codeSize() const noexcept {
+ Support::FastUInt8 of = 0;
+ uint64_t offset = 0;
+
+ for (Section* section : _sections) {
+ uint64_t realSize = section->realSize();
+
+ if (realSize) {
+ uint64_t alignedOffset = Support::alignUp(offset, section->alignment());
+ ASMJIT_ASSERT(alignedOffset >= offset);
+ offset = Support::addOverflow(alignedOffset, realSize, &of);
+ }
+ }
+
+ if ((sizeof(uint64_t) > sizeof(size_t) && offset > SIZE_MAX) || of)
+ return SIZE_MAX;
+
+ return size_t(offset);
+}
+
+Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
+ // Base address must be provided.
+ if (ASMJIT_UNLIKELY(baseAddress == Globals::kNoBaseAddress))
+ return DebugUtils::errored(kErrorInvalidArgument);
+
+ _baseAddress = baseAddress;
+ uint32_t addressSize = _environment.registerSize();
+
+ Section* addressTableSection = _addressTableSection;
+ uint32_t addressTableEntryCount = 0;
+ uint8_t* addressTableEntryData = nullptr;
+
+ if (addressTableSection) {
+ ASMJIT_PROPAGATE(
+ reserveBuffer(&addressTableSection->_buffer, size_t(addressTableSection->virtualSize())));
+ addressTableEntryData = addressTableSection->_buffer.data();
+ }
+
+ // Relocate all recorded locations.
+ for (const RelocEntry* re : _relocations) {
+ // Possibly deleted or optimized-out entry.
+ if (re->relocType() == RelocEntry::kTypeNone)
+ continue;
+
+ Section* sourceSection = sectionById(re->sourceSectionId());
+ Section* targetSection = nullptr;
+
+ if (re->targetSectionId() != Globals::kInvalidId)
+ targetSection = sectionById(re->targetSectionId());
+
+ uint64_t value = re->payload();
+ uint64_t sectionOffset = sourceSection->offset();
+ uint64_t sourceOffset = re->sourceOffset();
+
+ // Make sure that the `RelocEntry` doesn't go out of bounds.
+ size_t regionSize = re->leadingSize() + re->valueSize() + re->trailingSize();
+ if (ASMJIT_UNLIKELY(re->sourceOffset() >= sourceSection->bufferSize() ||
+ sourceSection->bufferSize() - size_t(re->sourceOffset()) < regionSize))
+ return DebugUtils::errored(kErrorInvalidRelocEntry);
+
+ uint8_t* buffer = sourceSection->data();
+ size_t valueOffset = size_t(re->sourceOffset()) + re->leadingSize();
+
+ switch (re->relocType()) {
+ case RelocEntry::kTypeExpression: {
+ Expression* expression = (Expression*)(uintptr_t(value));
+ ASMJIT_PROPAGATE(CodeHolder_evaluateExpression(this, expression, &value));
+ break;
+ }
+
+ case RelocEntry::kTypeAbsToAbs: {
+ break;
+ }
+
+ case RelocEntry::kTypeRelToAbs: {
+ // Value is currently a relative offset from the start of its section.
+ // We have to convert it to an absolute offset (including base address).
+ if (ASMJIT_UNLIKELY(!targetSection))
+ return DebugUtils::errored(kErrorInvalidRelocEntry);
+
+ //value += baseAddress + sectionOffset + sourceOffset + regionSize;
+ value += baseAddress + targetSection->offset();
+ break;
+ }
+
+ case RelocEntry::kTypeAbsToRel: {
+ value -= baseAddress + sectionOffset + sourceOffset + regionSize;
+ if (addressSize > 4 && !Support::isInt32(int64_t(value)))
+ return DebugUtils::errored(kErrorRelocOffsetOutOfRange);
+ break;
+ }
+
+ case RelocEntry::kTypeX64AddressEntry: {
+ if (re->valueSize() != 4 || re->leadingSize() < 2)
+ return DebugUtils::errored(kErrorInvalidRelocEntry);
+
+ // First try whether a relative 32-bit displacement would work.
+ value -= baseAddress + sectionOffset + sourceOffset + regionSize;
+ if (!Support::isInt32(int64_t(value))) {
+ // Relative 32-bit displacement is not possible, use '.addrtab' section.
+ AddressTableEntry* atEntry = _addressTableEntries.get(re->payload());
+ if (ASMJIT_UNLIKELY(!atEntry))
+ return DebugUtils::errored(kErrorInvalidRelocEntry);
+
+ // Cannot be null as we have just matched the `AddressTableEntry`.
+ ASMJIT_ASSERT(addressTableSection != nullptr);
+
+ if (!atEntry->hasAssignedSlot())
+ atEntry->_slot = addressTableEntryCount++;
+
+ size_t atEntryIndex = size_t(atEntry->slot()) * addressSize;
+ uint64_t addrSrc = sectionOffset + sourceOffset + regionSize;
+ uint64_t addrDst = addressTableSection->offset() + uint64_t(atEntryIndex);
+
+ value = addrDst - addrSrc;
+ if (!Support::isInt32(int64_t(value)))
+ return DebugUtils::errored(kErrorRelocOffsetOutOfRange);
+
+ // Bytes that replace [REX, OPCODE] bytes.
+ uint32_t byte0 = 0xFF;
+ uint32_t byte1 = buffer[valueOffset - 1];
+
+ if (byte1 == 0xE8) {
+ // Patch CALL/MOD byte to FF /2 (-> 0x15).
+ byte1 = x86EncodeMod(0, 2, 5);
+ }
+ else if (byte1 == 0xE9) {
+ // Patch JMP/MOD byte to FF /4 (-> 0x25).
+ byte1 = x86EncodeMod(0, 4, 5);
+ }
+ else {
+ return DebugUtils::errored(kErrorInvalidRelocEntry);
+ }
+
+ // Patch `jmp/call` instruction.
+ buffer[valueOffset - 2] = uint8_t(byte0);
+ buffer[valueOffset - 1] = uint8_t(byte1);
+
+ Support::writeU64uLE(addressTableEntryData + atEntryIndex, re->payload());
+ }
+ break;
+ }
+
+ default:
+ return DebugUtils::errored(kErrorInvalidRelocEntry);
+ }
+
+ switch (re->valueSize()) {
+ case 1:
+ Support::writeU8(buffer + valueOffset, uint32_t(value & 0xFFu));
+ break;
+
+ case 2:
+ Support::writeU16uLE(buffer + valueOffset, uint32_t(value & 0xFFFFu));
+ break;
+
+ case 4:
+ Support::writeU32uLE(buffer + valueOffset, uint32_t(value & 0xFFFFFFFFu));
+ break;
+
+ case 8:
+ Support::writeU64uLE(buffer + valueOffset, value);
+ break;
+
+ default:
+ return DebugUtils::errored(kErrorInvalidRelocEntry);
+ }
+ }
+
+ // Fixup the virtual size of the address table if it's the last section.
+ if (_sections.last() == addressTableSection) {
+ size_t addressTableSize = addressTableEntryCount * addressSize;
+ addressTableSection->_buffer._size = addressTableSize;
+ addressTableSection->_virtualSize = addressTableSize;
+ }
+
+ return kErrorOk;
+}
+
+Error CodeHolder::copySectionData(void* dst, size_t dstSize, uint32_t sectionId, uint32_t copyOptions) noexcept {
+ if (ASMJIT_UNLIKELY(!isSectionValid(sectionId)))
+ return DebugUtils::errored(kErrorInvalidSection);
+
+ Section* section = sectionById(sectionId);
+ size_t bufferSize = section->bufferSize();
+
+ if (ASMJIT_UNLIKELY(dstSize < bufferSize))
+ return DebugUtils::errored(kErrorInvalidArgument);
+
+ memcpy(dst, section->data(), bufferSize);
+
+ if (bufferSize < dstSize && (copyOptions & kCopyPadSectionBuffer)) {
+ size_t paddingSize = dstSize - bufferSize;
+ memset(static_cast<uint8_t*>(dst) + bufferSize, 0, paddingSize);
+ }
+
+ return kErrorOk;
+}
+
+Error CodeHolder::copyFlattenedData(void* dst, size_t dstSize, uint32_t copyOptions) noexcept {
+ size_t end = 0;
+ for (Section* section : _sections) {
+ if (section->offset() > dstSize)
+ return DebugUtils::errored(kErrorInvalidArgument);
+
+ size_t bufferSize = section->bufferSize();
+ size_t offset = size_t(section->offset());
+
+ if (ASMJIT_UNLIKELY(dstSize - offset < bufferSize))
+ return DebugUtils::errored(kErrorInvalidArgument);
+
+ uint8_t* dstTarget = static_cast<uint8_t*>(dst) + offset;
+ size_t paddingSize = 0;
+ memcpy(dstTarget, section->data(), bufferSize);
+
+ if ((copyOptions & kCopyPadSectionBuffer) && bufferSize < section->virtualSize()) {
+ paddingSize = Support::min<size_t>(dstSize - offset, size_t(section->virtualSize())) - bufferSize;
+ memset(dstTarget + bufferSize, 0, paddingSize);
+ }
+
+ end = Support::max(end, offset + bufferSize + paddingSize);
+ }
+
+ if (end < dstSize && (copyOptions & kCopyPadTargetBuffer)) {
+ memset(static_cast<uint8_t*>(dst) + end, 0, dstSize - end);
+ }
+
+ return kErrorOk;
+}
+
+ASMJIT_END_NAMESPACE