mirror of
https://github.com/burrowers/garble.git
synced 2025-10-06 08:16:53 +08:00

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.
278 lines
5.3 KiB
Plaintext
278 lines
5.3 KiB
Plaintext
exec garble build
|
|
exec ./main$exe
|
|
cmp stderr main.stderr
|
|
|
|
! binsubstr main$exe 'localName' 'globalConst' 'globalVar' 'globalType' 'valuable information' 'private.source' 'remoteIntReturn' 'intReturn' 'neverInlined'
|
|
|
|
[short] stop # no need to verify this with -short
|
|
|
|
# Check that the program works as expected without garble.
|
|
go build
|
|
exec ./main$exe
|
|
cmp stderr main.stderr
|
|
|
|
binsubstr main$exe 'globalVar' # 'globalType' matches on some, but not all, platforms
|
|
! binsubstr main$exe 'localName' 'globalConst' 'remoteIntReturn' 'intReturn'
|
|
-- extra/go.mod --
|
|
module private.source/extra
|
|
|
|
go 1.23
|
|
-- extra/extra.go --
|
|
package extra
|
|
|
|
func Func() string {
|
|
return "This is a separate module to obfuscate."
|
|
}
|
|
-- go.mod --
|
|
module test/main
|
|
|
|
go 1.23
|
|
|
|
// We include an extra module to obfuscate, included in the same original source
|
|
// code via a replace directive.
|
|
require private.source/extra v0.0.0-00010101000000-000000000000
|
|
|
|
replace private.source/extra => ./extra
|
|
-- main.go --
|
|
package main
|
|
|
|
import (
|
|
"go/ast"
|
|
"runtime"
|
|
|
|
"private.source/extra"
|
|
"test/main/sub"
|
|
)
|
|
|
|
// This comment contains valuable information. Ensure it's not in the final binary.
|
|
var V any
|
|
|
|
type T struct {
|
|
ast.Node
|
|
*ast.Ident
|
|
}
|
|
|
|
type Embedded int
|
|
|
|
type Embedding struct {
|
|
Embedded
|
|
}
|
|
|
|
type embedded int
|
|
|
|
type embedding struct {
|
|
embedded
|
|
}
|
|
|
|
// embedded fields whose type is in the universe scope used to crash garble
|
|
type EmbeddingUniverseScope struct {
|
|
error
|
|
int
|
|
string
|
|
}
|
|
|
|
// TODO: test that go:noinline still works without using debugdir
|
|
|
|
func ensureInlined(wantInlined bool) {
|
|
pc := make([]uintptr, 1)
|
|
// We skip two caller frames; runtime.Callers, and ensureInlined.
|
|
// This way, the frame we get is our caller, like neverInlined.
|
|
n := runtime.Callers(2, pc)
|
|
if n == 0 {
|
|
panic("got zero callers?")
|
|
}
|
|
pc = pc[:n]
|
|
|
|
frames := runtime.CallersFrames(pc)
|
|
|
|
frame, _ := frames.Next()
|
|
gotInlined := frame.Func == nil
|
|
if wantInlined && !gotInlined {
|
|
panic("caller should be inlined but wasn't")
|
|
} else if !wantInlined && gotInlined {
|
|
panic("caller shouldn't be inlined but was")
|
|
}
|
|
}
|
|
|
|
//go:noinline
|
|
func neverInlined() {
|
|
ensureInlined(false)
|
|
println("This func is never inlined.")
|
|
}
|
|
|
|
func alwaysInlined() {
|
|
ensureInlined(true)
|
|
println("This func is always inlined.")
|
|
}
|
|
|
|
type EmbeddingOuter struct {
|
|
EmbeddingInner
|
|
}
|
|
|
|
type EmbeddingInner struct {
|
|
SomeField int
|
|
}
|
|
|
|
func main() {
|
|
switch V := V.(type) {
|
|
case int:
|
|
var _ int = V
|
|
case nil:
|
|
println("nil case")
|
|
}
|
|
|
|
scopesTest()
|
|
println(extra.Func())
|
|
sub.Test()
|
|
neverInlined()
|
|
alwaysInlined()
|
|
|
|
_ = sub.EmbeddingExternalForeignAlias{
|
|
ExternalForeignAlias: nil,
|
|
Reader: nil,
|
|
}
|
|
|
|
var emb sub.EmbeddingAlias
|
|
_ = emb.EmbeddedAlias
|
|
_ = emb.Foo
|
|
_ = emb.EmbeddedAliasSameName
|
|
_ = emb.Bar
|
|
}
|
|
|
|
-- scopes.go --
|
|
package main
|
|
|
|
const globalConst = 1
|
|
|
|
type globalType int
|
|
|
|
var (
|
|
globalVar = 1
|
|
globalVarTyped globalType = 1
|
|
)
|
|
|
|
func scopesTest() {
|
|
println(globalVar, globalConst, globalVarTyped)
|
|
const localNameConst = 1
|
|
|
|
localNameShort := 4
|
|
|
|
type localNameType int
|
|
|
|
var (
|
|
localNameVar = 5
|
|
localNameTypeVar localNameType = 1
|
|
)
|
|
|
|
println(localNameConst, localNameShort, localNameVar, localNameTypeVar, input("input"))
|
|
}
|
|
|
|
func input(localNameParam string) (localNameReturn string) { return localNameParam }
|
|
|
|
-- sub/names.go --
|
|
package sub
|
|
|
|
import (
|
|
"io"
|
|
|
|
"test/main/external"
|
|
)
|
|
|
|
var someGlobalVar0 = "0"
|
|
var someGlobalVar1 = "1"
|
|
var someGlobalVar2 = "2"
|
|
|
|
func Test() {
|
|
var A, B, C, D, E string
|
|
noop(A, B, C, D, E)
|
|
if someGlobalVar0 != "0" || someGlobalVar1 != "1" || someGlobalVar2 != "2"{
|
|
panic("name collision detected")
|
|
}
|
|
}
|
|
|
|
func noop(...any) {}
|
|
|
|
// Funcs that almost look like test funcs used to make garble panic.
|
|
|
|
func TestFoo(s string) {}
|
|
|
|
func TestBar(*struct{}) {}
|
|
|
|
// If we obfuscate the alias name, we must obfuscate its use here too.
|
|
type EmbeddingAlias struct {
|
|
EmbeddedAlias
|
|
EmbeddedAliasSameName
|
|
}
|
|
|
|
type EmbeddedAlias = external.NamedExternal
|
|
|
|
type EmbeddedAliasSameName = external.EmbeddedAliasSameName
|
|
|
|
// We obfuscate the name foreignAlias, but not the name Reader,
|
|
// as it's not declared in a private package.
|
|
// Both names must do the same when used as struct fields,
|
|
// both in the struct declaration and in the composite literal below.
|
|
type embeddingForeignAlias struct {
|
|
foreignAlias
|
|
io.Reader
|
|
}
|
|
|
|
type foreignAlias = io.Reader
|
|
|
|
var _ = embeddingForeignAlias{
|
|
foreignAlias: nil,
|
|
Reader: nil,
|
|
}
|
|
|
|
// Similar to embeddingForeignAlias,
|
|
// but the alias is declared in a dependency,
|
|
// and this type is used in a dependent.
|
|
type EmbeddingExternalForeignAlias struct {
|
|
external.ExternalForeignAlias
|
|
io.Reader
|
|
}
|
|
|
|
// Like the cases above,
|
|
// but this time the alias doesn't rename its destination named type.
|
|
// We can't tell this apart from "struct { io.Reader }" at the type info level.
|
|
// It's fine to ignore the alias entirely, in this case.
|
|
type embeddingAliasSameName struct {
|
|
external.Reader
|
|
}
|
|
|
|
var _ = embeddingAliasSameName{
|
|
Reader: nil,
|
|
}
|
|
|
|
type embeddingBuiltinAlias struct {
|
|
byte
|
|
}
|
|
|
|
var _ = embeddingBuiltinAlias{3}
|
|
var _ = embeddingBuiltinAlias{byte: 3}
|
|
|
|
-- external/external.go --
|
|
package external
|
|
|
|
import "io"
|
|
|
|
type NamedExternal struct {
|
|
Foo int
|
|
}
|
|
|
|
type EmbeddedAliasSameName struct {
|
|
Bar int
|
|
}
|
|
|
|
type ExternalForeignAlias = io.Reader
|
|
|
|
type Reader = io.Reader
|
|
|
|
-- main.stderr --
|
|
nil case
|
|
1 1 1
|
|
1 4 5 1 input
|
|
This is a separate module to obfuscate.
|
|
This func is never inlined.
|
|
This func is always inlined.
|