env GOPRIVATE=test/main # Unknown build flags should result in errors. ! garble reverse -badflag stderr 'flag provided but not defined' garble build exec ./main cp stderr main.stderr # Ensure that the garbled panic output looks correct. # This output is not reproducible between 'go test' runs, # so we can't use a static golden file. grep 'goroutine 1 \[running\]' main.stderr # Note that ExportedLibMethod isn't obfuscated. ! grep 'ExportedLib(Type|Field)|unexportedMainFunc|test/main|main\.go|lib\.go' main.stderr stdin main.stderr garble reverse cmp stdout reverse.stdout ! garble build ./build-error cp stderr build-error.stderr stdin build-error.stderr garble reverse ./build-error cmp stdout build-error-reverse.stdout [short] stop # no need to verify this with -short # Ensure that the reversed output matches the non-garbled output. go build -trimpath exec ./main cmp stderr reverse.stdout # Ensure that we can still reverse with -literals. garble -literals build exec ./main cp stderr main-literals.stderr stdin main-literals.stderr garble -literals reverse cmp stdout reverse.stdout # Reversing a -literals output without the flag should fail. stdin main-literals.stderr ! garble reverse cmp stdout main-literals.stderr -- go.mod -- module test/main go 1.16 -- main.go -- package main import ( "os" "runtime" "test/main/lib" ) func main() { unexportedMainFunc() _, filename, _, _ := runtime.Caller(0) println() println("main filename:", filename) } func unexportedMainFunc() { anonFunc := func() { lt := lib.ExportedLibType{} if err := lt.ExportedLibMethod(os.Stderr); err != nil { panic(err) } } anonFunc() } -- lib/lib.go -- package lib import ( "io" "regexp" "runtime" "runtime/debug" ) type ExportedLibType struct{} func (*ExportedLibType) ExportedLibMethod(w io.Writer) error { _, filename, _, _ := runtime.Caller(0) println("lib filename:", filename) println() return printStackTrace(w) } func printStackTrace(w io.Writer) error { // Panic outputs include "0xNN" pointers and offsets which change // between platforms. // Strip them out here, to have portable static stdout files. rxVariableSuffix := regexp.MustCompile(`0x[0-9a-f]+`) // Keep this comment here, because comments affect line numbers. stack := debug.Stack() stack = rxVariableSuffix.ReplaceAll(stack, []byte("0x??")) _, err := w.Write(stack) return err } -- build-error/error.go -- package p import "reflect" // This program is especially crafted to work with "go build", // but fail with "garble build". // This is because we attempt to convert from two different struct types, // since only the anonymous one has its field name obfuscated. // This is useful, because we test that build errors can be reversed, // and it also includes a field name. type UnobfuscatedStruct struct { SomeField int } var _ = reflect.TypeOf(UnobfuscatedStruct{}) var _ = struct{SomeField int}(UnobfuscatedStruct{}) -- reverse.stdout -- lib filename: test/main/lib/lib.go goroutine 1 [running]: runtime/debug.Stack(0x??, 0x??, 0x??) runtime/debug/stack.go:24 +0x?? test/main/lib.printStackTrace(0x??, 0x??, 0x??, 0x??) test/main/lib/lib.go:28 +0x?? test/main/lib.(*ExportedLibType).ExportedLibMethod(0x??, 0x??, 0x??, 0x??, 0x??) test/main/lib/lib.go:17 +0x?? main.unexportedMainFunc.func1(...) test/main/main.go:21 main.unexportedMainFunc() test/main/main.go:25 +0x?? main.main() test/main/main.go:11 +0x?? main filename: test/main/main.go -- build-error-reverse.stdout -- # test/main/build-error test/main/build-error/error.go:18: cannot convert UnobfuscatedStruct{} (type UnobfuscatedStruct) to type struct { SomeField int } exit status 2 exit status 2