diff --git a/README.md b/README.md index f8b9e49..dbe1a7d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ information about the original source code as possible. The tool is designed to be: * Coupled with `cmd/go`, to support both `GOPATH` and modules with ease -* Deterministic, though the output is not yet reproducible +* Deterministic and reproducible, given the same initial source code * Reversible given the original source, to un-garble panic stack traces ### Mechanism diff --git a/main.go b/main.go index 1969df4..5ba8504 100644 --- a/main.go +++ b/main.go @@ -197,10 +197,24 @@ func transformCompile(args []string) ([]string, error) { return nil, fmt.Errorf("typecheck error: %v", err) } + tempDir, err := ioutil.TempDir("", "garble-build") + if err != nil { + return nil, err + } + 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. + flags = flagSetValue(flags, "-trimpath", tempDir+"=>;"+trimpath) + // log.Println(flags) args = flags - for _, file := range files { + // TODO: randomize the order and names of the files + for i, file := range files { file := transformGo(file, info) - f, err := ioutil.TempFile("", "garble") + tempFile := filepath.Join(tempDir, fmt.Sprintf("z%d.go", i)) + f, err := os.Create(tempFile) if err != nil { return nil, err } @@ -212,9 +226,6 @@ func transformCompile(args []string) ([]string, error) { if err := f.Close(); err != nil { return nil, err } - deferred = append(deferred, func() error { - return os.Remove(f.Name()) - }) args = append(args, f.Name()) } return args, nil @@ -397,3 +408,31 @@ func flagValue(flags []string, name string) string { } return "" } + +func flagSetValue(flags []string, name, value string) []string { + for i, arg := range flags { + if strings.HasPrefix(arg, name+"=") { + // -name=value + if value == "true" { + flags[i] = name + } else { + flags[i] = name + "=" + value + } + return flags + } + if arg == name { + if i+1 < len(flags) { + if val := flags[i+1]; !strings.HasPrefix(val, "-") { + flags[i+1] = value + return flags + } + } + // -name, equivalent to -name=true + if value != "true" { + flags[i] = name + "=" + value + } + return flags + } + } + return append(flags, name+"="+value) +} diff --git a/main_test.go b/main_test.go index f199601..a1bc4fb 100644 --- a/main_test.go +++ b/main_test.go @@ -7,6 +7,7 @@ import ( "flag" "fmt" "os" + "os/exec" "path/filepath" "regexp" "runtime" @@ -95,7 +96,14 @@ func bincmp(ts *testscript.TestScript, neg bool, args []string) { data1 := ts.ReadFile(args[0]) data2 := ts.ReadFile(args[1]) if data1 != data2 { + if _, err := exec.LookPath("diffoscope"); err != nil { + ts.Logf("diffoscope is not installing; skipping binary diff") + } else { + // We'll error below; ignore the exec error here. + ts.Exec("diffoscope", ts.MkAbs(args[0]), ts.MkAbs(args[1])) + } sizeDiff := len(data2) - len(data1) - ts.Fatalf("%s and %s differ; size diff: %+d", args[0], args[1], sizeDiff) + ts.Fatalf("%s and %s differ; diffoscope above, size diff: %+d", + args[0], args[1], sizeDiff) } } diff --git a/testdata/scripts/basic.txt b/testdata/scripts/basic.txt index 1907ca1..6a268cf 100644 --- a/testdata/scripts/basic.txt +++ b/testdata/scripts/basic.txt @@ -26,11 +26,13 @@ cmp stderr main.stderr ! bingrep main$exe ${WORK@R} 'globalVar' 'globalFunc' -# Finally, check that the 'garble build' shortcut works. -# cp main main_old +# Finally, check that the 'garble build' shortcut works, and produces the same +# binary. +cp main main_old +rm main garble build main.go ! bingrep main$exe 'globalVar' -# bincmp main main_old +bincmp main main_old -- main.go -- package main diff --git a/testdata/scripts/imports.txt b/testdata/scripts/imports.txt index aaef4c1..d12d645 100644 --- a/testdata/scripts/imports.txt +++ b/testdata/scripts/imports.txt @@ -4,6 +4,12 @@ cmp stdout main.stdout ! bingrep main$exe 'ImportedVar' 'ImportedConst' 'ImportedFunc' 'ImportedType' +# Also check that the binary is reproducible when many imports are involved. +cp main main_old +rm main +garble build +bincmp main main_old + -- go.mod -- module foo.com/main -- main.go --