Skip to content

Fix Go 1.26 support: intrinsics, checkLinkname, and nosplit literals#1003

Closed
LOVECHEN wants to merge 10 commits intoburrowers:masterfrom
LOVECHEN:go1.26-fixes
Closed

Fix Go 1.26 support: intrinsics, checkLinkname, and nosplit literals#1003
LOVECHEN wants to merge 10 commits intoburrowers:masterfrom
LOVECHEN:go1.26-fixes

Conversation

@LOVECHEN
Copy link

@LOVECHEN LOVECHEN commented Mar 4, 2026

This PR fixes 3 issues that prevented garble from working correctly with Go 1.26, building on top of the work in #992.

Changes

1. go_std_tables: register Go 1.26 compiler intrinsics

Go 1.26 introduced crypto/internal/constanttime with compiler intrinsic functions Select and boolToUint8. Without these entries in the compilerIntrinsics table, garble obfuscates their names, preventing the compiler from recognizing them as intrinsics. At runtime, the stub functions panic with unreachable; must be intrinsicified.

Also regenerates the table for Go 1.26 (removes stale entries).

Fixes TestScript/reflect

2. main: disable linker checkLinkname for patched linker

Go 1.23 introduced -checklinkname (default: enabled) which validates that //go:linkname references come from approved packages. Since garble obfuscates package import paths, the linker rejects references from obfuscated package names. Go 1.26 significantly expanded the blockedLinknames table with goroutine leak profiling, crypto/fips140, and runtime/secret entries.

Fix: prepend -checklinkname=0 to linker args using Go's official flag.

Fixes TestScript/test

3. literals: skip obfuscation inside //go:nosplit functions

The -literals flag injects lambda calls to decode obfuscated literals, which adds stack frames. Functions marked //go:nosplit have a strict 800-byte stack limit. When literal obfuscation injects decryption code into these functions (e.g. in the reflect package), the combined stack usage exceeds the limit.

Fix: check for //go:nosplit in the AST pre-order traversal and skip literal obfuscation for those function bodies.

Fixes TestScript/imports (with -literals)

Testing

All tests pass with Go 1.26.0 on macOS arm64:

go test ./... -count=1
ok  mvdan.cc/garble
ok  mvdan.cc/garble/internal/ctrlflow
ok  mvdan.cc/garble/internal/literals
ok  mvdan.cc/garble/internal/ssa2ast

Relates to #992

mvdan and others added 8 commits March 1, 2026 23:31
…ime deps

Regenerate the runtimeAndDeps table for Go 1.26 across all supported
platforms (darwin, linux, windows, freebsd × amd64, arm64).

Add crypto/internal/constanttime compiler intrinsics (Select, boolToUint8)
introduced in Go 1.26. Without these entries, garble obfuscates the
intrinsic function names, causing crypto/ecdh to panic at runtime with
'unreachable; must be intrinsicified'.

Add platform-specific runtime dependencies that were missing from the
cross-platform table:
  - internal/runtime/cgroup (linux)
  - internal/runtime/syscall/linux (linux)
  - internal/runtime/syscall/windows (windows)

Without these entries, garble treats these packages as obfuscatable and
corrupts their assembly files during cross-compilation, causing errors
like 'expected pseudo-register; found CX' on windows/amd64.

Fixes TestScript/reflect.
Go 1.23 introduced the -checklinkname flag (enabled by default) which
validates that //go:linkname references come from approved packages
listed in cmd/link/internal/loader/loader.go's blockedLinknames table.

Since garble obfuscates package import paths, the linker sees references
from obfuscated package names (e.g. 'v05rX8m') instead of the original
ones (e.g. 'runtime/pprof'), causing checkLinkname to reject them with
'invalid reference to runtime.pprof_goroutineLeakProfileWithLabels'.

Go 1.26 expanded blockedLinknames significantly with new entries for
goroutine leak profiling, crypto/fips140, runtime/secret, and others,
making this issue more prevalent — particularly visible in 'garble test'
which pulls in runtime/pprof via the testing framework.

Disable the check using Go's official -checklinkname=0 flag prepended
to the linker arguments. This is correct because garble already handles
linkname resolution internally via runtimeAndLinknamed and the
transformDirectives/transformLinkname pipeline.

Fixes TestScript/test.
@mvdan
Copy link
Member

mvdan commented Mar 4, 2026

Thanks, appreciate the ideas. I'll take a look when I have some free time.

@LOVECHEN LOVECHEN force-pushed the go1.26-fixes branch 4 times, most recently from 592d40d to 6d9ff6c Compare March 4, 2026 11:29
love added 2 commits March 4, 2026 19:31
The -literals flag injects anonymous function calls to decode obfuscated
string literals at runtime. These add stack frames that push nosplit
functions over the 800-byte stack limit.

Skip literal obfuscation entirely for nosplit functions to prevent
linker errors like 'nosplit stack over 792 byte limit'.
crossbuild.txtar: Go 1.26 changed which math/bits functions are
intrinsified on 386. Update to TrailingZeros32/Ctz32.

gotoolchain.txtar: Go 1.26 go mod edit -go no longer accepts the
go prefix. Use -go=1.23 -toolchain=${GOVERSION_UPGRADE}.
@mvdan
Copy link
Member

mvdan commented Mar 4, 2026

You're starting to do all sorts of changes. I would encourage you to send any changes unrelated to go 1.26 support as separate PRs. I will only incorporate anything that seems necessary for 1.26 support.

@LOVECHEN
Copy link
Author

LOVECHEN commented Mar 4, 2026

You're starting to do all sorts of changes. I would encourage you to send any changes unrelated to go 1.26 support as separate PRs. I will only incorporate anything that seems necessary for 1.26 support.
roger

@LOVECHEN
Copy link
Author

LOVECHEN commented Mar 5, 2026

Closing to reorganize. Will resubmit Go 1.26 changes as a cleaner PR with only the necessary fixes, as requested. Thank you for the review feedback.

@LOVECHEN LOVECHEN closed this Mar 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants