99
99
})}
100
100
101
101
buildInfo = struct {
102
- buildID string // from -buildid
102
+ actionID [] byte // from -buildid
103
103
importCfg string // from -importcfg
104
104
105
105
// TODO: replace part of this with goobj.ParseImportCfg, so that
@@ -218,7 +218,7 @@ func garbledImport(path string) (*types.Package, error) {
218
218
219
219
type importedPkg struct {
220
220
packagefile string
221
- buildID string
221
+ actionID [] byte
222
222
223
223
pkg * types.Package
224
224
}
@@ -260,7 +260,7 @@ How to install Go: https://golang.org/doc/install
260
260
return false
261
261
}
262
262
263
- rawVersion := string ( bytes .TrimPrefix (bytes .TrimSpace (out ), [] byte ( "go version " )) )
263
+ rawVersion := strings .TrimPrefix (strings .TrimSpace (string ( out )), "go version " )
264
264
265
265
tagIdx := strings .IndexByte (rawVersion , ' ' )
266
266
tag := rawVersion [:tagIdx ]
@@ -424,10 +424,13 @@ func mainErr(args []string) error {
424
424
}
425
425
goArgs := []string {
426
426
cmd ,
427
- "-a" ,
428
427
"-trimpath" ,
429
428
"-toolexec=" + execPath ,
430
429
}
430
+ if flagDebugDir != "" {
431
+ // TODO: don't make -debugdir force rebuilding all packages
432
+ goArgs = append (goArgs , "-a" )
433
+ }
431
434
if cmd == "test" {
432
435
// vet is generally not useful on garbled code; keep it
433
436
// disabled by default.
@@ -455,6 +458,38 @@ func mainErr(args []string) error {
455
458
transformed := args [1 :]
456
459
// log.Println(tool, transformed)
457
460
if transform != nil {
461
+ if len (args ) == 2 && args [1 ] == "-V=full" {
462
+ cmd := exec .Command (args [0 ], args [1 :]... )
463
+ out , err := cmd .Output ()
464
+ if err != nil {
465
+ if err , _ := err .(* exec.ExitError ); err != nil {
466
+ return fmt .Errorf ("%v: %s" , err , err .Stderr )
467
+ }
468
+ return err
469
+ }
470
+ line := string (bytes .TrimSpace (out ))
471
+ f := strings .Fields (line )
472
+ if len (f ) < 3 || f [0 ] != tool || f [1 ] != "version" || f [2 ] == "devel" && ! strings .HasPrefix (f [len (f )- 1 ], "buildID=" ) {
473
+ return fmt .Errorf ("%s -V=full: unexpected output:\n \t %s" , args [0 ], line )
474
+ }
475
+ var toolID []byte
476
+ if f [2 ] == "devel" {
477
+ // On the development branch, use the content ID part of the build ID.
478
+ toolID = decodeHash (contentID (f [len (f )- 1 ]))
479
+ } else {
480
+ // For a release, the output is like: "compile version go1.9.1 X:framepointer".
481
+ // Use the whole line.
482
+ toolID = []byte (line )
483
+ }
484
+
485
+ out = bytes .TrimSpace (out ) // no trailing newline
486
+ contentID , err := ownContentID (toolID )
487
+ if err != nil {
488
+ return fmt .Errorf ("cannot obtain garble's own version: %v" , err )
489
+ }
490
+ fmt .Printf ("%s +garble buildID=_/_/_/%s\n " , line , contentID )
491
+ return nil
492
+ }
458
493
var err error
459
494
if transformed , err = transform (transformed ); err != nil {
460
495
return err
@@ -476,6 +511,38 @@ func mainErr(args []string) error {
476
511
return nil
477
512
}
478
513
514
+ const buildIDSeparator = "/"
515
+
516
+ // actionID returns the action ID half of a build ID, the first element.
517
+ func actionID (buildID string ) string {
518
+ i := strings .Index (buildID , buildIDSeparator )
519
+ if i < 0 {
520
+ return buildID
521
+ }
522
+ return buildID [:i ]
523
+ }
524
+
525
+ // contentID returns the content ID half of a build ID, the last element.
526
+ func contentID (buildID string ) string {
527
+ return buildID [strings .LastIndex (buildID , buildIDSeparator )+ 1 :]
528
+ }
529
+
530
+ // decodeHash isthe opposite of hashToString, but with a panic for error
531
+ // handling since it should never happen.
532
+ func decodeHash (str string ) []byte {
533
+ h , err := base64 .RawURLEncoding .DecodeString (str )
534
+ if err != nil {
535
+ panic (fmt .Sprintf ("invalid hash %q: %v" , str , err ))
536
+ }
537
+ return h
538
+ }
539
+
540
+ // hashToString encodes the first 120 bits of a sha256 sum in base64, the same
541
+ // format used for elements in a build ID.
542
+ func hashToString (h []byte ) string {
543
+ return base64 .RawURLEncoding .EncodeToString (h [:15 ])
544
+ }
545
+
479
546
var transformFuncs = map [string ]func ([]string ) ([]string , error ){
480
547
"compile" : transformCompile ,
481
548
"link" : transformLink ,
@@ -484,10 +551,6 @@ var transformFuncs = map[string]func([]string) ([]string, error){
484
551
func transformCompile (args []string ) ([]string , error ) {
485
552
var err error
486
553
flags , paths := splitFlagsFromFiles (args , ".go" )
487
- if len (paths ) == 0 {
488
- // Nothing to transform; probably just ["-V=full"].
489
- return args , nil
490
- }
491
554
492
555
// We will force the linker to drop DWARF via -w, so don't spend time
493
556
// generating it.
@@ -542,7 +605,7 @@ func transformCompile(args []string) ([]string, error) {
542
605
543
606
mathrand .Seed (int64 (binary .BigEndian .Uint64 (seed )))
544
607
} else {
545
- mathrand .Seed (int64 (binary .BigEndian .Uint64 ([]byte (buildInfo .buildID ))))
608
+ mathrand .Seed (int64 (binary .BigEndian .Uint64 ([]byte (buildInfo .actionID ))))
546
609
}
547
610
548
611
info := & types.Info {
@@ -723,12 +786,12 @@ func isPrivate(path string) bool {
723
786
724
787
// fillBuildInfo initializes the global buildInfo struct via the supplied flags.
725
788
func fillBuildInfo (flags []string ) error {
726
- buildInfo . buildID = flagValue (flags , "-buildid" )
727
- switch buildInfo . buildID {
789
+ buildID : = flagValue (flags , "-buildid" )
790
+ switch buildID {
728
791
case "" , "true" :
729
792
return fmt .Errorf ("could not find -buildid argument" )
730
793
}
731
- buildInfo .buildID = trimBuildID ( buildInfo . buildID )
794
+ buildInfo .actionID = decodeHash ( actionID ( buildID ) )
732
795
buildInfo .importCfg = flagValue (flags , "-importcfg" )
733
796
if buildInfo .importCfg == "" {
734
797
return fmt .Errorf ("could not find -importcfg argument" )
@@ -755,32 +818,24 @@ func fillBuildInfo(flags []string) error {
755
818
continue
756
819
}
757
820
importPath , objectPath := args [:j ], args [j + 1 :]
758
- fileID , err := buildidOf (objectPath )
821
+ buildID , err := buildidOf (objectPath )
759
822
if err != nil {
760
823
return err
761
824
}
762
- // log.Println("buildid:", fileID )
825
+ // log.Println("buildid:", buildID )
763
826
764
827
if len (buildInfo .imports ) == 0 {
765
828
buildInfo .firstImport = importPath
766
829
}
767
830
buildInfo .imports [importPath ] = importedPkg {
768
831
packagefile : objectPath ,
769
- buildID : fileID ,
832
+ actionID : decodeHash ( actionID ( buildID )) ,
770
833
}
771
834
}
772
835
// log.Printf("%#v", buildInfo)
773
836
return nil
774
837
}
775
838
776
- func trimBuildID (id string ) string {
777
- id = strings .TrimSpace (id )
778
- if i := strings .IndexByte (id , '/' ); i > 0 {
779
- id = id [:i ]
780
- }
781
- return id
782
- }
783
-
784
839
func buildidOf (path string ) (string , error ) {
785
840
cmd := exec .Command ("go" , "tool" , "buildid" , path )
786
841
out , err := cmd .Output ()
@@ -790,19 +845,19 @@ func buildidOf(path string) (string, error) {
790
845
}
791
846
return "" , err
792
847
}
793
- return trimBuildID ( string (bytes . TrimSpace ( out )) ), nil
848
+ return string (out ), nil
794
849
}
795
850
796
- func hashWith (salt , value string ) string {
851
+ func hashWith (salt [] byte , name string ) string {
797
852
const length = 4
798
853
799
854
d := sha256 .New ()
800
- io . WriteString ( d , salt )
855
+ d . Write ( salt )
801
856
d .Write (seed )
802
- io .WriteString (d , value )
857
+ io .WriteString (d , name )
803
858
sum := b64 .EncodeToString (d .Sum (nil ))
804
859
805
- if token .IsExported (value ) {
860
+ if token .IsExported (name ) {
806
861
return "Z" + sum [:length ]
807
862
}
808
863
return "z" + sum [:length ]
@@ -997,12 +1052,12 @@ func transformGo(file *ast.File, info *types.Info, blacklist map[types.Object]st
997
1052
default :
998
1053
return true // we only want to rename the above
999
1054
}
1000
- buildID := buildInfo .buildID
1055
+ actionID := buildInfo .actionID
1001
1056
path := pkg .Path ()
1002
1057
if ! isPrivate (path ) {
1003
1058
return true // only private packages are transformed
1004
1059
}
1005
- if id := buildInfo .imports [path ].buildID ; id != "" {
1060
+ if id := buildInfo .imports [path ].actionID ; len ( id ) > 0 {
1006
1061
garbledPkg , err := garbledImport (path )
1007
1062
if err != nil {
1008
1063
panic (err ) // shouldn't happen
@@ -1011,12 +1066,12 @@ func transformGo(file *ast.File, info *types.Info, blacklist map[types.Object]st
1011
1066
if garbledPkg .Scope ().Lookup (obj .Name ()) != nil {
1012
1067
return true
1013
1068
}
1014
- buildID = id
1069
+ actionID = id
1015
1070
}
1016
1071
1017
1072
// The exported names cannot be shortened as counter synchronization between packages is not currently implemented
1018
1073
if token .IsExported (node .Name ) {
1019
- node .Name = hashWith (buildID , node .Name )
1074
+ node .Name = hashWith (actionID , node .Name )
1020
1075
return true
1021
1076
}
1022
1077
@@ -1031,7 +1086,7 @@ func transformGo(file *ast.File, info *types.Info, blacklist map[types.Object]st
1031
1086
// orig := node.Name
1032
1087
privateNameMap [fullName ] = name
1033
1088
node .Name = name
1034
- // log.Printf("%q hashed with %q to %q", orig, buildID , node.Name)
1089
+ // log.Printf("%q hashed with %q to %q", orig, actionID , node.Name)
1035
1090
return true
1036
1091
}
1037
1092
return astutil .Apply (file , pre , nil ).(* ast.File )
@@ -1077,11 +1132,9 @@ func isTestSignature(sign *types.Signature) bool {
1077
1132
}
1078
1133
1079
1134
func transformLink (args []string ) ([]string , error ) {
1080
- flags , paths := splitFlagsFromFiles (args , ".a" )
1081
- if len (paths ) == 0 {
1082
- // Nothing to transform; probably just ["-V=full"].
1083
- return args , nil
1084
- }
1135
+ // We can't split by the ".a" extension, because cached object files
1136
+ // lack any extension.
1137
+ flags , paths := splitFlagsFromArgs (args )
1085
1138
1086
1139
if err := fillBuildInfo (flags ); err != nil {
1087
1140
return nil , err
@@ -1117,7 +1170,7 @@ func transformLink(args []string) ([]string, error) {
1117
1170
// the import config map.
1118
1171
pkgPath = buildInfo .firstImport
1119
1172
}
1120
- if id := buildInfo .imports [pkgPath ].buildID ; id != "" {
1173
+ if id := buildInfo .imports [pkgPath ].actionID ; len ( id ) > 0 {
1121
1174
// If the name is not in the map file, it means that the name was not obfuscated or is public
1122
1175
newName , ok := privateNameMap [pkg + "." + name ]
1123
1176
if ! ok {
@@ -1291,3 +1344,45 @@ func flagSetValue(flags []string, name, value string) []string {
1291
1344
}
1292
1345
return append (flags , name + "=" + value )
1293
1346
}
1347
+
1348
+ func ownContentID (toolID []byte ) (string , error ) {
1349
+ // We can't rely on the module version to exist, because it's
1350
+ // missing in local builds without 'go get'.
1351
+ // For now, use 'go tool buildid' on the binary that's running. Just
1352
+ // like Go's own cache, we use hex-encoded sha256 sums.
1353
+ // Once https://github.com/golang/go/issues/37475 is fixed, we
1354
+ // can likely just use that.
1355
+ path , err := os .Executable ()
1356
+ if err != nil {
1357
+ return "" , err
1358
+ }
1359
+ buildID , err := buildidOf (path )
1360
+ if err != nil {
1361
+ return "" , err
1362
+ }
1363
+ ownID := decodeHash (contentID (buildID ))
1364
+
1365
+ // Join the two content IDs together into a single base64-encoded sha256
1366
+ // sum. This includes the original tool's content ID, and garble's own
1367
+ // content ID.
1368
+ h := sha256 .New ()
1369
+ h .Write (toolID )
1370
+ h .Write (ownID )
1371
+
1372
+ // We also need to add the selected options to the full version string,
1373
+ // because all of them result in different output.
1374
+ if envGoPrivate != "" {
1375
+ fmt .Fprintf (h , " GOPRIVATE=%s" , envGoPrivate )
1376
+ }
1377
+ if envGarbleLiterals {
1378
+ fmt .Fprintf (h , " -literals" )
1379
+ }
1380
+ if envGarbleTiny {
1381
+ fmt .Fprintf (h , " -tiny" )
1382
+ }
1383
+ if envGarbleSeed != "" {
1384
+ fmt .Fprintf (h , " -seed=%x" , envGarbleSeed )
1385
+ }
1386
+
1387
+ return hashToString (h .Sum (nil )), nil
1388
+ }
0 commit comments