388 Commits

Author SHA1 Message Date
KismetDev 900696b816 do not create ./build in cwd when cache init is skipped (#1035)
sharedCache is allocated before goVersionOK runs, but CacheDir is
only set once the version check passes. The trim defer in mainErr
only checked sharedCache != nil, so on the too-new-version path
openCache joined an empty CacheDir with "build" and created the
directory in CWD.

Fixes #995.
2026-04-21 17:00:42 +01:00
Daniel Martí 1186bca36d make flagValueIter an iter.Seq
This is purely a cosmetic change, to make the callers nicer.
2026-04-14 01:08:52 +02:00
dajinglingpake 2e5692934c fix random seed warning 2026-04-13 08:05:06 +01:00
Daniel Martí 7114403786 drop Go 1.25, support Go 1.26
Beyond the usual updating of version strings, re-running `go generate`,
and updating the patches for the linker, we needed extra changes.

First, the linker started being stricter about which packages
are allowed to linkname runtime names by import path.
Stop obfuscating import paths in runtimeAndLinknamed.

Second, the compiler's intrinsics code adds an "add" function
much like the existing "addF", so tweak the regular expression.

Third, note that there is a new runtimeAndDeps windows-only package,
which we find thanks to the recent improvement to cover many platforms.

Fourth, the code around magic numbers in the runtime has changed,
so rewrite the code which patches it via go/ast.

Fixes #990.
2026-03-15 23:36:56 +01:00
love 88d963720f main: warn when -seed exceeds 8 bytes
Only the first 8 bytes of the seed are used for deterministic
obfuscation. Warn the user via stderr when extra bytes are provided,
and remove the TODO that noted this was confusing.
2026-03-05 12:43:05 +01:00
love f7a10bf961 main: fix "oursrelves" typo in comment
Fix typo in splitFlagsFromFiles comment.
2026-03-05 12:42:24 +01:00
Paul Scheduikat 53c5f022b4 Make -debugdir also output the unobfuscated source tree
This makes the unobfuscated state easily accessible, for debugging.

Obfuscated code is output to: `./<debugdir>/garbled`
The original source code is output to:
`./<debugdir>/source`

Also enable caching for debugdir to allow for faster iteration.
2026-03-01 22:55:02 +00:00
Daniel Martí 37e582d581 support Go versions with X: suffixes for GOEXPERIMENTs
A workaround until https://github.com/golang/go/issues/75953 is fixed.

See #978.
2025-10-18 06:42:52 +02:00
Daniel Martí 15a385283b reject invalid Go toolchain versions early
Otherwise we might do odd things, such as trying to open the linker
patches directory "patches/" given that go/version.Lang
returns an empty string for invalid versions.

See #978.
2025-10-18 06:42:52 +02:00
Daniel Martí 9d7c84b0c6 parse go env GOVERSION with go/version directly
We don't need to use a regular expression to find "goN.M" anymore,
as go/version seems to deal with "devel" versions from master just fine.
We can then also stop having two separate fields for the version
of the Go toolchain currently being used.
2025-10-18 06:42:52 +02:00
jtimperio 28f7a7ffbf refactor main into pieces
* reflect_abi_patch.go was added into reflect.go
* shared.go was renamed into cache_shared.go and package caching was moved to cache_pkg.go
* transformer methods in main.go are moved to transformer.go
2025-09-09 00:28:15 +01:00
Daniel Martí 193f19ab5f use x/tools/cmd/bundle via go tool
The behavior is the same, but now we track the dependency in go.mod
via `go get -tool`, which is a better approach.

While here, make a package loading error slightly clearer.
2025-08-30 21:38:24 +01:00
Daniel Martí aed2fd2659 add support for Go 1.25 and drop support for 1.24
While strictly speaking it would be okay to leave Go 1.24 support
in place for the time being, we are behind on a few tasks at the moment
so it's best to keep the setup at master simpler for the next release.
Go 1.25 already came out two weeks ago, and it seems to have been
a fairly smooth release, so I don't suspect any end users will have
trouble upgrading to it.

Note that two changes were necessary for garble to work on Go 1.25.0.

First, we stop deduplicating runtimeAndLinknamed with runtimeAndDeps.
Otherwise, for GOOS=windows, internal/runtime/cgroup would be missing
as it is a //go:linkname target from runtime on all platforms,
but it is not transitively imported from runtime on GOOS=windows.

Second, the testing/synctest package is now part of std,
and it is a //go:linkname target from the testing package
but not a transitive import from it. Teach appendListedPackages that,
when loading all packages for a `go test` run, it should load
the new testing/synctest package too.

Fixes #968.
2025-08-30 21:38:24 +01:00
Paul Scheduikat 8d8ba00515 properly handle controlflow obfuscation in code that uses unsafe
Due to unsafe not being a real dependency, type checking during control-flow obfuscation was performed incorrectly.
This is fixed by excluding unsafe from the dependency checks.


Fixes #903
2025-06-12 14:25:09 +02:00
pagran d47e0761eb Prevent automated plaintext extraction of literals with current tools (#930)
Some programs which could automatically reverse string literals obfuscated with `-literals` exist.

They currently work by emulating the string literal decryption functions we insert.

We prevent this naive emulation from succeeding by making the decryption functions dependent on global state.

This can still be broken with enough effort, we are curious which approach reverse-engineers come up with next, we certainly still have some ideas to make this harder.

Fixes #926
---------

Co-authored-by: Paul Scheduikat <lu4p@pm.me>
2025-06-03 02:37:51 +02:00
Daniel Martí 6dca875017 remove two misguided TODOs
The way computePkgCache caches per-package results in GARBLE_CACHE,
under normal circumstances where a user isn't deleting GARBLE_CACHE
we will only load gob files for direct imports, not indirect ones.
Hence, loading a package's gob file twice is not happening normally.

And the way we use go/types, we don't need to set Config.GoVersion.
2025-04-26 16:42:54 +02:00
Daniel Martí 9cf2a6a77f properly patch the linker when GOROOT is a symlink
Some Go version managers like github.com/voidint/g use GOROOT symlinks,
which silently broke the way we patch the linker via go build -overlay.

Reproduced the original crash via the following testscript:

    env GARBLE_CACHE=${WORK}/garble-cache
    symlink goroot -> /usr/lib/go
    env GOROOT=${WORK}/goroot
    exec garble run main.go

    -- main.go --
    package main

    import "fmt"

    func main() {
        fmt.Println("hello world")
    }

We don't commit this testscript given how it's an expensive test
and for a relatively rare edge case whose fix is now well documented.
Moreover, as GOTOOLCHAIN is now available, I expect version managers
for Go to fade away with time.

While here, remove a debugging 'exec cat' from a testscript.

Fixes #915.
2025-04-26 16:42:15 +02:00
Daniel Martí aa67c654dc refuse to obfuscate bytedance/sonic/loader
Or any other package which uses a //go:linkname to the runtime names
"lastmoduledatap" or "moduledataverify1". These are used by sonic
to inject function headers to the runtime, which does not work
as garble patches the runtime as part of obfuscation.
The way it alters the magic number in function headers breaks this.

Add a summary of this as a comment too.

Fixes #898.
2025-04-22 14:55:58 +02:00
Daniel Martí b34a7e3926 avoid patching our reflect code into _cgo_gotypes.go
When obfuscating a main package whose Go files all import "C",
the first Go file in CompiledGoFiles ends up being _cgo_gotypes.go.
We cannot add our code from reflect_abi_code.go there,
as it leads to the following error about its _trieNode type:

    typecheck error: $WORK/b001/_cgo_gotypes.go:185:10: cannot define new methods on non-local type _trieNode

Avoid patching any _cgo_*.go file with our reflect code,
as all of those files are special glue code for cgo.

While here, tweak reflectMainPrePatch to return a string
for consistency with abiNamePatch.

Fixes #916.
2025-04-21 07:41:15 +02:00
Daniel Martí 32e1e0aa2b take advantage of some APIs added in Go 1.24
Primarily new iterator APIs in the strings and go/types packages.

While here, verify that the processImportCfg hack is stil needed
as of go1.24.2.
2025-04-13 23:10:38 +02:00
Daniel Martí 2bb1d49874 rely on go build stamping a version for local builds
Before Go 1.24, `go build` only stamped module versions for modules
resolved via GOPROXY, as the local module only had VCS information.
For that reason, we manually built a pseudo-version from the VCS
timestamp and revision stamped for local builds.

Go 1.24 started stamping the main module with a module version
derived from the local VCS information, much like we already did.
For example, comparing a clean build before this change
against a build with this uncommitted change:

    $ go install
    $ garble version
    mvdan.cc/garble v0.14.3-0.20250413182748-e97968ccae46
    [...]
    $ git stash pop
    $ go install
    $ garble version
    mvdan.cc/garble v0.14.3-0.20250413182748-e97968ccae46+dirty
    [...]

The only user-visible change is that local builds with any
uncommitted changes now get a `+dirty` suffix, but that's probably
a good thing for the majority of users, and provides a useful hint
in case a user forgot about local changes.

The test logic to inject VCS information via an env var
and see that the resulting pseudo-version is what we expect can go too,
as that was testing our own main module version logic.
We now rely on `go build` to do the right thing, so don't test that.
2025-04-13 23:10:38 +02:00
Daniel Martí ffed9e5438 drop support for Go 1.23
A pretty small patch, given that 1.23 and 1.24 are quite similar
in terms of what garble does.
2025-04-13 23:10:38 +02:00
Daniel Martí a3a92356d9 refuse to delete unknown files with -debugdir
When creating a new debugdir directory, add a sentinel .garble-debugdir
file at its root so that we can later know that we created it
and the user is very unlikely to have left important data there.

When emptying an existing debugdir directory, only do so if it has
that sentinel file, for added safety.

Fixes #932.
2025-04-05 15:51:00 +01:00
Daniel Martí db3003b9fa use the correct toolchain "go" tool under GOTOOLCHAIN=auto
We call `go list` to collect information about all the packages
to obfuscate and build, which is crucial to be able to perform
obfuscation of names used across packages.

However, when GOTOOLCHAIN causes a toolchain upgrade,
we must ensure that we use the upgraded Go tool;
otherwise we are mixing information from different toolchain versions.

Fixes #934.
2025-03-31 01:34:31 +02:00
Daniel Martí 275737aabd start using go/types.Func.Signature
Guaranteed to return a *types.Signature, so no need to type assert.
2025-02-22 15:05:23 +00:00
Daniel Martí 2adfc43326 bump unsupportedGo to mark Go 1.24 as supported
debugdir.txtar also needed tweaking as runtime/map.go is gone
starting in Go 1.24.

Finally, modinfo.txtar needed tweaking since Go 1.24 started stamping
Go binaries with VCS-derived module versions, so we no longer end up
with empty "(devel)" versions.
2025-02-09 21:41:54 +01:00
Paul Scheduikat 97833204f8 skip all type parameters in recordType
We only did this for Container in the type switch, but not for Struct.
The added test case panics otherwise.
Just like in the previous case, we still don't need to recurse
into type parameters for fieldToStruct to be filled correctly.

Fixes #899
2025-01-19 14:13:55 +00:00
Daniel Martí f5dc4e784a simplify reflectInspector method signatures
A parent paramter was unused, and a cache parameter could be reached
via the receiver.
2025-01-19 02:45:52 +01:00
Daniel Martí 97ae350b0e apply minor cleanups suggested by tools
GARBLE_PARENT_WORK hasn't been used for a while.
A couple of other minor simplifications.
2025-01-18 05:51:03 +01:00
Daniel Martí 83a06019be rely on go/types alias tracking
Allows us to remove EmbeddedAliasFields, recordedObjectString,
and all the logic around using them.

Resolves an issue where a user was running into a panic in
our logic to record embedded aliases.

Note that this means we require Go 1.23.5 or later now,
which also meant some changes to goversion.txtar to keep it green.

Fixes #827.
2025-01-18 05:51:03 +01:00
Daniel Martí 3c742dac65 centralize logic dealing with main package import paths
obfuscatedImportPath already handled ToObfuscate, so the callers
don't have to do that as well. Handle main packages too, whose logic
was sprinkled and repeated throughout the project.
2025-01-12 14:31:59 +01:00
Daniel Martí 066771481b obfuscate package names separately from import paths
Reflection can show package names alone via reflect.Type.String,
and I'm sure they are available in other ways.
We were obfuscating "package p" exactly like the import path "foo.com/p"
which worked OK for the most part, but not for reflection.

This is also a slight improvement to the quality of the obfuscation,
given that package names and import paths will no longer be aligned
when obfuscated.
2025-01-12 14:31:59 +01:00
Daniel Martí 0b69dcd472 use lpkg directly in reflectInspector
There's no need to reach for sharedCache.ListedPackages
when the caller is computePkgCache, which already has the lpkg value.

While here, compare *go/types.Package pointers directly
rather than via their import path strings.
2025-01-12 14:31:59 +01:00
Daniel Martí 76905ba3bc use the latest testscript to drop func() int
And don't fail when requesting help, which is what Go's flag package
has been moving towards.
2024-12-26 15:10:14 +01:00
Jamison Lahman 7678613fd0 only parse stdout from "go env"
I use a wrapper around go that prints some debug to stderr
which causes the unmarshalling below to fail.
2024-12-06 12:11:07 +00:00
Paul Scheduikat 926f3de60d obfuscate all names used in reflection
Go code can retrieve and use field and method names via the `reflect` package.
For that reason, historically we did not obfuscate names of fields and methods
underneath types that we detected as used for reflection, via e.g. `reflect.TypeOf`.

However, that caused a number of issues. Since we obfuscate and build one package
at a time, we could only detect when types were used for reflection in their own package
or in upstream packages. Use of reflection in downstream packages would be detected
too late, causing one package to obfuscate the names and the other not to, leading to a build failure.

A different approach is implemented here. All names are obfuscated now, but we collect
those types used for reflection, and at the end of a build in `package main`,
we inject a function into the runtime's `internal/abi` package to reverse the obfuscation
for those names which can be used for reflection.

This does mean that the obfuscation for these names is very weak, as the binary
contains a one-to-one mapping to their original names, but they cannot be obfuscated
without breaking too many Go packages out in the wild. There is also some amount
of overhead in `internal/abi` due to this, but we aim to make the overhead insignificant.

Fixes #884, #799, #817, #881, #858, #843, #842

Closes #406
2024-11-27 22:38:43 +01:00
Daniel Martí 515358b18d remove two unused fields from sharedCache
ExecPath is only used within toolexecCmd, so make it a local variable.

GOMOD hasn't been used since we dropped the use of GOPRIVATE.
2024-11-24 16:27:01 +01:00
Daniel Martí ec5b6df439 support collecting deep cpu and memory profiles from benchmarks
This allows me to collect a full CPU profile, showing us that we clearly
spend too much CPU time in garbage collection.

When collecting a full memory profile, we can see where the allocations
come from now:

    Showing nodes accounting for 5636770, 31.07% of 18141579 total
    Dropped 630 nodes (cum <= 90707)
    Showing top 10 nodes out of 278
          flat  flat%   sum%        cum   cum%
       1692229  9.33%  9.33%    1910679 10.53%  encoding/gob.decStringSlice
       1005596  5.54% 14.87%    1005596  5.54%  golang.org/x/tools/go/ssa.buildReferrers
        458753  2.53% 17.40%     458753  2.53%  go/scanner.(*Scanner).scanIdentifier
        458752  2.53% 19.93%     458752  2.53%  reflect.(*structType).Field
        425984  2.35% 22.28%     448136  2.47%  go/parser.(*parser).parseIdent
        390049  2.15% 24.43%     390049  2.15%  golang.org/x/tools/go/ssa.(*BasicBlock).emit
        327683  1.81% 26.23%     371373  2.05%  golang.org/x/tools/go/ssa.NewConst
        311296  1.72% 27.95%    1024551  5.65%  mvdan.cc/garble.(*transformer).transformGoFile.func1
        287891  1.59% 29.54%     287891  1.59%  encoding/gob.decString
        278537  1.54% 31.07%     657043  3.62%  golang.org/x/tools/go/ssa.(*builder).compLit

This method can work for any invocation of garble,
but for now we only directly wire it up for `go test -bench`.
It can still be used for regular invocations of `garble build`.
2024-11-24 16:27:01 +01:00
Daniel Martí 4963af3311 all: drop x/exp in favor of std
x/exp/rand was being used for no apparent reason; use math/rand.

x/exp/maps and x/exp/slices can be replaced with maps and slices
respectively now that we require Go 1.23 or later.
Note that the APIs are slightly different due to iterators.
2024-11-19 08:03:21 +01:00
Daniel Martí 30357af923 drop Go 1.22 and require Go 1.23.0 or later (#876)
This lets us start taking advantage of featurs from Go 1.23,
particularly tracking aliases in go/types and iterators.

Note that we need to add code to properly handle or skip over the new
*types.Alias type which go/types produces for Go type aliases.
Also note that we actually turn this mode off entirely for now,
due to the bug reported at https://go.dev/issue/70394.

We don't yet remove our own alias tracking code yet due to the above.
We hope to be able to remove it very soon.
2024-11-17 16:06:57 +01:00
NHAS 69d7b84f35 Fix reflection detection for linknamed methods (#883) 2024-11-02 05:46:03 +01:00
Daniel Martí 48dd2263a9 fix printf issues spotted by the latest go vet 2024-09-06 14:30:44 +01:00
Daniel Martí 48fac78ecc set up go/types.Config.Sizes according to GOARCH
Otherwise we miscalculate int sizes, type sizes, alignments, and so on.
Caught by the GOARCH=386 go test on CI, since the os package imports
internal/syscall/unix, which uses arch-dependent padding.

The different padding between our incorrect use of go/types
and the correct typechecking done by the compiler caused different
obfuscation of fields, as the struct types stringified differently,
and they are used as a hash salt for field name obfuscation.
2024-09-04 21:37:06 +01:00
Daniel Martí 04df5ea4df add linker patches for Go 1.23
Rebasing the Go 1.22 patches on top of Go 1.23.0,
as published on https://github.com/burrowers/go-patches/pull/7.

Updates #859.
2024-09-04 21:37:06 +01:00
Daniel Martí b49a13c556 code generate a single compiler intrinsics table
This simplifies the code generator and global variables a bit.
2024-08-27 11:07:19 +01:00
Daniel Martí f09db67c89 use types.Info.PkgNameOf
It accomplishes the same Implicits/Defs logic we were doing here.
2024-02-18 17:23:13 +03:00
Daniel Martí 9a2ef369b2 fail early if we know we lack Go linker patches
Right now, we only have linker patches for Go 1.22.x.
We only ever maintain those for one or two major Go versions at a time.

If a user tries to use the Go toolchain from 1.21, we already fail
with "Go version too old" messages early on, but we don't for 1.23,
causing a relatively confusing error later on when we link a binary:

    cannot get modified linker: cannot retrieve linker patches: open patches/go1.23: file does not exist

Instead, fail early and with a good error message.
2024-02-18 17:23:13 +03:00
Daniel Martí d138afaf32 don't panic when we can error as easily
Panicking in small helpers or in funcs that don't return error
has proved useful to keep code easier to maintain,
particularly for cases that should typically never happen.

However, in these cases we can error just as easily.
In particular, I was getting a panic whenever I forgot
that I was running garble with Go master (1.23), which is over the top.
2024-02-18 17:23:13 +03:00
Daniel Martí 69bc62c56c start using some Go 1.22 features
We no longer need to worry about the scope of range variables,
we can iterate over integers directly, and we can use cmp.Or too.

I haven't paid close attention to using these everywhere.
This is mainly testing out the new features where I saw some benefit.
2024-02-12 14:07:57 +03:00
Daniel Martí ad2ecc7f2f drop Go 1.21 and start using go/version
Needing to awkwardly treat Go versions as if they were semver
is no longer necessary thanks to go/version being in Go 1.22.0 now.
2024-02-12 14:07:57 +03:00