]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go, cmd/link: implement -buildmode=pie on windows
authorAlex Brainman <alex.brainman@gmail.com>
Tue, 25 Feb 2020 07:42:24 +0000 (18:42 +1100)
committerAlex Brainman <alex.brainman@gmail.com>
Sun, 1 Mar 2020 05:00:10 +0000 (05:00 +0000)
This CL implements windows version of -buildmode=pie code in both
cmd/go and cmd/link.

Windows executables built with -buildmode=pie set (unlike the one
built with -buildmode=exe) will have extra .reloc PE section, and
will have no IMAGE_FILE_RELOCS_STRIPPED flag set. They will also
have IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag set, and
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag set for windows/amd64.

Both cgo and non-cgo versions are implemented. And TestBuildmodePIE
is extended to test both cgo and non-cgo versions on windows and
linux.

This CL used some code from CLs 152759 and 203602.

RELNOTE=yes

Fixes #27144
Updates #35192

Change-Id: I1249e4ffbd79bd4277efefb56db321c390c0f76f
Reviewed-on: https://go-review.googlesource.com/c/go/+/214397
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/cmd/dist/test.go
src/cmd/go/go_test.go
src/cmd/go/internal/work/init.go
src/cmd/go/testdata/script/version.txt
src/cmd/internal/sys/supported.go
src/cmd/link/internal/ld/config.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/pe.go

index ca617e917eff0a682cbcdd15e5e5c9b72ee81ab1..48c36a63fcbe3a661537b74c11b2754de1b6d2d2 100644 (file)
@@ -941,6 +941,8 @@ func (t *tester) internalLinkPIE() bool {
        case "linux-amd64", "linux-arm64",
                "android-arm64":
                return true
+       case "windows-amd64", "windows-386", "windows-arm":
+               return true
        }
        return false
 }
@@ -997,6 +999,8 @@ func (t *tester) supportedBuildmode(mode string) bool {
                        return true
                case "darwin-amd64":
                        return true
+               case "windows-amd64", "windows-386", "windows-arm":
+                       return true
                }
                return false
 
