# garble Obfuscates Go code by wrapping the Go toolchain via `go build -toolexec=garble`. The top-level process does CLI/setup; sub-processes wrap each `compile`/`asm`/`link` call to transform source before the real tool runs. Read `CONTRIBUTING.md` for the architecture (toolexec model, caching, terminology) and `README.md` for design goals (deterministic, reproducible, reversible). ## Layout - Top-level `*.go`: CLI (`main.go`), the AST/type transformer (`transformer.go`), name hashing (`hash.go`), reflection handling (`reflect*.go`), caches (`cache_*.go`), runtime/linker patching (`runtime_patch.go`). - `internal/`: `literals` (string obfuscation), `ctrlflow`, `linker`, `ssa2ast`, `asthelper`. - `bundled_xtools.go`: patched copies of x/tools packages; see below before touching. - `go_std_tables.go` is generated by `scripts/gen_go_std_tables.go` (`go generate`). ## Testing - `go test ./...` does real builds and is slow (30s+). Use `go test -short` and `go test -run Script/` to target one `testdata/script/.txtar`. - Integration tests are testscript `.txtar` files under `testdata/script/`. Most behavior is tested there, not in Go unit tests. - Every change that alters behavior (feature, flag, bug) needs a test. - Tests must be deterministic — avoid randomness so failures reproduce; prefer fixed seeds/inputs. - Test cost matters: keep builds minimal (smallest package that triggers the bug, reuse caches, avoid full std rebuilds and `-debugdir` outside `debugdir.txtar`). - New Go version support, dependency bumps, and CI tuning are routine recurring work. - garble rejects Go toolchains newer than it supports, including `devel` builds. If the `go` in `$PATH` is too new, run tests via `GOTOOLCHAIN` with the latest bugfix release of the newest supported major version, such as `GOTOOLCHAIN=go1.27.0 go test -short` as of August 2026. ## Bundled x/tools packages `bundled_xtools.go` holds modified copies of x/tools packages, needed so that `typeutil.Hasher` does not hash struct field tags, given that tags do not affect type identity. typeutil imports the internal `typeparams` package, so that is bundled too, with the import replaced by the bundled `typeparams_` names. Each package's code is prefixed with its name and introduced by a `NOTE(garble)` comment naming its origin. Our patches are marked `// NOTE(garble)`. They are applied by an agent following the steps below, not regenerated by a tool. We also drop any API which is not reachable from `typeutil_Hasher`. Line 1 records the x/tools version the file was bundled from. Bumping `golang.org/x/tools` requires re-syncing the file. Never overwrite it with raw `go tool bundle` output, as the pruning and patches would have to be redone. Instead: * `go build -o /tmp/bundle golang.org/x/tools/cmd/bundle` after the bump; use this new binary throughout, as older ones fail to load packages of a newer Go release * bundle both packages at the old version via a throwaway module requiring it and blank-importing them, e.g. `/tmp/bundle -o /tmp/old_typeutil.go golang.org/x/tools/go/types/typeutil`, and at the new version from this repository * diff old against new to see what upstream changed; apply only the hunks that touch code we still keep * diff `bundled_xtools.go` against the new bundles to confirm the only differences are the removed API and our `NOTE(garble)` patches, then bump the version in line 1 * `go build ./...` and check `staticcheck` reports no unused code ## Commits - Subject: lowercase, imperative, no trailing period, concise. Optionally prefixed with an area: `testdata:`, `CI:`, `internal/literals:`, `README:`, `scripts:`, `all:`. - Body explains *why*, not just what. For perf changes, include benchstat tables or before/after timings. For bug fixes, paste the failing build/test output and end with `Fixes #NNN.`. Credit external contributors by name when relevant. - Keep prose tight — say what matters and stop. No restating, no padding. ## Bug-fix workflow Split into two commits: first a regression test that passes against the broken code (asserting current wrong behavior or with a TODO), then the fix flipping the test to assert correct behavior. Skip this for panics or hangs.