aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Brainman <alex.brainman@gmail.com>2020-02-25 18:42:24 +1100
committerAlex Brainman <alex.brainman@gmail.com>2020-03-01 05:00:10 +0000
commit95f382139043059a2a0780ba577b53893408f7e4 (patch)
tree6529ca6e764bf447a67f439bdb73887882c224d2
parent91bc75b4870308b668d497ff22eada75219c3c2e (diff)
downloadgo-95f382139043059a2a0780ba577b53893408f7e4.tar.gz
go-95f382139043059a2a0780ba577b53893408f7e4.zip
cmd/go, cmd/link: implement -buildmode=pie on windows
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>
-rw-r--r--src/cmd/dist/test.go4
-rw-r--r--src/cmd/go/go_test.go57
-rw-r--r--src/cmd/go/internal/work/init.go8
-rw-r--r--src/cmd/go/testdata/script/version.txt6
-rw-r--r--src/cmd/internal/sys/supported.go3
-rw-r--r--src/cmd/link/internal/ld/config.go3
-rw-r--r--src/cmd/link/internal/ld/lib.go16
-rw-r--r--src/cmd/link/internal/ld/pe.go58
8 files changed, 122 insertions, 33 deletions
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index ca617e917e..48c36a63fc 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -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
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 4d5136deea..6654bd3143 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -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")
}
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index 9091f98636..e970272954 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -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":
diff --git a/src/cmd/go/testdata/script/version.txt b/src/cmd/go/testdata/script/version.txt
index 0ed1194840..0123ac6d53 100644
--- a/src/cmd/go/testdata/script/version.txt
+++ b/src/cmd/go/testdata/script/version.txt
@@ -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
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index c8ab2181b5..639827be86 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -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
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index 0eba4dc162..2373b500e3 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -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"
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 7c5877bfbd..a4b4b60ca1 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -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")
}
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index 4ab346e733..2c6be2d6f3 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -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:
}