[go] cmd/link: improve PE section handling

7 views
Skip to first unread message

Gerrit Bot (Gerrit)

unread,
Dec 20, 2025, 3:26:10 PM (yesterday) Dec 20
to goph...@pubsubhelper.golang.org, Zxilly Chou, golang-co...@googlegroups.com

Gerrit Bot has uploaded the change for review

Commit message

cmd/link: improve PE section handling

This change improves how the PE linker handles sections, laying
groundwork for proper section separation on Windows.

The peshbits function now handles section creation for all segment
types, similar to how elfshbits works for ELF.

Updates #76864
Updates #76038
Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
GitHub-Last-Rev: 22ce4823926d527396690d0c09af1633fb9f25bc
GitHub-Pull-Request: golang/go#76943

Change diff

diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 5b6dabb..faab424 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1828,7 +1828,7 @@
sname = ".go." + sname[len("go:"):]
}
sect := addsection(ldr, state.ctxt.Arch, seg, sname, rwx)
- sect.Align = symalign(ldr, s)
+ sect.Align = peSectionAlign(state.ctxt, symalign(ldr, s))
state.datsize = Rnd(state.datsize, int64(sect.Align))
sect.Vaddr = uint64(state.datsize)
return sect
@@ -1853,6 +1853,7 @@
}
}
}
+ sect.Align = peSectionAlign(state.ctxt, sect.Align)
state.datsize = Rnd(state.datsize, int64(sect.Align))
sect.Vaddr = uint64(state.datsize)
return sect
@@ -1946,7 +1947,7 @@
}
s := state.data[sym.SMODULEDATA][0]
sect := addsection(ldr, ctxt.Arch, &Segdata, ".go.module", 06)
- sect.Align = symalign(ldr, s)
+ sect.Align = peSectionAlign(ctxt, symalign(ldr, s))
state.datsize = Rnd(state.datsize, int64(sect.Align))
sect.Vaddr = uint64(state.datsize)
ldr.SetSymSect(s, sect)
@@ -2523,9 +2524,7 @@
// Could parallelize, by assigning to text
// and then letting threads copy down, but probably not worth it.
sect := Segtext.Sections[0]
-
- sect.Align = int32(Funcalign)
-
+ sect.Align = peSectionAlign(ctxt, int32(Funcalign))
ldr := ctxt.loader

if *flagRandLayout != 0 {
@@ -2757,7 +2756,7 @@
sect = addsection(ctxt.loader, ctxt.Arch, &Segtext, ".text", 05)

sect.Vaddr = va
- sect.Align = sectAlign
+ sect.Align = peSectionAlign(ctxt, sectAlign)
ldr.SetSymSect(s, sect)

// Create a symbol for the start of the secondary text sections
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 5cd39fb..a29063b 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -2458,7 +2458,7 @@
compressedSegName = ".zdebug_" + ldr.SymSect(s).Name[len(".debug_"):]
}
sect := addsection(ctxt.loader, ctxt.Arch, &Segdwarf, compressedSegName, 04)
- sect.Align = int32(ctxt.Arch.Alignment)
+ sect.Align = peSectionAlign(ctxt, int32(ctxt.Arch.Alignment))
sect.Length = uint64(len(z.compressed))
sect.Compressed = true
newSym := ldr.MakeSymbolBuilder(compressedSegName)
@@ -2488,7 +2488,11 @@
sect := ldr.SymSect(s)
if sect != prevSect {
if ctxt.IsWindows() {
- pos = uint64(Rnd(int64(pos), PEFILEALIGN))
+ align := int64(PEFILEALIGN)
+ if ctxt.LinkMode == LinkInternal {
+ align = PESECTALIGN
+ }
+ pos = uint64(Rnd(int64(pos), align))
}
sect.Vaddr = pos
prevSect = sect
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
index b49da42..cbf6993 100644
--- a/src/cmd/link/internal/ld/pe.go
+++ b/src/cmd/link/internal/ld/pe.go
@@ -63,6 +63,20 @@
PEFILEALIGN int64 = 2 << 8
)

+// peSectionAlign returns the appropriate section alignment for PE files.
+// For Windows internal linking, sections must be aligned to PESECTALIGN.
+func peSectionAlign(ctxt *Link, align int32) int32 {
+ if ctxt.HeadType == objabi.Hwindows && ctxt.LinkMode == LinkInternal {
+ if peAlign := int32(PESECTALIGN); align < peAlign {
+ return peAlign
+ }
+ }
+ return align
+}
+
+// peSectionHeaderReserve reserves header space for section headers.
+const peSectionHeaderReserve = 64
+
const (
IMAGE_SCN_CNT_CODE = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
@@ -379,6 +393,27 @@
}
}

+// checkSection verifies COFF section sect matches sym.Section s.
+func (sect *peSection) checkSection(s *sym.Section, linkmode LinkMode) {
+ wantVA := s.Vaddr - uint64(PEBASE)
+ if wantVA != uint64(sect.virtualAddress) {
+ Errorf("%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(wantVA)))
+ errorexit()
+ }
+ if s.Vaddr < s.Seg.Vaddr+s.Seg.Filelen {
+ wantOff := s.Seg.Fileoff + s.Vaddr - s.Seg.Vaddr
+ if wantOff != uint64(sect.pointerToRawData) {
+ Errorf("%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(wantOff)))
+ errorexit()
+ }
+ return
+ }
+ if sect.pointerToRawData != 0 && linkmode != LinkExternal {
+ Errorf("%s.PointerToRawData = %#x, want 0 for BSS", sect.name, uint64(int64(sect.pointerToRawData)))
+ errorexit()
+ }
+}
+
// pad adds zeros to the section sect. It writes as many bytes
// as necessary to make section sect.SizeOfRawData bytes long.
// It assumes that n bytes are already written to the file.
@@ -445,56 +480,52 @@
symtabOffset int64 // offset to the start of symbol table
symbolCount int // number of symbol table records written
dataDirectory [16]pe.DataDirectory
+ sectMap map[*sym.Section]*peSection // maps sym.Section to peSection
}

-// addSection adds section to the COFF file f.
+// addSection adds a dynamically created section to the COFF file f.
+// The section's virtual address and file offset are allocated from
+// f.nextSectOffset and f.nextFileOffset.
func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection {
+ return f.appendSection(name, sectsize, filesize, f.nextSectOffset, f.nextFileOffset)
+}
+
+// appendSection is the internal implementation for adding sections.
+func (f *peFile) appendSection(name string, sectsize int, filesize int, vaddr uint32, fileoff uint32) *peSection {
sect := &peSection{
name: name,
shortName: name,
index: len(f.sections) + 1,
- virtualAddress: f.nextSectOffset,
- pointerToRawData: f.nextFileOffset,
+ virtualAddress: vaddr,
+ pointerToRawData: fileoff,
}
- f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN))
if filesize > 0 {
sect.virtualSize = uint32(sectsize)
sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
- f.nextFileOffset += sect.sizeOfRawData
} else {
sect.sizeOfRawData = uint32(sectsize)
}
+ endVA := uint32(Rnd(int64(vaddr)+int64(sectsize), PESECTALIGN))
+ if endVA > f.nextSectOffset {
+ f.nextSectOffset = endVA
+ }
+ if filesize > 0 {
+ endFile := fileoff + sect.sizeOfRawData
+ if endFile > f.nextFileOffset {
+ f.nextFileOffset = endFile
+ }
+ }
f.sections = append(f.sections, sect)
return sect
}

