Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,27 @@ else()
embed_binary(dll/msvcrt.cpp "${MSVCRT_DLL}")
endif()

find_program(WIBO_MINGW_CXX i686-w64-mingw32-g++)
if (WIBO_MINGW_CXX)
set(MSPDB_DLL ${CMAKE_CURRENT_BINARY_DIR}/vendor/mspdb80.dll)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/vendor)
add_custom_command(OUTPUT ${MSPDB_DLL}
COMMAND ${WIBO_MINGW_CXX} -shared -O2 -Wall -static
-fno-exceptions -fno-rtti
-o ${MSPDB_DLL}
${CMAKE_CURRENT_SOURCE_DIR}/dll/mspdb/mspdb_dll.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dll/mspdb/mspdb.def
DEPENDS dll/mspdb/mspdb_dll.cpp dll/mspdb/mspdb.def)
add_custom_target(mspdb_dll DEPENDS ${MSPDB_DLL})
add_dependencies(wibo mspdb_dll)
target_compile_definitions(wibo PRIVATE WIBO_HAS_MSPDB=1)
target_sources(wibo PRIVATE dll/mspdb_embed.cpp)
embed_binary(dll/mspdb_embed.cpp "${MSPDB_DLL}")
else()
message(WARNING "i686-w64-mingw32-g++ not found; mspdb DLL will not be built")
target_compile_definitions(wibo PRIVATE WIBO_HAS_MSPDB=0)
endif()

if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_sources(wibo PRIVATE
src/async_io_epoll.cpp
Expand Down Expand Up @@ -478,6 +499,7 @@ if (WIBO_ENABLE_FIXTURE_TESTS)
wibo_add_fixture_bin(NAME test_srw_lock SOURCES test/test_srw_lock.c)
wibo_add_fixture_bin(NAME test_init_once SOURCES test/test_init_once.c)
wibo_add_fixture_bin(NAME test_wait_on_address SOURCES test/test_wait_on_address.c COMPILE_OPTIONS -lsynchronization)
wibo_add_fixture_bin(NAME test_mspdb SOURCES test/test_mspdb.c)

# DLLs for fixture tests
wibo_add_fixture_dll(NAME external_exports SOURCES test/external_exports.c)
Expand Down
62 changes: 55 additions & 7 deletions dll/kernel32/fileapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -928,8 +928,6 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar
return INVALID_HANDLE_VALUE;
}

// TODO: verify share mode against existing opens

bool allowCreate = false;
bool truncateExisting = false;
bool existedBefore = pathExists;
Expand Down Expand Up @@ -1036,9 +1034,9 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar
if (dwCreationDisposition == CREATE_NEW) {
openFlags |= O_EXCL;
}
if (truncateExisting && !isDirectory) {
openFlags |= O_TRUNC;
}
// Don't use O_TRUNC directly — we need to check for active memory mappings first
// (Windows prevents truncation of files with active mapped sections)
bool deferredTruncate = truncateExisting && !isDirectory;

if (isDirectory) {
openFlags |= O_RDONLY | O_DIRECTORY;
Expand All @@ -1065,6 +1063,19 @@ HANDLE WINAPI CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShar
return INVALID_HANDLE_VALUE;
}

// Apply deferred truncation — skip if the file has active memory mappings (Windows behavior)
if (deferredTruncate) {
if (files::isFileMapped(fd)) {
DEBUG_LOG(" skipping truncation: file has active memory mapping\n");
} else {
if (ftruncate(fd, 0) != 0) {
setLastErrorFromErrno();
close(fd);
return INVALID_HANDLE_VALUE;
}
}
}

struct stat st{};
if (fstat(fd, &st) == 0 && S_ISDIR(st.st_mode)) {
isDirectory = true;
Expand Down Expand Up @@ -1609,8 +1620,6 @@ DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lp
return 0;
}

DEBUG_LOG(" -> %s\n", info.path.c_str());

