From 25bec04e12c49945fd399997bb4624e052456dd2 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Fri, 30 Oct 2026 10:50:18 -0700 Subject: [PATCH 1/3] Revise PIE and immediate binding docs --- eng/doc/AdditionalFeatures.md | 29 ++++++++++++++++++++++------- eng/doc/MigrationGuide.md | 4 ++-- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/eng/doc/AdditionalFeatures.md b/eng/doc/AdditionalFeatures.md index 99dc494fcb..09940e99e4 100644 --- a/eng/doc/AdditionalFeatures.md +++ b/eng/doc/AdditionalFeatures.md @@ -17,17 +17,21 @@ See also the [Data Collection policy for the Microsoft build of Go](/README.md#d ## Position-independent executables on Linux Starting with Go 1.28, the default build mode on Linux is `pie` on architectures that support internally linked position-independent executables: amd64, arm64, loong64, ppc64, ppc64le, and s390x. +Other architectures continue to use the upstream default `exe`. The PIE default aligns with Microsoft's security policies and enables address space layout randomization (ASLR) for executables. -Windows and macOS (`darwin`) binaries already used PIE by default. -Other Linux architectures and builds using `-race` or `-asan` retain the upstream default of `exe`. -Keeping `-asan` builds non-PIE preserves compatibility with system AddressSanitizer runtimes. + +> [!NOTE] +> Windows and macOS (`darwin`) binaries already used PIE by default, prior to Go 1.28. + +Builds on Linux using `-race` or `-asan` continue to use `exe` by default, not `pie`. +We do this because Go does not support PIE with the race detector on Linux, and using `pie` with `-asan` may break compatibility with system AddressSanitizer runtimes. The default applies to `go build`, `go install`, `go run`, and `go test`, including cross-compilation. It is independent of system crypto and does not require cgo or a C compiler. Explicit build modes are unchanged; use `-buildmode=exe` to opt out. -Programs without direct or indirect dependencies on Go's `crypto` packages now also require a dynamic loader at runtime. -To keep such programs fully static, for example to run in a container without a dynamic loader, use: +On builds affected by the new PIE default, even pure-Go programs require a dynamic loader at runtime. +To produce a fully static executable instead, for example to run in a container without a dynamic loader, use: ```sh CGO_ENABLED=0 go build -buildmode=exe @@ -38,10 +42,21 @@ CGO_ENABLED=0 go build -buildmode=exe Starting with Go 1.28, the linker enables immediate symbol binding (`BIND_NOW`) by default for dynamically linked ELF executables and shared libraries to align with Microsoft's security policies. This applies to internal and external linking, with or without cgo or system crypto. +Go produces ELF binaries on Linux and several other Unix-like operating systems. +Windows and macOS use different executable formats and are unaffected. + +Statically linked ELF binaries are unaffected by this change: they don't require dynamic symbol binding. + The dynamic linker resolves function symbols when a binary is loaded rather than when each function is first called. -Together with RELRO, this allows relocation tables to be made read-only after loading. Missing symbols are reported at load time rather than when the affected function is first called. -Static binaries remain static, and Windows and macOS binaries are unaffected. + +> [!NOTE] +> [RELRO](https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro) protects some relocation-related data from modification after loading. +> The linker marks a memory range for RELRO protection, and the dynamic linker makes that range read-only after applying relocations. +> Go enables RELRO for ELF PIE binaries and shared libraries, including executables built with the Microsoft build of Go [Linux PIE default](#position-independent-executables-on-linux). +> +> Immediate binding extends this protection to the global offset table entries used for function calls. +> Without immediate binding, these entries must remain writable so the dynamic linker can update them when each function is first called. To restore the upstream linker default for ordinary executables, use: diff --git a/eng/doc/MigrationGuide.md b/eng/doc/MigrationGuide.md index 9d75a3ce04..8852226a2d 100644 --- a/eng/doc/MigrationGuide.md +++ b/eng/doc/MigrationGuide.md @@ -42,8 +42,8 @@ The Microsoft build of Go includes [patches](/patches/) that: - **Disable [GOTOOLCHAIN](https://go.dev/doc/toolchain) by default** to avoid mixups with the official Go distribution. - **Remove use of undocumented Windows APIs** for compatibility, security, and compliance. - **Embed Microsoft-specific version information** into built Go binaries for [easier identification](./MicrosoftToolsetIdentification.md). -- **Default to [position-independent executables on Linux](./AdditionalFeatures.md#position-independent-executables-on-linux)** to align with Microsoft's security policies. Starting with Go 1.28, this applies to architectures that support internal PIE linking. These executables require a dynamic loader; use `-buildmode=exe` when a non-PIE executable is needed. Builds using `-race` or `-asan` retain their previous defaults. -- **Enable [immediate binding for ELF binaries](./AdditionalFeatures.md#immediate-binding-for-elf-binaries)** by default starting with Go 1.28 to align with Microsoft's security policies. Dynamic function symbols are resolved at load time rather than on first use; use `-ldflags=-bindnow=false` to opt out. +- **Default to [position-independent executables on Linux on applicable GOARCHes](./AdditionalFeatures.md#position-independent-executables-on-linux)** starting with Go 1.28. +- **Enable [immediate binding for ELF binaries](./AdditionalFeatures.md#immediate-binding-for-elf-binaries) by default** starting with Go 1.28. For a detailed description of every feature, see the [Additional Features](./AdditionalFeatures.md) document. From f1c31fd7a78cd066e557cbb356d8e46c9eaabec0 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Fri, 30 Oct 2026 10:57:37 -0700 Subject: [PATCH 2/3] Update migration guide note: not just applicable GOARCHes, simply configs --- eng/doc/MigrationGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/doc/MigrationGuide.md b/eng/doc/MigrationGuide.md index 8852226a2d..106bc4482d 100644 --- a/eng/doc/MigrationGuide.md +++ b/eng/doc/MigrationGuide.md @@ -42,7 +42,7 @@ The Microsoft build of Go includes [patches](/patches/) that: - **Disable [GOTOOLCHAIN](https://go.dev/doc/toolchain) by default** to avoid mixups with the official Go distribution. - **Remove use of undocumented Windows APIs** for compatibility, security, and compliance. - **Embed Microsoft-specific version information** into built Go binaries for [easier identification](./MicrosoftToolsetIdentification.md). -- **Default to [position-independent executables on Linux on applicable GOARCHes](./AdditionalFeatures.md#position-independent-executables-on-linux)** starting with Go 1.28. +- **Default to [position-independent executables on Linux with applicable build configurations](./AdditionalFeatures.md#position-independent-executables-on-linux)** starting with Go 1.28. - **Enable [immediate binding for ELF binaries](./AdditionalFeatures.md#immediate-binding-for-elf-binaries) by default** starting with Go 1.28. For a detailed description of every feature, see the [Additional Features](./AdditionalFeatures.md) document. From 1f34c614a95e224b0f4e59e7d489f6991ab9e167 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Fri, 30 Oct 2026 11:02:36 -0700 Subject: [PATCH 3/3] Fix note about fully static executable vs. PIE --- eng/doc/AdditionalFeatures.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/eng/doc/AdditionalFeatures.md b/eng/doc/AdditionalFeatures.md index 09940e99e4..6d3d06e17d 100644 --- a/eng/doc/AdditionalFeatures.md +++ b/eng/doc/AdditionalFeatures.md @@ -30,13 +30,16 @@ The default applies to `go build`, `go install`, `go run`, and `go test`, includ It is independent of system crypto and does not require cgo or a C compiler. Explicit build modes are unchanged; use `-buildmode=exe` to opt out. -On builds affected by the new PIE default, even pure-Go programs require a dynamic loader at runtime. -To produce a fully static executable instead, for example to run in a container without a dynamic loader, use: +On builds affected by the new PIE default, even programs without crypto dependencies require a dynamic loader at runtime. +For programs without direct or indirect dependencies on Go's `crypto` packages, use the following command to produce a fully static executable: ```sh CGO_ENABLED=0 go build -buildmode=exe ``` +Programs using systemcrypto still require shared libraries and a dynamic loader, even with `CGO_ENABLED=0`. +See [No-cgo OpenSSL backend runtime dependencies](./NocgoOpenSSL.md#runtime-dependencies). + ## Immediate binding for ELF binaries Starting with Go 1.28, the linker enables immediate symbol binding (`BIND_NOW`) by default for dynamically linked ELF executables and shared libraries to align with Microsoft's security policies.