August 11, 2020·6 min read·engineeringgox509toolchain

Go 1.15 changes certificate names and linker costs

Go 1.15 disables legacy CommonName hostname fallback and redesigns parts of the linker. Both changes affect existing programs without changing the language.

A program can keep compiling while the certificates it accepts change. Go 1.15 disables the legacy fallback that interpreted a certificate's CommonName as its hostname when the certificate had no Subject Alternative Names. An application rebuilt with the new release can therefore reject an old certificate even though its source code is unchanged. That is a deliberate change to identity verification, not a new language rule.

The release announcement also highlights linker and allocator improvements. The versioned release notes say there are no language changes. This makes the release interesting for existing systems. It changes several costs and defaults beneath an established program while preserving the language used to express it.

I value that kind of work because a readable Go program should not need a rewrite to benefit from better tooling. Certificate policy needs a more explicit assessment, since preserving source compatibility cannot imply preserving every legacy trust decision.

The hostname needs its certificate field

The old behavior applied only when Subject Alternative Names were absent. It treated the CommonName as a hostname in that case. Go 1.15 disables that behavior by default. Certificates that already supply the required names in Subject Alternative Names do not depend on this particular fallback.

The distinction helps explain an otherwise confusing upgrade failure. A certificate may still parse, carry a signature, and link to a trusted issuer while failing hostname verification. Those checks establish different properties. Trusting an issuer does not establish that the certificate names the endpoint the client requested.

The release provides a temporary compatibility control through GODEBUG. Adding x509ignoreCN=0 restores the legacy behavior within the conditions described by the notes. This setting is an explicit request to retain the deprecated fallback. It does not add a missing Subject Alternative Name to a certificate or repair how that certificate was issued.

The exception is narrower still for invalid CommonNames. They remain ignored regardless of the setting. The notes identify invalid names as including characters outside letters, digits, hyphens, and underscores, along with empty labels and trailing dots. Enabling the fallback therefore cannot make every old subject string function as a valid hostname.

The certificate structure keeps these names in distinct fields. A certificate-generation template can express the intended DNS identity directly in Go.

var template = x509.Certificate{
    Subject: pkix.Name{
        CommonName: "service.example",
    },
    DNSNames: []string{"service.example"},
}

This fragment uses crypto/x509 and crypto/x509/pkix. It shows the distinction between a subject name and the DNS names used for identity verification. The remaining certificate fields and signing operation depend on the issuer. Merely choosing the same text for both fields does not make the fields interchangeable.

The release also changes comparisons when a certificate name or requested name is invalid. Such names compare case-insensitively without normal hostname processing. Wildcards do not receive their usual interpretation, and trailing dots are not stripped. This is another reason to distinguish a certificate accepted through a legacy path from a certificate that follows the intended hostname representation.

Linker improvements change the build budget

The linker changes address the time and memory needed to produce a binary. For a representative collection of large Go programs on ELF-based systems running amd64, the notes report linking 20% faster while using 30% less memory on average. Those figures describe that measured set and platform group. They are not a claim that every Go build becomes 20% faster.

Compilation and linking contribute different portions of a build. A program whose build spends little time linking cannot receive the same end-to-end improvement as one dominated by the linker. The stated memory reduction also concerns linker resource use. It should not be read as a promise that the resulting service consumes 30% less memory while running.

The mechanism includes a redesigned object-file format and changes to internal phases that allow more concurrency. Applying relocations to symbols in parallel is one example named in the notes. Relocations are part of resolving references in the program being linked. Making that phase concurrent changes how the linker performs the work, while the object format changes the information it receives from compilation.

The tradeoff includes slightly larger object files than those produced by Go 1.14. Intermediate build artifacts and final executable size are separate measurements. A larger object representation can support a linker that needs less time and memory to produce the final result. The notes report the resulting costs separately instead of presenting a single undifferentiated improvement.

The compiler also removes certain garbage-collection metadata and more aggressively eliminates unused type metadata. The notes attribute a typical binary-size reduction around 5% to those changes. At the same time, amd64 code receives function alignment and jump padding to mitigate Intel erratum SKX102. Those additions increase size, but the documented overall size reduction accounts for them.

These details matter when interpreting build artifacts. A smaller final executable does not prove that every function became shorter. Metadata elimination can reduce one part of the binary while alignment expands another. The release improves the total through several changes with different local effects.

High core counts expose coordination costs

Small-object allocation improves at high core counts, with lower worst-case latency according to the release notes. That qualification identifies the affected operating conditions. It does not mean a single allocation in every program has the same relative improvement, or that allocation ceases to matter in a heavily allocating application.

The runtime also avoids allocation when converting a small integer into an interface value. That is a particular conversion improvement, separate from the general small-object allocation work. Code may benefit without changing its visible interface, but the claim should stay attached to the conversion described in the release.

I find the separation between build cost and runtime coordination useful here. Linker memory affects development and build infrastructure. Allocation latency affects the running program. Both can improve through a toolchain update, but they answer different operational questions and should retain separate measurements.

Dependency fallback becomes an explicit choice

Go 1.15 also adds a meaningful distinction to GOPROXY separators. A comma permits trying the next entry only after HTTP 404 or 410. A pipe permits trying the next entry after any error. The default remains https://proxy.golang.org,direct, so a general proxy failure does not automatically cause a direct fetch.

This is policy encoded in configuration punctuation. A deployment can choose whether an unavailable proxy should stop resolution or allow another path. That affects the behavior of dependency fetching under errors. Replacing commas with pipes is therefore a change in failure handling, even if the list of proxy addresses remains identical.

The module cache can now live at a path selected by GOMODCACHE. Its default remains the existing location beneath the first GOPATH entry. Separating that location from the rest of GOPATH gives build environments a direct way to configure cache storage. It does not change which modules a program declares as dependencies.

Another deployment-oriented addition is time/tzdata. A blank import embeds timezone data so the program can find it when the local system does not provide a timezone database. The timetzdata build tag offers another way to include it. The notes put the additional binary size at about 800 KB. That makes the cost of carrying the data explicit.

Embedding that database moves responsibility into the program artifact. A minimal runtime environment can omit system timezone files while the program still resolves the included timezone information. The cost is additional data in each binary that selects the package or tag, so the release offers the behavior explicitly instead of adding it to every program.

System-call interruption gets a library repair

The os and net packages now retry system calls that fail with EINTR. Earlier spurious failures were connected in the notes to the increased signal activity introduced by asynchronous preemption in Go 1.14. The standard library handles those interruptions transparently in these packages.

That is a useful distinction from low-level system-call wrappers an application owns. The documented improvement concerns os and net. It does not establish that every separate use of a raw platform interface receives identical retry behavior. The release repairs the library boundary it controls.

For existing programs, Go 1.15 combines lower implementation costs with changes that are visible at configuration and verification boundaries. The linker can consume fewer resources while producing the same program. Certificate hostname verification now expects the identity in Subject Alternative Names by default, and dependency fallback follows the separators selected in GOPROXY. Those behaviors remain specific even though the language itself is unchanged.