auto widePath = stringToWideString(info.path.c_str());
const size_t wideLen = widePath.size();
const auto required = static_cast<DWORD>(wideLen);
Expand Down Expand Up @@ -1836,6 +1845,45 @@ HANDLE WINAPI FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelI
return findFirstFileCommon(std::string(lpFileName), findData);
}

HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("FindFirstFileExW(%p, %d, %p, %d, %p, 0x%x)\n", lpFileName, fInfoLevelId,
lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
if (!lpFindFileData) {
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (!lpFileName) {
setLastError(ERROR_PATH_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
if (fInfoLevelId != FindExInfoStandard) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need debug logs for all these branches to indicate unimplemented behavior

DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (fInfoLevelId=%d)\n", fInfoLevelId);
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (fSearchOp != FindExSearchNameMatch) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (fSearchOp=%d)\n", fSearchOp);
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (lpSearchFilter) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (lpSearchFilter=%p)\n", lpSearchFilter);
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
if (dwAdditionalFlags != 0) {
DEBUG_LOG(" -> ERROR_INVALID_PARAMETER (dwAdditionalFlags=0x%x)\n", dwAdditionalFlags);
setLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}

std::string narrowName = wideStringToString(lpFileName);
auto *findData = static_cast<LPWIN32_FIND_DATAW>(lpFindFileData);
return findFirstFileCommon(narrowName, findData);
}

BOOL WINAPI FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("FindNextFileA(%p, %p)\n", hFindFile, lpFindFileData);
Expand Down
2 changes: 2 additions & 0 deletions dll/kernel32/fileapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ HANDLE WINAPI FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileDat
HANDLE WINAPI FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData);
HANDLE WINAPI FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
HANDLE WINAPI FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags);
BOOL WINAPI FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData);
BOOL WINAPI FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData);
BOOL WINAPI FindClose(HANDLE hFindFile);
Expand Down
75 changes: 71 additions & 4 deletions dll/kernel32/memoryapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "common.h"
#include "context.h"
#include "errors.h"
#include "files.h"
#include "handles.h"
#include "heap.h"
#include "internal.h"
Expand All @@ -17,6 +18,7 @@
#include <map>
#include <mutex>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <utility>

Expand Down Expand Up @@ -56,6 +58,9 @@ struct ViewInfo {
DWORD allocationProtect = PAGE_NOACCESS;
DWORD type = MEM_PRIVATE;
bool managed = false;
bool trackedInode = false;
dev_t trackedDev = 0;
ino_t trackedIno = 0;
};

