Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c80b7cb
Integrate mimalloc (#88)
encounter Aug 15, 2025
6d0bff4
Update CI release action
encounter Aug 15, 2025
836f485
Initial external DLL support
encounter Sep 26, 2025
720e663
Return ERROR_RESOURCE_DATA_NOT_FOUND if open_resource_stream fails
encounter Sep 26, 2025
01ddf95
Add proper resource implementation
encounter Sep 26, 2025
82e2809
Update README.md
encounter Sep 26, 2025
c14ad86
Implement version.dll properly
encounter Sep 26, 2025
104e9e8
Add proper testing framework & integrate with CI
encounter Sep 26, 2025
b5da26a
msvcrt: Clean up __wgetmainargs, _wdupenv_s, _wgetenv_s; add __getmai…
encounter Sep 26, 2025
b27ca28
Ignore .cache from clangd
encounter Sep 26, 2025
f83d228
strutil cleanup & fixes
encounter Sep 26, 2025
8b29e1b
clang-format and fix release build
encounter Sep 26, 2025
a27d5c2
msvcrt: Implement strlen, strcmp, strncmp
encounter Sep 26, 2025
a17a3c5
Invoke dllMain with proper TIB selector
encounter Sep 26, 2025
042a43c
DLL fixes; impl GetSystemInfo, __dllonexit, and more
encounter Sep 26, 2025
b4ea1da
Improve DLL loading and process launch handling
encounter Sep 26, 2025
f23224b
cl.exe works! but I didn't review most of this code
encounter Sep 26, 2025
b53ae15
Force builtin lmgr stub and stub missing imports
encounter Sep 27, 2025
2c5fdd6
msvcrt: export calloc
encounter Sep 28, 2025
2732bd5
More msvcrt impls for Ubuntu mingw
encounter Sep 28, 2025
bc33bae
Formatting, fixes, deduplication
encounter Sep 28, 2025
8cac50e
Rewrite BCryptGenRandom and add tests
encounter Sep 28, 2025
fd47411
Rework CI into a build matrix; update Dockerfiles
encounter Sep 29, 2025
3d22538
Add command line arguments (--chdir/--debug, ...)
encounter Sep 29, 2025
1a4944e
More msvcrt, fix loading DLLs in program dir regression
encounter Sep 29, 2025
1c35da4
Implement GetComputerNameW
encounter Sep 29, 2025
8884c95
Merge remote-tracking branch 'origin/main' into dll
encounter Sep 29, 2025
d54ff9b
Add git to Dockerfile.ubuntu
encounter Sep 29, 2025
d1ed066
Also add ca-certificates
encounter Sep 29, 2025
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
4 changes: 0 additions & 4 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.git/
.vscode/
build/
test/

Dockerfile
README.md
113 changes: 79 additions & 34 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,96 @@ env:
DOCKER_BUILDKIT: 1

jobs:
build_and_test:
name: Build and test
build:
name: Build (${{ matrix.display }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- id: ubuntu-debug
display: Debug
dockerfile: Dockerfile.ubuntu
build_type: Debug
image: wibo-host-debug
- id: ubuntu-release
display: Release
dockerfile: Dockerfile.ubuntu
build_type: Release
image: wibo-host-release
- id: static-debug
display: Static, Debug
dockerfile: Dockerfile
build_type: Debug
image: wibo-static-debug
- id: static-release
display: Static, Release
dockerfile: Dockerfile
build_type: Release
image: wibo-static-release
steps:
- uses: actions/checkout@v4
- name: Checkout
uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y file unzip wget

- name: Build debug
run: docker build --build-arg build_type=Debug --target export --output build_debug .
- name: Build
run: >-
docker build
-f ${{ matrix.dockerfile }}
--build-arg build_type=${{ matrix.build_type }}
--target build
-t ${{ matrix.image }}
.

- name: Build release
run: docker build --build-arg build_type=Release --target export --output build .
- name: Tests
run: docker run --rm ${{ matrix.image }} ctest --test-dir /wibo/build --output-on-failure

- name: Test
shell: bash
- name: Export binary
run: |
mv build_debug/wibo build/wibo_debug
wget -q https://files.decomp.dev/compilers_latest.zip
unzip -q compilers_latest.zip
set -x
build/wibo_debug Wii/1.7/mwcceppc.exe -nodefaults -c test/test.c -Itest -o test_debug.o
file test_debug.o
build/wibo Wii/1.7/mwcceppc.exe -nodefaults -c test/test.c -Itest -o test.o
file test.o

- name: Upload release
rm -rf dist
docker build \
-f ${{ matrix.dockerfile }} \
--build-arg build_type=${{ matrix.build_type }} \
--target export \
--output dist \
.

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: wibo
path: build/wibo
name: ${{ matrix.id }}
path: dist/wibo

- name: Upload debug
uses: actions/upload-artifact@v4
release:
name: Publish Release
if: startsWith(github.ref, 'refs/tags/')
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download static debug
uses: actions/download-artifact@v4
with:
name: static-debug
path: artifacts/static-debug

- name: Download static release
uses: actions/download-artifact@v4
with:
name: wibo_debug
path: build/wibo_debug
name: static-release
path: artifacts/static-release

- name: Prepare assets
run: |
mkdir -p artifacts/out
cp artifacts/static-debug/wibo artifacts/out/wibo_debug
cp artifacts/static-release/wibo artifacts/out/wibo

- name: Publish release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
files: |
build/wibo
build/wibo_debug
artifacts/out/wibo
artifacts/out/wibo_debug
draft: true
generate_release_notes: true
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ build/
.vscode/
# CLion
.idea/
cmake-build-*/
cmake-build-*/
# clangd
.cache/
39 changes: 39 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Repository Guidelines

## Project Structure & Module Organization
- Core launcher logic lives in `main.cpp`, `loader.cpp`, `files.cpp`, `handles.cpp` and `module_registry.cpp`; shared interfaces in headers near them.
- Windows API shims reside in `dll/`, grouped by emulated DLL name; keep new APIs in the matching file instead of creating ad-hoc helpers.
- Reusable utilities sit in `strutil.*`, `processes.*` and `resources.*`; prefer extending these before introducing new singleton modules.
- Sample fixtures for exercising the loader live in `test/`; keep new repros small and self-contained.

## Build, Test, and Development Commands
- `cmake -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` configures a 32-bit toolchain; ensure multilib packages are present.
- `cmake --build build --target wibo` compiles the shim; switch to `-DCMAKE_BUILD_TYPE=Release` for optimised binaries.
- `./build/wibo /path/to/program.exe` runs a Windows binary. Use `WIBO_DEBUG=1` (or `--debug`/`-D`) for verbose logging. Use `--chdir`/`-C` to set the working directory.
- `cmake -B build -DBUILD_TESTING=ON` + `ctest --test-dir build --output-on-failure` runs the self-checking WinAPI fixtures (requires `i686-w64-mingw32-gcc` and `i686-w64-mingw32-windres`).
- `clang-format -i path/to/file.cpp` and `clang-tidy path/to/file.cpp -p build` keep contributions aligned with the repo's tooling.
- DON'T use `clang-format` on existing files, only new or heavily modified ones; the repo hasn't been fully formatted yet.

## Coding Style & Naming Conventions
- Formatting follows `.clang-format` (LLVM base, tabbed indentation width 4, 120 column limit); never hand-wrap differently.
- Prefer PascalCase for emulated Win32 entry points, camelCase for internal helpers, and SCREAMING_SNAKE_CASE for constants or macros.
- Document non-obvious control flow with short comments and keep platform-specific code paths behind descriptive helper functions.

## Shim Implementation Guidelines
- Target pre-XP behavior; our binaries are old and don't expect modern WinAPI behavior.
- Use the `microsoft_docs` tools to fetch WinAPI signatures and documentation; always fetch the documentation when working on an API function.
- Create minimal, self-contained repros in `test/` when implementing or debugging APIs; this aids both development and future testing.
- Stub unimplemented APIs with `DEBUG_LOG` calls to track usage; prioritize based on the needs of real-world binaries.

## Testing Guidelines
- Fixture binaries live in `test/` and are compiled automatically when `BUILD_TESTING` is enabled; keep new repros small and self-contained (`test_<feature>.c`).
- All fixtures must self-assert; use `test_assert.h` helpers so `ctest` fails on mismatched WinAPI behaviour.
- Cross-compile new repros with `i686-w64-mingw32-gcc` (and `i686-w64-mingw32-windres` for resources); CMake handles this during the build, but direct invocation is useful while iterating.
- Run `ctest --test-dir build --output-on-failure` after rebuilding to verify changes; ensure failures print actionable diagnostics.

## Debugging Workflow
- Reproduce crashes under `gdb` (or `lldb`) with `-q -batch` to capture backtraces, register state, and the faulting instruction without interactive prompts.
- Enable `WIBO_DEBUG=1` and tee output to a log when running the guest binary; loader traces often pinpoint missing imports, resource lookups, or API shims that misbehave.
- Inspect relevant source right away—most issues stem from stubbed shims in `dll/`; compare the guest stack from `gdb` with those implementations.
- When host-side behaviour is suspect (filesystem, execve, etc.), rerun under `strace -f -o <log>`; this highlights missing files or permissions before the shim faults.
- If the `ghidra` MCP tool is available, request that the user import and analyze the guest binary; you can then use it to disassemble/decompile code around the crash site.
132 changes: 129 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,27 @@ if(NOT CMAKE_BUILD_TYPE)
"Build type options: Debug Release RelWithDebInfo MinSizeRel" FORCE)
endif()

set(CMAKE_C_FLAGS_INIT "-m32")
set(CMAKE_CXX_FLAGS_INIT "-m32")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-m32")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-m32")

project(wibo LANGUAGES CXX)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-pie -no-pie -D_LARGEFILE64_SOURCE")

find_package(Filesystem REQUIRED)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -Wall -fno-pie -no-pie -D_LARGEFILE64_SOURCE")
include(FetchContent)
FetchContent_Declare(
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG dfa50c37d951128b1e77167dd9291081aa88eea4 # v3.1.5
)
FetchContent_MakeAvailable(mimalloc)

include_directories(.)
add_executable(wibo
Expand All @@ -24,16 +37,129 @@ add_executable(wibo
dll/mscoree.cpp
dll/msvcrt.cpp
dll/ntdll.cpp
dll/rpcrt4.cpp
dll/ole32.cpp
dll/user32.cpp
dll/vcruntime.cpp
dll/version.cpp
files.cpp
handles.cpp
loader.cpp
resources.cpp
module_registry.cpp
main.cpp
processes.cpp
strutil.cpp
)
target_link_libraries(wibo PRIVATE std::filesystem)
target_link_libraries(wibo PRIVATE std::filesystem mimalloc-static)
install(TARGETS wibo DESTINATION bin)

include(CTest)

if(BUILD_TESTING)
find_program(WIBO_MINGW_CC i686-w64-mingw32-gcc)
find_program(WIBO_MINGW_WINDRES i686-w64-mingw32-windres)

set(WIBO_HAVE_MINGW_TOOLCHAIN FALSE)
if(WIBO_MINGW_CC AND WIBO_MINGW_WINDRES)
set(WIBO_HAVE_MINGW_TOOLCHAIN TRUE)
endif()

option(WIBO_ENABLE_FIXTURE_TESTS "Build and run Windows fixture binaries through wibo" ${WIBO_HAVE_MINGW_TOOLCHAIN})

if(WIBO_ENABLE_FIXTURE_TESTS)
if(NOT WIBO_HAVE_MINGW_TOOLCHAIN)
message(WARNING "MinGW toolchain not found; skipping fixture tests")
else()
set(WIBO_TEST_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR}/test)
file(MAKE_DIRECTORY ${WIBO_TEST_BIN_DIR})

add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/external_exports.dll
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2 -shared
-o external_exports.dll
${CMAKE_CURRENT_SOURCE_DIR}/test/external_exports.c
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/test/external_exports.c)