-// addDWARFSection adds DWARF section to the COFF file f.
-// This function is similar to addSection, but DWARF section names are
-// longer than 8 characters, so they need to be stored in the string table.
-func (f *peFile) addDWARFSection(name string, size int) *peSection {
- if size == 0 {
- Exitf("DWARF section %q is empty", name)
- }
- // DWARF section names are longer than 8 characters.
- // PE format requires such names to be stored in string table,
- // and section names replaced with slash (/) followed by
- // correspondent string table index.
- // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx
- // for details
- off := f.stringTable.add(name)
- h := f.addSection(name, size, size)
- h.shortName = fmt.Sprintf("/%d", off)
- h.characteristics = IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA
- return h
-}
-
// addDWARF adds DWARF information to the COFF file f.
-func (f *peFile) addDWARF() {
+func (f *peFile) addDWARF(ctxt *Link) {
if *FlagW { // disable dwarf
return
}
for _, sect := range Segdwarf.Sections {
- h := f.addDWARFSection(sect.Name, int(sect.Length))
+ h := f.peshbits(ctxt, sect, &Segdwarf)
fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
if uint64(h.pointerToRawData) != fileoff {
Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff)
@@ -502,6 +533,60 @@
}
}

+// peshbits creates a PE section for the given sym.Section.
+func (f *peFile) peshbits(ctxt *Link, sect *sym.Section, seg *sym.Segment) *peSection {
+ if sect.Length == 0 {
+ return nil
+ }
+
+ name := sect.Name
+ shortName := name
+ if len(name) > 8 {
+ off := f.stringTable.add(name)
+ shortName = fmt.Sprintf("/%d", off)
+ }
+
+ fileSize := int(sect.Length)
+ if sect.Vaddr >= seg.Vaddr+seg.Filelen {
+ fileSize = 0 // BSS
+ }
+ fileoff := uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr)
+ h := f.appendSection(name, int(sect.Length), fileSize, uint32(sect.Vaddr-uint64(PEBASE)), fileoff)
+ h.shortName = shortName
+
+ switch seg {
+ case &Segtext:
+ h.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
+ case &Segrodata:
+ h.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ case &Segrelrodata:
+ h.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ case &Segdata:
+ if fileSize > 0 {
+ h.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
+ } else {
+ h.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
+ }
+ case &Segdwarf:
+ h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA
+ if ctxt.LinkMode == LinkExternal {
+ h.characteristics |= IMAGE_SCN_ALIGN_1BYTES
+ }
+ }
+
+ if ctxt.LinkMode == LinkExternal && seg != &Segdwarf {
+ h.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+ }
+ if fileSize == 0 {
+ h.pointerToRawData = 0
+ h.virtualSize = uint32(sect.Length)
+ h.sizeOfRawData = 0
+ }
+
+ f.sectMap[sect] = h
+ return h
+}
+
// addSEH adds SEH information to the COFF file f.
func (f *peFile) addSEH(ctxt *Link) {
// .pdata section can exist without the .xdata section.
@@ -509,27 +594,64 @@
if Segpdata.Length == 0 {
return
}
- d := pefile.addSection(".pdata", int(Segpdata.Length), int(Segpdata.Length))
- d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
- if ctxt.LinkMode == LinkExternal {
- // Some gcc versions don't honor the default alignment for the .pdata section.
- d.characteristics |= IMAGE_SCN_ALIGN_4BYTES
- }
- pefile.pdataSect = d
- d.checkSegment(&Segpdata)
- pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = d.virtualAddress
- pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = d.virtualSize
-
- if Segxdata.Length > 0 {
- d = pefile.addSection(".xdata", int(Segxdata.Length), int(Segxdata.Length))
+ for _, sect := range Segpdata.Sections {
+ fileoff := sect.Vaddr - Segpdata.Vaddr + Segpdata.Fileoff
+ d := f.appendSection(sect.Name, int(sect.Length), int(sect.Length), uint32(sect.Vaddr-uint64(PEBASE)), uint32(fileoff))
d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
if ctxt.LinkMode == LinkExternal {
- // Some gcc versions don't honor the default alignment for the .xdata section.
+ // Some gcc versions don't honor the default alignment for the .pdata section.
d.characteristics |= IMAGE_SCN_ALIGN_4BYTES
}
- pefile.xdataSect = d
- d.checkSegment(&Segxdata)
+ f.sectMap[sect] = d
+ if sect.Name == ".pdata" {
+ f.pdataSect = d
+ }
}
+ if f.pdataSect == nil {
+ return
+ }
+ f.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = f.pdataSect.virtualAddress
+ f.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = f.pdataSect.virtualSize
+
+ if Segxdata.Length > 0 {
+ for _, sect := range Segxdata.Sections {
+ fileoff := sect.Vaddr - Segxdata.Vaddr + Segxdata.Fileoff
+ d := f.appendSection(sect.Name, int(sect.Length), int(sect.Length), uint32(sect.Vaddr-uint64(PEBASE)), uint32(fileoff))
+ d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+ if ctxt.LinkMode == LinkExternal {
+ // Some gcc versions don't honor the default alignment for the .xdata section.
+ d.characteristics |= IMAGE_SCN_ALIGN_4BYTES
+ }
+ f.sectMap[sect] = d
+ if sect.Name == ".xdata" {
+ f.xdataSect = d
+ }
+ }
+ }
+}
+
+// syncNextOffsets sets next offsets to the end of the segment layout.
+func (f *peFile) syncNextOffsets() {
+ var maxFile uint64
+ var maxVA uint64
+ for _, seg := range Segments {
+ if seg.Length == 0 && seg.Filelen == 0 {
+ continue
+ }
+ fileEnd := seg.Fileoff + uint64(Rnd(int64(seg.Filelen), PEFILEALIGN))
+ if fileEnd > maxFile {
+ maxFile = fileEnd
+ }
+ vaEnd := seg.Vaddr + uint64(Rnd(int64(seg.Length), PESECTALIGN))
+ if vaEnd > maxVA {
+ maxVA = vaEnd
+ }
+ }
+ if maxVA < uint64(PEBASE) {
+ maxVA = uint64(PEBASE)
+ }
+ f.nextFileOffset = uint32(maxFile)
+ f.nextSectOffset = uint32(maxVA - uint64(PEBASE))
}

// addInitArray adds .ctors COFF section to the file f.
@@ -623,30 +745,43 @@
return int(sect.Rellen / relocLen)
}