index 4d5136deeaad086acf5320c239fa55d39b3b8836..6654bd3143cd865e80d162c8872aef03beb02239 100644 (file)
@@ -9,6 +9,7 @@ import (
        "context"
        "debug/elf"
        "debug/macho"
+       "debug/pe"
        "flag"
        "fmt"
        "go/format"
@@ -2146,19 +2147,37 @@ func TestBuildmodePIE(t *testing.T) {
        switch platform {
        case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
                "android/amd64", "android/arm", "android/arm64", "android/386",
-               "freebsd/amd64":
+               "freebsd/amd64",
+               "windows/386", "windows/amd64", "windows/arm":
        case "darwin/amd64":
        default:
                t.Skipf("skipping test because buildmode=pie is not supported on %s", platform)
        }
+       t.Run("non-cgo", func(t *testing.T) {
+               testBuildmodePIE(t, false)
+       })
+       if canCgo {
+               switch runtime.GOOS {
+               case "darwin", "freebsd", "linux", "windows":
+                       t.Run("cgo", func(t *testing.T) {
+                               testBuildmodePIE(t, true)
+                       })
+               }
+       }
+}
 
+func testBuildmodePIE(t *testing.T, useCgo bool) {
        tg := testgo(t)
        defer tg.cleanup()
        tg.parallel()
 
-       tg.tempFile("main.go", `package main; func main() { print("hello") }`)
+       var s string
+       if useCgo {
+               s = `import "C";`
+       }
+       tg.tempFile("main.go", fmt.Sprintf(`package main;%s func main() { print("hello") }`, s))
        src := tg.path("main.go")
-       obj := tg.path("main")
+       obj := tg.path("main.exe")
        tg.run("build", "-buildmode=pie", "-o", obj, src)
 
        switch runtime.GOOS {
@@ -2183,6 +2202,38 @@ func TestBuildmodePIE(t *testing.T) {
                if f.Flags&macho.FlagPIE == 0 {
                        t.Error("PIE must have PIE flag, but not")
                }
+       case "windows":
+               f, err := pe.Open(obj)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               defer f.Close()
+               const (
+                       IMAGE_FILE_RELOCS_STRIPPED               = 0x0001
+                       IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020
+                       IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE    = 0x0040
+               )
+               if f.Section(".reloc") == nil {
+                       t.Error(".reloc section is not present")
+               }
+               if (f.FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) != 0 {
+                       t.Error("IMAGE_FILE_RELOCS_STRIPPED flag is set")
+               }
+               var dc uint16
+               switch oh := f.OptionalHeader.(type) {
+               case *pe.OptionalHeader32:
+                       dc = oh.DllCharacteristics
+               case *pe.OptionalHeader64:
+                       dc = oh.DllCharacteristics
+                       if (dc & IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) == 0 {
+                               t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
+                       }
+               default:
+                       t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
+               }
+               if (dc & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0 {
+                       t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
+               }
        default:
                panic("unreachable")
        }
index 9091f986368f4d62bfd576cdea18397d849f0b78..e970272954e8bf7c2b3849ec1752415e9d7ed891 100644 (file)
@@ -161,8 +161,12 @@ func buildModeInit() {
                }
                if gccgo {
                        codegenArg = "-fPIE"
-               } else if cfg.Goos != "aix" {
-                       codegenArg = "-shared"
+               } else {
+                       switch cfg.Goos {
+                       case "aix", "windows":
+                       default:
+                               codegenArg = "-shared"
+                       }
                }
                ldBuildmode = "pie"
        case "shared":
index 0ed1194840773416cf5ce91cc518c2d2378b1059..0123ac6d53f3934fb4ca088e906436317b736ba5 100644 (file)
@@ -22,8 +22,6 @@ stdout '^\tpath\trsc.io/fortune'
 stdout '^\tmod\trsc.io/fortune\tv1.0.0'
 
 # Repeat the test with -buildmode=pie.
-# TODO(golang.org/issue/27144): don't skip after -buildmode=pie is implemented
-# on Windows.
 [!buildmode:pie] stop
 go build -buildmode=pie -o external.exe rsc.io/fortune
 go version external.exe
@@ -33,8 +31,8 @@ stdout '^\tpath\trsc.io/fortune'
 stdout '^\tmod\trsc.io/fortune\tv1.0.0'
 
 # Also test PIE with internal linking.
-# currently only supported on linux/amd64 and linux/arm64.
-[!linux] stop
+# currently only supported on linux/amd64, linux/arm64 and windows/amd64.
+[!linux] [!windows] stop
 [!amd64] [!arm64] stop
 go build -buildmode=pie -ldflags=-linkmode=internal -o internal.exe rsc.io/fortune
 go version internal.exe
index c8ab2181b524c669fc6d2af5e73de4326a583a6d..639827be866f7c4ef1fcfabb644e34ccb17a76f1 100644 (file)
@@ -87,7 +87,8 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
                        "android/amd64", "android/arm", "android/arm64", "android/386",
                        "freebsd/amd64",
                        "darwin/amd64",
-                       "aix/ppc64":
+                       "aix/ppc64",
+                       "windows/386", "windows/amd64", "windows/arm":
                        return true
                }
                return false
index 0eba4dc1626c7d49d1a3f3a2f884452d50dda73d..2373b500e3aae1b879470e66cf8ececc16ec793f 100644 (file)
@@ -38,7 +38,7 @@ func (mode *BuildMode) Set(s string) error {
                *mode = BuildModeExe
        case "pie":
                switch objabi.GOOS {
-               case "aix", "android", "linux":
+               case "aix", "android", "linux", "windows":
                case "darwin", "freebsd":
                        switch objabi.GOARCH {
                        case "amd64":
@@ -209,6 +209,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
        case BuildModePIE:
                switch objabi.GOOS + "/" + objabi.GOARCH {
                case "linux/amd64", "linux/arm64", "android/arm64":
+               case "windows/386", "windows/amd64", "windows/arm":
                default:
                        // Internal linking does not support TLS_IE.
                        return true, "buildmode=pie"
index 7c5877bfbd01a996afc4e0f492f043664a742a26..a4b4b60ca1f304cf7c0890b8c7e6a00222dbca78 100644 (file)
@@ -1258,8 +1258,20 @@ func (ctxt *Link) hostlink() {
                        }
                }
        case BuildModePIE:
-               // ELF.
-               if ctxt.HeadType != objabi.Hdarwin && ctxt.HeadType != objabi.Haix {
+               switch ctxt.HeadType {
+               case objabi.Hdarwin, objabi.Haix:
+               case objabi.Hwindows:
+                       // Enable ASLR.
+                       argv = append(argv, "-Wl,--dynamicbase")
+                       // enable high-entropy ASLR on 64-bit.
+                       if ctxt.Arch.PtrSize >= 8 {
+                               argv = append(argv, "-Wl,--high-entropy-va")
+                       }
+                       // Work around binutils limitation that strips relocation table for dynamicbase.
+                       // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
+                       argv = append(argv, "-Wl,--export-all-symbols")
+               default:
+                       // ELF.
                        if ctxt.UseRelro() {
                                argv = append(argv, "-Wl,-z,relro")
                        }
index 4ab346e733a97fe05fa1ecd3fea9fc55d43ce7ad..2c6be2d6f39a4fd9a7de59b677e4be3c1fcbd058 100644 (file)
@@ -94,6 +94,7 @@ const (
        IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR           = 14
        IMAGE_SUBSYSTEM_WINDOWS_GUI                    = 2
        IMAGE_SUBSYSTEM_WINDOWS_CUI                    = 3
+       IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA       = 0x0020
        IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE          = 0x0040
        IMAGE_DLLCHARACTERISTICS_NX_COMPAT             = 0x0100
        IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
@@ -126,6 +127,7 @@ const (
        IMAGE_REL_ARM_SECREL   = 0x000F
 
        IMAGE_REL_BASED_HIGHLOW = 3
+       IMAGE_REL_BASED_DIR64   = 10
 )
 
 const (
@@ -752,12 +754,12 @@ func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) {
 }
 
 // writeFileHeader writes COFF file header for peFile f.
-func (f *peFile) writeFileHeader(arch *sys.Arch, out *OutBuf, linkmode LinkMode) {
+func (f *peFile) writeFileHeader(ctxt *Link) {
        var fh pe.FileHeader
 
-       switch arch.Family {
+       switch ctxt.Arch.Family {
        default:
-               Exitf("unknown PE architecture: %v", arch.Family)
+               Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
        case sys.AMD64:
                fh.Machine = IMAGE_FILE_MACHINE_AMD64
        case sys.I386:
@@ -772,16 +774,15 @@ func (f *peFile) writeFileHeader(arch *sys.Arch, out *OutBuf, linkmode LinkMode)
        // much more beneficial than having build timestamp in the header.
        fh.TimeDateStamp = 0
 
-       if linkmode == LinkExternal {
+       if ctxt.LinkMode == LinkExternal {
                fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED
        } else {
-               switch arch.Family {
-               default:
-                       Exitf("write COFF(ext): unknown PE architecture: %v", arch.Family)
+               fh.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
+               switch ctxt.Arch.Family {
                case sys.AMD64, sys.I386:
-                       fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
-               case sys.ARM:
-                       fh.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
+                       if ctxt.BuildMode != BuildModePIE {
+                               fh.Characteristics |= IMAGE_FILE_RELOCS_STRIPPED
+                       }
                }
        }
        if pe64 != 0 {
@@ -797,7 +798,7 @@ func (f *peFile) writeFileHeader(arch *sys.Arch, out *OutBuf, linkmode LinkMode)
        fh.PointerToSymbolTable = uint32(f.symtabOffset)
        fh.NumberOfSymbols = uint32(f.symbolCount)
 
-       binary.Write(out, binary.LittleEndian, &fh)
+       binary.Write(ctxt.Out, binary.LittleEndian, &fh)
 }
 
 // writeOptionalHeader writes COFF optional header for peFile f.
@@ -859,12 +860,6 @@ func (f *peFile) writeOptionalHeader(ctxt *Link) {
                oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
        }
 
-       switch ctxt.Arch.Family {
-       case sys.ARM:
-               oh64.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
-               oh.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
-       }
-
        // Mark as having awareness of terminal services, to avoid ancient compatibility hacks.
        oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
        oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
@@ -873,6 +868,23 @@ func (f *peFile) writeOptionalHeader(ctxt *Link) {
        oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NX_COMPAT
        oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NX_COMPAT
 
+       // The DLL can be relocated at load time.
+       switch ctxt.Arch.Family {
+       case sys.AMD64, sys.I386:
+               if ctxt.BuildMode == BuildModePIE {
+                       oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+                       oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+               }
+       case sys.ARM:
+               oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+               oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
+       }
+
+       // Image can handle a high entropy 64-bit virtual address space.
+       if ctxt.BuildMode == BuildModePIE {
+               oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
+       }
+
        // Disable stack growth as we don't want Windows to
        // fiddle with the thread stack limits, which we set
        // ourselves to circumvent the stack checks in the
@@ -997,7 +1009,7 @@ func pewrite(ctxt *Link) {
                ctxt.Out.WriteStringN("PE", 4)
        }
 
-       pefile.writeFileHeader(ctxt.Arch, ctxt.Out, ctxt.LinkMode)
+       pefile.writeFileHeader(ctxt)
 
        pefile.writeOptionalHeader(ctxt)
 
@@ -1376,6 +1388,8 @@ func (rt *peBaseRelocTable) addentry(ctxt *Link, s *sym.Symbol, r *sym.Reloc) {
                Exitf("unsupported relocation size %d\n", r.Siz)
        case 4:
                e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12)
+       case 8:
+               e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12)
        }
 
        b.entries = append(b.entries, e)
@@ -1430,11 +1444,15 @@ func addPEBaseRelocSym(ctxt *Link, s *sym.Symbol, rt *peBaseRelocTable) {
 }
 
 func addPEBaseReloc(ctxt *Link) {
-       // We only generate base relocation table for ARM (and ... ARM64), x86, and AMD64 are marked as legacy
-       // archs and can use fixed base with no base relocation information
+       // Arm does not work without base relocation table.
+       // 386 and amd64 will only require the table for BuildModePIE.
        switch ctxt.Arch.Family {
        default:
                return
+       case sys.I386, sys.AMD64:
+               if ctxt.BuildMode != BuildModePIE {
+                       return
+               }
        case sys.ARM:
        }