The Go PE linker does not support enough generalized PE logic to
properly handle .rsrc sections gracefully. Instead a few things are
special cased for these. The linker also does not support PE's "grouped
sections" features, in which input objects have several named sections
that are sorted, merged, and renamed in the output file. In the past,
more sophisticated support for resources or for PE features like grouped
sections have not been necessary, as Go's own object formats are pretty
vanilla, and GNU binutils also produces pretty vanilla objects where all
sections are already merged.
However, GNU binutils is lagging with arm support, and here LLVM has
picked up the slack. In particular, LLVM has its own rc/cvtres combo,
which are glued together in mingw LLVM distributions as windres, a
command line compatible tool with binutils' windres, which supports arm
and arm64. But there's a key difference between binutils' windres and
LLVM's windres: the LLVM one uses proper grouped sections.
So, this commit adds grouped sections support for resource sections to
the linker. We don't attempt to plumb generic support for grouped
sections, just as there isn't generic support already for what resources
require. Instead we augment the resource handling logic to deal with
standard two-section resource objects.
We also add a test for this, akin to the current test for more vanilla
binutils resource objects, and make sure that the rsrc tests are always
performed.
Fixes#42866.
Fixes#43182.
Change-Id: I059450021405cdf2ef1c195ddbab3960764ad711
Reviewed-on: https://go-review.googlesource.com/c/go/+/268337
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Alex Brainman <alex.brainman@gmail.com>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
When doing external linking on Windows, auto-detect the linker flavor
(bfd vs gold vs lld) and when linking with "lld", avoid the use of
"-T" (linker script), since this option is not supported by lld.
[Note: the Go linker currently employs -T to ensure proper placement
of the .debug_gdb_scripts section, to work around issues in older
versions of binutils; LLD recognizes this section and does place it
properly].
Updates #39326.
Change-Id: I3ea79cdceef2316bf86eccdb60188ac3655264ed
Reviewed-on: https://go-review.googlesource.com/c/go/+/278932
Trust: Than McIntosh <thanm@google.com>
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
When testing if a flag (e.g. "-no-pie") is supported by the
external linker, pass arch-specific flags (like "-marm").
In particular, on the ARM builder, if CGO_LDFLAGS=-march=armv6
is set, the C toolchain fails to build if -marm is not passed.
# cc -march=armv6 1.c
1.c: In function 'main':
1.c:3:1: sorry, unimplemented: Thumb-1 hard-float VFP ABI
int main() {
^~~
This makes the Go linker think "-no-pie" is not supported when it
actually is.
Passing -marm makes it work.
Fixes#43202.
Change-Id: I4e8b71f08818993cbbcb2494b310c68d812d6b50
Reviewed-on: https://go-review.googlesource.com/c/go/+/278592
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
The linker recognizes headers for 386 and amd64 PE objects, but not arm
objects. This is easily overlooked, since its the same as the 386 header
value, except the two nibbles of the first word are swapped. This commit
simply adds the check for this. Without it, .syso objects are rejected,
which means Windows binaries can't have resources built into them. At
the same time, we add comments to better indicate which condition
applies to which arch.
Change-Id: I210411d978504c1a9540e23abc5a180e24f159ad
Reviewed-on: https://go-review.googlesource.com/c/go/+/268237
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Alex Brainman <alex.brainman@gmail.com>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
This CL lets the linker code-sign output binaries on
darwin/arm64, as the kernel requires binaries must be signed in
order to run.
This signature will likely be invalidated when we stamp the
buildid after linking. We still do it in the linker, for
- plain "go tool link" works.
- the linker generates the LC_CODE_SIGNATURE load command with
the right size and offset, so we don't need to update it when
stamping the buildid.
Updates #38485, #42684.
Change-Id: Ia306328906d73217221ba31093fe61a935a46122
Reviewed-on: https://go-review.googlesource.com/c/go/+/272256
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This reverts CL 252478.
Reason for revert: debug/Elfhdr has no Flags fields, some other CLs has removed it.
Change-Id: Ie199ac29f382c56aaf37a2e8338f2dafe6e79297
Reviewed-on: https://go-review.googlesource.com/c/go/+/265317
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Meng Zhuo <mzh@golangcn.org>
The GNU strip will shrink text section while xcodetool strip don't.
We have to use xcodetool strip from system explicitly.
Fixes#41967
Change-Id: Ida372869e0ebc9e93f883640b1614836cea3672f
Reviewed-on: https://go-review.googlesource.com/c/go/+/262398
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Meng Zhuo <mzh@golangcn.org>
This CL adds support of PIE internal linking on darwin/amd64.
This is also preparation for supporting internal linking on
darwin/arm64 (macOS), which requires PIE for everything.
Updates #38485.
Change-Id: I2ed58583dcc102f5e0521982491fc7ba6f2754ed
Reviewed-on: https://go-review.googlesource.com/c/go/+/261642
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
It just works, after the plugin work.
Updates #38485.
Change-Id: I55aa11b380a33a729fccb731b77f48bc7d0dea2e
Reviewed-on: https://go-review.googlesource.com/c/go/+/259443
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Windows binaries built with -buildmode=c-shared set will have
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag set, and
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag set for windows/amd64.
ASLR can be disabled on windows by using the new linker -aslr flag.
RELNOTE=yes
Fixes#41421
Change-Id: I62bd88c6d7e0f87173b093a0ad8e1a4d269ec790
Reviewed-on: https://go-review.googlesource.com/c/go/+/255259
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Alex Brainman <alex.brainman@gmail.com>
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
2 conflicts, that make sense.
src/cmd/internal/obj/objfile.go
src/cmd/link/internal/loader/loader.go
Change-Id: Ib224e2d248cb568fa1e888af79dd908b2f5e05ff
In CL 231397, we stopped marking symbols' GoType reachable in
general, but not when -linkshared. It was left as a TODO. This CL
addresses it.
The problem was that the type names are mangled in the shared
library, so we need to mangle the name consistently in the
executable as well (regardless of whether the symbol is reachable
or not), so that the GCProg generation code can find the
corresponding symbol from the shared library.
Change-Id: I1040747402929a983ec581109f1681a77893682e
Reviewed-on: https://go-review.googlesource.com/c/go/+/255964
Trust: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
It appears the machoCalcStart function is meant to align the
segment, but it doesn't. Replace it with an actual alignment
calculation. Also, use the alignment from the configuration,
instead of hardcode.
With this fix we could enable DWARF combining on macOS ARM64.
Change-Id: I19ec771b77d752b83a54c53b6ee65af78a31b8ae
Reviewed-on: https://go-review.googlesource.com/c/go/+/253558
Reviewed-by: Than McIntosh <thanm@google.com>
On darwin, with external linking, the system linker produces STAB
(symbolic debugging) symbols in the binary's symbol table. These
include paths of the intermediate object files, like
<tmpdir>/go.o, which changes from run to run, making the build
non-reproducible.
Since we run dsymutil to produce debug info and combine them
back into the binary, we don't need those STAB symbols anymore.
Strip them after running dsymutil.
If DWARF is not enabled, we don't run dsymutil. We can pass
"-Wl,-S" to let the system linker not generate those symbols.
While here, also make it more consistent about DWARF combining.
Currently we only do DWARF combining on macOS/AMD64, when DWARF
is enabled. On ARM64, we run dsymutil, but then throw the result
away. This CL changes it to not run dsymutil (and strip) on
ARM64.
TODO: add a test. We don't do it here as it fails on some
(non-darwin) platforms.
Fixes#40979.
Change-Id: If770f7828cdb858857d6079e0585bf067f8f7a92
Reviewed-on: https://go-review.googlesource.com/c/go/+/250944
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
The linker assumed macOS is AMD64 (and 386 in the past). It
passes darwin/amd64-specific flags to the external linker when
building for macOS. They don't work for ARM64-based macOS. So
only pass them on AMD64.
Disable DWARF combining for macOS ARM64 for now. The generated
binary doesn't run. (TODO: fix.)
For macOS ARM64 port. External linking now works.
Change-Id: Iab53bc48f4fadd9b91de8898b4b450ea442667a2
Reviewed-on: https://go-review.googlesource.com/c/go/+/253019
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
I think they are no longer experimental status. Might as well promote
them to permanent.
Change-Id: Id1259601b3dd2061dd60df86ee48080bfb575d2f
Reviewed-on: https://go-review.googlesource.com/c/go/+/249857
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Switch pcdata over to content addressable symbols. This is the last
step before removing these from pclntab_old.
No meaningful benchmarks changes come from this work.
Change-Id: I3f74f3d6026a278babe437c8010e22992c92bd89
Reviewed-on: https://go-review.googlesource.com/c/go/+/247399
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Move the function names out of runtime.pclntab_old, creating
runtime.funcnametab. There is an unfortunate artifact in this change in
that calculating the funcID still requires loading the name. Future work
will likely pull this out and put it into the object file Funcs.
ls -l cmd/compile (darwin):
before: 18524016
after: 18519952
The difference in size can be attributed to alignment in pclntab_old.
Change-Id: Ibcbb230d4632178f8fcd0667165f5335786381f8
Reviewed-on: https://go-review.googlesource.com/c/go/+/243223
Reviewed-by: Austin Clements <austin@google.com>
Rename Reloc2 to Reloc, At2 to At, Aux2 to Aux.
Change-Id: Ic98d83c080e8cd80fbe1837c8f0aa134033508ce
Reviewed-on: https://go-review.googlesource.com/c/go/+/245578
Reviewed-by: Jeremy Faller <jeremy@golang.org>
We have Reloc and Reloc2. Reloc2 is the better approach and most
code uses Reloc2. There are still uses of Reloc. This CL migrates
them to Reloc2, and removes Reloc.
Change-Id: Id5f6a6019e1e044add682d05e70ebb1548ec58d9
Reviewed-on: https://go-review.googlesource.com/c/go/+/245577
Reviewed-by: Jeremy Faller <jeremy@golang.org>
We used to generate all external relocations in memory, then emit
the relocation records at a later pass. The data structures were
chosen so that it takes as little memory as possible. Now we just
stream out external relocations, and ExtReloc is just a local
variable. Change the data structure to avoid repeated read of
some fields. Also get rid of ExtRelocView, as it is no longer
necessary.
Change-Id: I40209bbe4387af231b29788125c3b4ebb0ff4a33
Reviewed-on: https://go-review.googlesource.com/c/go/+/245479
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
During the transitioning period, we mark symbols from Go shared
libraries reachable unconditionally. That might be useful when
there was still a large portion of the linker using sym.Symbols,
and only reachable symbols were converted to sym.Symbols. Marking
them reachable brings them to the dynamic symbol table, even if
they are not needed, increased the binary size unexpectedly.
That time has passed. Now we largely operate on loader symbols,
and it is not needed to mark them reachable anymore.
Fixes#40416.
Change-Id: I1e2bdb93a960ba7dc96575fabe15af93d8e95329
Reviewed-on: https://go-review.googlesource.com/c/go/+/244839
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Support streaming external relocations on ARM64. Support
architecture-specific relocations.
Also support streaming external relocations on Darwin. Do it in
the same CL so ARM64's archreloc doesn't need to support both
streaming and non-streaming.
Change-Id: Ia7fee9957892f98c065022c69a51f47402f4d6e2
Reviewed-on: https://go-review.googlesource.com/c/go/+/243644
Reviewed-by: Jeremy Faller <jeremy@golang.org>
The special case is no longer needed, didn't actually work, and
we no longer even save this map anywhere (see CL 240621 for more
information).
Change-Id: I19bcf32cace22decf50fd6414d4519cc51cbb0be
Reviewed-on: https://go-review.googlesource.com/c/go/+/241982
Reviewed-by: Austin Clements <austin@google.com>
Following CL 240399 and CL 240400, do the same for Mach-O.
Linking cmd/compile with external linking,
name old time/op new time/op delta
Asmb2_GC 32.7ms ± 2% 13.5ms ± 6% -58.56% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
Asmb2_GC 16.5MB ± 0% 6.4MB ± 0% -61.15% (p=0.008 n=5+5)
Change-Id: I0fd7019d8713d1940e5fbbce4ee8eebd926451a1
Reviewed-on: https://go-review.googlesource.com/c/go/+/241178
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
In CL 240399 we changed to precompute the size for ELF relocation
records and use mmap to write them, but we left architectures
where elfreloc1 write non-fixed number of bytes. This CL handles
those architectures. When a Go relocation will turn into multiple
ELF relocations, in relocsym we account this difference and add
it to the size calculation. So when emitting ELF relocations, we
know the number of ELF relocations to be emitted.
Change-Id: I6732ab674b442f4618405e5412a77f6e4a3315d7
Reviewed-on: https://go-review.googlesource.com/c/go/+/241079
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Now that we write ELF relocation records in mapped memory with
known sizes and offsets, we can write them in parallel.
Further speed up Asmb2 pass. Linking cmd/compile with external
linking,
Asmb2 141ms ± 4% 97ms ± 5% -30.98% (p=0.000 n=10+9)
Change-Id: I52c2b9230e90ed4421c21d7ef13a4f1e996f6054
Reviewed-on: https://go-review.googlesource.com/c/go/+/240400
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Currently, ELF relocations are generated sequentially in the heap
and flushed to output file periodically. In fact, in some cases,
the output size of the relocation records can be easily computed,
as a relocation entry has fixed size. We only need to count the
number of relocation records to compute the size.
Once the size is computed, we can mmap the output with the proper
size, and directly write relocation records in the mapped memory.
It also opens the possibility of writing relocations in parallel
(not done in this CL).
Note: on some architectures, a Go relocation may turn into
multiple ELF relocations, which makes size calculation harder.
This CL does not handle those cases, and it still writes
sequentially in the heap there.
Linking cmd/compile with external linking,
name old time/op new time/op delta
Asmb2 190ms ± 2% 141ms ± 4% -25.74% (p=0.000 n=10+10)
name old alloc/op new alloc/op delta
Asmb2_GC 66.8MB ± 0% 8.2MB ± 0% -87.79% (p=0.008 n=5+5)
name old live-B new live-B delta
Asmb2_GC 66.9M ± 0% 55.2M ± 0% -17.58% (p=0.008 n=5+5)
Change-Id: If7056bbe909dc90033eef6b9c4891fcca310602c
Reviewed-on: https://go-review.googlesource.com/c/go/+/240399
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
When linking against a Go shared library, when a global variable
in the main module has a type defined in the shared library, the
linker needs to pull the GC data from the shared library to build
the GC program for the global variable. Currently, this fails
silently, as the shared library file is closed too early and the
read failed (with no error check), causing a zero GC map emitted
for the variable, which in turn causes the runtime to treat the
variable as pointerless.
For now, fix this by keeping the file open. In the future we may
want to use mmap to read from the shared library instead.
Also add error checking. And fix a (mostly harmless) mistake in
size caluculation.
Also remove an erroneous condition for ARM64. ARM64 used to have
a special case to get the addend from the relocation on the
gcdata field. That was removed, but the new code accidentally
returned 0 unconditionally. It's no longer necessary to have any
special case, since the addend is now applied directly to the
gcdata field on ARM64, like on all the other platforms.
Fixes#39927.
This is the second attempt of CL 240462. And this reverts
CL 240616.
Change-Id: I01c82422b9f67e872d833336885935bc509bc91b
Reviewed-on: https://go-review.googlesource.com/c/go/+/240621
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
The special symbols are linker-created symbols for special
purposes, therefore reachable (otherwise the linker won't create
them). Mark them so, so they get converted to sym.Symbols when we
convert to old symbol representation.
In particular, the failure for building shared library on PPC64
is due to .TOC. symbol not being converted to sym.Symbol, but
referenced in addmoduledata.
Change-Id: Iaf5d145ffa5d15122e86a6e6983514e56dd5d456
Reviewed-on: https://go-review.googlesource.com/c/go/+/240620
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
This reverts CL 240462.
Reason for revert: test fails on PPC64LE.
Updates #39927.
Change-Id: I4f14fd0c36e604a80ae9f2f86d1e643e28945e93
Reviewed-on: https://go-review.googlesource.com/c/go/+/240616
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
When linking against a Go shared library, when a global variable
in the main module has a type defined in the shared library, the
linker needs to pull the GC data from the shared library to build
the GC program for the global variable. Currently, this fails
silently, as the shared library file is closed too early and the
read failed (with no error check), causing a zero GC map emitted
for the variable, which in turn causes the runtime to treat the
variable as pointerless.
For now, fix this by keeping the file open. In the future we may
want to use mmap to read from the shared library instead.
Also add error checking. And fix a (mostly harmless) mistake in
size caluculation.
Also remove an erroneous condition for ARM64. ARM64 used to have
a special case to get the addend from the relocation on the
gcdata field. That was removed, but the new code accidentally
returned 0 unconditionally. It's no longer necessary to have any
special case, since the addend is now applied directly to the
gcdata field on ARM64, like on all the other platforms.
Fixes#39927.
Change-Id: Iecd32315b326c7059587fdc190e2fa99426e497e
Reviewed-on: https://go-review.googlesource.com/c/go/+/240462
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Austin Clements <austin@google.com>
Currently, on most platforms, the start/end symbols runtime.text
and runtime.etext are defined in symtab pass and assigned values
in address pass. In some cases (darwin+dynlink or AIX+external),
however, they are defined and assigned values in textaddress pass
(because they need non-zero sizes). Then their values get
overwritten in address pass. This is bad. The linker expects
their values to be consistent. In particular, in CL 239281,
findfunctab is split to two parts. The two parts need to have a
consistent view of the start/end symbols. If its value changes in
between, bad things can happen.
This CL fixes it by always defining runtime.text/etext symbols in
the textaddress pass.
Fix darwin and AIX builds.
Change-Id: Ifdc1bcb69d99be1b7e5b4fd31d473650c03e3b9d
Reviewed-on: https://go-review.googlesource.com/c/go/+/240065
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
The loader's SymbolBuilder Add*/Set* methods include a call to mark
the underlying symbol as reachable (as a convenience, so that callers
would not have to set it explicitly). This code was carried over from
the corresponding sym.Symbol methods; back in the sym.Symbol world
unreachable symbols were never removed from the AllSyms slice, hence
setting and checking reachability was a good deal more important.
With the advent of the loader and the new deadcode implementation,
there is less of a need for this sort of fallback, and in addition the
implicit attr setting introduces data races in the the loader if there
are SymbolBuilder Add*/Set* method calls in parallel threads, as well
as adding overhead to the methods.
This patch gets rid of the implicit reachability setting, and instead
marks reachability in CreateSymForUpdate, as well as adding a few
explicit SetAttrReachable calls where needed.
Change-Id: I029a0c5a4a24237826a7831f9cbe5180d44cbc40
Reviewed-on: https://go-review.googlesource.com/c/go/+/237678
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Merge conflicts are mostly recently changed nm/objdump output
format and its tests. Resolved easily (mostly just using the
format on master branch).
Change-Id: I99d8410a9a02947ecf027d9cae5762861562baf5
Now the compiler-generated fingerprint is a hash of the export
data. We don't need to hash it ourselves in the linker. And the
linker doesn't need to read export data at all.
Fixes#33820.
Change-Id: I54bf3ebfd0f0c72aa43a352d7b2e0575dd62970d
Reviewed-on: https://go-review.googlesource.com/c/go/+/236119
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Safe mode in the compiler is removed in CL 142717 in Go 1.12. I
think we can delete safe mode from the linker as well.
Change-Id: I201e84fca3a566a1bb84434ab4d504516160ac4e
Reviewed-on: https://go-review.googlesource.com/c/go/+/236117
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>