- type relsect struct {
- peSect *peSection
- seg *sym.Segment
- syms []loader.Sym
+ emitForSection := func(sect *sym.Section, syms []loader.Sym) {
+ pesect := f.sectMap[sect]
+ if pesect == nil {
+ if sect.Length == 0 {
+ return
+ }
+ Errorf("emitRelocations: could not find %q section", sect.Name)
+ return
+ }
+ pesect.emitRelocations(ctxt.Out, func() int {
+ return relocsect(sect, syms, sect.Vaddr)
+ })
}
- sects := []relsect{
- {f.textSect, &Segtext, ctxt.Textp},
- {f.rdataSect, &Segrodata, ctxt.datap},
- {f.dataSect, &Segdata, ctxt.datap},
+
+ for _, sect := range Segtext.Sections {
+ emitForSection(sect, ctxt.Textp)
}
+ for _, sect := range Segrodata.Sections {
+ emitForSection(sect, ctxt.datap)
+ }
+ for _, sect := range Segrelrodata.Sections {
+ emitForSection(sect, ctxt.datap)
+ }
+ for _, sect := range Segdata.Sections {
+ emitForSection(sect, ctxt.datap)
+ }
+
+ // Handle SEH sections
if len(sehp.pdata) != 0 {
- sects = append(sects, relsect{f.pdataSect, &Segpdata, sehp.pdata})
+ for _, sect := range Segpdata.Sections {
+ emitForSection(sect, sehp.pdata)
+ }
}
if len(sehp.xdata) != 0 {
- sects = append(sects, relsect{f.xdataSect, &Segxdata, sehp.xdata})
- }
- for _, s := range sects {
- s.peSect.emitRelocations(ctxt.Out, func() int {
- var n int
- for _, sect := range s.seg.Sections {
- n += relocsect(sect, s.syms, s.seg.Vaddr)
- }
- return n
- })
+ for _, sect := range Segxdata.Sections {
+ emitForSection(sect, sehp.xdata)
+ }
}

dwarfLoop:
@@ -657,13 +792,11 @@
ldr.SymSect(si.secSym()) != sect {
panic("inconsistency between dwarfp and Segdwarf")
}
- for _, pesect := range f.sections {
- if sect.Name == pesect.name {
- pesect.emitRelocations(ctxt.Out, func() int {
- return relocsect(sect, si.syms, sect.Vaddr)
- })
- continue dwarfLoop
- }
+ if pesect := f.sectMap[sect]; pesect != nil {
+ pesect.emitRelocations(ctxt.Out, func() int {
+ return relocsect(sect, si.syms, sect.Vaddr)
+ })
+ continue dwarfLoop
}
Errorf("emitRelocations: could not find %q section", sect.Name)
}
@@ -719,28 +852,14 @@
if sect == nil {
return 0, 0, fmt.Errorf("could not map %s symbol with no section", ldr.SymName(s))
}
- if sect.Seg == &Segtext {
- return f.textSect.index, int64(uint64(ldr.SymValue(s)) - Segtext.Vaddr), nil
+
+ if pesect, ok := f.sectMap[sect]; ok {
+ offset = int64(uint64(ldr.SymValue(s)) - sect.Vaddr)
+ return pesect.index, offset, nil
}
- if sect.Seg == &Segrodata {
- return f.rdataSect.index, int64(uint64(ldr.SymValue(s)) - Segrodata.Vaddr), nil
- }
- if sect.Seg != &Segdata {
- return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", ldr.SymName(s))
- }
- v := uint64(ldr.SymValue(s)) - Segdata.Vaddr
- if linkmode != LinkExternal {
- return f.dataSect.index, int64(v), nil
- }
- if ldr.SymType(s).IsDATA() {
- return f.dataSect.index, int64(v), nil
- }
- // Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section
- // it still belongs to the .data section, not the .bss section.
- if v < Segdata.Filelen {
- return f.dataSect.index, int64(v), nil
- }
- return f.bssSect.index, int64(v - Segdata.Filelen), nil
+
+ return 0, 0, fmt.Errorf("could not map %s symbol: section %s not in sectMap",
+ ldr.SymName(s), sect.Name)
}

var isLabel = make(map[loader.Sym]bool)
@@ -952,12 +1071,39 @@
func (f *peFile) writeOptionalHeader(ctxt *Link) {
var oh pe.OptionalHeader32
var oh64 pe.OptionalHeader64
+ sumSections := func(mask uint32) uint32 {
+ var total uint32
+ for _, sect := range f.sections {
+ if sect.characteristics&mask == mask {
+ total += sect.sizeOfRawData
+ }
+ }
+ return total
+ }
+ minVA := func(mask uint32) (uint32, bool) {
+ var min uint32
+ var ok bool
+ for _, sect := range f.sections {
+ if sect.characteristics&mask != mask {
+ continue
+ }
+ if !ok || sect.virtualAddress < min {
+ min = sect.virtualAddress
+ ok = true
+ }
+ }
+ return min, ok
+ }

if pe64 {
oh64.Magic = 0x20b // PE32+
} else {
oh.Magic = 0x10b // PE32
- oh.BaseOfData = f.dataSect.virtualAddress
+ if f.dataSect != nil {
+ oh.BaseOfData = f.dataSect.virtualAddress
+ } else if base, ok := minVA(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE); ok {
+ oh.BaseOfData = base
+ }
}

// Fill out both oh64 and oh. We only use one. Oh well.
@@ -965,18 +1111,23 @@
oh.MajorLinkerVersion = 3
oh64.MinorLinkerVersion = 0
oh.MinorLinkerVersion = 0
- oh64.SizeOfCode = f.textSect.sizeOfRawData
- oh.SizeOfCode = f.textSect.sizeOfRawData
- oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData
- oh.SizeOfInitializedData = f.dataSect.sizeOfRawData
+ oh64.SizeOfCode = sumSections(IMAGE_SCN_CNT_CODE)
+ oh.SizeOfCode = oh64.SizeOfCode
+ oh64.SizeOfInitializedData = sumSections(IMAGE_SCN_CNT_INITIALIZED_DATA)
+ oh.SizeOfInitializedData = oh64.SizeOfInitializedData
oh64.SizeOfUninitializedData = 0
oh.SizeOfUninitializedData = 0
if ctxt.LinkMode != LinkExternal {
oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
}
- oh64.BaseOfCode = f.textSect.virtualAddress
- oh.BaseOfCode = f.textSect.virtualAddress
+ if f.textSect != nil {
+ oh64.BaseOfCode = f.textSect.virtualAddress
+ oh.BaseOfCode = f.textSect.virtualAddress
+ } else if base, ok := minVA(IMAGE_SCN_CNT_CODE); ok {
+ oh64.BaseOfCode = base
+ oh.BaseOfCode = base
+ }
oh64.ImageBase = uint64(PEBASE)
oh.ImageBase = uint32(PEBASE)
oh64.SectionAlignment = uint32(PESECTALIGN)
@@ -1121,7 +1272,7 @@
}
}

- var sh [16]pe.SectionHeader32
+ var sh [peSectionHeaderReserve]pe.SectionHeader32
var fh pe.FileHeader
PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN))
if ctxt.LinkMode != LinkExternal {
@@ -1274,10 +1425,14 @@
return dlls
}

