|  | // 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/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       *sym.Symbol | 
|  | off     uint64 | 
|  | next    *Imp | 
|  | argsize int | 
|  | } | 
|  |  | 
|  | type Dll struct { | 
|  | name     string | 
|  | nameoff  uint64 | 
|  | thunkoff uint64 | 
|  | ms       *Imp | 
|  | next     *Dll | 
|  | } | 
|  |  | 
|  | var ( | 
|  | rsrcsym     *sym.Symbol | 
|  | PESECTHEADR int32 | 
|  | PEFILEHEADR int32 | 
|  | pe64        int | 
|  | dr          *Dll | 
|  | dexport     [1024]*sym.Symbol | 
|  | nexport     int | 
|  | ) | 
|  |  | 
|  | // 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, datap}, | 
|  | {f.dataSect, &Segdata, 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 _, sect := range Segdwarf.Sections { | 
|  | for _, pesect := range f.sections { | 
|  | if sect.Name == pesect.name { | 
|  | pesect.emitRelocations(ctxt.Out, func() int { | 
|  | return relocsect(sect, dwarfp, 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, gotype *sym.Symbol) { | 
|  | 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 | 
|  | ctxt.xdefine("__image_base__", sym.SDATA, PEBASE) | 
|  | ctxt.xdefine("_image_base__", sym.SDATA, PEBASE) | 
|  | } | 
|  |  | 
|  | 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 { | 
|  | var d *Dll | 
|  |  | 
|  | dr = nil | 
|  | var m *Imp | 
|  | for _, s := range ctxt.Syms.Allsym { | 
|  | if !s.Attr.Reachable() || s.Type != sym.SDYNIMPORT { | 
|  | continue | 
|  | } | 
|  | for d = dr; d != nil; d = d.next { | 
|  | if d.name == s.Dynimplib() { | 
|  | m = new(Imp) | 
|  | break | 
|  | } | 
|  | } | 
|  |  | 
|  | if d == nil { | 
|  | d = new(Dll) | 
|  | d.name = s.Dynimplib() | 
|  | 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 := s.Extname() | 
|  | if i := strings.IndexByte(extName, '%'); i >= 0 { | 
|  | var err error | 
|  | m.argsize, err = strconv.Atoi(extName[i+1:]) | 
|  | if err != nil { | 
|  | Errorf(s, "failed to parse stdcall decoration: %v", err) | 
|  | } | 
|  | m.argsize *= ctxt.Arch.PtrSize | 
|  | s.SetExtname(extName[:i]) | 
|  | } | 
|  |  | 
|  | m.s = s | 
|  | m.next = d.ms | 
|  | d.ms = m | 
|  | } | 
|  |  | 
|  | if ctxt.LinkMode == LinkExternal { | 
|  | // Add real symbol name | 
|  | for d := dr; d != nil; d = d.next { | 
|  | for m = d.ms; m != nil; m = m.next { | 
|  | m.s.Type = sym.SDATA | 
|  | m.s.Grow(int64(ctxt.Arch.PtrSize)) | 
|  | dynName := m.s.Extname() | 
|  | // only windows/386 requires stdcall decoration | 
|  | if ctxt.Arch.Family == sys.I386 && m.argsize >= 0 { | 
|  | dynName += fmt.Sprintf("@%d", m.argsize) | 
|  | } | 
|  | dynSym := ctxt.Syms.Lookup(dynName, 0) | 
|  | dynSym.Attr |= sym.AttrReachable | 
|  | dynSym.Type = sym.SHOSTOBJ | 
|  | r := m.s.AddRel() | 
|  | r.Sym = dynSym | 
|  | r.Off = 0 | 
|  | r.Siz = uint8(ctxt.Arch.PtrSize) | 
|  | r.Type = objabi.R_ADDR | 
|  | } | 
|  | } | 
|  | } else { | 
|  | dynamic := ctxt.Syms.Lookup(".windynamic", 0) | 
|  | dynamic.Attr |= sym.AttrReachable | 
|  | dynamic.Type = sym.SWINDOWS | 
|  | for d := dr; d != nil; d = d.next { | 
|  | for m = d.ms; m != nil; m = m.next { | 
|  | m.s.Type = sym.SWINDOWS | 
|  | m.s.Attr |= sym.AttrSubSymbol | 
|  | m.s.Sub = dynamic.Sub | 
|  | dynamic.Sub = m.s | 
|  | m.s.Value = dynamic.Size | 
|  | dynamic.Size += int64(ctxt.Arch.PtrSize) | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | 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, 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) | 
|  | } | 
|  |  | 
|  | type byExtname []*sym.Symbol | 
|  |  | 
|  | func (s byExtname) Len() int           { return len(s) } | 
|  | func (s byExtname) Swap(i, j int)      { s[i], s[j] = s[j], s[i] } | 
|  | func (s byExtname) Less(i, j int) bool { return s[i].Extname() < s[j].Extname() } | 
|  |  | 
|  | func initdynexport(ctxt *Link) { | 
|  | nexport = 0 | 
|  | for _, s := range ctxt.Syms.Allsym { | 
|  | if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() { | 
|  | continue | 
|  | } | 
|  | if nexport+1 > len(dexport) { | 
|  | Errorf(s, "pe dynexport table is full") | 
|  | errorexit() | 
|  | } | 
|  |  | 
|  | dexport[nexport] = s | 
|  | nexport++ | 
|  | } | 
|  |  | 
|  | sort.Sort(byExtname(dexport[:nexport])) | 
|  | } | 
|  |  | 
|  | func addexports(ctxt *Link) { | 
|  | var e IMAGE_EXPORT_DIRECTORY | 
|  |  | 
|  | size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1 | 
|  | for i := 0; i < nexport; i++ { | 
|  | size += len(dexport[i].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 i := 0; i < nexport; i++ { | 
|  | out.Write32(uint32(dexport[i].Value - PEBASE)) | 
|  | } | 
|  |  | 
|  | // put EXPORT Name Pointer Table | 
|  | v := int(e.Name + uint32(len(*flagOutfile)) + 1) | 
|  |  | 
|  | for i := 0; i < nexport; i++ { | 
|  | out.Write32(uint32(v)) | 
|  | v += len(dexport[i].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 i := 0; i < nexport; i++ { | 
|  | out.WriteStringN(dexport[i].Extname(), len(dexport[i].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 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 *sym.Symbol) { | 
|  | if rsrcsym != nil { | 
|  | Errorf(sym, "too many .rsrc sections") | 
|  | } | 
|  |  | 
|  | rsrcsym = sym | 
|  | } | 
|  |  | 
|  | func addpersrc(ctxt *Link) { | 
|  | if rsrcsym == nil { | 
|  | return | 
|  | } | 
|  |  | 
|  | h := pefile.addSection(".rsrc", int(rsrcsym.Size), int(rsrcsym.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 rsrcsym.R { | 
|  | r := &rsrcsym.R[ri] | 
|  | p := rsrcsym.P[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(rsrcsym.P) | 
|  | h.pad(ctxt.Out, uint32(rsrcsym.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: | 
|  | } | 
|  |  | 
|  | 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) | 
|  | } |