add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_external_dll.exe
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
-I${CMAKE_CURRENT_SOURCE_DIR}/test
-o test_external_dll.exe
${CMAKE_CURRENT_SOURCE_DIR}/test/test_external_dll.c
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/test/test_external_dll.c
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)

add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_bcrypt.exe
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
-I${CMAKE_CURRENT_SOURCE_DIR}/test
-o test_bcrypt.exe
${CMAKE_CURRENT_SOURCE_DIR}/test/test_bcrypt.c
-lbcrypt
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/test/test_bcrypt.c
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)

add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_resources_res.o
COMMAND ${WIBO_MINGW_WINDRES}
${CMAKE_CURRENT_SOURCE_DIR}/test/test_resources.rc
-O coff -o test_resources_res.o
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/test/test_resources.rc)

add_custom_command(
OUTPUT ${WIBO_TEST_BIN_DIR}/test_resources.exe
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
-I${CMAKE_CURRENT_SOURCE_DIR}/test
-o test_resources.exe
${CMAKE_CURRENT_SOURCE_DIR}/test/test_resources.c
test_resources_res.o -lversion
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/test/test_resources.c
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h
${WIBO_TEST_BIN_DIR}/test_resources_res.o)

add_custom_target(wibo_test_fixtures
DEPENDS
${WIBO_TEST_BIN_DIR}/external_exports.dll
${WIBO_TEST_BIN_DIR}/test_external_dll.exe
${WIBO_TEST_BIN_DIR}/test_bcrypt.exe
${WIBO_TEST_BIN_DIR}/test_resources.exe)

