diff --git a/main.go b/main.go index 8aec03c..1f15808 100644 --- a/main.go +++ b/main.go @@ -112,8 +112,10 @@ func garbledImport(path string) (*types.Package, error) { } type packageInfo struct { - buildID string - imports map[string]importedPkg + buildID string // from -buildid + + imports map[string]importedPkg // from -importcfg + firstImport string // first from -importcfg; the main package when linking } type importedPkg struct { @@ -307,6 +309,7 @@ func transformCompile(args []string) ([]string, error) { deferred = append(deferred, func() error { return os.RemoveAll(tempDir) }) + // Add our temporary dir to the beginning of -trimpath, so that we don't // leak temporary dirs. Needs to be at the beginning, since there may be // shorter prefixes later in the list, such as $PWD if TMPDIR=$PWD/tmp. @@ -352,8 +355,10 @@ func transformCompile(args []string) ([]string, error) { // To allow using garble without GOPRIVATE for standalone main packages, it will // default to not matching standard library packages. func isPrivate(pkgPath string) bool { - if pkgPath == "main" { - // TODO: why don't we see the full package path for main packages? + if pkgPath == "main" || strings.HasPrefix(pkgPath, "plugin/unnamed") { + // TODO: why don't we see the full package path for main + // packages? The linker has it at the top of -importcfg, but not + // the compiler. return true } return GlobsMatchPath(envGoPrivate, pkgPath) @@ -396,6 +401,9 @@ func readBuildIDs(flags []string) error { if err != nil { return err } + if len(buildInfo.imports) == 0 { + buildInfo.firstImport = importPath + } buildInfo.imports[importPath] = importedPkg{ packagefile: objectPath, buildID: fileID, @@ -639,6 +647,39 @@ func transformLink(args []string) ([]string, error) { // Nothing to transform; probably just ["-V=full"]. return args, nil } + + // Make sure -X works with garbled identifiers. To cover both garbled + // and non-garbled names, duplicate each flag with a garbled version. + if err := readBuildIDs(flags); err != nil { + return nil, err + } + flagValueIter(flags, "-X", func(val string) { + // val is in the form of "pkg.name=str" + i := strings.IndexByte(val, '=') + if i <= 0 { + return + } + name := val[:i] + str := val[i+1:] + j := strings.IndexByte(name, '.') + if j <= 0 { + return + } + pkg := name[:j] + name = name[j+1:] + + pkgPath := pkg + if pkgPath == "main" { + // The main package is known under its import path in + // the import config map. + pkgPath = buildInfo.firstImport + } + if id := buildInfo.imports[pkgPath].buildID; id != "" { + name = hashWith(id, name) + flags = append(flags, fmt.Sprintf("-X=%s.%s=%s", pkg, name, str)) + } + }) + flags = append(flags, "-w", "-s") return append(flags, paths...), nil } @@ -657,21 +698,32 @@ func splitFlagsFromFiles(args []string, ext string) (flags, paths []string) { } // flagValue retrieves the value of a flag such as "-foo", from strings in the -// list of arguments like "-foo=bar" or "-foo" "bar". +// list of arguments like "-foo=bar" or "-foo" "bar". If the flag is repeated, +// the last value is returned. func flagValue(flags []string, name string) string { + lastVal := "" + flagValueIter(flags, name, func(val string) { + lastVal = val + }) + return lastVal +} + +// flagValueIter retrieves all the values for a flag such as "-foo", like +// flagValue. The difference is that it allows handling complex flags, such as +// those whose values compose a list. +func flagValueIter(flags []string, name string, fn func(string)) { for i, arg := range flags { if val := strings.TrimPrefix(arg, name+"="); val != arg { // -name=value - return val + fn(val) } if arg == name { // -name ... if i+1 < len(flags) { // -name value - return flags[i+1] + fn(flags[i+1]) } } } - return "" } func flagSetValue(flags []string, name, value string) []string { diff --git a/testdata/scripts/asm.txt b/testdata/scripts/asm.txt index 65ee650..05b842b 100644 --- a/testdata/scripts/asm.txt +++ b/testdata/scripts/asm.txt @@ -1,13 +1,13 @@ garble build exec ./main -cmp stdout main.stdout +cmp stderr main.stderr binsubstr main$exe 'privateAdd' 'PublicAdd' [short] stop # no need to verify this with -short go build exec ./main -cmp stdout main.stdout +cmp stderr main.stderr -- go.mod -- module test/main @@ -15,16 +15,14 @@ module test/main package main import ( - "fmt" - "test/main/imported" ) func privateAdd(x, y int64) int64 func main() { - fmt.Println(privateAdd(1, 2)) - fmt.Println(imported.PublicAdd(3, 4)) + println(privateAdd(1, 2)) + println(imported.PublicAdd(3, 4)) } -- main.s -- TEXT ·privateAdd(SB),$0-24 @@ -44,6 +42,6 @@ TEXT ·PublicAdd(SB),$0-24 ADDQ BP, BX MOVQ BX, ret+16(FP) RET --- main.stdout -- +-- main.stderr -- 3 7 diff --git a/testdata/scripts/cgo.txt b/testdata/scripts/cgo.txt index 2d41ea2..370a410 100644 --- a/testdata/scripts/cgo.txt +++ b/testdata/scripts/cgo.txt @@ -1,13 +1,13 @@ garble build exec ./main -cmp stdout main.stdout +cmp stderr main.stderr binsubstr main$exe 'privateAdd' [short] stop # no need to verify this with -short go build exec ./main -cmp stdout main.stdout +cmp stderr main.stderr -- go.mod -- module test/main @@ -21,10 +21,8 @@ static int privateAdd(int a, int b) { */ import "C" -import "fmt" - func main() { - fmt.Println(C.privateAdd(C.int(1), C.int(2))) + println(C.privateAdd(C.int(1), C.int(2))) } --- main.stdout -- +-- main.stderr -- 3 diff --git a/testdata/scripts/ldflags.txt b/testdata/scripts/ldflags.txt new file mode 100644 index 0000000..170b3be --- /dev/null +++ b/testdata/scripts/ldflags.txt @@ -0,0 +1,34 @@ +garble build -ldflags='-X=main.unexportedVersion=v1.0.0 -X=test/main/imported.ExportedVar=replaced' +exec ./main +cmp stderr main.stderr +! binsubstr main$exe 'unexportedVersion' + +[short] stop # no need to verify this with -short + +exec go build -ldflags='-X=main.unexportedVersion=v1.0.0 -X=test/main/imported.ExportedVar=replaced' +exec ./main +cmp stderr main.stderr +binsubstr main$exe 'unexportedVersion' + +-- go.mod -- +module test/main +-- main.go -- +package main + +import ( + "test/main/imported" +) + +var unexportedVersion = "unknown" + +func main() { + println("version:", unexportedVersion) + println("var:", imported.ExportedVar) +} +-- imported/imported.go -- +package imported + +var ExportedVar = "original" +-- main.stderr -- +version: v1.0.0 +var: replaced diff --git a/testdata/scripts/modinfo.txt b/testdata/scripts/modinfo.txt index f99e963..91d58d4 100644 --- a/testdata/scripts/modinfo.txt +++ b/testdata/scripts/modinfo.txt @@ -1,13 +1,13 @@ garble build exec ./main -cmp stdout main.stdout +cmp stderr main.stderr ! binsubstr main$exe '(devel)' [short] stop # no need to verify this with -short exec go build exec ./main -cmp stdout main.stdout-orig +cmp stderr main.stderr-orig binsubstr main$exe '(devel)' -- go.mod -- @@ -15,15 +15,16 @@ module test/main -- main.go -- package main -import ( - "fmt" - "runtime/debug" -) +import "runtime/debug" func main() { - fmt.Println(debug.ReadBuildInfo()) + if info, ok := debug.ReadBuildInfo(); ok { + println("version", info.Main.Version) + } else { + println("no version") + } } --- main.stdout-orig -- -&{test/main {test/main (devel) } []} true --- main.stdout -- - false +-- main.stderr-orig -- +version (devel) +-- main.stderr -- +no version diff --git a/testdata/scripts/plugin.txt b/testdata/scripts/plugin.txt index 9d31966..9271f48 100644 --- a/testdata/scripts/plugin.txt +++ b/testdata/scripts/plugin.txt @@ -7,30 +7,38 @@ binsubstr plugin.so 'PublicVar' 'PublicFunc' # Note that we need -trimpath; see the caveat section in the README. go build -trimpath exec ./main -cmp stdout main.stdout +cmp stderr main.stderr binsubstr main$exe 'PublicVar' 'PublicFunc' ! binsubstr plugin.so 'privateFunc' [short] stop # no need to verify this with -short +# This used to fail, since in this case the package path for the ad-hoc plugin +# package isn't "main", but "plugin/unnamed-*". +garble build -buildmode=plugin plugin/main.go + go build -buildmode=plugin ./plugin binsubstr plugin.so 'PublicVar' 'PublicFunc' 'privateFunc' go build exec ./main -cmp stdout main.stdout +cmp stderr main.stderr -- go.mod -- module test/main -- plugin/main.go -- package main -import "fmt" +import "test/main/plugin/lib" -var PublicVar int +var PublicVar int = lib.ImportedFunc() -func privateFunc(n int) { fmt.Printf("Hello, number %d\n", n) } +func privateFunc(n int) { println("Hello, number", n) } func PublicFunc() { privateFunc(PublicVar) } +-- plugin/lib/lib.go -- +package lib + +func ImportedFunc() int { return 4 } -- main.go -- package main @@ -52,5 +60,5 @@ func main() { *v.(*int) = 7 f.(func())() } --- main.stdout -- +-- main.stderr -- Hello, number 7