-func addimports(ctxt *Link, datsect *peSection) {
+func addimports(ctxt *Link) {
ldr := ctxt.loader
startoff := ctxt.Out.Offset()
dynamic := ldr.LookupOrCreateSym(".windynamic", 0)
+ dynsect := pefile.sectMap[ldr.SymSect(dynamic)]
+ if dynsect == nil {
+ Exitf("missing PE section for .windynamic")
+ }

// skip import descriptor table (will write it later)
n := uint64(0)
@@ -1332,10 +1487,10 @@
isect.pad(ctxt.Out, uint32(n))
endoff := ctxt.Out.Offset()

- // write FirstThunks (allocated in .data section)
- ftbase := uint64(ldr.SymValue(dynamic)) - uint64(datsect.virtualAddress) - uint64(PEBASE)
+ // write FirstThunks (allocated in .windynamic section)
+ ftbase := uint64(ldr.SymValue(dynamic)) - uint64(dynsect.virtualAddress) - uint64(PEBASE)

- ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase))
+ ctxt.Out.SeekSet(int64(uint64(dynsect.pointerToRawData) + ftbase))
for d := dr; d != nil; d = d.next {
for m := d.ms; m != nil; m = m.next {
if pe64 {
@@ -1361,7 +1516,7 @@
out.Write32(0)
out.Write32(0)
out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff))
- out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff))
+ out.Write32(uint32(uint64(dynsect.virtualAddress) + ftbase + d.thunkoff))
}

out.Write32(0) //end
@@ -1692,46 +1847,52 @@
}

func asmbPe(ctxt *Link) {
- t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
- t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
- if ctxt.LinkMode == LinkExternal {
- // some data symbols (e.g. masks) end up in the .text section, and they normally
- // expect larger alignment requirement than the default text section alignment.
- t.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+ pefile.sectMap = make(map[*sym.Section]*peSection)
+
+ for _, sect := range Segtext.Sections {
+ h := pefile.peshbits(ctxt, sect, &Segtext)
+ if h == nil {
+ continue
+ }
+ if sect.Name == ".text" && pefile.textSect == nil {
+ pefile.textSect = h
+ h.checkSection(sect, ctxt.LinkMode)
+ }
}
- t.checkSegment(&Segtext)
- pefile.textSect = t
-
- ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length))
- ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
- if ctxt.LinkMode == LinkExternal {
- // some data symbols (e.g. masks) end up in the .rdata section, and they normally
- // expect larger alignment requirement than the default text section alignment.
- ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES
+ for _, sect := range Segrodata.Sections {
+ h := pefile.peshbits(ctxt, sect, &Segrodata)
+ if h == nil {
+ continue
+ }
+ if sect.Name == ".rodata" {
+ pefile.rdataSect = h
+ h.checkSection(sect, ctxt.LinkMode)
+ } else if pefile.rdataSect == nil {
+ pefile.rdataSect = h
+ }
}
- ro.checkSegment(&Segrodata)
- pefile.rdataSect = ro
-
- var d *peSection
- if ctxt.LinkMode != LinkExternal {
- d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen))
- d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
- d.checkSegment(&Segdata)
- pefile.dataSect = d
- } else {
- d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen))
- d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
- d.checkSegment(&Segdata)
- pefile.dataSect = d
-
- b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0)
- b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
- b.pointerToRawData = 0
- pefile.bssSect = b
+ for _, sect := range Segrelrodata.Sections {
+ _ = pefile.peshbits(ctxt, sect, &Segrelrodata)
+ }
+ for _, sect := range Segdata.Sections {
+ h := pefile.peshbits(ctxt, sect, &Segdata)
+ if h == nil {
+ continue
+ }
+ if sect.Name == ".data" {
+ pefile.dataSect = h
+ h.checkSection(sect, ctxt.LinkMode)
+ } else if pefile.dataSect == nil && h.characteristics&IMAGE_SCN_MEM_WRITE != 0 && h.characteristics&IMAGE_SCN_CNT_INITIALIZED_DATA != 0 {
+ pefile.dataSect = h
+ }
+ if sect.Name == ".bss" {
+ pefile.bssSect = h
+ }
}

pefile.addSEH(ctxt)
- pefile.addDWARF()
+ pefile.addDWARF(ctxt)
+ pefile.syncNextOffsets()