if(CMAKE_CONFIGURATION_TYPES)
set(_wibo_fixture_build_command
${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config $<CONFIG> --target wibo_test_fixtures)
else()
set(_wibo_fixture_build_command
${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target wibo_test_fixtures)
endif()

add_test(NAME wibo.build_fixtures COMMAND ${_wibo_fixture_build_command})

add_test(NAME wibo.test_external_dll
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_external_dll.exe)
set_tests_properties(wibo.test_external_dll PROPERTIES
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS wibo.build_fixtures)

add_test(NAME wibo.test_bcrypt
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_bcrypt.exe)
set_tests_properties(wibo.test_bcrypt PROPERTIES
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS wibo.build_fixtures)

add_test(NAME wibo.test_resources
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_resources.exe)
set_tests_properties(wibo.test_resources PROPERTIES
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
DEPENDS wibo.build_fixtures)
endif()
endif()
endif()
18 changes: 16 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,30 @@
FROM --platform=linux/i386 alpine:latest AS build

# Install dependencies
RUN apk add --no-cache cmake ninja g++ linux-headers binutils
RUN apk add --no-cache \
bash \
cmake \
ninja \
g++ \
linux-headers \
binutils \
git \
mingw-w64-binutils \
mingw-w64-gcc

# Copy source files
WORKDIR /wibo
COPY . /wibo

# Build type (Release, Debug, RelWithDebInfo, MinSizeRel)
ARG build_type=Release

# Build static binary
RUN cmake -S /wibo -B /wibo/build -G Ninja -DCMAKE_BUILD_TYPE="$build_type" -DCMAKE_CXX_FLAGS="-static" \
RUN cmake -S /wibo -B /wibo/build -G Ninja \
-DCMAKE_BUILD_TYPE="$build_type" \
-DCMAKE_CXX_FLAGS="-static" \
-DBUILD_TESTING=ON \
-DWIBO_ENABLE_FIXTURE_TESTS=ON \
&& cmake --build /wibo/build \
&& ( [ "$build_type" != "Release" ] || strip -g /wibo/build/wibo )

Expand Down
Loading