wrap types.Importer to canonicalize import paths

The docs for go/importer.ForCompiler say:

	The lookup function is called each time the resulting importer
	needs to resolve an import path. In this mode the importer can
	only be invoked with canonical import paths (not relative or
	absolute ones); it is assumed that the translation to canonical
	import paths is being done by the client of the importer.

We use a lookup func for two reasons: first, to support modules, and
second, to be able to use our information from "go list -json -export".

However, go/types does not canonicalize import paths before calling
ImportFrom. This is somewhat understandable; it doesn't know whether an
importer was created with a lookup func, and ImportFrom only requires
the input path to be canonicalized in that scenario. When the lookup
func is nil, the importer canonicalizes by itself via go/build.Import.

Before this change, the added crossbuild test would fail:

	> garble build net/http
	[stderr]
	# vendor/golang.org/x/crypto/chacha20
	typecheck error: /usr/lib/go/src/vendor/golang.org/x/crypto/chacha20/chacha_generic.go:10:2: could not import crypto/cipher (can't find import: "crypto/cipher")
	# vendor/golang.org/x/text/secure/bidirule
	typecheck error: /usr/lib/go/src/vendor/golang.org/x/text/secure/bidirule/bidirule.go:12:2: could not import errors (can't find import: "errors")
	# vendor/golang.org/x/crypto/cryptobyte
	typecheck error: /usr/lib/go/src/vendor/golang.org/x/crypto/cryptobyte/asn1.go:8:16: could not import encoding/asn1 (can't find import: "encoding/asn1")
	# vendor/golang.org/x/text/unicode/norm
	typecheck error: /usr/lib/go/src/vendor/golang.org/x/text/unicode/norm/composition.go:7:8: could not import unicode/utf8 (can't find import: "unicode/utf8")

This is because we'd fall back to importer.Default, which only knows how
to find packages in $GOROOT/pkg. Those are missing for cross-builds,
unsurprisingly, as those built archives end up in the build cache.

After this change, we properly support importing std-vendored packages,
so we can get rid of the importer.Default workaround. And, by extension,
cross-builds now work as well.

Note that, in the added test script, the full build of the binary fails,
as there seems to be some sort of linker problem:

	> garble build
	[stderr]
	# test/main
	d9rqJyxo.uoqIiDs5: relocation target runtime.os9A16A3 not defined

We leave that as a TODO for now, as this change is subtle enough as it
is.
pull/317/head
Daniel Martí 3 years ago committed by lu4p
parent 1a9fdb4e8e
commit 2fad0e1583

@ -97,13 +97,13 @@ var (
// origImporter is a go/types importer which uses the original versions
// of packages, without any obfuscation. This is helpful to make
// decisions on how to obfuscate our input code.
origImporter = importer.ForCompiler(fset, "gc", func(path string) (io.ReadCloser, error) {
origImporter = importerWithMap(importer.ForCompiler(fset, "gc", func(path string) (io.ReadCloser, error) {
pkg, err := listPackage(path)
if err != nil {
return nil, err
}
return os.Open(pkg.Export)
})
}).(types.ImporterFrom).ImportFrom)
// Basic information about the package being currently compiled or linked.
curPkg *listedPackage
@ -119,6 +119,19 @@ var (
opts *flagOptions
)
type importerWithMap func(path, dir string, mode types.ImportMode) (*types.Package, error)
func (fn importerWithMap) Import(path string) (*types.Package, error) {
panic("should never be called")
}
func (fn importerWithMap) ImportFrom(path, dir string, mode types.ImportMode) (*types.Package, error) {
if path2 := curPkg.ImportMap[path]; path2 != "" {
path = path2
}
return fn(path, dir, mode)
}
func obfuscatedTypesPackage(path string) *types.Package {
entry, ok := importCfgEntries[path]
if !ok {
@ -585,20 +598,6 @@ func transformCompile(args []string) ([]string, error) {
},
}
// The standard library vendors external packages, which results in them
// listing "golang.org/x/foo" in go list -json's Deps, plus an ImportMap
// entry to remap them to "vendor/golang.org/x/foo".
// We support that edge case in listPackage, presumably, though it seems
// like importer.ForCompiler with a lookup function isn't capable of it.
// It does work without an explicit lookup func though, which results in
// extra calls to 'go list'.
// Since this is a rare edge case and only occurs for a few std
// packages, do the extra 'go list' calls for now.
// TODO(mvdan): remove once https://github.com/golang/go/issues/44630 is fixed
if curPkg.Standard && len(curPkg.ImportMap) > 0 {
origImporter = importer.Default()
}
origTypesConfig := types.Config{Importer: origImporter}
tf.pkg, err = origTypesConfig.Check(curPkg.ImportPath, fset, files, tf.info)
if err != nil {

@ -0,0 +1,26 @@
# TODO: always cross-build, even on a windows/arm host
env GOOS=windows
env GOARCH=arm
env GOPRIVATE='*'
# TODO: seems like the linker fails for some reason with the full binary build below.
# For now, just test that we can build the library.
garble build net/http
# Link a binary importing net/http, which will catch whether or not we
# support ImportMap when linking.
# garble build
-- go.mod --
module test/main
go 1.16
-- main.go --
package main
import "net/http"
func main() {
http.ListenAndServe("", nil)
}
Loading…
Cancel
Save