diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 13:14:23 +0000 |
commit | 73df946d56c74384511a194dd01dbe099584fd1a (patch) | |
tree | fd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/link/internal/ld/pe.go | |
parent | Initial commit. (diff) | |
download | golang-1.16-upstream.tar.xz golang-1.16-upstream.zip |
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/link/internal/ld/pe.go')
-rw-r--r-- | src/cmd/link/internal/ld/pe.go | 1628 |
1 files changed, 1628 insertions, 0 deletions
diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go new file mode 100644 index 0000000..5edaf54 --- /dev/null +++ b/src/cmd/link/internal/ld/pe.go @@ -0,0 +1,1628 @@ +// 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 ( + rsrcsyms []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.loader.Lookup(*flagEntrySymbol, 0) + addr := uint64(ctxt.loader.SymValue(init_entry)) - ctxt.loader.SymSect(init_entry).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) + } + + ldr := ctxt.loader + + // relocsect relocates symbols from first in section sect, and returns + // the total number of relocations emitted. + relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) int { + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return 0 + } + nrelocs := 0 + sect.Reloff = uint64(ctxt.Out.Offset()) + for i, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if uint64(ldr.SymValue(s)) >= sect.Vaddr { + syms = syms[i:] + break + } + } + eaddr := int32(sect.Vaddr + sect.Length) + for _, s := range syms { + if !ldr.AttrReachable(s) { + continue + } + if ldr.SymValue(s) >= int64(eaddr) { + break + } + // Compute external relocations on the go, and pass to PEreloc1 + // to stream out. + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + rr, ok := extreloc(ctxt, ldr, s, r) + if !ok { + continue + } + if rr.Xsym == 0 { + ctxt.Errorf(s, "missing xsym in relocation") + continue + } + if ldr.SymDynid(rr.Xsym) < 0 { + ctxt.Errorf(s, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym())) + } + if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) { + ctxt.Errorf(s, "unsupported obj reloc %d/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym())) + } + nrelocs++ + } + } + sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff + return nrelocs + } + + sects := []struct { + peSect *peSection + seg *sym.Segment + syms []loader.Sym + }{ + {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() != loader.Sym(sect.Sym) || + ldr.SymSect(si.secSym()) != sect { + panic("inconsistency between dwarfp and Segdwarf") + } + for _, pesect := range f.sections { + if sect.Name == pesect.name { + pesect.emitRelocations(ctxt.Out, func() int { + return relocsect(sect, si.syms, sect.Vaddr) + }) + continue dwarfLoop + } + } + Errorf(nil, "emitRelocations: could not find %q section", sect.Name) + } + + f.ctorsSect.emitRelocations(ctxt.Out, func() int { + dottext := ldr.Lookup(".text", 0) + ctxt.Out.Write32(0) + ctxt.Out.Write32(uint32(ldr.SymDynid(dottext))) + switch objabi.GOARCH { + default: + ctxt.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, ldr *loader.Loader, s loader.Sym, name string, value int64, sectidx int, typ uint16, class uint8) { + if len(name) > 8 { + out.Write32(0) + out.Write32(uint32(f.stringTable.add(name))) + } else { + out.WriteStringN(name, 8) + } + out.Write32(uint32(value)) + out.Write16(uint16(sectidx)) + out.Write16(typ) + out.Write8(class) + out.Write8(0) // no aux entries + + ldr.SetSymDynid(s, 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(ldr *loader.Loader, s loader.Sym, linkmode LinkMode) (pesectidx int, offset int64, err error) { + sect := ldr.SymSect(s) + if sect == nil { + return 0, 0, fmt.Errorf("could not map %s symbol with no section", ldr.SymName(s)) + } + if sect.Seg == &Segtext { + return f.textSect.index, int64(uint64(ldr.SymValue(s)) - Segtext.Vaddr), nil + } + if sect.Seg == &Segrodata { + return f.rdataSect.index, int64(uint64(ldr.SymValue(s)) - Segrodata.Vaddr), nil + } + if sect.Seg != &Segdata { + return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", ldr.SymName(s)) + } + v := uint64(ldr.SymValue(s)) - Segdata.Vaddr + if linkmode != LinkExternal { + return f.dataSect.index, int64(v), nil + } + if ldr.SymType(s) == 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) { + ldr := ctxt.loader + addsym := func(s loader.Sym) { + t := ldr.SymType(s) + if ldr.SymSect(s) == nil && t != sym.SDYNIMPORT && t != sym.SHOSTOBJ && t != sym.SUNDEFEXT { + return + } + + name := ldr.SymName(s) + + // Only windows/386 requires underscore prefix on external symbols. + if ctxt.Is386() && ctxt.IsExternal() && + (t == sym.SHOSTOBJ || t == sym.SUNDEFEXT || ldr.AttrCgoExport(s)) { + name = "_" + name + } + + var peSymType uint16 + if ctxt.IsExternal() { + peSymType = IMAGE_SYM_TYPE_NULL + } else { + // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308 + // peSymType = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT + peSymType = 0x0308 // "array of structs" + } + sect, value, err := f.mapToPESection(ldr, s, ctxt.LinkMode) + if err != nil { + if t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT { + peSymType = IMAGE_SYM_DTYPE_FUNCTION + } else { + ctxt.Errorf(s, "addpesym: %v", err) + } + } + class := IMAGE_SYM_CLASS_EXTERNAL + if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) { + class = IMAGE_SYM_CLASS_STATIC + } + f.writeSymbol(ctxt.Out, ldr, s, name, value, sect, peSymType, 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 { + s := ldr.LookupOrCreateSym(pesect.name, 0) + f.writeSymbol(ctxt.Out, ldr, s, pesect.name, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC) + } + } + + // Add special runtime.text and runtime.etext symbols. + s := ldr.Lookup("runtime.text", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + s = ldr.Lookup("runtime.etext", 0) + if ldr.SymType(s) == sym.STEXT { + addsym(s) + } + + // Add text symbols. + for _, s := range ctxt.Textp { + addsym(s) + } + + shouldBeInSymbolTable := func(s loader.Sym) bool { + if ldr.AttrNotInSymbolTable(s) { + return false + } + name := ldr.RawSymName(s) // TODO: try not to read the name + if name == "" || name[0] == '.' { + return false + } + return true + } + + // Add data symbols and external references. + for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { + if !ldr.AttrReachable(s) { + continue + } + t := ldr.SymType(s) + if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata + if t == sym.STLSBSS { + continue + } + if !shouldBeInSymbolTable(s) { + continue + } + addsym(s) + } + + switch t { + case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: + addsym(s) + } + } +} + +// 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__"} { + sb := ctxt.loader.CreateSymForUpdate(name, 0) + sb.SetType(sym.SDATA) + sb.SetValue(PEBASE) + ctxt.loader.SetAttrSpecial(sb.Sym(), true) + ctxt.loader.SetAttrLocal(sb.Sym(), 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.SetType(sym.SHOSTOBJ) + r, _ := sb.AddRel(objabi.R_ADDR) + r.SetSym(dynSym.Sym()) + r.SetSiz(uint8(ctxt.Arch.PtrSize)) + } + } + } else { + dynamic := ldr.CreateSymForUpdate(".windynamic", 0) + 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) + sb.SetValue(dynamic.Size()) + dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) + dynamic.AddInteriorSym(m.s) + } + + 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 := ldr.LookupOrCreateSym(".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.SymExtname(m.s)) + } + } + + // 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(ldr.SymValue(dynamic)) - 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(ldr.SymValue(dynamic) - PEBASE) + pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(ldr.SymSize(dynamic)) + + 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.SymExtname(s)) + 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.SymValue(s) - 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.SymExtname(s)) + 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 { + name := ldr.SymExtname(s) + out.WriteStringN(name, len(name)+1) + } + sect.pad(out, uint32(size)) +} + +// peBaseRelocEntry represents a single relocation entry. +type peBaseRelocEntry struct { + typeOff uint16 +} + +// 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(ldr *loader.Loader, s loader.Sym, r *loader.Reloc) { + // pageSize is the size in bytes of a page + // described by a base relocation block. + const pageSize = 0x1000 + const pageMask = pageSize - 1 + + addr := ldr.SymValue(s) + 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), + } + + // 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(ldr *loader.Loader, s loader.Sym, rt *peBaseRelocTable) { + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if r.Type() >= objabi.ElfRelocOffset { + continue + } + if r.Siz() == 0 { // informational relocation + continue + } + if r.Type() == objabi.R_DWARFFILEREF { + continue + } + rs := r.Sym() + rs = ldr.ResolveABIAlias(rs) + if rs == 0 { + continue + } + if !ldr.AttrReachable(s) { + continue + } + + switch r.Type() { + default: + case objabi.R_ADDR: + rt.addentry(ldr, 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 + ldr := ctxt.loader + for _, s := range ctxt.Textp { + addPEBaseRelocSym(ldr, s, &rt) + } + for _, s := range ctxt.datap { + addPEBaseRelocSym(ldr, 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, syms []loader.Sym) { + if len(rsrcsyms) != 0 { + Errorf(nil, "too many .rsrc sections") + } + rsrcsyms = syms +} + +func addpersrc(ctxt *Link) { + if len(rsrcsyms) == 0 { + return + } + + var size int64 + for _, rsrcsym := range rsrcsyms { + size += ctxt.loader.SymSize(rsrcsym) + } + h := pefile.addSection(".rsrc", int(size), int(size)) + h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA + h.checkOffset(ctxt.Out.Offset()) + + for _, rsrcsym := range rsrcsyms { + // A split resource happens when the actual resource data and its relocations are + // split across multiple sections, denoted by a $01 or $02 at the end of the .rsrc + // section name. + splitResources := strings.Contains(ctxt.loader.SymName(rsrcsym), ".rsrc$") + relocs := ctxt.loader.Relocs(rsrcsym) + data := ctxt.loader.Data(rsrcsym) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + p := data[r.Off():] + val := uint32(int64(h.virtualAddress) + r.Add()) + if splitResources { + // If we're a split resource section, and that section has relocation + // symbols, then the data that it points to doesn't actually begin at + // the virtual address listed in this current section, but rather + // begins at the section immediately after this one. So, in order to + // calculate the proper virtual address of the data it's pointing to, + // we have to add the length of this section to the virtual address. + // This works because .rsrc sections are divided into two (but not more) + // of these sections. + val += uint32(len(data)) + } + binary.LittleEndian.PutUint32(p, val) + } + 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: + } + + 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) +} |