std::map<uintptr_t, ViewInfo> g_viewInfo;
Expand Down Expand Up @@ -222,6 +227,15 @@ HANDLE WINAPI CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappi
return NO_HANDLE;
}
size = static_cast<uint64_t>(fileSize);
} else {
// Windows extends the file to the mapping size if it's smaller.
off_t fileSize = lseek(dupFd, 0, SEEK_END);
if (fileSize >= 0 && static_cast<uint64_t>(fileSize) < size) {
if (ftruncate(dupFd, static_cast<off_t>(size)) != 0) {
setLastErrorFromErrno();
return NO_HANDLE;
}
}
}
mapping->maxSize = size;
}
Expand Down Expand Up @@ -276,6 +290,10 @@ static LPVOID mapViewOfFileInternal(Pin<MappingObject> mapping, DWORD dwDesiredA
bool wantAllAccess = (dwDesiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS;
if (wantAllAccess) {
wantWrite = true;
// FILE_MAP_ALL_ACCESS includes FILE_MAP_COPY as part of its bitmask,
// but on Windows WRITE takes precedence over COPY. Clear wantCopy so
// we use MAP_SHARED (write-through) instead of MAP_PRIVATE (COW).
wantCopy = false;
}
int prot = PROT_READ;
if (mapping->protect == PAGE_READWRITE) {
Expand Down Expand Up @@ -330,11 +348,7 @@ static LPVOID mapViewOfFileInternal(Pin<MappingObject> mapping, DWORD dwDesiredA
return nullptr;
}
requestedBase = reinterpret_cast<void *>(mapBaseAddr);
#ifdef MAP_FIXED_NOREPLACE
mapFlags |= MAP_FIXED_NOREPLACE;
#else
mapFlags |= MAP_FIXED;
#endif
} else {
void *candidate = nullptr;
wibo::heap::VmStatus reserveStatus = wibo::heap::reserveViewRange(mapLength, 0, 0, &candidate);
Expand Down Expand Up @@ -386,8 +400,25 @@ static LPVOID mapViewOfFileInternal(Pin<MappingObject> mapping, DWORD dwDesiredA
view.allocationProtect = protect;
view.type = MEM_MAPPED;
view.managed = reservedMapping;
// Track inode for file-backed views so we can prevent truncation (Windows behavior)
DEBUG_LOG("mapViewOfFileInternal: mmapFd=%d anonymous=%d\n", mmapFd, view.owner ? view.owner->anonymous : -1);
if (mmapFd != -1) {
struct stat st {};
int rc = fstat(mmapFd, &st);
DEBUG_LOG("mapViewOfFileInternal: fstat(%d) = %d dev=%lu ino=%lu\n",
mmapFd, rc, rc == 0 ? (unsigned long)st.st_dev : 0, rc == 0 ? (unsigned long)st.st_ino : 0);
if (rc == 0) {
view.trackedInode = true;
view.trackedDev = st.st_dev;
view.trackedIno = st.st_ino;
files::trackMappedFile(st.st_dev, st.st_ino);
}
}
if (reservedMapping) {
wibo::heap::registerViewRange(mapBase, mapLength, protect, view.protect);
} else if (baseAddress) {
view.managed = true;
wibo::heap::registerViewRange(mapBase, mapLength, protect, view.protect);
}
{
std::lock_guard guard(g_viewInfoMutex);
Expand Down Expand Up @@ -438,6 +469,9 @@ BOOL WINAPI UnmapViewOfFile(LPCVOID lpBaseAddress) {
void *base = reinterpret_cast<void *>(it->second.allocationBase);
size_t length = it->second.allocationLength;
bool managed = it->second.managed;
bool trackedInode = it->second.trackedInode;
dev_t trackedDev = it->second.trackedDev;
ino_t trackedIno = it->second.trackedIno;
g_viewInfo.erase(it);
lk.unlock();
if (length != 0) {
Expand All @@ -446,6 +480,9 @@ BOOL WINAPI UnmapViewOfFile(LPCVOID lpBaseAddress) {
if (managed) {
wibo::heap::releaseViewRange(base);
}
if (trackedInode) {
files::untrackMappedFile(trackedDev, trackedIno);
}
return TRUE;
}

Expand Down Expand Up @@ -628,4 +665,34 @@ BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSet
return TRUE;
}

void flushAllFileViews() {
std::lock_guard guard(g_viewInfoMutex);
for (const auto &entry : g_viewInfo) {
const ViewInfo &view = entry.second;
if (view.allocationLength == 0) {
continue;
}
void *base = reinterpret_cast<void *>(view.allocationBase);
size_t length = view.allocationLength;
// Check first bytes of the view for debugging
const unsigned char *p = static_cast<const unsigned char *>(base);
bool allZero = true;
size_t checkLen = length < 256 ? length : 256;
for (size_t i = 0; i < checkLen; i++) {
if (p[i] != 0) {
allZero = false;
break;
}
}
DEBUG_LOG("flushAllFileViews: syncing view at %p length %zu first256=%s first4=%02x%02x%02x%02x\n",
base, length, allZero ? "ALL_ZERO" : "HAS_DATA",
checkLen > 0 ? p[0] : 0, checkLen > 1 ? p[1] : 0,
checkLen > 2 ? p[2] : 0, checkLen > 3 ? p[3] : 0);
int rc = msync(base, length, MS_SYNC);
if (rc != 0) {
DEBUG_LOG("flushAllFileViews: msync failed errno=%d\n", errno);
}
}
}

} // namespace kernel32
2 changes: 2 additions & 0 deletions dll/kernel32/memoryapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSe
PSIZE_T lpMaximumWorkingSetSize);
BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize, SIZE_T dwMaximumWorkingSetSize);

void flushAllFileViews();

} // namespace kernel32
76 changes: 76 additions & 0 deletions dll/kernel32/winbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <strings.h>
#include <filesystem>
#include <limits>
#include <mimalloc.h>
Expand Down Expand Up @@ -982,6 +983,7 @@ DWORD WINAPI GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) {
if (!tryGetCurrentDirectoryPath(path)) {
return 0;
}
DEBUG_LOG("GetCurrentDirectoryW result: %s\n", path.c_str());
auto widePath = stringToWideString(path.c_str());
const DWORD required = static_cast<DWORD>(widePath.size());
if (nBufferLength == 0) {
Expand Down Expand Up @@ -1189,4 +1191,78 @@ BOOL WINAPI GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, PULARGE_INTEGER lpFreeB
lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes);
}

int WINAPI lstrcmpA(LPCSTR lpString1, LPCSTR lpString2) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("lstrcmpA(%s, %s)\n", lpString1 ? lpString1 : "(null)", lpString2 ? lpString2 : "(null)");
if (!lpString1 || !lpString2) {
setLastError(ERROR_INVALID_PARAMETER);
return 0;
}
return std::strcmp(lpString1, lpString2);
}

