mirror of
https://github.com/decompals/wibo.git
synced 2025-12-14 15:46:11 +00:00
Download and use embedded msvcrt.dll
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<DWORD>(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<DWORD>(len);
|
||||
}
|
||||
|
||||
24
dll/msvcrt.cpp
Normal file
24
dll/msvcrt.cpp
Normal file
@@ -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),
|
||||
};
|
||||
432
src/loader.cpp
432
src/loader.cpp
@@ -6,10 +6,18 @@
|
||||
#include "types.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <strings.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <variant>
|
||||
|
||||
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<const uint8_t> 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 <typename T> std::optional<T> readObject(uint64_t offset) const {
|
||||
T value{};
|
||||
if (!read(offset, &value, sizeof(T))) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> size() const {
|
||||
return std::visit([](const auto &source) -> std::optional<uint64_t> { return sizeImpl(source); }, source_);
|
||||
}
|
||||
|
||||
private:
|
||||
struct FileSource {
|
||||
FILE *file;
|
||||
std::optional<uint64_t> fileSize;
|
||||
};
|
||||
struct SpanSource {
|
||||
std::span<const uint8_t> bytes;
|
||||
};
|
||||
|
||||
static std::optional<uint64_t> 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<uint64_t>(st.st_size);
|
||||
}
|
||||
|
||||
static bool readImpl(const FileSource &source, uint64_t offset, void *dest, size_t size) {
|
||||
if (!source.file) {
|
||||
return false;
|
||||
if (header.machine != 0x14C) // i386
|
||||
}
|
||||
if (offset > static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
|
||||
return false;
|
||||
|
||||
DEBUG_LOG("Sections: %d / Size of optional header: %x\n", header.numberOfSections, header.sizeOfOptionalHeader);
|
||||
|
||||
PE32Header header32;
|
||||
memset(&header32, 0, sizeof header32);
|
||||
fread(&header32, std::min(sizeof(header32), (size_t)header.sizeOfOptionalHeader), 1, file);
|
||||
if (header32.magic != 0x10B)
|
||||
}
|
||||
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<off_t>(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<size_t>(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<uint64_t> sizeImpl(const FileSource &source) { return source.fileSize; }
|
||||
static std::optional<uint64_t> sizeImpl(const SpanSource &source) {
|
||||
return static_cast<uint64_t>(source.bytes.size());
|
||||
}
|
||||
|
||||
std::variant<FileSource, SpanSource> 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<uint16_t>(0);
|
||||
if (!dosSignature || *dosSignature != 0x5A4D) {
|
||||
DEBUG_LOG("loadPE: missing MZ header signature\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto offsetToPeOpt = source.readObject<uint32_t>(0x3C);
|
||||
if (!offsetToPeOpt) {
|
||||
DEBUG_LOG("loadPE: failed to read e_lfanew\n");
|
||||
return false;
|
||||
}
|
||||
uint32_t offsetToPE = *offsetToPeOpt;
|
||||
|
||||
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<unsigned long long>(*totalSize));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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<std::size_t>(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<size_t>(pageSize) : static_cast<size_t>(4096);
|
||||
DEBUG_LOG("Page size: %x\n", static_cast<unsigned int>(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<void *>(static_cast<uintptr_t>(header32.imageBase));
|
||||
void *allocatedBase = preferredBase;
|
||||
std::size_t allocationSize = static_cast<std::size_t>(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<unsigned>(allocStatus));
|
||||
allocatedBase = nullptr;
|
||||
allocationSize = static_cast<std::size_t>(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<unsigned>(allocStatus));
|
||||
imageBase = nullptr;
|
||||
return false;
|
||||
}
|
||||
imageBase = allocatedBase;
|
||||
relocationDelta =
|
||||
static_cast<intptr_t>(reinterpret_cast<uintptr_t>(imageBase) - static_cast<uintptr_t>(header32.imageBase));
|
||||
memset(imageBase, 0, header32.sizeOfImage);
|
||||
sections.clear();
|
||||
uintptr_t imageBaseAddr = reinterpret_cast<uintptr_t>(imageBase);
|
||||
|
||||
std::unique_ptr<void, ImageMemoryDeleter> imageGuard(allocatedBase);
|
||||
executable.imageBase = allocatedBase;
|
||||
executable.relocationDelta = static_cast<intptr_t>(reinterpret_cast<uintptr_t>(executable.imageBase) -
|
||||
static_cast<uintptr_t>(header32.imageBase));
|
||||
std::memset(executable.imageBase, 0, header32.sizeOfImage);
|
||||
executable.sections.clear();
|
||||
|
||||
uintptr_t imageBaseAddr = reinterpret_cast<uintptr_t>(executable.imageBase);
|
||||
uintptr_t headerSpan = alignUp(static_cast<uintptr_t>(header32.sizeOfHeaders), pageSizeValue);
|
||||
if (headerSpan != 0) {
|
||||
Executable::SectionInfo headerInfo{};
|
||||
wibo::Executable::SectionInfo headerInfo{};
|
||||
headerInfo.base = imageBaseAddr;
|
||||
headerInfo.size = static_cast<size_t>(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);
|
||||
const uint64_t sectionHeadersOffset =
|
||||
static_cast<uint64_t>(offsetToPE) + sizeof(header) + header.sizeOfOptionalHeader;
|
||||
if (auto totalSize = source.size()) {
|
||||
uint64_t sectionTableBytes = static_cast<uint64_t>(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<uint64_t>(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;
|
||||
}
|
||||
|
||||
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,
|
||||
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);
|
||||
|
||||
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 sectionEndVirtual =
|
||||
static_cast<uint64_t>(section.virtualAddress) + static_cast<uint64_t>(section.virtualSize);
|
||||
if (section.virtualAddress > header32.sizeOfImage || sectionEndVirtual > header32.sizeOfImage) {
|
||||
DEBUG_LOG("loadPE: section %s exceeds image size\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(name, ".rsrc") == 0) {
|
||||
rsrcBase = sectionBase;
|
||||
rsrcSize = std::max(section.virtualSize, section.sizeOfRawData);
|
||||
void *sectionBase = reinterpret_cast<void *>(imageBaseAddr + section.virtualAddress);
|
||||
if (section.pointerToRawData != 0 && section.sizeOfRawData != 0) {
|
||||
uint64_t sectionDataEnd =
|
||||
static_cast<uint64_t>(section.pointerToRawData) + static_cast<uint64_t>(section.sizeOfRawData);
|
||||
if (sectionDataEnd < static_cast<uint64_t>(section.pointerToRawData)) {
|
||||
DEBUG_LOG("loadPE: raw data overflow for section %s\n", name);
|
||||
return false;
|
||||
}
|
||||
uint64_t mappedEnd =
|
||||
static_cast<uint64_t>(section.virtualAddress) + static_cast<uint64_t>(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<uintptr_t>(section.virtualAddress) +
|
||||
static_cast<uintptr_t>(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<size_t>(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<uint8_t>(relocationDirectoryRVA);
|
||||
uint8_t *relocEnd = relocCursor + relocationDirectorySize;
|
||||
uint8_t *relocCursor = executable.fromRVA<uint8_t>(executable.relocationDirectoryRVA);
|
||||
uint8_t *relocEnd = relocCursor + executable.relocationDirectorySize;
|
||||
while (relocCursor < relocEnd) {
|
||||
auto *block = reinterpret_cast<PEBaseRelocationBlock *>(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<uintptr_t>(imageBase) + block->virtualAddress + offset;
|
||||
uintptr_t target = reinterpret_cast<uintptr_t>(executable.imageBase) + block->virtualAddress + offset;
|
||||
switch (type) {
|
||||
case IMAGE_REL_BASED_HIGHLOW: {
|
||||
auto *addr = reinterpret_cast<uint32_t *>(target);
|
||||
*addr += static_cast<uint32_t>(relocationDelta);
|
||||
*addr += static_cast<uint32_t>(executable.relocationDelta);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -366,11 +573,38 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
}
|
||||
}
|
||||
|
||||
entryPoint = header32.addressOfEntryPoint ? fromRVA<void>(header32.addressOfEntryPoint) : nullptr;
|
||||
executable.entryPoint =
|
||||
header32.addressOfEntryPoint ? executable.fromRVA<void>(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<const uint8_t> 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) {
|
||||
|
||||
51
src/macros.S
51
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_\@
|
||||
|
||||
73
src/macros.h
Normal file
73
src/macros.h
Normal file
@@ -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<std::size_t>(reinterpret_cast<std::uintptr_t>(&GLUE(symbol, End)) - \
|
||||
reinterpret_cast<std::uintptr_t>(&symbol))
|
||||
#define INCLUDE_BIN_SPAN(symbol) std::span<const std::uint8_t>(symbol, INCLUDE_BIN_SIZE(symbol))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
141
src/modules.cpp
141
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 <size_t Index> 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<std::pair<std::string_view, std::string_view>, 17> kApiSet = {
|
||||
@@ -85,20 +66,6 @@ const std::array<std::pair<std::string_view, std::string_view>, 17> kApiSet = {
|
||||
std::pair{"api-ms-win-crt-utility-l1-1-0.dll", "ucrtbase.dll"},
|
||||
};
|
||||
|
||||
const std::array<std::pair<std::string_view, std::string_view>, 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<wibo::Executable> executable;
|
||||
if (!module->dllData.empty()) {
|
||||
executable = std::make_unique<wibo::Executable>();
|
||||
if (!executable->loadPE(module->dllData, true)) {
|
||||
DEBUG_LOG(" loadPE failed for %s\n", module->names[0] ? module->names[0] : "<unnamed builtin>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wibo::ModulePtr entry = std::make_shared<wibo::ModuleInfo>();
|
||||
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;
|
||||
}
|
||||
if (info->moduleStub) {
|
||||
return reinterpret_cast<void *>(resolveMissingFuncName(info->originalName.c_str(), funcName));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) {
|
||||
@@ -1383,7 +1394,6 @@ 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<size_t>(ordinal - info->exportOrdinalBase);
|
||||
@@ -1394,9 +1404,10 @@ void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
if (info->moduleStub) {
|
||||
return reinterpret_cast<void *>(resolveMissingFuncOrdinal(info->originalName.c_str(), ordinal));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *resolveMissingImportByName(const char *dllName, const char *funcName) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "types.h"
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
|
||||
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<const uint8_t> dllData{};
|
||||
};
|
||||
|
||||
class Executable {
|
||||
@@ -35,6 +37,7 @@ class Executable {
|
||||
~Executable();
|
||||
|
||||
bool loadPE(FILE *file, bool exec);
|
||||
bool loadPE(std::span<const uint8_t> image, bool exec);
|
||||
bool resolveImports();
|
||||
bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name, std::optional<uint16_t> language,
|
||||
ResourceLocation &out) const;
|
||||
|
||||
33
src/setup.S
33
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
|
||||
|
||||
19
src/types.h
19
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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user