if ctxt.LinkMode == LinkExternal {
pefile.ctorsSect = pefile.addInitArray(ctxt)
@@ -1739,7 +1900,7 @@

ctxt.Out.SeekSet(int64(pefile.nextFileOffset))
if ctxt.LinkMode != LinkExternal {
- addimports(ctxt, d)
+ addimports(ctxt)
addexports(ctxt)
addPEBaseReloc(ctxt)
}
@@ -1748,6 +1909,9 @@
if ctxt.LinkMode == LinkExternal {
pefile.emitRelocations(ctxt)
}
+ if len(pefile.sections) > peSectionHeaderReserve {
+ Exitf("too many PE sections (%d), increase peSectionHeaderReserve", len(pefile.sections))
+ }

pewrite(ctxt)
}
diff --git a/src/cmd/link/internal/ld/pe_test.go b/src/cmd/link/internal/ld/pe_test.go
new file mode 100644
index 0000000..2e15ce6
--- /dev/null
+++ b/src/cmd/link/internal/ld/pe_test.go
@@ -0,0 +1,460 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build windows
+
+package ld
+
+import (
+ "debug/pe"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "path/filepath"
+ "sort"
+ "testing"
+)
+
+func TestPESectionsReadOnly(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+
+ const (
+ prog = `package main; func main() {}`
+ progC = `package main; import "C"; func main() {}`
+ )
+
+ tests := []struct {
+ name string
+ args []string
+ prog string
+ wantSecsRO []string
+ wantSecsROIfPresent []string
+ mustHaveCGO bool
+ mustInternalLink bool
+ }{
+ {
+ name: "linkmode-internal",
+ args: []string{"-ldflags", "-linkmode=internal"},
+ prog: prog,
+ mustInternalLink: true,
+ wantSecsRO: []string{".rodata", ".gopclntab"},
+ wantSecsROIfPresent: []string{
+ ".typelink",
+ ".itablink",
+ },
+ },
+ {
+ name: "linkmode-external",
+ args: []string{"-ldflags", "-linkmode=external"},
+ prog: progC,
+ mustHaveCGO: true,
+ wantSecsRO: []string{".rodata", ".gopclntab"},
+ wantSecsROIfPresent: []string{
+ ".typelink",
+ ".itablink",
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ if test.mustInternalLink {
+ testenv.MustInternalLink(t, testenv.SpecialBuildTypes{Cgo: test.mustHaveCGO})
+ }
+ if test.mustHaveCGO {
+ testenv.MustHaveCGO(t)
+ }
+
+ dir := t.TempDir()
+ src := filepath.Join(dir, fmt.Sprintf("pe_%s.go", test.name))
+ binFile := filepath.Join(dir, test.name)
+
+ if err := os.WriteFile(src, []byte(test.prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmdArgs := append([]string{"build", "-o", binFile}, append(test.args, src)...)
+ cmd := testenv.Command(t, testenv.GoToolPath(t), cmdArgs...)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("failed to build %v: %v:\n%s", cmd.Args, err, out)
+ }
+
+ pf, err := pe.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open PE file: %v", err)
+ }
+ defer pf.Close()
+
+ secByName := make(map[string]*pe.Section, len(pf.Sections))
+ for _, sec := range pf.Sections {
+ secByName[sec.Name] = sec
+ }
+
+ checkRO := func(name string, required bool) {
+ sec := secByName[name]
+ if sec == nil {
+ if required {
+ t.Fatalf("test %s: can't locate %q section", test.name, name)
+ }
+ return
+ }
+ if sec.Characteristics&pe.IMAGE_SCN_MEM_READ == 0 {
+ t.Errorf("section %s missing IMAGE_SCN_MEM_READ", name)
+ }
+ if sec.Characteristics&pe.IMAGE_SCN_MEM_WRITE != 0 {
+ t.Errorf("section %s unexpectedly writable", name)
+ }
+ }
+
+ for _, name := range test.wantSecsRO {
+ checkRO(name, true)
+ }
+ for _, name := range test.wantSecsROIfPresent {
+ checkRO(name, false)
+ }
+ })
+ }
+}
+
+func TestPESectionAlignment(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t, testenv.SpecialBuildTypes{})
+
+ const prog = `package main; func main() { println("hello") }`
+
+ dir := t.TempDir()
+ src := filepath.Join(dir, "align.go")
+ binFile := filepath.Join(dir, "align.exe")
+
+ if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, "-ldflags", "-linkmode=internal", src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("failed to build: %v:\n%s", err, out)
+ }
+
+ pf, err := pe.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open PE file: %v", err)
+ }
+ defer pf.Close()
+
+ // Get section alignment from optional header
+ var sectionAlignment uint32
+ switch oh := pf.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ sectionAlignment = oh.SectionAlignment
+ case *pe.OptionalHeader64:
+ sectionAlignment = oh.SectionAlignment
+ default:
+ t.Fatal("unknown optional header type")
+ }
+
+ if sectionAlignment != 0x1000 {
+ t.Errorf("unexpected section alignment: got %#x, want %#x", sectionAlignment, 0x1000)
+ }
+
+ // Verify all sections are aligned to section alignment
+ for _, sec := range pf.Sections {
+ if sec.VirtualAddress%sectionAlignment != 0 {
+ t.Errorf("section %s virtual address %#x not aligned to %#x",
+ sec.Name, sec.VirtualAddress, sectionAlignment)
+ }
+ }
+}
+
+func TestPESectionCharacteristics(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t, testenv.SpecialBuildTypes{})
+
+ const prog = `
+package main
+
+var globalData = []int{1, 2, 3}
+var globalBss [1024]byte
+
+func main() {
+ println(globalData[0])
+ globalBss[0] = 1
+}
+`
+
+ dir := t.TempDir()
+ src := filepath.Join(dir, "chars.go")
+ binFile := filepath.Join(dir, "chars.exe")
+
+ if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, "-ldflags", "-linkmode=internal", src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("failed to build: %v:\n%s", err, out)
+ }
+
+ pf, err := pe.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open PE file: %v", err)
+ }
+ defer pf.Close()
+
+ tests := []struct {
+ name string
+ wantFlags uint32
+ wantClear uint32
+ }{
+ {
+ name: ".text",
+ wantFlags: pe.IMAGE_SCN_CNT_CODE | pe.IMAGE_SCN_MEM_EXECUTE | pe.IMAGE_SCN_MEM_READ,
+ wantClear: pe.IMAGE_SCN_MEM_WRITE,
+ },
+ {
+ name: ".rodata",
+ wantFlags: pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ,
+ wantClear: pe.IMAGE_SCN_MEM_WRITE | pe.IMAGE_SCN_MEM_EXECUTE,
+ },
+ {
+ name: ".data",
+ wantFlags: pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ | pe.IMAGE_SCN_MEM_WRITE,
+ wantClear: pe.IMAGE_SCN_MEM_EXECUTE,
+ },
+ }
+
+ for _, test := range tests {
+ sec := pf.Section(test.name)
+ if sec == nil {
+ t.Errorf("section %s not found", test.name)
+ continue
+ }
+ if sec.Characteristics&test.wantFlags != test.wantFlags {
+ t.Errorf("section %s: want flags %#x set, got characteristics %#x",
+ test.name, test.wantFlags, sec.Characteristics)
+ }
+ if sec.Characteristics&test.wantClear != 0 {
+ t.Errorf("section %s: want flags %#x clear, got characteristics %#x",
+ test.name, test.wantClear, sec.Characteristics)
+ }
+ }
+}
+
+func TestPESectionNoOverlap(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t, testenv.SpecialBuildTypes{})
+
+ const prog = `
+package main
+
+import "reflect"
+
+type A string
+type B int
+
+type describer interface{ What() string }
+
+func (a *A) What() string { return "string" }
+func (b *B) What() string { return "int" }
+
+func example(a A, b B) describer {
+ if b == 1 {
+ return &a
+ }
+ return &b
+}
+
+func main() {
+ println(example("", 1).What())
+ println(reflect.TypeOf(example("", 1)).String())
+}
+`
+
+ dir := t.TempDir()
+ src := filepath.Join(dir, "overlap.go")
+ binFile := filepath.Join(dir, "overlap.exe")
+
+ if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, "-ldflags", "-linkmode=internal", src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("failed to build: %v:\n%s", err, out)
+ }
+
+ pf, err := pe.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open PE file: %v", err)
+ }
+ defer pf.Close()
+
+ // Collect sections with non-zero virtual address and size
+ type secInfo struct {
+ name string
+ start uint32
+ end uint32
+ }
+ var secs []secInfo
+ for _, s := range pf.Sections {
+ if s.VirtualAddress == 0 || s.VirtualSize == 0 {
+ continue
+ }
+ secs = append(secs, secInfo{
+ name: s.Name,
+ start: s.VirtualAddress,
+ end: s.VirtualAddress + s.VirtualSize,
+ })
+ }
+
+ // Sort by start address
+ sort.Slice(secs, func(i, j int) bool {
+ return secs[i].start < secs[j].start
+ })
+
+ // Check for overlaps
+ for i := 0; i < len(secs)-1; i++ {
+ for j := i + 1; j < len(secs); j++ {
+ s1, s2 := secs[i], secs[j]
+ // Check if they overlap: max(start1, start2) < min(end1, end2)
+ maxStart := s1.start
+ if s2.start > maxStart {
+ maxStart = s2.start
+ }
+ minEnd := s1.end
+ if s2.end < minEnd {
+ minEnd = s2.end
+ }
+ if maxStart < minEnd {
+ t.Errorf("sections overlap: %s [%#x-%#x] and %s [%#x-%#x]",
+ s1.name, s1.start, s1.end, s2.name, s2.start, s2.end)
+ }
+ }
+ }
+}
+
+func TestPEDWARFSections(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t, testenv.SpecialBuildTypes{})
+
+ const prog = `package main; func main() { println("hello") }`
+
+ dir := t.TempDir()
+ src := filepath.Join(dir, "dwarf.go")
+ binFile := filepath.Join(dir, "dwarf.exe")
+
+ if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ // Build without -w to include DWARF
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, "-ldflags", "-linkmode=internal", src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("failed to build: %v:\n%s", err, out)
+ }
+
+ pf, err := pe.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open PE file: %v", err)
+ }
+ defer pf.Close()
+
+ // DWARF sections should be present and have correct characteristics
+ dwarfSections := []string{".debug_abbrev", ".debug_info", ".debug_line"}
+ for _, name := range dwarfSections {
+ sec := pf.Section(name)
+ if sec == nil {
+ // DWARF section names > 8 chars are stored in string table
+ // and section name becomes /N where N is offset
+ // Try to find by checking DWARF data
+ continue
+ }
+
+ // DWARF sections should be readable and discardable
+ if sec.Characteristics&pe.IMAGE_SCN_MEM_READ == 0 {
+ t.Errorf("DWARF section %s missing IMAGE_SCN_MEM_READ", name)
+ }
+ if sec.Characteristics&pe.IMAGE_SCN_MEM_DISCARDABLE == 0 {
+ t.Errorf("DWARF section %s missing IMAGE_SCN_MEM_DISCARDABLE", name)
+ }
+ if sec.Characteristics&pe.IMAGE_SCN_MEM_WRITE != 0 {
+ t.Errorf("DWARF section %s unexpectedly writable", name)
+ }
+ }
+
+ // Verify DWARF data is accessible
+ dwarfData, err := pf.DWARF()
+ if err != nil {
+ t.Fatalf("failed to get DWARF data: %v", err)
+ }
+ if dwarfData == nil {
+ t.Error("DWARF data is nil")
+ }
+}
+
+func TestPEOptionalHeaderSizes(t *testing.T) {
+ t.Parallel()
+ testenv.MustHaveGoBuild(t)
+ testenv.MustInternalLink(t, testenv.SpecialBuildTypes{})
+
+ const prog = `package main; func main() {}`
+
+ dir := t.TempDir()
+ src := filepath.Join(dir, "header.go")
+ binFile := filepath.Join(dir, "header.exe")
+
+ if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, "-ldflags", "-linkmode=internal", src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("failed to build: %v:\n%s", err, out)
+ }
+
+ pf, err := pe.Open(binFile)
+ if err != nil {
+ t.Fatalf("failed to open PE file: %v", err)
+ }
+ defer pf.Close()
+
+ switch oh := pf.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ if oh.Magic != 0x10b {
+ t.Errorf("32-bit magic: got %#x, want %#x", oh.Magic, 0x10b)
+ }
+ if oh.SizeOfHeaders == 0 {
+ t.Error("SizeOfHeaders is 0")
+ }
+ if oh.SizeOfImage == 0 {
+ t.Error("SizeOfImage is 0")
+ }
+ // Verify SizeOfImage is aligned to section alignment
+ if oh.SizeOfImage%oh.SectionAlignment != 0 {
+ t.Errorf("SizeOfImage %#x not aligned to SectionAlignment %#x",
+ oh.SizeOfImage, oh.SectionAlignment)
+ }
+ case *pe.OptionalHeader64:
+ if oh.Magic != 0x20b {
+ t.Errorf("64-bit magic: got %#x, want %#x", oh.Magic, 0x20b)
+ }
+ if oh.SizeOfHeaders == 0 {
+ t.Error("SizeOfHeaders is 0")
+ }
+ if oh.SizeOfImage == 0 {
+ t.Error("SizeOfImage is 0")
+ }
+ // Verify SizeOfImage is aligned to section alignment
+ if oh.SizeOfImage%oh.SectionAlignment != 0 {
+ t.Errorf("SizeOfImage %#x not aligned to SectionAlignment %#x",
+ oh.SizeOfImage, oh.SectionAlignment)
+ }
+ default:
+ t.Fatal("unknown optional header type")
+ }
+}
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index bc7504e..98437a8 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -2205,16 +2205,77 @@
}
}

