// Copyright 2009 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. // PE (Portable Executable) file writing // https://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx package ld import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/pe" "encoding/binary" "fmt" "sort" "strconv" "strings" ) type IMAGE_IMPORT_DESCRIPTOR struct { OriginalFirstThunk uint32 TimeDateStamp uint32 ForwarderChain uint32 Name uint32 FirstThunk uint32 } type IMAGE_EXPORT_DIRECTORY struct { Characteristics uint32 TimeDateStamp uint32 MajorVersion uint16 MinorVersion uint16 Name uint32 Base uint32 NumberOfFunctions uint32 NumberOfNames uint32 AddressOfFunctions uint32 AddressOfNames uint32 AddressOfNameOrdinals uint32 } const ( PEBASE = 0x00400000 ) var ( // SectionAlignment must be greater than or equal to FileAlignment. // The default is the page size for the architecture. PESECTALIGN int64 = 0x1000 // FileAlignment should be a power of 2 between 512 and 64 K, inclusive. // The default is 512. If the SectionAlignment is less than // the architecture's page size, then FileAlignment must match SectionAlignment. PEFILEALIGN int64 = 2 << 8 ) const ( IMAGE_SCN_CNT_CODE = 0x00000020 IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 IMAGE_SCN_MEM_EXECUTE = 0x20000000 IMAGE_SCN_MEM_READ = 0x40000000 IMAGE_SCN_MEM_WRITE = 0x80000000 IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000 IMAGE_SCN_ALIGN_32BYTES = 0x600000 ) // TODO(crawshaw): add these constants to debug/pe. const ( // TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2 IMAGE_SYM_TYPE_NULL = 0 IMAGE_SYM_TYPE_STRUCT = 8 IMAGE_SYM_DTYPE_FUNCTION = 0x20 IMAGE_SYM_DTYPE_ARRAY = 0x30 IMAGE_SYM_CLASS_EXTERNAL = 2 IMAGE_SYM_CLASS_STATIC = 3 IMAGE_REL_I386_DIR32 = 0x0006 IMAGE_REL_I386_SECREL = 0x000B IMAGE_REL_I386_REL32 = 0x0014 IMAGE_REL_AMD64_ADDR64 = 0x0001 IMAGE_REL_AMD64_ADDR32 = 0x0002 IMAGE_REL_AMD64_REL32 = 0x0004 IMAGE_REL_AMD64_SECREL = 0x000B IMAGE_REL_ARM_ABSOLUTE = 0x0000 IMAGE_REL_ARM_ADDR32 = 0x0001 IMAGE_REL_ARM_ADDR32NB = 0x0002 IMAGE_REL_ARM_BRANCH24 = 0x0003 IMAGE_REL_ARM_BRANCH11 = 0x0004 IMAGE_REL_ARM_SECREL = 0x000F IMAGE_REL_BASED_HIGHLOW = 3 IMAGE_REL_BASED_DIR64 = 10 ) const ( PeMinimumTargetMajorVersion = 6 PeMinimumTargetMinorVersion = 1 ) // DOS stub that prints out // "This program cannot be run in DOS mode." var dosstub = []uint8{ 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } type Imp struct { s loader.Sym off uint64 next *Imp argsize int } type Dll struct { name string nameoff uint64 thunkoff uint64 ms *Imp next *Dll } var ( rsrcsym loader.Sym PESECTHEADR int32 PEFILEHEADR int32 pe64 int dr *Dll dexport = make([]loader.Sym, 0, 1024) ) // peStringTable is a COFF string table. type peStringTable struct { strings []string stringsLen int } // size returns size of string table t. func (t *peStringTable) size() int { // string table starts with 4-byte length at the beginning return t.stringsLen + 4 } // add adds string str to string table t. func (t *peStringTable) add(str string) int { off := t.size() t.strings = append(t.strings, str) t.stringsLen += len(str) + 1 // each string will have 0 appended to it return off } // write writes string table t into the output file. func (t *peStringTable) write(out *OutBuf) { out.Write32(uint32(t.size())) for _, s := range t.strings { out.WriteString(s) out.Write8(0) } } // peSection represents section from COFF section table. type peSection struct { name string shortName string index int // one-based index into the Section Table virtualSize uint32 virtualAddress uint32 sizeOfRawData uint32 pointerToRawData uint32 pointerToRelocations uint32 numberOfRelocations uint16 characteristics uint32 } // checkOffset verifies COFF section sect offset in the file. func (sect *peSection) checkOffset(off int64) { if off != int64(sect.pointerToRawData) { Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off)) errorexit() } } // checkSegment verifies COFF section sect matches address // and file offset provided in segment seg. func (sect *peSection) checkSegment(seg *sym.Segment) { if seg.Vaddr-PEBASE != uint64(sect.virtualAddress) { Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-PEBASE))) errorexit() } if seg.Fileoff != uint64(sect.pointerToRawData) { Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff))) 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. func (sect *peSection) pad(out *OutBuf, n uint32) { out.WriteStringN("", int(sect.sizeOfRawData-n)) } // write writes COFF section sect into the output file. func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error { h := pe.SectionHeader32{ VirtualSize: sect.virtualSize, SizeOfRawData: sect.sizeOfRawData, PointerToRawData: sect.pointerToRawData, PointerToRelocations: sect.pointerToRelocations, NumberOfRelocations: sect.numberOfRelocations, Characteristics: sect.characteristics, } if linkmode != LinkExternal { h.VirtualAddress = sect.virtualAddress } copy(h.Name[:], sect.shortName) return binary.Write(out, binary.LittleEndian, h) } // emitRelocations emits the relocation entries for the sect. // The actual relocations are emitted by relocfn. // This updates the corresponding PE section table entry // with the relocation offset and count. func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) { sect.pointerToRelocations = uint32(out.Offset()) // first entry: extended relocs out.Write32(0) // placeholder for number of relocation + 1 out.Write32(0) out.Write16(0) n := relocfn() + 1 cpos := out.Offset() out.SeekSet(int64(sect.pointerToRelocations)) out.Write32(uint32(n)) out.SeekSet(cpos) if n > 0x10000 { n = 0x10000 sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL } else { sect.pointerToRelocations += 10 // skip the extend reloc entry } sect.numberOfRelocations = uint16(n - 1) } // peFile is used to build COFF file. type peFile struct { sections []*peSection stringTable peStringTable textSect *peSection rdataSect *peSection dataSect *peSection bssSect *peSection ctorsSect *peSection nextSectOffset uint32 nextFileOffset uint32 symtabOffset int64 // offset to the start of symbol table symbolCount int // number of symbol table records written dataDirectory [16]pe.DataDirectory } // addSection adds section to the COFF file f. func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection { sect := &peSection{ name: name, shortName: name, index: len(f.sections) + 1, virtualSize: uint32(sectsize), virtualAddress: f.nextSectOffset, pointerToRawData: f.nextFileOffset, } f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN)) if filesize > 0 { sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) f.nextFileOffset += sect.sizeOfRawData } 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_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE return h } // addDWARF adds DWARF information to the COFF file f. func (f *peFile) addDWARF() { if *FlagS { // disable symbol table return } if *FlagW { // disable dwarf return } for _, sect := range Segdwarf.Sections { h := f.addDWARFSection(sect.Name, int(sect.Length)) fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff if uint64(h.pointerToRawData) != fileoff { Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff) } } } // addInitArray adds .ctors COFF section to the file f. func (f *peFile) addInitArray(ctxt *Link) *peSection { // The size below was determined by the specification for array relocations, // and by observing what GCC writes here. If the initarray section grows to // contain more than one constructor entry, the size will need to be 8 * constructor_count. // However, the entire Go runtime is initialized from just one function, so it is unlikely // that this will need to grow in the future. var size int switch objabi.GOARCH { default: Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", objabi.GOARCH) case "386": size = 4 case "amd64": size = 8 case "arm": size = 4 } sect := f.addSection(".ctors", size, size) sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ sect.sizeOfRawData = uint32(size) ctxt.Out.SeekSet(int64(sect.pointerToRawData)) sect.checkOffset(ctxt.Out.Offset()) init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0) addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr switch objabi.GOARCH { case "386", "arm": ctxt.Out.Write32(uint32(addr)) case "amd64": ctxt.Out.Write64(addr) } return sect } // emitRelocations emits relocation entries for go.o in external linking. func (f *peFile) emitRelocations(ctxt *Link) { for ctxt.Out.Offset()&7 != 0 { ctxt.Out.Write8(0) } // relocsect relocates symbols from first in section sect, and returns // the total number of relocations emitted. relocsect := func(sect *sym.Section, syms []*sym.Symbol, base uint64) int { // If main section has no bits, nothing to relocate. if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { return 0 } relocs := 0 sect.Reloff = uint64(ctxt.Out.Offset()) for i, s := range syms { if !s.Attr.Reachable() { continue } if uint64(s.Value) >= sect.Vaddr { syms = syms[i:] break } } eaddr := int32(sect.Vaddr + sect.Length) for _, sym := range syms { if !sym.Attr.Reachable() { continue } if sym.Value >= int64(eaddr) { break } for ri := range sym.R { r := &sym.R[ri] if r.Done { continue } if r.Xsym == nil { Errorf(sym, "missing xsym in relocation") continue } if r.Xsym.Dynid < 0 { Errorf(sym, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type) } if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, sym, r, int64(uint64(sym.Value+int64(r.Off))-base)) { Errorf(sym, "unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) } relocs++ } } sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff return relocs } sects := []struct { peSect *peSection seg *sym.Segment syms []*sym.Symbol }{ {f.textSect, &Segtext, ctxt.Textp}, {f.rdataSect, &Segrodata, ctxt.datap}, {f.dataSect, &Segdata, ctxt.datap}, } 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 }) } dwarfLoop: for i := 0; i < len(Segdwarf.Sections); i++ { sect := Segdwarf.Sections[i] si := dwarfp[i] if si.secSym() != sect.Sym || si.secSym().Sect != 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 } } Errorf(nil, "emitRelocations: could not find %q section", sect.Name) } f.ctorsSect.emitRelocations(ctxt.Out, func() int { dottext := ctxt.Syms.Lookup(".text", 0) ctxt.Out.Write32(0) ctxt.Out.Write32(uint32(dottext.Dynid)) switch objabi.GOARCH { default: Errorf(dottext, "unknown architecture for PE: %q\n", objabi.GOARCH) case "386": ctxt.Out.Write16(IMAGE_REL_I386_DIR32) case "amd64": ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64) case "arm": ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32) } return 1 }) } // writeSymbol appends symbol s to file f symbol table. // It also sets s.Dynid to written symbol number. func (f *peFile) writeSymbol(out *OutBuf, s *sym.Symbol, value int64, sectidx int, typ uint16, class uint8) { if len(s.Name) > 8 { out.Write32(0) out.Write32(uint32(f.stringTable.add(s.Name))) } else { out.WriteStringN(s.Name, 8) } out.Write32(uint32(value)) out.Write16(uint16(sectidx)) out.Write16(typ) out.Write8(class) out.Write8(0) // no aux entries s.Dynid = int32(f.symbolCount) f.symbolCount++ } // mapToPESection searches peFile f for s symbol's location. // It returns PE section index, and offset within that section. func (f *peFile) mapToPESection(s *sym.Symbol, linkmode LinkMode) (pesectidx int, offset int64, err error) { if s.Sect == nil { return 0, 0, fmt.Errorf("could not map %s symbol with no section", s.Name) } if s.Sect.Seg == &Segtext { return f.textSect.index, int64(uint64(s.Value) - Segtext.Vaddr), nil } if s.Sect.Seg == &Segrodata { return f.rdataSect.index, int64(uint64(s.Value) - Segrodata.Vaddr), nil } if s.Sect.Seg != &Segdata { return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", s.Name) } v := uint64(s.Value) - Segdata.Vaddr if linkmode != LinkExternal { return f.dataSect.index, int64(v), nil } if s.Type == sym.SDATA { 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 } // writeSymbols writes all COFF symbol table records. func (f *peFile) writeSymbols(ctxt *Link) { put := func(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64) { if s == nil { return } if s.Sect == nil && type_ != UndefinedSym { return } switch type_ { default: return case DataSym, BSSSym, TextSym, UndefinedSym: } // Only windows/386 requires underscore prefix on external symbols. if ctxt.Arch.Family == sys.I386 && ctxt.LinkMode == LinkExternal && (s.Type == sym.SHOSTOBJ || s.Type == sym.SUNDEFEXT || s.Attr.CgoExport()) { s.Name = "_" + s.Name } var typ uint16 if ctxt.LinkMode == LinkExternal { typ = IMAGE_SYM_TYPE_NULL } else { // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308 typ = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT typ = 0x0308 // "array of structs" } sect, value, err := f.mapToPESection(s, ctxt.LinkMode) if err != nil { if type_ == UndefinedSym { typ = IMAGE_SYM_DTYPE_FUNCTION } else { Errorf(s, "addpesym: %v", err) } } class := IMAGE_SYM_CLASS_EXTERNAL if s.IsFileLocal() || s.Attr.VisibilityHidden() || s.Attr.Local() { class = IMAGE_SYM_CLASS_STATIC } f.writeSymbol(ctxt.Out, s, value, sect, typ, uint8(class)) } if ctxt.LinkMode == LinkExternal { // Include section symbols as external, because // .ctors and .debug_* section relocations refer to it. for _, pesect := range f.sections { sym := ctxt.Syms.Lookup(pesect.name, 0) f.writeSymbol(ctxt.Out, sym, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC) } } genasmsym(ctxt, put) } // writeSymbolTableAndStringTable writes out symbol and string tables for peFile f. func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) { f.symtabOffset = ctxt.Out.Offset() // write COFF symbol table if !*FlagS || ctxt.LinkMode == LinkExternal { f.writeSymbols(ctxt) } // update COFF file header and section table size := f.stringTable.size() + 18*f.symbolCount var h *peSection if ctxt.LinkMode != LinkExternal { // We do not really need .symtab for go.o, and if we have one, ld // will also include it in the exe, and that will confuse windows. h = f.addSection(".symtab", size, size) h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE h.checkOffset(f.symtabOffset) } // write COFF string table f.stringTable.write(ctxt.Out) if ctxt.LinkMode != LinkExternal { h.pad(ctxt.Out, uint32(size)) } } // writeFileHeader writes COFF file header for peFile f. func (f *peFile) writeFileHeader(ctxt *Link) { var fh pe.FileHeader switch ctxt.Arch.Family { default: Exitf("unknown PE architecture: %v", ctxt.Arch.Family) case sys.AMD64: fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64 case sys.I386: fh.Machine = pe.IMAGE_FILE_MACHINE_I386 case sys.ARM: fh.Machine = pe.IMAGE_FILE_MACHINE_ARMNT } fh.NumberOfSections = uint16(len(f.sections)) // Being able to produce identical output for identical input is // much more beneficial than having build timestamp in the header. fh.TimeDateStamp = 0 if ctxt.LinkMode == LinkExternal { fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED } else { fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED switch ctxt.Arch.Family { case sys.AMD64, sys.I386: if ctxt.BuildMode != BuildModePIE { fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED } } } if pe64 != 0 { var oh64 pe.OptionalHeader64 fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE } else { var oh pe.OptionalHeader32 fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE } fh.PointerToSymbolTable = uint32(f.symtabOffset) fh.NumberOfSymbols = uint32(f.symbolCount) binary.Write(ctxt.Out, binary.LittleEndian, &fh) } // writeOptionalHeader writes COFF optional header for peFile f. func (f *peFile) writeOptionalHeader(ctxt *Link) { var oh pe.OptionalHeader32 var oh64 pe.OptionalHeader64 if pe64 != 0 { oh64.Magic = 0x20b // PE32+ } else { oh.Magic = 0x10b // PE32 oh.BaseOfData = f.dataSect.virtualAddress } // Fill out both oh64 and oh. We only use one. Oh well. oh64.MajorLinkerVersion = 3 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.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 oh64.ImageBase = PEBASE oh.ImageBase = PEBASE oh64.SectionAlignment = uint32(PESECTALIGN) oh.SectionAlignment = uint32(PESECTALIGN) oh64.FileAlignment = uint32(PEFILEALIGN) oh.FileAlignment = uint32(PEFILEALIGN) oh64.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion oh.MajorOperatingSystemVersion = PeMinimumTargetMajorVersion oh64.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion oh.MinorOperatingSystemVersion = PeMinimumTargetMinorVersion oh64.MajorImageVersion = 1 oh.MajorImageVersion = 1 oh64.MinorImageVersion = 0 oh.MinorImageVersion = 0 oh64.MajorSubsystemVersion = PeMinimumTargetMajorVersion oh.MajorSubsystemVersion = PeMinimumTargetMajorVersion oh64.MinorSubsystemVersion = PeMinimumTargetMinorVersion oh.MinorSubsystemVersion = PeMinimumTargetMinorVersion oh64.SizeOfImage = f.nextSectOffset oh.SizeOfImage = f.nextSectOffset oh64.SizeOfHeaders = uint32(PEFILEHEADR) oh.SizeOfHeaders = uint32(PEFILEHEADR) if windowsgui { oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI } else { oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI } // Mark as having awareness of terminal services, to avoid ancient compatibility hacks. oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE // Enable DEP oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT // The DLL can be relocated at load time. switch ctxt.Arch.Family { case sys.AMD64, sys.I386: if ctxt.BuildMode == BuildModePIE { oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE } case sys.ARM: oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE } // Image can handle a high entropy 64-bit virtual address space. if ctxt.BuildMode == BuildModePIE { oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA } // Disable stack growth as we don't want Windows to // fiddle with the thread stack limits, which we set // ourselves to circumvent the stack checks in the // Windows exception dispatcher. // Commit size must be strictly less than reserve // size otherwise reserve will be rounded up to a // larger size, as verified with VMMap. // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is // okay with much smaller stacks, but the syscall package // makes it easy to call into arbitrary C code without cgo, // and system calls even in "pure" Go code are actually C // calls that may need more stack than we think. // // The default stack reserve size directly affects only the main // thread, ctrlhandler thread, and profileloop thread. For // these, it must be greater than the stack size assumed by // externalthreadhandler. // // For other threads, the runtime explicitly asks the kernel // to use the default stack size so that all stacks are // consistent. // // At thread start, in minit, the runtime queries the OS for // the actual stack bounds so that the stack size doesn't need // to be hard-coded into the runtime. oh64.SizeOfStackReserve = 0x00200000 if !iscgo { oh64.SizeOfStackCommit = 0x00001000 } else { // TODO(brainman): Maybe remove optional header writing altogether for cgo. // For cgo it is the external linker that is building final executable. // And it probably does not use any information stored in optional header. oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages } oh.SizeOfStackReserve = 0x00100000 if !iscgo { oh.SizeOfStackCommit = 0x00001000 } else { oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages } oh64.SizeOfHeapReserve = 0x00100000 oh.SizeOfHeapReserve = 0x00100000 oh64.SizeOfHeapCommit = 0x00001000 oh.SizeOfHeapCommit = 0x00001000 oh64.NumberOfRvaAndSizes = 16 oh.NumberOfRvaAndSizes = 16 if pe64 != 0 { oh64.DataDirectory = f.dataDirectory } else { oh.DataDirectory = f.dataDirectory } if pe64 != 0 { binary.Write(ctxt.Out, binary.LittleEndian, &oh64) } else { binary.Write(ctxt.Out, binary.LittleEndian, &oh) } } var pefile peFile func Peinit(ctxt *Link) { var l int switch ctxt.Arch.Family { // 64-bit architectures case sys.AMD64: pe64 = 1 var oh64 pe.OptionalHeader64 l = binary.Size(&oh64) // 32-bit architectures default: var oh pe.OptionalHeader32 l = binary.Size(&oh) } if ctxt.LinkMode == LinkExternal { // .rdata section will contain "masks" and "shifts" symbols, and they // need to be aligned to 16-bytes. So make all sections aligned // to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external // linker will honour that requirement. PESECTALIGN = 32 PEFILEALIGN = 0 } var sh [16]pe.SectionHeader32 var fh pe.FileHeader PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN)) if ctxt.LinkMode != LinkExternal { PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN)) } else { PESECTHEADR = 0 } pefile.nextSectOffset = uint32(PESECTHEADR) pefile.nextFileOffset = uint32(PEFILEHEADR) if ctxt.LinkMode == LinkInternal { // some mingw libs depend on this symbol, for example, FindPESectionByName for _, name := range [2]string{"__image_base__", "_image_base__"} { s := ctxt.loader.LookupOrCreateSym(name, 0) sb := ctxt.loader.MakeSymbolUpdater(s) sb.SetType(sym.SDATA) sb.SetValue(PEBASE) ctxt.loader.SetAttrReachable(s, true) ctxt.loader.SetAttrSpecial(s, true) ctxt.loader.SetAttrLocal(s, true) } } HEADR = PEFILEHEADR if *FlagTextAddr == -1 { *FlagTextAddr = PEBASE + int64(PESECTHEADR) } if *FlagRound == -1 { *FlagRound = int(PESECTALIGN) } } func pewrite(ctxt *Link) { ctxt.Out.SeekSet(0) if ctxt.LinkMode != LinkExternal { ctxt.Out.Write(dosstub) ctxt.Out.WriteStringN("PE", 4) } pefile.writeFileHeader(ctxt) pefile.writeOptionalHeader(ctxt) for _, sect := range pefile.sections { sect.write(ctxt.Out, ctxt.LinkMode) } } func strput(out *OutBuf, s string) { out.WriteString(s) out.Write8(0) // string must be padded to even size if (len(s)+1)%2 != 0 { out.Write8(0) } } func initdynimport(ctxt *Link) *Dll { ldr := ctxt.loader var d *Dll dr = nil var m *Imp for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT { continue } dynlib := ldr.SymDynimplib(s) for d = dr; d != nil; d = d.next { if d.name == dynlib { m = new(Imp) break } } if d == nil { d = new(Dll) d.name = dynlib d.next = dr dr = d m = new(Imp) } // Because external link requires properly stdcall decorated name, // all external symbols in runtime use %n to denote that the number // of uinptrs this function consumes. Store the argsize and discard // the %n suffix if any. m.argsize = -1 extName := ldr.SymExtname(s) if i := strings.IndexByte(extName, '%'); i >= 0 { var err error m.argsize, err = strconv.Atoi(extName[i+1:]) if err != nil { ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err) } m.argsize *= ctxt.Arch.PtrSize ldr.SetSymExtname(s, extName[:i]) } m.s = s m.next = d.ms d.ms = m } if ctxt.IsExternal() { // Add real symbol name for d := dr; d != nil; d = d.next { for m = d.ms; m != nil; m = m.next { sb := ldr.MakeSymbolUpdater(m.s) sb.SetType(sym.SDATA) sb.Grow(int64(ctxt.Arch.PtrSize)) dynName := sb.Extname() // only windows/386 requires stdcall decoration if ctxt.Is386() && m.argsize >= 0 { dynName += fmt.Sprintf("@%d", m.argsize) } dynSym := ldr.CreateSymForUpdate(dynName, 0) dynSym.SetReachable(true) dynSym.SetType(sym.SHOSTOBJ) sb.AddReloc(loader.Reloc{Sym: dynSym.Sym(), Type: objabi.R_ADDR, Off: 0, Size: uint8(ctxt.Arch.PtrSize)}) } } } else { dynamic := ldr.CreateSymForUpdate(".windynamic", 0) dynamic.SetReachable(true) dynamic.SetType(sym.SWINDOWS) for d := dr; d != nil; d = d.next { for m = d.ms; m != nil; m = m.next { sb := ldr.MakeSymbolUpdater(m.s) sb.SetType(sym.SWINDOWS) dynamic.PrependSub(m.s) sb.SetValue(dynamic.Size()) dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) } dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) } } return dr } // peimporteddlls returns the gcc command line argument to link all imported // DLLs. func peimporteddlls() []string { var dlls []string for d := dr; d != nil; d = d.next { dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll")) } return dlls } func addimports(ctxt *Link, datsect *peSection) { ldr := ctxt.loader startoff := ctxt.Out.Offset() dynamic := ctxt.Syms.Lookup(".windynamic", 0) // skip import descriptor table (will write it later) n := uint64(0) for d := dr; d != nil; d = d.next { n++ } ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) // write dll names for d := dr; d != nil; d = d.next { d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff) strput(ctxt.Out, d.name) } // write function names for d := dr; d != nil; d = d.next { for m := d.ms; m != nil; m = m.next { m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff) ctxt.Out.Write16(0) // hint strput(ctxt.Out, ldr.Syms[m.s].Extname()) } } // write OriginalFirstThunks oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff) n = uint64(ctxt.Out.Offset()) for d := dr; d != nil; d = d.next { d.thunkoff = uint64(ctxt.Out.Offset()) - n for m := d.ms; m != nil; m = m.next { if pe64 != 0 { ctxt.Out.Write64(m.off) } else { ctxt.Out.Write32(uint32(m.off)) } } if pe64 != 0 { ctxt.Out.Write64(0) } else { ctxt.Out.Write32(0) } } // add pe section and pad it at the end n = uint64(ctxt.Out.Offset()) - uint64(startoff) isect := pefile.addSection(".idata", int(n), int(n)) isect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE isect.checkOffset(startoff) isect.pad(ctxt.Out, uint32(n)) endoff := ctxt.Out.Offset() // write FirstThunks (allocated in .data section) ftbase := uint64(dynamic.Value) - uint64(datsect.virtualAddress) - PEBASE ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase)) for d := dr; d != nil; d = d.next { for m := d.ms; m != nil; m = m.next { if pe64 != 0 { ctxt.Out.Write64(m.off) } else { ctxt.Out.Write32(uint32(m.off)) } } if pe64 != 0 { ctxt.Out.Write64(0) } else { ctxt.Out.Write32(0) } } // finally write import descriptor table out := ctxt.Out out.SeekSet(startoff) for d := dr; d != nil; d = d.next { out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff)) 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(0) //end out.Write32(0) out.Write32(0) out.Write32(0) out.Write32(0) // update data directory pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size) out.SeekSet(endoff) } func initdynexport(ctxt *Link) { ldr := ctxt.loader for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) { continue } if len(dexport)+1 > cap(dexport) { ctxt.Errorf(s, "pe dynexport table is full") errorexit() } dexport = append(dexport, s) } sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) }) } func addexports(ctxt *Link) { ldr := ctxt.loader var e IMAGE_EXPORT_DIRECTORY nexport := len(dexport) size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1 for _, s := range dexport { size += len(ldr.Syms[s].Extname()) + 1 } if nexport == 0 { return } sect := pefile.addSection(".edata", size, size) sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ sect.checkOffset(ctxt.Out.Offset()) va := int(sect.virtualAddress) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize vaName := va + binary.Size(&e) + nexport*4 vaAddr := va + binary.Size(&e) vaNa := va + binary.Size(&e) + nexport*8 e.Characteristics = 0 e.MajorVersion = 0 e.MinorVersion = 0 e.NumberOfFunctions = uint32(nexport) e.NumberOfNames = uint32(nexport) e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. e.Base = 1 e.AddressOfFunctions = uint32(vaAddr) e.AddressOfNames = uint32(vaName) e.AddressOfNameOrdinals = uint32(vaNa) out := ctxt.Out // put IMAGE_EXPORT_DIRECTORY binary.Write(out, binary.LittleEndian, &e) // put EXPORT Address Table for _, s := range dexport { out.Write32(uint32(ldr.Syms[s].Value - PEBASE)) } // put EXPORT Name Pointer Table v := int(e.Name + uint32(len(*flagOutfile)) + 1) for _, s := range dexport { out.Write32(uint32(v)) v += len(ldr.Syms[s].Extname()) + 1 } // put EXPORT Ordinal Table for i := 0; i < nexport; i++ { out.Write16(uint16(i)) } // put Names out.WriteStringN(*flagOutfile, len(*flagOutfile)+1) for _, s := range dexport { ss := ldr.Syms[s] out.WriteStringN(ss.Extname(), len(ss.Extname())+1) } sect.pad(out, uint32(size)) } // peBaseRelocEntry represents a single relocation entry. type peBaseRelocEntry struct { typeOff uint16 rel *sym.Reloc sym *sym.Symbol // For debug } // peBaseRelocBlock represents a Base Relocation Block. A block // is a collection of relocation entries in a page, where each // entry describes a single relocation. // The block page RVA (Relative Virtual Address) is the index // into peBaseRelocTable.blocks. type peBaseRelocBlock struct { entries []peBaseRelocEntry } // pePages is a type used to store the list of pages for which there // are base relocation blocks. This is defined as a type so that // it can be sorted. type pePages []uint32 func (p pePages) Len() int { return len(p) } func (p pePages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p pePages) Less(i, j int) bool { return p[i] < p[j] } // A PE base relocation table is a list of blocks, where each block // contains relocation information for a single page. The blocks // must be emitted in order of page virtual address. // See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-reloc-section-image-only type peBaseRelocTable struct { blocks map[uint32]peBaseRelocBlock // pePages is a list of keys into blocks map. // It is stored separately for ease of sorting. pages pePages } func (rt *peBaseRelocTable) init(ctxt *Link) { rt.blocks = make(map[uint32]peBaseRelocBlock) } func (rt *peBaseRelocTable) addentry(ctxt *Link, s *sym.Symbol, r *sym.Reloc) { // pageSize is the size in bytes of a page // described by a base relocation block. const pageSize = 0x1000 const pageMask = pageSize - 1 addr := s.Value + int64(r.Off) - int64(PEBASE) page := uint32(addr &^ pageMask) off := uint32(addr & pageMask) b, ok := rt.blocks[page] if !ok { rt.pages = append(rt.pages, page) } e := peBaseRelocEntry{ typeOff: uint16(off & 0xFFF), rel: r, sym: s, } // Set entry type switch r.Siz { default: Exitf("unsupported relocation size %d\n", r.Siz) case 4: e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12) case 8: e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12) } b.entries = append(b.entries, e) rt.blocks[page] = b } func (rt *peBaseRelocTable) write(ctxt *Link) { out := ctxt.Out // sort the pages array sort.Sort(rt.pages) for _, p := range rt.pages { b := rt.blocks[p] const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32) blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2) out.Write32(p) out.Write32(blockSize) for _, e := range b.entries { out.Write16(e.typeOff) } } } func addPEBaseRelocSym(ctxt *Link, s *sym.Symbol, rt *peBaseRelocTable) { for ri := 0; ri < len(s.R); ri++ { r := &s.R[ri] if r.Sym == nil { continue } if !r.Sym.Attr.Reachable() { continue } if r.Type >= objabi.ElfRelocOffset { continue } if r.Siz == 0 { // informational relocation continue } if r.Type == objabi.R_DWARFFILEREF { continue } switch r.Type { default: case objabi.R_ADDR: rt.addentry(ctxt, s, r) } } } func addPEBaseReloc(ctxt *Link) { // Arm does not work without base relocation table. // 386 and amd64 will only require the table for BuildModePIE. switch ctxt.Arch.Family { default: return case sys.I386, sys.AMD64: if ctxt.BuildMode != BuildModePIE { return } case sys.ARM: } var rt peBaseRelocTable rt.init(ctxt) // Get relocation information for _, s := range ctxt.Textp { addPEBaseRelocSym(ctxt, s, &rt) } for _, s := range ctxt.datap { addPEBaseRelocSym(ctxt, s, &rt) } // Write relocation information startoff := ctxt.Out.Offset() rt.write(ctxt) size := ctxt.Out.Offset() - startoff // Add a PE section and pad it at the end rsect := pefile.addSection(".reloc", int(size), int(size)) rsect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE rsect.checkOffset(startoff) rsect.pad(ctxt.Out, uint32(size)) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize } func (ctxt *Link) dope() { initdynimport(ctxt) initdynexport(ctxt) } func setpersrc(ctxt *Link, sym loader.Sym) { if rsrcsym != 0 { Errorf(nil, "too many .rsrc sections") } rsrcsym = sym ctxt.loader.SetAttrReachable(rsrcsym, true) } func addpersrc(ctxt *Link) { if rsrcsym == 0 { return } rsrc := ctxt.loader.Syms[rsrcsym] data := rsrc.P size := len(data) h := pefile.addSection(".rsrc", size, size) h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA h.checkOffset(ctxt.Out.Offset()) // relocation for ri := range rsrc.R { r := &rsrc.R[ri] p := data[r.Off:] val := uint32(int64(h.virtualAddress) + r.Add) // 32-bit little-endian p[0] = byte(val) p[1] = byte(val >> 8) p[2] = byte(val >> 16) p[3] = byte(val >> 24) } ctxt.Out.Write(data) h.pad(ctxt.Out, uint32(size)) // update data directory pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize } func Asmbpe(ctxt *Link) { switch ctxt.Arch.Family { default: Exitf("unknown PE architecture: %v", ctxt.Arch.Family) case sys.AMD64, sys.I386, sys.ARM: } if rsrcsym != 0 { // The resource symbol may have been copied to the mmap'd // output buffer. If so, certain conditions can cause that // mmap'd output buffer to be munmap'd before we get a chance // to use it. To avoid any issues we copy the data to the heap // when the resource symbol exists. rsrc := ctxt.loader.Syms[rsrcsym] data := make([]byte, len(rsrc.P)) copy(data, rsrc.P) rsrc.P = data } t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length)) t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | 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 } 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 } 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 } pefile.addDWARF() if ctxt.LinkMode == LinkExternal { pefile.ctorsSect = pefile.addInitArray(ctxt) } ctxt.Out.SeekSet(int64(pefile.nextFileOffset)) if ctxt.LinkMode != LinkExternal { addimports(ctxt, d) addexports(ctxt) addPEBaseReloc(ctxt) } pefile.writeSymbolTableAndStringTable(ctxt) addpersrc(ctxt) if ctxt.LinkMode == LinkExternal { pefile.emitRelocations(ctxt) } pewrite(ctxt) }