@ -40,7 +40,6 @@ import (
"golang.org/x/mod/module"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
"golang.org/x/mod/semver"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/ast/astutil"
"mvdan.cc/garble/internal/linker"
"mvdan.cc/garble/internal/linker"
"mvdan.cc/garble/internal/literals"
"mvdan.cc/garble/internal/literals"
)
)
@ -951,7 +950,7 @@ func transformCompile(args []string) ([]string, error) {
if flagTiny {
if flagTiny {
// strip unneeded runtime code
// strip unneeded runtime code
stripRuntime ( basename , file )
stripRuntime ( basename , file )
tf . removeUnnecessaryImports ( file )
tf . makeImportsUsed ( file )
}
}
if basename == "symtab.go" {
if basename == "symtab.go" {
updateMagicValue ( file , magicValue ( ) )
updateMagicValue ( file , magicValue ( ) )
@ -1517,6 +1516,7 @@ func newTransformer() *transformer {
Types : make ( map [ ast . Expr ] types . TypeAndValue ) ,
Types : make ( map [ ast . Expr ] types . TypeAndValue ) ,
Defs : make ( map [ * ast . Ident ] types . Object ) ,
Defs : make ( map [ * ast . Ident ] types . Object ) ,
Uses : make ( map [ * ast . Ident ] types . Object ) ,
Uses : make ( map [ * ast . Ident ] types . Object ) ,
Implicits : make ( map [ ast . Node ] types . Object ) ,
} ,
} ,
recordTypeDone : make ( map [ * types . Named ] bool ) ,
recordTypeDone : make ( map [ * types . Named ] bool ) ,
fieldToStruct : make ( map [ * types . Var ] * types . Struct ) ,
fieldToStruct : make ( map [ * types . Var ] * types . Struct ) ,
@ -1687,48 +1687,127 @@ func recordedAsNotObfuscated(obj types.Object) bool {
return ok
return ok
}
}
func ( tf * transformer ) removeUnnecessaryImports ( file * ast . File ) {
func ( tf * transformer ) resolveImportSpec ( imp * ast . ImportSpec ) * types . Package {
usedImports := make ( map [ string ] bool )
// Simple import has no ast.Ident and is stored in Implicits separately.
ast . Inspect ( file , func ( n ast . Node ) bool {
obj := tf . info . Implicits [ imp ]
node , ok := n . ( * ast . Ident )
if obj == nil {
if ! ok {
obj = tf . info . Defs [ imp . Name ] // renamed or dot import
return true
}
if obj == nil {
return nil
}
}
uses , ok := tf . info . Uses [ node ]
pkgObj , ok := obj . ( * types . PkgName )
if ! ok {
if ! ok {
return true
panic ( fmt . Sprintf ( "unexpected object type for %s: %v" , imp . Path . Value , obj ) )
}
}
return pkgObj . Imported ( )
if pkg := uses . Pkg ( ) ; pkg != nil {
usedImports [ pkg . Path ( ) ] = true
}
}
// isSafeForInstanceType returns true if the passed type is safe for var declaration.
// Unsafe types: generic types and non-method interfaces.
func isSafeForInstanceType ( typ types . Type ) bool {
switch t := typ . ( type ) {
case * types . Named :
if t . TypeParams ( ) . Len ( ) > 0 {
return false
}
return isSafeForInstanceType ( t . Underlying ( ) )
case * types . Signature :
return t . TypeParams ( ) . Len ( ) == 0
case * types . Interface :
return t . IsMethodSet ( )
}
return true
return true
} )
}
func ( tf * transformer ) makeImportsUsed ( file * ast . File ) {
for _ , imp := range file . Imports {
for _ , imp := range file . Imports {
if imp . Name != nil && imp . Name . Name == "_" {
if imp . Name != nil && imp . Name . Name == "_" {
continue
continue
}
}
path , err := strconv . Unquote ( imp . Path . Value )
p kg := tf . resolveImportSpec ( imp )
if err != nil {
if pkg = = nil {
panic ( err )
panic ( fmt. Sprintf ( "import %s not found" , imp . Path . Value ) )
}
}
// The import path can't be used directly here, because the actual
generated := false
// path resolved via go/types might be different from the naive path.
scope := pkg . Scope ( )
lpkg , err := listPackage ( path )
for _ , name := range scope . Names ( ) {
if err != nil {
if ! token . IsExported ( name ) {
panic ( err )
continue
}
}
obj := scope . Lookup ( name )
if usedImports [ lpkg . ImportPath ] {
if obj == nil {
panic ( fmt . Sprintf ( "%s not found in %s" , name , imp . Path . Value ) )
}
if ! isSafeForInstanceType ( obj . Type ( ) ) {
continue
continue
}
}
imp . Name = ast . NewIdent ( "_" )
nameIdent := ast . NewIdent ( name )
getFullName := func ( ) ast . Expr {
if imp . Name == nil {
return & ast . SelectorExpr {
X : ast . NewIdent ( pkg . Name ( ) ) ,
Sel : nameIdent ,
}
}
if imp . Name . Name == "." {
return nameIdent
}
return & ast . SelectorExpr {
X : ast . NewIdent ( imp . Name . Name ) ,
Sel : nameIdent ,
}
}
var decl * ast . GenDecl
switch obj . ( type ) {
case * types . Const :
// const _ = <value>
decl = & ast . GenDecl {
Tok : token . CONST ,
Specs : [ ] ast . Spec { & ast . ValueSpec {
Names : [ ] * ast . Ident { ast . NewIdent ( "_" ) } ,
Values : [ ] ast . Expr { getFullName ( ) } ,
} } ,
}
case * types . Var , * types . Func :
// var _ = <value>
decl = & ast . GenDecl {
Tok : token . VAR ,
Specs : [ ] ast . Spec { & ast . ValueSpec {
Names : [ ] * ast . Ident { ast . NewIdent ( "_" ) } ,
Values : [ ] ast . Expr { getFullName ( ) } ,
} } ,
}
case * types . TypeName :
// var _ <type>
decl = & ast . GenDecl {
Tok : token . VAR ,
Specs : [ ] ast . Spec { & ast . ValueSpec {
Names : [ ] * ast . Ident { ast . NewIdent ( "_" ) } ,
Type : getFullName ( ) ,
} } ,
}
default :
continue // Skip *types.Builtin and other
}
// Manually add a new variable for correct name obfuscation
tf . info . Uses [ nameIdent ] = obj
file . Decls = append ( file . Decls , decl )
generated = true
break
}
if ! generated {
// A very unlikely situation where there is no suitable declaration for a reference variable
// and almost certainly means that there is another import reference in code.
log . Printf ( "generate reference variable for %s failed" , imp . Path . Value )
}
}
}
}
}
@ -1744,7 +1823,7 @@ func (tf *transformer) transformGoFile(file *ast.File) *ast.File {
file = literals . Obfuscate ( obfRand , file , tf . info , tf . linkerVariableStrings )
file = literals . Obfuscate ( obfRand , file , tf . info , tf . linkerVariableStrings )
// some imported constants might not be needed anymore, remove unnecessary imports
// some imported constants might not be needed anymore, remove unnecessary imports
tf . removeUnnecessaryImports ( file )
tf . makeImportsUsed ( file )
}
}
pre := func ( cursor * astutil . Cursor ) bool {
pre := func ( cursor * astutil . Cursor ) bool {