- case pf != nil, xf != nil:
- if pf != nil {
- defer pf.Close()
+ case pf != nil:
+ defer pf.Close()
+
+ var moddataSym *pe.Symbol
+ for _, sym := range pf.Symbols {
+ if sym.Name == moddataSymName || sym.Name == "_"+moddataSymName {
+ moddataSym = sym
+ break
+ }
}
- if xf != nil {
- defer xf.Close()
+ if moddataSym == nil {
+ t.Fatalf("could not find symbol %s", moddataSymName)
+ }
+ if moddataSym.SectionNumber <= 0 {
+ t.Fatalf("moduledata not in a section (section number %d)", moddataSym.SectionNumber)
+ }
+ sec := pf.Sections[moddataSym.SectionNumber-1]
+ if sec.Name != ".go.module" {
+ t.Errorf("moduledata in section %s, not .go.module", sec.Name)
+ }
+ if moddataSym.Value != 0 {
+ t.Errorf("moduledata offset %#x != 0", moddataSym.Value)
}

- // On Windows and AIX all the Go specific sections
+ case xf != nil:
+ defer xf.Close()
+
+ // On AIX all the Go specific sections
// get stuffed into a few sections,
// so there is nothing to test here.
}
}
+
+func TestPEEdataSection(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ tmpdir := t.TempDir()
+ src := filepath.Join(tmpdir, "x.go")
+ if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(tmpdir, "x.exe")
+ cmd := goCmd(t, "build", "-o", exe, src)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("build failed; %v, output:\n%s", err, out)
+ }
+
+ pf, err := pe.Open(exe)
+ if err != nil {
+ t.Skip("not a PE executable")
+ }
+ defer pf.Close()
+
+ var edataSym *pe.Symbol
+ for _, sym := range pf.Symbols {
+ if sym.Name == "runtime.edata" || sym.Name == "_runtime.edata" {
+ edataSym = sym
+ break
+ }
+ }
+ if edataSym == nil {
+ t.Fatal("could not find symbol runtime.edata")
+ }
+ if edataSym.SectionNumber <= 0 {
+ t.Fatalf("runtime.edata not in a section (section number %d)", edataSym.SectionNumber)
+ }
+
+ sec := pf.Sections[edataSym.SectionNumber-1]
+ if sec.Name != ".data" {
+ t.Errorf("runtime.edata in section %s, not .data", sec.Name)
+ }
+}

Change information

Files:
  • M src/cmd/link/internal/ld/data.go
  • M src/cmd/link/internal/ld/dwarf.go
  • M src/cmd/link/internal/ld/pe.go
  • A src/cmd/link/internal/ld/pe_test.go
  • M src/cmd/link/link_test.go
Change size: L
Delta: 5 files changed, 841 insertions(+), 153 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
Gerrit-Change-Number: 731740
Gerrit-PatchSet: 1
Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Ian Lance Taylor (Gerrit)

unread,
Dec 20, 2025, 5:29:56 PM (yesterday) Dec 20
to Zxilly Chou, Gerrit Bot, goph...@pubsubhelper.golang.org, Ian Lance Taylor, Cherry Mui, Russ Cox, Gopher Robot, golang-co...@googlegroups.com
Attention needed from Cherry Mui and Russ Cox