int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("lstrcmpW\n");
if (!lpString1 || !lpString2) {
setLastError(ERROR_INVALID_PARAMETER);
return 0;
}
const uint16_t *s1 = reinterpret_cast<const uint16_t *>(lpString1);
const uint16_t *s2 = reinterpret_cast<const uint16_t *>(lpString2);
while (*s1 && *s2) {
if (*s1 != *s2)
return (*s1 > *s2) ? 1 : -1;
++s1;
++s2;
}
if (*s1 == *s2) return 0;
return (*s1 > *s2) ? 1 : -1;
}

int WINAPI lstrcmpiA(LPCSTR lpString1, LPCSTR lpString2) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("lstrcmpiA(%s, %s)\n", lpString1 ? lpString1 : "(null)", lpString2 ? lpString2 : "(null)");
if (!lpString1 || !lpString2) {
setLastError(ERROR_INVALID_PARAMETER);
return 0;
}
return strcasecmp(lpString1, lpString2);
}

int WINAPI lstrcmpiW(LPCWSTR lpString1, LPCWSTR lpString2) {
HOST_CONTEXT_GUARD();
DEBUG_LOG("lstrcmpiW\n");
if (!lpString1 || !lpString2) {
setLastError(ERROR_INVALID_PARAMETER);
return 0;
}
const uint16_t *s1 = reinterpret_cast<const uint16_t *>(lpString1);
const uint16_t *s2 = reinterpret_cast<const uint16_t *>(lpString2);
while (*s1 && *s2) {
uint16_t c1 = wcharToLower(*s1);
uint16_t c2 = wcharToLower(*s2);
if (c1 != c2)
return (c1 > c2) ? 1 : -1;
++s1;
++s2;
}
uint16_t c1 = wcharToLower(*s1);
uint16_t c2 = wcharToLower(*s2);
if (c1 == c2) return 0;
return (c1 > c2) ? 1 : -1;
}

int WINAPI lstrlenA(LPCSTR lpString) {
HOST_CONTEXT_GUARD();
if (!lpString) return 0;
return static_cast<int>(std::strlen(lpString));
}

int WINAPI lstrlenW(LPCWSTR lpString) {
HOST_CONTEXT_GUARD();
if (!lpString) return 0;
return static_cast<int>(wstrlen(reinterpret_cast<const uint16_t *>(lpString)));
}

} // namespace kernel32
Loading
Loading