|
|
|
# Check that the simplest use of garble works. Note the lack of a module or GOPRIVATE.
|
|
|
|
garble build main.go
|
|
|
|
exec ./main
|
|
|
|
cmp stderr main.stderr
|
|
|
|
|
|
|
|
# Ensure that -w and -s worked.
|
|
|
|
[!windows] [exec:readelf] exec readelf --section-headers main$exe
|
|
|
|
[!windows] [exec:readelf] ! stdout 'debug_info'
|
|
|
|
[!windows] [exec:readelf] ! stdout '\.symtab'
|
|
|
|
|
|
|
|
# The buildid needs to be missing from the binary. Otherwise, we leak
|
|
|
|
# information unnecessarily, which is made worse by how we use part of said
|
|
|
|
# buildid to obfuscate the main package.
|
|
|
|
[!windows] [exec:readelf] ! stdout 'buildid'
|
|
|
|
go tool buildid main$exe
|
|
|
|
! stdout .
|
|
|
|
|
|
|
|
# The build version needs to be missing too.
|
|
|
|
go version main$exe
|
|
|
|
stdout 'unknown'
|
|
|
|
! stdout 'go1'
|
|
|
|
! stdout 'devel'
|
|
|
|
! stdout $gofullversion
|
|
|
|
|
|
|
|
# The binary can't contain the version string either.
|
obfuscate unexported names like exported ones (#227)
In 90fa325da7, the obfuscation logic was changed to use hashes for
exported names, but incremental names starting at just one letter for
unexported names. Presumably, this was done for the sake of binary size.
I argue that this is not a good idea for the default mode for a number
of reasons:
1) It makes reversing of stack traces nearly impossible for unexported
names, since replacing an obfuscated name "c" with "originalName"
would trigger too many false positives by matching single characters.
2) Exported and unexported names aren't different. We need to know how
names were obfuscated at a later time in both cases, thanks to use
cases like -ldflags=-X. Using short names for one but not the other
doesn't make a lot of sense, and makes the logic inconsistent.
3) Shaving off three bytes for unexported names doesn't seem like a huge
deal for the default mode, when we already have -tiny to optimize for
size.
This saves us a bit of work, but most importantly, simplifies the
obfuscation state as we no longer need to carry privateNameMap between
the compile and link stages.
name old time/op new time/op delta
Build-8 153ms ± 2% 150ms ± 2% ~ (p=0.065 n=6+6)
name old bin-B new bin-B delta
Build-8 7.09M ± 0% 7.08M ± 0% -0.24% (p=0.002 n=6+6)
name old sys-time/op new sys-time/op delta
Build-8 296ms ± 5% 277ms ± 6% -6.50% (p=0.026 n=6+6)
name old user-time/op new user-time/op delta
Build-8 562ms ± 1% 558ms ± 3% ~ (p=0.329 n=5+6)
Note that I do not oppose using short names for both exported and
unexported names in the future for -tiny, since reversing of stack
traces will by design not work there. The code can be resurrected from
the git history if we want to improve -tiny that way in the future, as
we'd need to store state in header files again.
Another major cleanup we can do here is to no longer use the
garbledImports map. From a look at obfuscateImports, we hash a package's
import path with its action ID, much like exported names, so we can
simply re-do that hashing for the linker's -X flag.
garbledImports does have some logic to handle duplicate package names,
but it's worth noting that should not affect package paths, as they are
always unique. That area of code could probably do with some
simplification in the future, too.
While at it, make hashWith panic if either parameter is empty.
obfuscateImports was hashing the main package path without a salt due to
a bug, so we want to catch those in the future.
Finally, make some tiny spacing and typo tweaks to the README.
4 years ago
|
|
|
! binsubstr main$exe ${WORK@R} 'main.go' 'globalVar' 'globalFunc' 'garble' $gofullversion
|
|
|
|
|
|
|
|
[short] stop # checking that the build is reproducible is slow
|
|
|
|
|
make -coverprofile include toolexec processes (#216)
testscript already included magic to also account for commands in the
total code coverage. That does not happen with plain tests, since those
only include coverage from the main test process.
The main problem was that, before, indirectly executed commands did not
properly save their coverage profile anywhere for testscript to collect
it at the end. In other words, we only collected coverage from direct
garble executions like "garble -help", but not indirect ones like "go
build -toolexec=garble".
$ go test -coverprofile=cover.out
PASS
coverage: 3.6% of statements
total coverage: 16.6% of statements
ok mvdan.cc/garble 6.453s
After the delicate changes to testscript, any direct or indirect
executions of commands all go through $PATH and properly count towards
the total coverage:
$ go test -coverprofile=cover.out
PASS
coverage: 3.6% of statements
total coverage: 90.5% of statements
ok mvdan.cc/garble 33.258s
Note that we can also get rid of our code to set up $PATH, since
testscript now does it for us.
goversion.txt needed minor tweaks, since we no longer set up $WORK/.bin.
Finally, note that we disable the reuse of $GOCACHE when collecting
coverage information. This is to do "full builds", as otherwise the
cached package builds would result in lower coverage.
Fixes #35.
4 years ago
|
|
|
# Check that we fail if the user used "go build -toolexec garble" instead of "garble build"
|
|
|
|
! exec go build -a -toolexec=garble main.go
|
|
|
|
stderr 'not running "garble \[command\]"'
|
|
|
|
|
|
|
|
# Also check that the binary is reproducible.
|
initial support for build caching (#142)
As per the discussion in https://github.com/golang/go/issues/41145, it
turns out that we don't need special support for build caching in
-toolexec. We can simply modify the behavior of "[...]/compile -V=full"
and "[...]/link -V=full" so that they include garble's own version and
options in the printed build ID.
The part of the build ID that matters is the last, since it's the
"content ID" which is used to work out whether there is a need to redo
the action (build) or not. Since cmd/go parses the last word in the
output as "buildID=...", we simply add "+garble buildID=_/_/_/${hash}".
The slashes let us imitate a full binary build ID, but we assume that
the other components such as the action ID are not necessary, since the
only reader here is cmd/go and it only consumes the content ID.
The reported content ID includes the tool's original content ID,
garble's own content ID from the built binary, and the garble options
which modify how we obfuscate code. If any of the three changes, we
should use a different build cache key. GOPRIVATE also affects caching,
since a different GOPRIVATE value means that we might have to garble a
different set of packages.
Include tests, which mainly check that 'garble build -v' prints package
lines when we expect to always need to rebuild packages, and that it
prints nothing when we should be reusing the build cache even when the
built binary is missing.
After this change, 'go test' on Go 1.15.2 stabilizes at about 8s on my
machine, whereas it used to be at around 25s before.
5 years ago
|
|
|
# No packages should be rebuilt either, thanks to the build cache.
|
|
|
|
cp main$exe main_old$exe
|
|
|
|
rm main$exe
|
initial support for build caching (#142)
As per the discussion in https://github.com/golang/go/issues/41145, it
turns out that we don't need special support for build caching in
-toolexec. We can simply modify the behavior of "[...]/compile -V=full"
and "[...]/link -V=full" so that they include garble's own version and
options in the printed build ID.
The part of the build ID that matters is the last, since it's the
"content ID" which is used to work out whether there is a need to redo
the action (build) or not. Since cmd/go parses the last word in the
output as "buildID=...", we simply add "+garble buildID=_/_/_/${hash}".
The slashes let us imitate a full binary build ID, but we assume that
the other components such as the action ID are not necessary, since the
only reader here is cmd/go and it only consumes the content ID.
The reported content ID includes the tool's original content ID,
garble's own content ID from the built binary, and the garble options
which modify how we obfuscate code. If any of the three changes, we
should use a different build cache key. GOPRIVATE also affects caching,
since a different GOPRIVATE value means that we might have to garble a
different set of packages.
Include tests, which mainly check that 'garble build -v' prints package
lines when we expect to always need to rebuild packages, and that it
prints nothing when we should be reusing the build cache even when the
built binary is missing.
After this change, 'go test' on Go 1.15.2 stabilizes at about 8s on my
machine, whereas it used to be at around 25s before.
5 years ago
|
|
|
garble build -v main.go
|
|
|
|
! stderr .
|
|
|
|
bincmp main$exe main_old$exe
|
|
|
|
|
|
|
|
# Check that the program works as expected without garble. No need to verify
|
|
|
|
# this when we run with -short.
|
|
|
|
exec go build main.go
|
|
|
|
exec ./main
|
|
|
|
cmp stderr main.stderr
|
|
|
|
|
|
|
|
# The default build includes DWARF and the symbol table.
|
|
|
|
[!windows] [exec:readelf] exec readelf --section-headers main$exe
|
|
|
|
[!windows] [exec:readelf] stdout 'debug_info'
|
|
|
|
[!windows] [exec:readelf] stdout '\.symtab'
|
|
|
|
|
|
|
|
# The default build includes full non-trimmed paths, as well as our names.
|
|
|
|
# Only check $WORK on non-windows, because it's difficult to do it there.
|
|
|
|
binsubstr main$exe 'main.go' 'globalVar' 'globalFunc' $gofullversion
|
|
|
|
[!windows] binsubstr main$exe ${WORK@R}
|
|
|
|
|
initial support for build caching (#142)
As per the discussion in https://github.com/golang/go/issues/41145, it
turns out that we don't need special support for build caching in
-toolexec. We can simply modify the behavior of "[...]/compile -V=full"
and "[...]/link -V=full" so that they include garble's own version and
options in the printed build ID.
The part of the build ID that matters is the last, since it's the
"content ID" which is used to work out whether there is a need to redo
the action (build) or not. Since cmd/go parses the last word in the
output as "buildID=...", we simply add "+garble buildID=_/_/_/${hash}".
The slashes let us imitate a full binary build ID, but we assume that
the other components such as the action ID are not necessary, since the
only reader here is cmd/go and it only consumes the content ID.
The reported content ID includes the tool's original content ID,
garble's own content ID from the built binary, and the garble options
which modify how we obfuscate code. If any of the three changes, we
should use a different build cache key. GOPRIVATE also affects caching,
since a different GOPRIVATE value means that we might have to garble a
different set of packages.
Include tests, which mainly check that 'garble build -v' prints package
lines when we expect to always need to rebuild packages, and that it
prints nothing when we should be reusing the build cache even when the
built binary is missing.
After this change, 'go test' on Go 1.15.2 stabilizes at about 8s on my
machine, whereas it used to be at around 25s before.
5 years ago
|
|
|
-- go.mod --
|
|
|
|
module test/mainfoo
|
|
|
|
|
|
|
|
go 1.15
|
|
|
|
-- main.go --
|
|
|
|
package main
|
|
|
|
|
|
|
|
var globalVar = "global value"
|
|
|
|
|
|
|
|
func globalFunc() { println("global func body") }
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
println(globalVar)
|
|
|
|
globalFunc()
|
|
|
|
}
|
|
|
|
-- main.stderr --
|
|
|
|
global value
|
|
|
|
global func body
|