Ian Lance Taylor voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention is currently required from:
  • Cherry Mui
  • Russ Cox
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
Gerrit-Change-Number: 731740
Gerrit-PatchSet: 1
Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
Gerrit-Reviewer: Cherry Mui <cher...@google.com>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Gopher Robot <go...@golang.org>
Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Attention: Cherry Mui <cher...@google.com>
Gerrit-Comment-Date: Sat, 20 Dec 2025 22:29:50 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Zxilly Chou (Gerrit)

unread,
2:04 AM (21 hours ago) 2:04 AM
to Gerrit Bot, goph...@pubsubhelper.golang.org, Go LUCI, Ian Lance Taylor, Cherry Mui, Russ Cox, Gopher Robot, golang-co...@googlegroups.com
Attention needed from Cherry Mui, Ian Lance Taylor and Russ Cox

Zxilly Chou added 1 comment

Patchset-level comments
File-level comment, Patchset 1 (Latest):
Zxilly Chou . unresolved

I am unable to reproduce the error locally. My local gcc is

gcc.exe (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.2.0
Copyright (C) 2024 Free Software Foundation
This software is free software; copy it and/or other works based on it under the terms of this license only.
THERE IS NO WARRANTY, NOT EVEN A VERBAL OR WRITTEN WARRANTY, OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

Open in Gerrit

Related details

Attention is currently required from:
  • Cherry Mui
  • Ian Lance Taylor
  • Russ Cox
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
    Gerrit-Change-Number: 731740
    Gerrit-PatchSet: 1
    Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
    Gerrit-Reviewer: Cherry Mui <cher...@google.com>
    Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
    Gerrit-Reviewer: Russ Cox <r...@golang.org>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
    Gerrit-Attention: Russ Cox <r...@golang.org>
    Gerrit-Attention: Cherry Mui <cher...@google.com>
    Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
    Gerrit-Comment-Date: Sun, 21 Dec 2025 07:04:42 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Zxilly Chou (Gerrit)

    unread,
    4:03 AM (19 hours ago) 4:03 AM
    to Gerrit Bot, goph...@pubsubhelper.golang.org, Go LUCI, Ian Lance Taylor, Cherry Mui, Russ Cox, Gopher Robot, golang-co...@googlegroups.com
    Attention needed from Cherry Mui, Ian Lance Taylor and Russ Cox

    Zxilly Chou added 1 comment

    Patchset-level comments
    Zxilly Chou . resolved

    I am unable to reproduce the error locally. My local gcc is

    gcc.exe (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.2.0
    Copyright (C) 2024 Free Software Foundation
    This software is free software; copy it and/or other works based on it under the terms of this license only.
    THERE IS NO WARRANTY, NOT EVEN A VERBAL OR WRITTEN WARRANTY, OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

    Zxilly Chou

    Done

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Cherry Mui
    • Ian Lance Taylor
    • Russ Cox
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
      Gerrit-Change-Number: 731740
      Gerrit-PatchSet: 1
      Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
      Gerrit-Reviewer: Cherry Mui <cher...@google.com>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Russ Cox <r...@golang.org>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
      Gerrit-Attention: Russ Cox <r...@golang.org>
      Gerrit-Attention: Cherry Mui <cher...@google.com>
      Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Comment-Date: Sun, 21 Dec 2025 09:03:23 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Zxilly Chou <zxi...@outlook.com>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Gerrit Bot (Gerrit)

      unread,
      4:16 AM (19 hours ago) 4:16 AM
      to Zxilly Chou, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Cherry Mui, Ian Lance Taylor and Russ Cox

      Gerrit Bot uploaded new patchset

      Gerrit Bot uploaded patch set #2 to this change.
      Following approvals got outdated and were removed:
      • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Cherry Mui
      • Ian Lance Taylor
      • Russ Cox
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
      Gerrit-Change-Number: 731740
      Gerrit-PatchSet: 2
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Meng Zhuo (Gerrit)

      unread,
      5:20 AM (18 hours ago) 5:20 AM
      to Zxilly Chou, Gerrit Bot, goph...@pubsubhelper.golang.org, Meng Zhuo, Go LUCI, Ian Lance Taylor, Cherry Mui, Russ Cox, Gopher Robot, golang-co...@googlegroups.com
      Attention needed from Cherry Mui, Ian Lance Taylor and Russ Cox

      Meng Zhuo voted Commit-Queue+1

      Commit-Queue+1
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Cherry Mui
      • Ian Lance Taylor
      • Russ Cox
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
      Gerrit-Change-Number: 731740
      Gerrit-PatchSet: 2
      Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
      Gerrit-Reviewer: Cherry Mui <cher...@google.com>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Reviewer: Russ Cox <r...@golang.org>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
      Gerrit-Attention: Russ Cox <r...@golang.org>
      Gerrit-Attention: Cherry Mui <cher...@google.com>
      Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Comment-Date: Sun, 21 Dec 2025 10:20:46 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Gerrit Bot (Gerrit)

      unread,
      7:10 AM (16 hours ago) 7:10 AM
      to Zxilly Chou, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
      Attention needed from Cherry Mui, Ian Lance Taylor, Meng Zhuo and Russ Cox

      Gerrit Bot uploaded new patchset

      Gerrit Bot uploaded patch set #3 to this change.
      Following approvals got outdated and were removed:
      • TryBots-Pass: LUCI-TryBot-Result-1 by Go LUCI
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Cherry Mui
      • Ian Lance Taylor
      • Meng Zhuo
      • Russ Cox
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
      Gerrit-Change-Number: 731740
      Gerrit-PatchSet: 3
      Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
      Gerrit-Reviewer: Cherry Mui <cher...@google.com>
      Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
      Gerrit-Reviewer: Russ Cox <r...@golang.org>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
      Gerrit-Attention: Russ Cox <r...@golang.org>
      Gerrit-Attention: Cherry Mui <cher...@google.com>
      Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
      Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      xie cui (Gerrit)

      unread,
      9:45 AM (13 hours ago) 9:45 AM
      to Zxilly Chou, Gerrit Bot, goph...@pubsubhelper.golang.org, Go LUCI, Meng Zhuo, Ian Lance Taylor, Cherry Mui, Russ Cox, Gopher Robot, golang-co...@googlegroups.com
      Attention needed from Cherry Mui, Ian Lance Taylor, Meng Zhuo and Russ Cox

      xie cui voted Run-TryBot+1

      Run-TryBot+1
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Cherry Mui
      • Ian Lance Taylor
      • Meng Zhuo
      • Russ Cox
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedLegacy-TryBots-Pass
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement is not satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
        Gerrit-Change-Number: 731740
        Gerrit-PatchSet: 3
        Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
        Gerrit-Reviewer: Cherry Mui <cher...@google.com>
        Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
        Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
        Gerrit-Reviewer: Russ Cox <r...@golang.org>
        Gerrit-Reviewer: xie cui <cuiw...@gmail.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
        Gerrit-Attention: Russ Cox <r...@golang.org>
        Gerrit-Attention: Cherry Mui <cher...@google.com>
        Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
        Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
        Gerrit-Comment-Date: Sun, 21 Dec 2025 14:45:29 +0000
        Gerrit-HasComments: No
        Gerrit-Has-Labels: Yes
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Gerrit Bot (Gerrit)

        unread,
        12:39 PM (10 hours ago) 12:39 PM
        to Zxilly Chou, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Cherry Mui, Ian Lance Taylor, Meng Zhuo, Russ Cox and xie cui

        Gerrit Bot uploaded new patchset

        Gerrit Bot uploaded patch set #4 to this change.
        Following approvals got outdated and were removed:
        • Legacy-TryBots-Pass: Run-TryBot+1 by xie cui
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Cherry Mui
        • Ian Lance Taylor
        • Meng Zhuo
        • Russ Cox
        • xie cui
        Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: newpatchset
          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
          Gerrit-Change-Number: 731740
          Gerrit-PatchSet: 4
          Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
          Gerrit-Reviewer: Cherry Mui <cher...@google.com>
          Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
          Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
          Gerrit-Reviewer: Russ Cox <r...@golang.org>
          Gerrit-Reviewer: xie cui <cuiw...@gmail.com>
          Gerrit-CC: Gopher Robot <go...@golang.org>
          Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
          Gerrit-Attention: Russ Cox <r...@golang.org>
          Gerrit-Attention: Cherry Mui <cher...@google.com>
          Gerrit-Attention: xie cui <cuiw...@gmail.com>
          unsatisfied_requirement
          open
          diffy

          Ian Lance Taylor (Gerrit)

          unread,
          1:33 PM (10 hours ago) 1:33 PM
          to Zxilly Chou, Gerrit Bot, goph...@pubsubhelper.golang.org, Ian Lance Taylor, xie cui, Go LUCI, Meng Zhuo, Cherry Mui, Russ Cox, Gopher Robot, golang-co...@googlegroups.com
          Attention needed from Cherry Mui, Meng Zhuo, Russ Cox and xie cui

          Ian Lance Taylor voted Commit-Queue+1

          Commit-Queue+1
          Open in Gerrit

          Related details

          Attention is currently required from:
          • Cherry Mui
          • Meng Zhuo
          • Russ Cox
          • xie cui
          Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
          Gerrit-Change-Number: 731740
          Gerrit-PatchSet: 4
          Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
          Gerrit-Reviewer: Cherry Mui <cher...@google.com>
          Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
          Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
          Gerrit-Reviewer: Russ Cox <r...@golang.org>
          Gerrit-Reviewer: xie cui <cuiw...@gmail.com>
          Gerrit-CC: Gopher Robot <go...@golang.org>
          Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
          Gerrit-Attention: Russ Cox <r...@golang.org>
          Gerrit-Attention: Cherry Mui <cher...@google.com>
          Gerrit-Attention: xie cui <cuiw...@gmail.com>
          Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
          Gerrit-Comment-Date: Sun, 21 Dec 2025 18:33:42 +0000
          Gerrit-HasComments: No
          Gerrit-Has-Labels: Yes
          unsatisfied_requirement
          open
          diffy

          Ian Lance Taylor (Gerrit)

          unread,
          5:41 PM (5 hours ago) 5:41 PM
          to Zxilly Chou, Gerrit Bot, goph...@pubsubhelper.golang.org, Go LUCI, Ian Lance Taylor, xie cui, Meng Zhuo, Cherry Mui, Russ Cox, Gopher Robot, golang-co...@googlegroups.com
          Attention needed from Cherry Mui, Meng Zhuo, Russ Cox and xie cui

          Ian Lance Taylor added 5 comments

          File src/cmd/link/internal/ld/data.go
          Line 1831, Patchset 4 (Latest): sect.Align = peSectionAlign(state.ctxt, symalign(ldr, s))
          Ian Lance Taylor . unresolved

          This is the general part of the linker, and it shouldn't be calling a function with a name like peSectionAlign. Perhaps we can add an MinimumAlignment field to Target, and use that.

          File src/cmd/link/internal/ld/pe.go
          Line 70, Patchset 4 (Latest): if peAlign := int32(PESECTALIGN); align < peAlign {
          Ian Lance Taylor . unresolved

          return max(align, PESECTALIGN)

          Line 541, Patchset 4 (Latest):func (f *peFile) peshbits(ctxt *Link, sect *sym.Section, seg *sym.Segment) *peSection {
          Ian Lance Taylor . unresolved

          I'm not sure peshbits is the right name for this method. elfshbits makes sense because sh is a typical term for an ELF section header. This seems more like addPESection.

          File src/cmd/link/internal/ld/pe_test.go
          Line 5, Patchset 4 (Latest)://go:build windows
          Ian Lance Taylor . unresolved

          This test is going to test the installed linker, which is not necessarily the current linker sources. I know that there are several such tests already in this directory, but I'd rather not add more. Please move this test to cmd/link rather than cmd/link/internal/ld, and use the goCmd function (in cmd/link/link_test.go) to invoke the go tool with the current linker sources rather than the installed linker. Thanks.

          File src/cmd/link/link_test.go
          Line 2213, Patchset 4 (Latest): if sym.Name == moddataSymName || sym.Name == "_"+moddataSymName {
          Ian Lance Taylor . unresolved

          Rather than testing both with and without underscore, I'd rather say something like

              if runtime.GOARCH == "386" {
          prefix = "_"
          }
          ...
          if sym.Name == prefix + moddataSymName {

          Similarly in other tests elsewhere.

          Open in Gerrit

          Related details

          Attention is currently required from:
          • Cherry Mui
          • Meng Zhuo
          • Russ Cox
          • xie cui
          Submit Requirements:
            • requirement is not satisfiedCode-Review
            • requirement is not satisfiedNo-Unresolved-Comments
            • requirement is not satisfiedReview-Enforcement
            • requirement satisfiedTryBots-Pass
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: comment
            Gerrit-Project: go
            Gerrit-Branch: master
            Gerrit-Change-Id: I6f89f58bfa3ac911c6336d2cc8df7e0e8e3dcbb0
            Gerrit-Change-Number: 731740
            Gerrit-PatchSet: 4
            Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
            Gerrit-Reviewer: Cherry Mui <cher...@google.com>
            Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
            Gerrit-Reviewer: Meng Zhuo <mengzh...@gmail.com>
            Gerrit-Reviewer: Russ Cox <r...@golang.org>
            Gerrit-Reviewer: xie cui <cuiw...@gmail.com>
            Gerrit-CC: Gopher Robot <go...@golang.org>
            Gerrit-CC: Zxilly Chou <zxi...@outlook.com>
            Gerrit-Attention: Russ Cox <r...@golang.org>
            Gerrit-Attention: Cherry Mui <cher...@google.com>
            Gerrit-Attention: xie cui <cuiw...@gmail.com>
            Gerrit-Attention: Meng Zhuo <mengzh...@gmail.com>
            Gerrit-Comment-Date: Sun, 21 Dec 2025 22:41:18 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            unsatisfied_requirement
            satisfied_requirement
            open
            diffy
            Reply all
            Reply to author
            Forward
            0 new messages