Conversation
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
The Go runtime dynamically loads and calls several Windows APIs that speakeasy did not previously emulate, causing Go binaries to crash during initialization (issue #223). - Add winmm.dll to default module config so LoadLibraryExA succeeds - Add kernel32 stubs: AddVectoredContinueHandler, CreateWaitableTimerExW, GetProcessAffinityMask, SetConsoleCtrlHandler - Add ntdll stub: RtlGetNtVersionNumbers (returns Windows 10 version) - Add winmm stubs: timeBeginPeriod, timeEndPeriod Refs: #223
Follow-up to the initial Go emulation fix. After testing with a real Go binary (Go 1.25), additional missing APIs were discovered: - Add bcryptprimitives.dll module + ProcessPrng stub (Go 1.20+ uses this instead of advapi32!SystemFunction036 for CSPRNG) - Add kernel32 stubs: GetErrorMode, WerGetFlags, WerSetFlags - Add ntdll stubs: RtlGetCurrentPeb, RtlGetVersion With these changes, Go runtime initialization proceeds through heap setup, thread creation, and environment loading. The runtime still panics (exit code 2) due to deeper emulation gaps (thread scheduling, timer resolution), but the unsupported_api crashes from #223 are fully resolved. Refs: #223
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
807f7f2 to
9768a3c
Compare
Root cause of Go runtime panic "nanotime returning zero": Go reads time directly from KUSER_SHARED_DATA (0x7FFE0000) without calling QueryPerformanceCounter. Speakeasy mapped this page but left it zero-filled. - Populate KUSER_SHARED_DATA with realistic values: InterruptTime, SystemTime, QpcFrequency, NtMajorVersion, TickCount - Fix GetStdHandle on amd64: mask the DWORD argument to 32 bits so STD_ERROR_HANDLE (0xFFFFFFF4) matches when sign-extended to 64-bit (0xFFFFFFFFFFFFFFF4) - Add SwitchToThread stub With these fixes, Go runtime initialization proceeds past nanotime into the goroutine scheduler, where it hits "stoplockedm: not runnable" — a fundamental threading limitation (Go requires concurrent thread execution, speakeasy executes threads serially). Refs: #223
Investigation: What it takes to fully emulate Go binariesAfter getting the initial API stubs in place, "I" (read: Opus) did a deeper investigation into what's needed for a Go hello-world to actually print output. Here's the full picture. Root cause chain
Thread model analysisSpeakeasy's Two potential approaches to fix this (both non-trivial):
Other findings
Current stateWith all three commits, Go runtime initialization proceeds through: library loading → heap allocation (including |
mr-tz
left a comment
There was a problem hiding this comment.
very very nice, just one minor question
Summary
winmm.dllandbcryptprimitives.dllto the default module config so the Go runtime can load them viaLoadLibraryExWbcryptprimitives.ProcessPrngstub (Go 1.20+ requires this for CSPRNG)AddVectoredContinueHandler,CreateWaitableTimerExW,GetProcessAffinityMask,SetConsoleCtrlHandler,GetErrorMode,WerGetFlags,WerSetFlagsRtlGetNtVersionNumbers,RtlGetCurrentPeb,RtlGetVersiontimeBeginPeriod,timeEndPeriodCloses #223
Before
Go binaries crash immediately with
unsupported_apiafter ~7 API calls during runtime initialization.After
Go runtime initializes fully — loads libraries, allocates heap, spawns threads, reads environment (188+ API calls). The runtime still panics (exit code 2) due to deeper emulation gaps (thread scheduling, std handle initialization), but the
unsupported_apicrashes are resolved.Test plan
GOOS=windows GOARCH=amd64)🤖 Generated with Claude Code