diff --git a/CMakeLists.txt b/CMakeLists.txt index d762d03..2ccfefa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,22 @@ if (WIBO_ENABLE_LIBURING AND CMAKE_SYSTEM_NAME STREQUAL "Linux") target_include_directories(liburing PRIVATE ${liburing_SOURCE_DIR}) endif() +if (NOT DEFINED MSVCRT_DLL) + set(MSVCRT_DLL ${CMAKE_CURRENT_BINARY_DIR}/vendor/msvcrt.dll) + file(DOWNLOAD + https://github.com/encounter/winedll/releases/download/2025-11-08/msvcrt.dll + ${MSVCRT_DLL} + EXPECTED_HASH SHA256=4a29890d7ee8722415b7b185321476db2cd20dbe964124189b85893f0d301e29 + ) +endif() + +function(embed_binary SRC FILE) + set_source_files_properties(${SRC} PROPERTIES + OBJECT_DEPENDS "${FILE}" + COMPILE_DEFINITIONS "EMBED_PATH=\"${FILE}\"" + ) +endfunction() + add_executable(wibo dll/advapi32.cpp dll/advapi32/md5.c @@ -198,6 +214,13 @@ add_executable(wibo src/strutil.cpp src/tls.cpp ) +if ("${MSVCRT_DLL}" STREQUAL "") + target_compile_definitions(wibo PRIVATE WIBO_HAS_MSVCRT=0) +else() + target_compile_definitions(wibo PRIVATE WIBO_HAS_MSVCRT=1) + target_sources(wibo PRIVATE dll/msvcrt.cpp) + embed_binary(dll/msvcrt.cpp "${MSVCRT_DLL}") +endif() if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_sources(wibo PRIVATE diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp index cda4d3b..a650a70 100644 --- a/dll/kernel32/fileapi.cpp +++ b/dll/kernel32/fileapi.cpp @@ -1736,19 +1736,23 @@ DWORD WINAPI GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer) { return 0; } - const char *path = getenv("WIBO_TMP_DIR"); - if (!path) { - path = "Z:\\tmp\\"; + const char *tmpDirEnv = getenv("TMPDIR"); + std::string pathStr; + if (tmpDirEnv) { + auto path = std::filesystem::canonical(tmpDirEnv); + pathStr = files::pathToWindows(path); + } else { + pathStr = "Z:\\tmp\\"; } - size_t len = strlen(path); + size_t len = pathStr.length(); if (len + 1 > nBufferLength) { setLastError(ERROR_INSUFFICIENT_BUFFER); DEBUG_LOG(" -> ERROR_INSUFFICIENT_BUFFER\n"); return static_cast(len + 1); } - DEBUG_LOG(" -> %s\n", path); - strncpy(lpBuffer, path, nBufferLength); + DEBUG_LOG(" -> %s\n", pathStr.c_str()); + strncpy(lpBuffer, pathStr.c_str(), nBufferLength); lpBuffer[nBufferLength - 1] = '\0'; return static_cast(len); } diff --git a/dll/msvcrt.cpp b/dll/msvcrt.cpp new file mode 100644 index 0000000..9ba4d0c --- /dev/null +++ b/dll/msvcrt.cpp @@ -0,0 +1,24 @@ +#include "macros.h" +#include "modules.h" + +INCLUDE_BIN(_msvcrtDllData, EMBED_PATH) + +extern const wibo::ModuleStub lib_msvcrt = { + .names = + (const char *[]){ + "msvcrt", + "msvcrt40", + "msvcr70", + "msvcr71", + "msvcr80", + "msvcr90", + "msvcr100", + "msvcr110", + "msvcr120", + "msvcr130", + "msvcr140", + "ucrtbase", + nullptr, + }, + .dllData = INCLUDE_BIN_SPAN(_msvcrtDllData), +}; diff --git a/src/loader.cpp b/src/loader.cpp index 22356d6..f960027 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -6,10 +6,18 @@ #include "types.h" #include +#include +#include #include +#include +#include +#include +#include #include +#include #include #include +#include struct PEHeader { uint8_t magic[4]; // "PE\0\0" @@ -171,17 +179,6 @@ static DWORD sectionProtectFromCharacteristics(uint32_t characteristics) { return protect; } -uint16_t read16(FILE *file) { - uint16_t v = 0; - fread(&v, 2, 1, file); - return v; -} -uint32_t read32(FILE *file) { - uint32_t v = 0; - fread(&v, 4, 1, file); - return v; -} - wibo::Executable::~Executable() { if (imageBase) { wibo::heap::virtualFree(imageBase, 0, MEM_RELEASE); @@ -189,118 +186,323 @@ wibo::Executable::~Executable() { } } -/** - * Load a PE file into memory. - * - * @param file The file to load. - * @param exec Whether to make the loaded image executable. - */ -bool wibo::Executable::loadPE(FILE *file, bool exec) { - // Skip to PE header - fseek(file, 0x3C, SEEK_SET); - uint32_t offsetToPE = read32(file); - fseek(file, offsetToPE, SEEK_SET); +namespace { - // Read headers - PEHeader header; - fread(&header, sizeof header, 1, file); - if (memcmp(header.magic, "PE\0\0", 4) != 0) +struct ImageMemoryDeleter { + void operator()(void *ptr) const { + if (ptr) { + wibo::heap::virtualFree(ptr, 0, MEM_RELEASE); + } + } +}; + +class PeInputView { + public: + explicit PeInputView(FILE *file) : source_(FileSource{file, computeFileSize(file)}) {} + explicit PeInputView(std::span bytes) : source_(SpanSource{bytes}) {} + + bool read(uint64_t offset, void *dest, size_t size) const { + if (size == 0) { + return true; + } + return std::visit([&](const auto &source) { return readImpl(source, offset, dest, size); }, source_); + } + + template std::optional readObject(uint64_t offset) const { + T value{}; + if (!read(offset, &value, sizeof(T))) { + return std::nullopt; + } + return value; + } + + std::optional size() const { + return std::visit([](const auto &source) -> std::optional { return sizeImpl(source); }, source_); + } + + private: + struct FileSource { + FILE *file; + std::optional fileSize; + }; + struct SpanSource { + std::span bytes; + }; + + static std::optional computeFileSize(FILE *file) { + if (!file) { + return std::nullopt; + } + int fd = fileno(file); + if (fd < 0) { + return std::nullopt; + } + struct stat st {}; + if (fstat(fd, &st) != 0) { + return std::nullopt; + } + if (st.st_size < 0) { + return std::nullopt; + } + return static_cast(st.st_size); + } + + static bool readImpl(const FileSource &source, uint64_t offset, void *dest, size_t size) { + if (!source.file) { + return false; + } + if (offset > static_cast(std::numeric_limits::max())) { + return false; + } + if (source.fileSize) { + if (offset > *source.fileSize) { + return false; + } + uint64_t remaining = *source.fileSize - offset; + if (remaining < size) { + return false; + } + } + if (fseeko(source.file, static_cast(offset), SEEK_SET) != 0) { + return false; + } + size_t readCount = fread(dest, 1, size, source.file); + return readCount == size; + } + + static bool readImpl(const SpanSource &source, uint64_t offset, void *dest, size_t size) { + if (offset > source.bytes.size()) { + return false; + } + size_t start = static_cast(offset); + if (source.bytes.size() - start < size) { + return false; + } + auto slice = source.bytes.subspan(start, size); + std::memcpy(dest, slice.data(), size); + return true; + } + + static std::optional sizeImpl(const FileSource &source) { return source.fileSize; } + static std::optional sizeImpl(const SpanSource &source) { + return static_cast(source.bytes.size()); + } + + std::variant source_; +}; + +void resetExecutableState(wibo::Executable &executable) { + if (executable.imageBase) { + wibo::heap::virtualFree(executable.imageBase, 0, MEM_RELEASE); + } + executable.imageBase = nullptr; + executable.imageSize = 0; + executable.entryPoint = nullptr; + executable.rsrcBase = nullptr; + executable.rsrcSize = 0; + executable.preferredImageBase = 0; + executable.relocationDelta = 0; + executable.exportDirectoryRVA = 0; + executable.exportDirectorySize = 0; + executable.relocationDirectoryRVA = 0; + executable.relocationDirectorySize = 0; + executable.importDirectoryRVA = 0; + executable.importDirectorySize = 0; + executable.delayImportDirectoryRVA = 0; + executable.delayImportDirectorySize = 0; + executable.tlsDirectoryRVA = 0; + executable.tlsDirectorySize = 0; + executable.execMapped = false; + executable.importsResolved = false; + executable.importsResolving = false; + executable.sectionsProtected = false; + executable.sections.clear(); +} + +bool loadPEFromSource(wibo::Executable &executable, const PeInputView &source, bool exec) { + resetExecutableState(executable); + kernel32::setLastError(ERROR_BAD_EXE_FORMAT); + + auto dosSignature = source.readObject(0); + if (!dosSignature || *dosSignature != 0x5A4D) { + DEBUG_LOG("loadPE: missing MZ header signature\n"); return false; - if (header.machine != 0x14C) // i386 + } + + auto offsetToPeOpt = source.readObject(0x3C); + if (!offsetToPeOpt) { + DEBUG_LOG("loadPE: failed to read e_lfanew\n"); return false; + } + uint32_t offsetToPE = *offsetToPeOpt; - DEBUG_LOG("Sections: %d / Size of optional header: %x\n", header.numberOfSections, header.sizeOfOptionalHeader); + if (auto totalSize = source.size()) { + if (offsetToPE > *totalSize || (*totalSize - offsetToPE) < sizeof(PEHeader)) { + DEBUG_LOG("loadPE: PE header offset outside data (offset=%u size=%llu)\n", offsetToPE, + static_cast(*totalSize)); + return false; + } + } - PE32Header header32; - memset(&header32, 0, sizeof header32); - fread(&header32, std::min(sizeof(header32), (size_t)header.sizeOfOptionalHeader), 1, file); - if (header32.magic != 0x10B) + PEHeader header{}; + if (!source.read(offsetToPE, &header, sizeof(header))) { + DEBUG_LOG("loadPE: unable to read PE header\n"); return false; + } + if (std::memcmp(header.magic, "PE\0\0", 4) != 0) { + DEBUG_LOG("loadPE: invalid PE signature\n"); + return false; + } + if (header.machine != 0x14C) { + DEBUG_LOG("loadPE: unsupported machine 0x%x\n", header.machine); + return false; + } + if (header.numberOfSections == 0 || header.numberOfSections > 1024) { + DEBUG_LOG("loadPE: unreasonable section count %u\n", header.numberOfSections); + return false; + } + constexpr size_t kOptionalHeaderMinimumSize = offsetof(PE32Header, reserved) + sizeof(PEImageDataDirectory); + if (header.sizeOfOptionalHeader < kOptionalHeaderMinimumSize) { + DEBUG_LOG("loadPE: optional header too small (%u bytes)\n", header.sizeOfOptionalHeader); + return false; + } + + // IMAGE_OPTIONAL_HEADER32 layout: https://learn.microsoft.com/windows/win32/debug/pe-format + PE32Header header32{}; + size_t optionalBytes = std::min(sizeof(header32), header.sizeOfOptionalHeader); + if (!source.read(offsetToPE + sizeof(header), &header32, optionalBytes)) { + DEBUG_LOG("loadPE: failed to read optional header\n"); + return false; + } + if (header32.magic != 0x10B) { + DEBUG_LOG("loadPE: unsupported optional header magic 0x%x\n", header32.magic); + return false; + } + if (header32.sizeOfImage == 0 || header32.sizeOfHeaders == 0 || header32.sizeOfHeaders > header32.sizeOfImage) { + DEBUG_LOG("loadPE: invalid image/header sizes (image=%u headers=%u)\n", header32.sizeOfImage, + header32.sizeOfHeaders); + return false; + } + if (header32.fileAlignment == 0 || header32.sectionAlignment == 0) { + DEBUG_LOG("loadPE: invalid alignment (file=%u section=%u)\n", header32.fileAlignment, + header32.sectionAlignment); + return false; + } + + DEBUG_LOG("Sections: %u / Size of optional header: %x\n", header.numberOfSections, header.sizeOfOptionalHeader); DEBUG_LOG("Image Base: %x / Size: %x\n", header32.imageBase, header32.sizeOfImage); long pageSize = sysconf(_SC_PAGE_SIZE); - DEBUG_LOG("Page size: %x\n", (unsigned int)pageSize); const size_t pageSizeValue = pageSize > 0 ? static_cast(pageSize) : static_cast(4096); + DEBUG_LOG("Page size: %x\n", static_cast(pageSizeValue)); - preferredImageBase = header32.imageBase; - exportDirectoryRVA = header32.exportTable.virtualAddress; - exportDirectorySize = header32.exportTable.size; - relocationDirectoryRVA = header32.baseRelocationTable.virtualAddress; - relocationDirectorySize = header32.baseRelocationTable.size; - importDirectoryRVA = header32.importTable.virtualAddress; - importDirectorySize = header32.importTable.size; - delayImportDirectoryRVA = header32.delayImportDescriptor.virtualAddress; - delayImportDirectorySize = header32.delayImportDescriptor.size; - tlsDirectoryRVA = header32.tlsTable.virtualAddress; - tlsDirectorySize = header32.tlsTable.size; - execMapped = exec; - importsResolved = false; - importsResolving = false; + executable.preferredImageBase = header32.imageBase; + executable.exportDirectoryRVA = header32.exportTable.virtualAddress; + executable.exportDirectorySize = header32.exportTable.size; + executable.relocationDirectoryRVA = header32.baseRelocationTable.virtualAddress; + executable.relocationDirectorySize = header32.baseRelocationTable.size; + executable.importDirectoryRVA = header32.importTable.virtualAddress; + executable.importDirectorySize = header32.importTable.size; + executable.delayImportDirectoryRVA = header32.delayImportDescriptor.virtualAddress; + executable.delayImportDirectorySize = header32.delayImportDescriptor.size; + executable.tlsDirectoryRVA = header32.tlsTable.virtualAddress; + executable.tlsDirectorySize = header32.tlsTable.size; + executable.execMapped = exec; + executable.importsResolved = false; + executable.importsResolving = false; + executable.sectionsProtected = false; - // Build buffer - imageSize = header32.sizeOfImage; + executable.imageSize = header32.sizeOfImage; DWORD initialProtect = exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; void *preferredBase = reinterpret_cast(static_cast(header32.imageBase)); void *allocatedBase = preferredBase; std::size_t allocationSize = static_cast(header32.sizeOfImage); - wibo::heap::VmStatus allocStatus = wibo::heap::virtualAlloc( - &allocatedBase, &allocationSize, MEM_RESERVE | MEM_COMMIT, initialProtect, MEM_IMAGE); + wibo::heap::VmStatus allocStatus = + wibo::heap::virtualAlloc(&allocatedBase, &allocationSize, MEM_RESERVE | MEM_COMMIT, initialProtect, MEM_IMAGE); if (allocStatus != wibo::heap::VmStatus::Success) { DEBUG_LOG("loadPE: preferred base allocation failed (status=%u), retrying anywhere\n", - static_cast(allocStatus)); + static_cast(allocStatus)); allocatedBase = nullptr; allocationSize = static_cast(header32.sizeOfImage); - allocStatus = wibo::heap::virtualAlloc( - &allocatedBase, &allocationSize, MEM_RESERVE | MEM_COMMIT, initialProtect, MEM_IMAGE); + allocStatus = wibo::heap::virtualAlloc(&allocatedBase, &allocationSize, MEM_RESERVE | MEM_COMMIT, + initialProtect, MEM_IMAGE); } - if (allocStatus == wibo::heap::VmStatus::Success) { - DEBUG_LOG("loadPE: mapped image at %p\n", allocatedBase); - } else { + if (allocStatus != wibo::heap::VmStatus::Success) { DEBUG_LOG("Image mapping failed (status=%u)\n", static_cast(allocStatus)); - imageBase = nullptr; return false; } - imageBase = allocatedBase; - relocationDelta = - static_cast(reinterpret_cast(imageBase) - static_cast(header32.imageBase)); - memset(imageBase, 0, header32.sizeOfImage); - sections.clear(); - uintptr_t imageBaseAddr = reinterpret_cast(imageBase); + + std::unique_ptr imageGuard(allocatedBase); + executable.imageBase = allocatedBase; + executable.relocationDelta = static_cast(reinterpret_cast(executable.imageBase) - + static_cast(header32.imageBase)); + std::memset(executable.imageBase, 0, header32.sizeOfImage); + executable.sections.clear(); + + uintptr_t imageBaseAddr = reinterpret_cast(executable.imageBase); uintptr_t headerSpan = alignUp(static_cast(header32.sizeOfHeaders), pageSizeValue); if (headerSpan != 0) { - Executable::SectionInfo headerInfo{}; + wibo::Executable::SectionInfo headerInfo{}; headerInfo.base = imageBaseAddr; headerInfo.size = static_cast(headerSpan); headerInfo.protect = PAGE_READONLY; - sections.push_back(headerInfo); + executable.sections.push_back(headerInfo); } - // Read the sections - fseek(file, offsetToPE + sizeof header + header.sizeOfOptionalHeader, SEEK_SET); - - for (int i = 0; i < header.numberOfSections; i++) { - PESectionHeader section; - fread(§ion, sizeof section, 1, file); - - char name[9]; - memcpy(name, section.name, 8); - name[8] = 0; - DEBUG_LOG("Section %d: name=%s addr=%x size=%x (raw=%x) ptr=%x\n", i, name, section.virtualAddress, - section.virtualSize, section.sizeOfRawData, section.pointerToRawData); - - void *sectionBase = (void *)((uintptr_t)imageBase + section.virtualAddress); - if (section.pointerToRawData > 0 && section.sizeOfRawData > 0) { - // Grab this data - long savePos = ftell(file); - fseek(file, section.pointerToRawData, SEEK_SET); - fread(sectionBase, section.sizeOfRawData, 1, file); - fseek(file, savePos, SEEK_SET); + const uint64_t sectionHeadersOffset = + static_cast(offsetToPE) + sizeof(header) + header.sizeOfOptionalHeader; + if (auto totalSize = source.size()) { + uint64_t sectionTableBytes = static_cast(header.numberOfSections) * sizeof(PESectionHeader); + if (sectionHeadersOffset > *totalSize || sectionTableBytes > (*totalSize - sectionHeadersOffset)) { + DEBUG_LOG("loadPE: section table exceeds available data\n"); + return false; + } + } + for (uint16_t i = 0; i < header.numberOfSections; ++i) { + uint64_t currentOffset = sectionHeadersOffset + static_cast(i) * sizeof(PESectionHeader); + PESectionHeader section{}; + if (!source.read(currentOffset, §ion, sizeof(section))) { + DEBUG_LOG("loadPE: failed to read section header %u\n", i); + return false; } - if (strcmp(name, ".rsrc") == 0) { - rsrcBase = sectionBase; - rsrcSize = std::max(section.virtualSize, section.sizeOfRawData); + char name[9]; + std::memcpy(name, section.name, 8); + name[8] = '\0'; + DEBUG_LOG("Section %u: name=%s addr=%x size=%x (raw=%x) ptr=%x\n", i, name, section.virtualAddress, + section.virtualSize, section.sizeOfRawData, section.pointerToRawData); + + const uint64_t sectionEndVirtual = + static_cast(section.virtualAddress) + static_cast(section.virtualSize); + if (section.virtualAddress > header32.sizeOfImage || sectionEndVirtual > header32.sizeOfImage) { + DEBUG_LOG("loadPE: section %s exceeds image size\n", name); + return false; + } + + void *sectionBase = reinterpret_cast(imageBaseAddr + section.virtualAddress); + if (section.pointerToRawData != 0 && section.sizeOfRawData != 0) { + uint64_t sectionDataEnd = + static_cast(section.pointerToRawData) + static_cast(section.sizeOfRawData); + if (sectionDataEnd < static_cast(section.pointerToRawData)) { + DEBUG_LOG("loadPE: raw data overflow for section %s\n", name); + return false; + } + uint64_t mappedEnd = + static_cast(section.virtualAddress) + static_cast(section.sizeOfRawData); + if (mappedEnd > header32.sizeOfImage) { + DEBUG_LOG("loadPE: raw section data for %s exceeds image size\n", name); + return false; + } + if (!source.read(section.pointerToRawData, sectionBase, section.sizeOfRawData)) { + DEBUG_LOG("loadPE: failed to load section %s data\n", name); + return false; + } + } + + if (std::strcmp(name, ".rsrc") == 0) { + executable.rsrcBase = sectionBase; + executable.rsrcSize = std::max(section.virtualSize, section.sizeOfRawData); } size_t sectionSpan = std::max(section.virtualSize, section.sizeOfRawData); @@ -310,29 +512,34 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) { uintptr_t sectionEnd = alignUp(imageBaseAddr + static_cast(section.virtualAddress) + static_cast(sectionSpan), pageSizeValue); + if (sectionEnd < sectionStart) { + DEBUG_LOG("loadPE: invalid span for section %s\n", name); + return false; + } if (sectionEnd > sectionStart) { - Executable::SectionInfo sectionInfo{}; + wibo::Executable::SectionInfo sectionInfo{}; sectionInfo.base = sectionStart; sectionInfo.size = static_cast(sectionEnd - sectionStart); sectionInfo.protect = sectionProtectFromCharacteristics(section.characteristics); sectionInfo.characteristics = section.characteristics; - sections.push_back(sectionInfo); + executable.sections.push_back(sectionInfo); } } } - std::sort(sections.begin(), sections.end(), - [](const SectionInfo &lhs, const SectionInfo &rhs) { return lhs.base < rhs.base; }); - if (exec && relocationDelta != 0) { - if (relocationDirectoryRVA == 0 || relocationDirectorySize == 0) { + std::sort(executable.sections.begin(), executable.sections.end(), + [](const wibo::Executable::SectionInfo &lhs, const wibo::Executable::SectionInfo &rhs) { + return lhs.base < rhs.base; + }); + + if (exec && executable.relocationDelta != 0) { + if (executable.relocationDirectoryRVA == 0 || executable.relocationDirectorySize == 0) { DEBUG_LOG("Relocation required but no relocation directory present\n"); - wibo::heap::virtualFree(imageBase, 0, MEM_RELEASE); - imageBase = nullptr; return false; } - uint8_t *relocCursor = fromRVA(relocationDirectoryRVA); - uint8_t *relocEnd = relocCursor + relocationDirectorySize; + uint8_t *relocCursor = executable.fromRVA(executable.relocationDirectoryRVA); + uint8_t *relocEnd = relocCursor + executable.relocationDirectorySize; while (relocCursor < relocEnd) { auto *block = reinterpret_cast(relocCursor); if (block->sizeOfBlock < sizeof(PEBaseRelocationBlock) || @@ -350,11 +557,11 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) { uint16_t offset = entry & 0x0FFF; if (type == IMAGE_REL_BASED_ABSOLUTE) continue; - uintptr_t target = reinterpret_cast(imageBase) + block->virtualAddress + offset; + uintptr_t target = reinterpret_cast(executable.imageBase) + block->virtualAddress + offset; switch (type) { case IMAGE_REL_BASED_HIGHLOW: { auto *addr = reinterpret_cast(target); - *addr += static_cast(relocationDelta); + *addr += static_cast(executable.relocationDelta); break; } default: @@ -366,11 +573,38 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) { } } - entryPoint = header32.addressOfEntryPoint ? fromRVA(header32.addressOfEntryPoint) : nullptr; + executable.entryPoint = + header32.addressOfEntryPoint ? executable.fromRVA(header32.addressOfEntryPoint) : nullptr; + (void)imageGuard.release(); + kernel32::setLastError(ERROR_SUCCESS); return true; } +} // namespace + +/** + * Load a PE file into memory. + * + * @param file The file to load. + * @param exec Whether to make the loaded image executable. + */ +bool wibo::Executable::loadPE(FILE *file, bool exec) { + if (!file) { + kernel32::setLastError(ERROR_BAD_EXE_FORMAT); + return false; + } + return loadPEFromSource(*this, PeInputView(file), exec); +} + +bool wibo::Executable::loadPE(std::span image, bool exec) { + if (image.empty()) { + kernel32::setLastError(ERROR_BAD_EXE_FORMAT); + return false; + } + return loadPEFromSource(*this, PeInputView(image), exec); +} + bool wibo::Executable::resolveImports() { auto finalizeSections = [this]() -> bool { if (!execMapped || sectionsProtected) { @@ -385,7 +619,7 @@ bool wibo::Executable::resolveImports() { wibo::heap::virtualProtect(sectionAddress, section.size, section.protect, nullptr); if (status != wibo::heap::VmStatus::Success) { DEBUG_LOG("resolveImports: failed to set section protection at %p (size=%zu, protect=0x%x) status=%u\n", - sectionAddress, section.size, section.protect, static_cast(status)); + sectionAddress, section.size, section.protect, static_cast(status)); kernel32::setLastError(wibo::heap::win32ErrorFromVmStatus(status)); return false; } diff --git a/src/macros.S b/src/macros.S index 072e66e..97bac9f 100644 --- a/src/macros.S +++ b/src/macros.S @@ -1,31 +1,38 @@ -.intel_syntax noprefix +#include "macros.h" -.equ TEB_SELF, 0x18 # Self -.equ TEB_FS_SEL, 0xf98 # CurrentFsSelector -.equ TEB_GS_SEL, 0xf9a # CurrentGsSelector +#ifdef __clang__ +#define ASM_TYPE(NAME, TYPE) +#define ASM_END(NAME) +#else +#define ASM_TYPE(NAME, TYPE) .type NAME, TYPE +#define ASM_END(NAME) .size NAME, .- NAME +#endif -#ifdef __i386__ - -.equ TEB_SP, 0xf9c # CurrentStackPointer - -#endif // __i386__ - -#ifdef __x86_64__ - -.equ TEB_SP, 0xfa0 # CurrentStackPointer -.equ TEB_FSBASE, 0xfa8 # HostFsBase -.equ TEB_GSBASE, 0xfb0 # HostGsBase +#define ASM_GLOBAL(NAME, TYPE) \ + .globl SYMBOL_NAME(NAME); \ + ASM_TYPE(SYMBOL_NAME(NAME), TYPE); \ + SYMBOL_NAME(NAME) : +#ifdef __clang__ +#define ASM_WEAK(NAME, TYPE) \ + .globl SYMBOL_NAME(NAME); \ + .weak_definition SYMBOL_NAME(NAME); \ + ASM_TYPE(SYMBOL_NAME(NAME), TYPE) \ + SYMBOL_NAME(NAME) : +#else +#define ASM_WEAK(NAME, TYPE) \ + .weak SYMBOL_NAME(NAME); \ + ASM_TYPE(SYMBOL_NAME(NAME), TYPE) \ + SYMBOL_NAME(NAME) : +#endif #ifdef __linux__ -.equ CS_32, 0x23 # 32-bit code segment (Linux) -.equ CS_64, 0x33 # 64-bit code segment (Linux) -.equ DS_32, 0x2b # 32-bit data segment (Linux) -#elif defined(__APPLE__) -.equ CS_64, 0x2b # 64-bit code segment (macOS) -#else -#error "Unsupported platform" +.section .note.GNU-stack, "", @progbits #endif +.intel_syntax noprefix + +#ifdef __x86_64__ + .macro LJMP32 teb_reg #ifdef __APPLE__ #define m1632 m1632_\@ diff --git a/src/macros.h b/src/macros.h new file mode 100644 index 0000000..3c31414 --- /dev/null +++ b/src/macros.h @@ -0,0 +1,73 @@ +#pragma once + +#define TEB_SELF 0x18 // Self +#define TEB_FS_SEL 0xf98 // CurrentFsSelector +#define TEB_GS_SEL 0xf9a // CurrentGsSelector + +#ifdef __i386__ + +#define TEB_SP 0xf9c // CurrentStackPointer + +#endif // __i386__ + +#ifdef __x86_64__ + +#define TEB_SP 0xfa0 // CurrentStackPointer +#define TEB_FSBASE 0xfa8 // HostFsBase +#define TEB_GSBASE 0xfb0 // HostGsBase + +#ifdef __linux__ +#define CS_32 0x23 // 32-bit code segment (Linux) +#define CS_64 0x33 // 64-bit code segment (Linux) +#define DS_32 0x2b // 32-bit data segment (Linux) +#elif defined(__APPLE__) +#define CS_64 0x2b // 64-bit code segment (macOS) +#else +#error "Unsupported platform" +#endif + +#ifndef __USER_LABEL_PREFIX__ +#define __USER_LABEL_PREFIX__ +#endif + +#define GLUE2_(a, b) a##b +#define GLUE(a, b) GLUE2_(a, b) +#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) + +#ifndef __ASSEMBLER__ +#define STR_(S) #S +#define STR(S) STR_(S) +#define SYMBOL_NAME_STR_(name) STR(__USER_LABEL_PREFIX__) #name +#define SYMBOL_NAME_STR(name) SYMBOL_NAME_STR_(name) + +#if __SIZEOF_POINTER__ == 8 +#define ASM_SIZE_TYPE "quad" +#else +#define ASM_SIZE_TYPE "long" +#endif +#if __ELF__ +#define ASM_RODATA_SECTION ".rodata" +#else +#define ASM_RODATA_SECTION ".section __TEXT, __const" +#endif + +// clang-format off +#define INCLUDE_BIN(symbol, file) \ + asm(ASM_RODATA_SECTION "\n" \ + ".globl " SYMBOL_NAME_STR(symbol) "\n" \ + SYMBOL_NAME_STR(symbol) ":\n" \ + ".incbin \"" file "\"\n" \ + ".globl " SYMBOL_NAME_STR(GLUE(symbol, End)) "\n" \ + SYMBOL_NAME_STR(GLUE(symbol, End)) ":\n"); \ + extern "C" { \ + extern const uint8_t symbol[]; \ + extern const uint8_t GLUE(symbol, End)[]; \ + } +// clang-format on +#define INCLUDE_BIN_SIZE(symbol) \ + static_cast(reinterpret_cast(&GLUE(symbol, End)) - \ + reinterpret_cast(&symbol)) +#define INCLUDE_BIN_SPAN(symbol) std::span(symbol, INCLUDE_BIN_SIZE(symbol)) +#endif + +#endif diff --git a/src/modules.cpp b/src/modules.cpp index c8cbe91..c7eb385 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -28,11 +28,12 @@ extern const wibo::ModuleStub lib_advapi32; extern const wibo::ModuleStub lib_bcrypt; -extern const wibo::ModuleStub lib_crt; extern const wibo::ModuleStub lib_kernel32; extern const wibo::ModuleStub lib_lmgr; extern const wibo::ModuleStub lib_mscoree; +#if WIBO_HAS_MSVCRT extern const wibo::ModuleStub lib_msvcrt; +#endif extern const wibo::ModuleStub lib_ntdll; extern const wibo::ModuleStub lib_rpcrt4; extern const wibo::ModuleStub lib_ole32; @@ -43,26 +44,6 @@ extern const wibo::ModuleStub lib_version; // setup.S template void stubThunk(); -/* - apiset api-ms-win-core-crt-l1-1-0 = msvcrt.dll - apiset api-ms-win-core-crt-l2-1-0 = msvcrt.dll - apiset api-ms-win-crt-conio-l1-1-0 = msvcrt.dll - apiset api-ms-win-crt-convert-l1-1-0 = msvcrt.dll - apiset api-ms-win-crt-environment-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-filesystem-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-heap-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-locale-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-math-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-multibyte-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-private-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-process-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-runtime-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-stdio-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-string-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-time-l1-1-0 = ucrtbase.dll - apiset api-ms-win-crt-utility-l1-1-0 = ucrtbase.dll -*/ - namespace { const std::array, 17> kApiSet = { @@ -85,20 +66,6 @@ const std::array, 17> kApiSet = { std::pair{"api-ms-win-crt-utility-l1-1-0.dll", "ucrtbase.dll"}, }; -const std::array, 11> kFallbacks = { - std::pair{"ucrtbase.dll", "msvcrt.dll"}, - std::pair{"msvcrt40.dll", "msvcrt.dll"}, - std::pair{"msvcr70.dll", "msvcrt.dll"}, - std::pair{"msvcr71.dll", "msvcrt.dll"}, - std::pair{"msvcr80.dll", "msvcrt.dll"}, - std::pair{"msvcr90.dll", "msvcrt.dll"}, - std::pair{"msvcr100.dll", "msvcrt.dll"}, - std::pair{"msvcr110.dll", "msvcrt.dll"}, - std::pair{"msvcr120.dll", "msvcrt.dll"}, - std::pair{"msvcr130.dll", "msvcrt.dll"}, - std::pair{"msvcr140.dll", "msvcrt.dll"}, -}; - constexpr DWORD DLL_PROCESS_DETACH = 0; constexpr DWORD DLL_PROCESS_ATTACH = 1; constexpr DWORD DLL_THREAD_ATTACH = 2; @@ -217,10 +184,20 @@ LockedRegistry registry() { if (!reg.initialized) { reg.initialized = true; const wibo::ModuleStub *builtins[] = { - &lib_advapi32, &lib_bcrypt, /*&lib_crt,*/ &lib_kernel32, - &lib_lmgr, &lib_mscoree, /*&lib_msvcrt,*/ - &lib_ntdll, &lib_ole32, &lib_rpcrt4, - &lib_user32, &lib_vcruntime, &lib_version, + &lib_advapi32, + &lib_bcrypt, + &lib_kernel32, + &lib_lmgr, + &lib_mscoree, +#if WIBO_HAS_MSVCRT + &lib_msvcrt, +#endif + &lib_ntdll, + &lib_ole32, + &lib_rpcrt4, + &lib_user32, + &lib_vcruntime, + &lib_version, nullptr, }; for (const wibo::ModuleStub **module = builtins; *module; ++module) { @@ -560,15 +537,26 @@ void registerBuiltinModule(ModuleRegistry ®, const wibo::ModuleStub *module) if (!module) { return; } + + std::unique_ptr executable; + if (!module->dllData.empty()) { + executable = std::make_unique(); + if (!executable->loadPE(module->dllData, true)) { + DEBUG_LOG(" loadPE failed for %s\n", module->names[0] ? module->names[0] : ""); + return; + } + } + wibo::ModulePtr entry = std::make_shared(); HANDLE handle = g_nextStubHandle++; g_modules[handle] = entry; entry->handle = handle; entry->moduleStub = module; + entry->executable = std::move(executable); entry->refCount = UINT_MAX; entry->originalName = module->names[0] ? module->names[0] : ""; entry->normalizedName = normalizedBaseKey(parseModuleName(entry->originalName)); - entry->exportsInitialized = true; + entry->exportsInitialized = (entry->executable == nullptr); auto storageKey = storageKeyForBuiltin(entry->normalizedName); auto raw = entry.get(); reg.modulesByKey[storageKey] = std::move(entry); @@ -702,9 +690,6 @@ bool shouldDeliverThreadNotifications(const wibo::ModuleInfo &info) { if (&info == wibo::mainModule) { return false; } - if (info.moduleStub != nullptr) { - return false; - } if (!info.executable) { return false; } @@ -718,10 +703,9 @@ bool shouldDeliverThreadNotifications(const wibo::ModuleInfo &info) { } void ensureExportsInitialized(wibo::ModuleInfo &info) { - if (info.moduleStub || info.exportsInitialized) - return; - if (!info.executable) + if (info.exportsInitialized || !info.executable) { return; + } auto *exe = info.executable.get(); if (!exe->exportDirectoryRVA || !exe->exportDirectorySize) { info.exportsInitialized = true; @@ -765,6 +749,24 @@ void ensureExportsInitialized(wibo::ModuleInfo &info) { info.exportsInitialized = true; } +bool ensureModuleReady(wibo::ModuleInfo &info) { + ensureExportsInitialized(info); + if (!info.executable) { + return true; + } + if (!info.executable->resolveImports()) { + return false; + } + if (!wibo::initializeModuleTls(info)) { + return false; + } + if (!callDllMain(info, DLL_PROCESS_ATTACH, nullptr)) { + kernel32::setLastError(ERROR_DLL_INIT_FAILED); + return false; + } + return true; +} + } // namespace namespace entry { @@ -1251,7 +1253,12 @@ static ModuleInfo *loadModuleInternal(const std::string &dllName) { } } DEBUG_LOG(" returning builtin module %s\n", existing->originalName.c_str()); - return existing; + ModuleInfo *builtin = existing; + reg.lock.unlock(); + if (!ensureModuleReady(*builtin)) { + return nullptr; + } + return builtin; } if (ModuleInfo *external = resolveAndLoadExternal()) { @@ -1276,6 +1283,10 @@ static ModuleInfo *loadModuleInternal(const std::string &dllName) { } if (builtin && builtin->moduleStub != nullptr) { DEBUG_LOG(" falling back to builtin module %s\n", builtin->originalName.c_str()); + reg.lock.unlock(); + if (!ensureModuleReady(*builtin)) { + return nullptr; + } return builtin; } @@ -1283,7 +1294,7 @@ static ModuleInfo *loadModuleInternal(const std::string &dllName) { return nullptr; } -ModuleInfo *loadModule(const char* dllName) { +ModuleInfo *loadModule(const char *dllName) { if (!dllName || *dllName == '\0') { kernel32::setLastError(ERROR_INVALID_PARAMETER); return nullptr; @@ -1294,7 +1305,7 @@ ModuleInfo *loadModule(const char* dllName) { const auto parsed = parseModuleName(requested); std::string normalized = normalizedBaseKey(parsed); - for (auto& [alias, module] : kApiSet) { + for (auto &[alias, module] : kApiSet) { if (alias == normalized) { DEBUG_LOG(" resolved api set %s -> %s\n", alias.data(), module.data()); requested = module; @@ -1303,23 +1314,23 @@ ModuleInfo *loadModule(const char* dllName) { } } - DWORD lastError = kernel32::getLastError(); - if (auto* info = loadModuleInternal(std::string{requested})) { + // DWORD lastError = kernel32::getLastError(); + if (auto *info = loadModuleInternal(std::string{requested})) { return info; } if (kernel32::getLastError() != ERROR_MOD_NOT_FOUND) { return nullptr; } - kernel32::setLastError(lastError); - for (auto& [module, fallback] : kFallbacks) { - if (module == normalized) { - DEBUG_LOG(" trying fallback %s -> %s\n", module.data(), fallback.data()); - return loadModuleInternal(std::string{fallback}); - } - } + // kernel32::setLastError(lastError); + // for (auto &[module, fallback] : kFallbacks) { + // if (module == normalized) { + // DEBUG_LOG(" trying fallback %s -> %s\n", module.data(), fallback.data()); + // return loadModuleInternal(std::string{fallback}); + // } + // } - kernel32::setLastError(ERROR_MOD_NOT_FOUND); + // kernel32::setLastError(ERROR_MOD_NOT_FOUND); return nullptr; } @@ -1363,14 +1374,14 @@ void *resolveFuncByName(ModuleInfo *info, const char *funcName) { } } ensureExportsInitialized(*info); - if (!info->moduleStub) { - auto it = info->exportNameToOrdinal.find(funcName); - if (it != info->exportNameToOrdinal.end()) { - return resolveFuncByOrdinal(info, it->second); - } - return nullptr; + auto it = info->exportNameToOrdinal.find(funcName); + if (it != info->exportNameToOrdinal.end()) { + return resolveFuncByOrdinal(info, it->second); } - return reinterpret_cast(resolveMissingFuncName(info->originalName.c_str(), funcName)); + if (info->moduleStub) { + return reinterpret_cast(resolveMissingFuncName(info->originalName.c_str(), funcName)); + } + return nullptr; } void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) { @@ -1383,20 +1394,20 @@ void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) { return func; } } - if (!info->moduleStub) { - ensureExportsInitialized(*info); - if (!info->exportsByOrdinal.empty() && ordinal >= info->exportOrdinalBase) { - auto index = static_cast(ordinal - info->exportOrdinalBase); - if (index < info->exportsByOrdinal.size()) { - void *addr = info->exportsByOrdinal[index]; - if (addr) { - return addr; - } + ensureExportsInitialized(*info); + if (!info->exportsByOrdinal.empty() && ordinal >= info->exportOrdinalBase) { + auto index = static_cast(ordinal - info->exportOrdinalBase); + if (index < info->exportsByOrdinal.size()) { + void *addr = info->exportsByOrdinal[index]; + if (addr) { + return addr; } } - return nullptr; } - return reinterpret_cast(resolveMissingFuncOrdinal(info->originalName.c_str(), ordinal)); + if (info->moduleStub) { + return reinterpret_cast(resolveMissingFuncOrdinal(info->originalName.c_str(), ordinal)); + } + return nullptr; } void *resolveMissingImportByName(const char *dllName, const char *funcName) { diff --git a/src/modules.h b/src/modules.h index 716be8b..ac822ee 100644 --- a/src/modules.h +++ b/src/modules.h @@ -6,6 +6,7 @@ #include "types.h" #include +#include #include namespace wibo { @@ -18,8 +19,9 @@ struct ResourceLocation; struct ModuleStub { const char **names; - ResolveByName byName; - ResolveByOrdinal byOrdinal; + ResolveByName byName = nullptr; + ResolveByOrdinal byOrdinal = nullptr; + std::span dllData{}; }; class Executable { @@ -35,6 +37,7 @@ class Executable { ~Executable(); bool loadPE(FILE *file, bool exec); + bool loadPE(std::span image, bool exec); bool resolveImports(); bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name, std::optional language, ResourceLocation &out) const; diff --git a/src/setup.S b/src/setup.S index cc6042d..a28a900 100644 --- a/src/setup.S +++ b/src/setup.S @@ -1,20 +1,15 @@ #include "macros.S" -#ifndef __APPLE__ -.section .note.GNU-stack, "", @progbits -#endif .text #ifdef __APPLE__ -.zerofill RESV32,RESV32,_wibo_reserve,0x7E000000-0x1000 +.zerofill RESV32, RESV32, _wibo_reserve, 0x7E000000 - 0x1000 .no_dead_strip _wibo_reserve #endif -#if defined(__x86_64__) && !defined(__APPLE__) +#if defined(__x86_64__) && defined(__linux__) # int tebThreadSetup(int entryNumber, TEB *teb) -.globl tebThreadSetup -.type tebThreadSetup, @function -tebThreadSetup: +ASM_GLOBAL(tebThreadSetup, @function) push rbx # save rbx mov r8, rsp # save host stack rdfsbase r9 # read host FS base @@ -51,33 +46,23 @@ tebThreadSetup: wrfsbase r9 # restore host FS base pop rbx # restore rbx ret -.size tebThreadSetup, .-tebThreadSetup +ASM_END(tebThreadSetup) #endif // __x86_64__ .code32 .macro stubThunkX number -#if defined(__APPLE__) -.globl __Z9stubThunkILm\()\number\()EEvv -__Z9stubThunkILm\()\number\()EEvv: -#elif defined(__x86_64__) -.globl _Z9stubThunkILm\()\number\()EEvv -.type _Z9stubThunkILm\()\number\()EEvv, @function -_Z9stubThunkILm\()\number\()EEvv: +#if defined(__x86_64__) +ASM_GLOBAL(_Z9stubThunkILm\()\number\()EEvv, @function) #else -.globl _Z9stubThunkILj\()\number\()EEvv -.type _Z9stubThunkILj\()\number\()EEvv, @function -_Z9stubThunkILj\()\number\()EEvv: +ASM_GLOBAL(_Z9stubThunkILj\()\number\()EEvv, @function) #endif pop eax push \number push eax -#ifdef __APPLE__ - jmp _thunk_entry_stubBase -#else - jmp thunk_entry_stubBase -#endif + jmp SYMBOL_NAME(thunk_entry_stubBase) +ASM_END(STUB_THUNK_SYMBOL) .endm stubThunkX 0 diff --git a/src/types.h b/src/types.h index 7b1c9f9..c745805 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,10 @@ #pragma once +#include "macros.h" + +#ifndef offsetof +#define offsetof(type, member) __builtin_offsetof(type, member) +#endif #ifndef va_list #define va_list __builtin_va_list #endif @@ -543,17 +548,23 @@ typedef struct _TEB { #endif } TEB; typedef GUEST_PTR PTEB; -#ifndef offsetof -#define offsetof(type, member) __builtin_offsetof(type, member) -#endif -static_assert(offsetof(NT_TIB, Self) == 0x18, "Self pointer offset mismatch"); +static_assert(offsetof(NT_TIB, Self) == TEB_SELF, "Self pointer offset mismatch"); static_assert(offsetof(TEB, ThreadLocalStoragePointer) == 0x2C, "TLS pointer offset mismatch"); static_assert(offsetof(TEB, Peb) == 0x30, "PEB pointer offset mismatch"); static_assert(offsetof(TEB, LastErrorValue) == 0x34, "LastErrorValue offset mismatch"); static_assert(offsetof(TEB, GdiTebBatch) == 0x1FC, "GdiTebBatch offset mismatch"); static_assert(offsetof(TEB, DeallocationStack) == 0xE0C, "DeallocationStack offset mismatch"); static_assert(offsetof(TEB, TlsSlots) == 0xE10, "TLS slots offset mismatch"); +static_assert(offsetof(TEB, CurrentFsSelector) == TEB_FS_SEL); +static_assert(offsetof(TEB, CurrentGsSelector) == TEB_GS_SEL); +static_assert(offsetof(TEB, CurrentStackPointer) == TEB_SP); +#ifdef TEB_FSBASE +static_assert(offsetof(TEB, HostFsBase) == TEB_FSBASE); +#endif +#ifdef TEB_GSBASE +static_assert(offsetof(TEB, HostGsBase) == TEB_GSBASE); +#endif typedef struct _MEMORY_BASIC_INFORMATION { GUEST_PTR BaseAddress; diff --git a/tools/gen_trampolines.py b/tools/gen_trampolines.py index 1d4aaa0..2374671 100644 --- a/tools/gen_trampolines.py +++ b/tools/gen_trampolines.py @@ -49,8 +49,6 @@ if "LIBCLANG_PATH" in os.environ: f"Warning: LIBCLANG_PATH={libclang_path} is not a file or directory\n" ) -SYMBOL_PREFIX = "_" if sys.platform == "darwin" else "" - class Arch(str, Enum): X86 = "x86" @@ -924,7 +922,7 @@ def emit_guest_to_host_thunks( lines: List[str], dll: str, funcs: Iterable[FuncInfo], arch: Arch ) -> None: for f in funcs: - thunk = f"{SYMBOL_PREFIX}thunk_{dll}_{f.name}" + thunk = f"thunk_{dll}_{f.name}" lines.append("") lines.append( f"# {f.qualified_ns}::{f.name} (source_cc={f.source_cc.name}, target_cc={f.target_cc.name}, variadic={f.variadic})" @@ -938,20 +936,16 @@ def emit_guest_to_host_thunks( details.append(f"class={arg.arg_class.value}") details.append(f"sign_extended={arg.sign_extended}") lines.append(f"\t# Arg {i} ({', '.join(details)})") - lines.append(f".globl {thunk}") - if sys.platform != "darwin": - lines.append(f".type {thunk}, @function") - lines.append(f"{thunk}:") + lines.append(f"ASM_GLOBAL({thunk}, @function)") emit_cc_thunk(f, lines, arch) - if sys.platform != "darwin": - lines.append(f".size {thunk}, .-{thunk}") + lines.append(f"ASM_END({thunk})") def emit_host_to_guest_thunks( lines: List[str], typedefs: Iterable[TypedefInfo], arch: Arch ) -> None: for f in typedefs: - thunk = f"{SYMBOL_PREFIX}call_{f.name}" + thunk = f"call_{f.name}" lines.append("") lines.append( f"# {f.name} (target_cc={f.target_cc.name}, variadic={f.variadic})" @@ -969,16 +963,9 @@ def emit_host_to_guest_thunks( # details.append(f"class={f.return_type.arg_class.value}") # details.append(f"sign_extended={f.return_type.sign_extended}") # lines.append(f"\t# Ret ({', '.join(details)})") - if sys.platform == "darwin": - lines.append(f".globl {thunk}") - lines.append(f".weak_definition {thunk}") - else: - lines.append(f".weak {thunk}") - lines.append(f".type {thunk}, @function") - lines.append(f"{thunk}:") + lines.append(f"ASM_WEAK({thunk}, @function)") emit_cc_thunk(f, lines, arch) - if sys.platform != "darwin": - lines.append(f".size {thunk}, .-{thunk}") + lines.append(f"ASM_END({thunk})") def emit_header_mapping( @@ -1114,8 +1101,6 @@ def main() -> int: lines: List[str] = [] lines.append("# Auto-generated thunks; DO NOT EDIT.") lines.append('#include "macros.S"') - if sys.platform != "darwin": - lines.append('.section .note.GNU-stack, "", @progbits') lines.append(".text") emit_guest_to_host_thunks(lines, args.dll, funcs, arch)