// 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://docs.microsoft.com/en-us/windows/win32/debug/pe-format package ld import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/loader" "cmd/link/internal/sym" "debug/pe" "encoding/binary" "fmt" "internal/buildcfg" "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 } var ( // PEBASE is the base address for the executable. // It is small for 32-bit and large for 64-bit. PEBASE int64 // 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_LNK_OTHER = 0x00000100 IMAGE_SCN_LNK_INFO = 0x00000200 IMAGE_SCN_LNK_REMOVE = 0x00000800 IMAGE_SCN_LNK_COMDAT = 0x00001000 IMAGE_SCN_GPREL = 0x00008000 IMAGE_SCN_MEM_PURGEABLE = 0x00020000 IMAGE_SCN_MEM_16BIT = 0x00020000 IMAGE_SCN_MEM_LOCKED = 0x00040000 IMAGE_SCN_MEM_PRELOAD = 0x00080000 IMAGE_SCN_ALIGN_1BYTES = 0x00100000 IMAGE_SCN_ALIGN_2BYTES = 0x00200000 IMAGE_SCN_ALIGN_4BYTES = 0x00300000 IMAGE_SCN_ALIGN_8BYTES = 0x00400000 IMAGE_SCN_ALIGN_16BYTES = 0x00500000 IMAGE_SCN_ALIGN_32BYTES = 0x00600000 IMAGE_SCN_ALIGN_64BYTES = 0x00700000 IMAGE_SCN_ALIGN_128BYTES = 0x00800000 IMAGE_SCN_ALIGN_256BYTES = 0x00900000 IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 IMAGE_SCN_MEM_SHARED = 0x10000000 IMAGE_SCN_MEM_EXECUTE = 0x20000000 IMAGE_SCN_MEM_READ = 0x40000000 IMAGE_SCN_MEM_WRITE = 0x80000000 ) // See https://docs.microsoft.com/en-us/windows/win32/debug/pe-format. // TODO(crawshaw): add these constants to debug/pe. const ( IMAGE_SYM_TYPE_NULL = 0 IMAGE_SYM_TYPE_STRUCT = 8 IMAGE_SYM_DTYPE_FUNCTION = 2 IMAGE_SYM_DTYPE_ARRAY = 3 IMAGE_SYM_CLASS_EXTERNAL = 2 IMAGE_SYM_CLASS_STATIC = 3 IMAGE_REL_I386_DIR32 = 0x0006 IMAGE_REL_I386_DIR32NB = 0x0007 IMAGE_REL_I386_SECREL = 0x000B IMAGE_REL_I386_REL32 = 0x0014 IMAGE_REL_AMD64_ADDR64 = 0x0001 IMAGE_REL_AMD64_ADDR32 = 0x0002 IMAGE_REL_AMD64_ADDR32NB = 0x0003 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_ARM64_ABSOLUTE = 0x0000 IMAGE_REL_ARM64_ADDR32 = 0x0001 IMAGE_REL_ARM64_ADDR32NB = 0x0002 IMAGE_REL_ARM64_BRANCH26 = 0x0003 IMAGE_REL_ARM64_PAGEBASE_REL21 = 0x0004 IMAGE_REL_ARM64_REL21 = 0x0005 IMAGE_REL_ARM64_PAGEOFFSET_12A = 0x0006 IMAGE_REL_ARM64_PAGEOFFSET_12L = 0x0007 IMAGE_REL_ARM64_SECREL = 0x0008 IMAGE_REL_ARM64_SECREL_LOW12A = 0x0009 IMAGE_REL_ARM64_SECREL_HIGH12A = 0x000A IMAGE_REL_ARM64_SECREL_LOW12L = 0x000B IMAGE_REL_ARM64_TOKEN = 0x000C IMAGE_REL_ARM64_SECTION = 0x000D IMAGE_REL_ARM64_ADDR64 = 0x000E IMAGE_REL_ARM64_BRANCH19 = 0x000F IMAGE_REL_ARM64_BRANCH14 = 0x0010 IMAGE_REL_ARM64_REL32 = 0x0011 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." // See IMAGE_DOS_HEADER in the Windows SDK for the format of the header used here. var dosstub = []uint8{ 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 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-uint64(PEBASE) != uint64(sect.virtualAddress) { Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-uint64(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 pdataSect *peSection xdataSect *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, virtualAddress: f.nextSectOffset, pointerToRawData: f.nextFileOffset, } f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN)) if filesize > 0 { sect.virtualSize = uint32(sectsize) sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) f.nextFileOffset += sect.sizeOfRawData } else { sect.sizeOfRawData = uint32(sectsize) } f.sections = append(f.sections, sect) return sect } // addDWARFSection adds DWARF section to the COFF file f. // This function is similar to addSection, but DWARF section names are // longer than 8 characters, so they need to be stored in the string table. func (f *peFile) addDWARFSection(name string, size int) *peSection { if size == 0 { Exitf("DWARF section %q is empty", name) } // DWARF section names are longer than 8 characters. // PE format requires such names to be stored in string table, // and section names replaced with slash (/) followed by // correspondent string table index. // see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx // for details off := f.stringTable.add(name) h := f.addSection(name, size, size) h.shortName = fmt.Sprintf("/%d", off) h.characteristics = IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_CNT_INITIALIZED_DATA return h } // addDWARF adds DWARF information to the COFF file f. func (f *peFile) addDWARF() { 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) } } } // addSEH adds SEH information to the COFF file f. func (f *peFile) addSEH(ctxt *Link) { // .pdata section can exist without the .xdata section. // .xdata section depends on the .pdata section. if Segpdata.Length == 0 { return } d := pefile.addSection(".pdata", int(Segpdata.Length), int(Segpdata.Length)) d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ if ctxt.LinkMode == LinkExternal { // Some gcc versions don't honor the default alignment for the .pdata section. d.characteristics |= IMAGE_SCN_ALIGN_4BYTES } pefile.pdataSect = d d.checkSegment(&Segpdata) pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = d.virtualAddress pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = d.virtualSize if Segxdata.Length > 0 { d = pefile.addSection(".xdata", int(Segxdata.Length), int(Segxdata.Length)) d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ if ctxt.LinkMode == LinkExternal { // Some gcc versions don't honor the default alignment for the .xdata section. d.characteristics |= IMAGE_SCN_ALIGN_4BYTES } pefile.xdataSect = d d.checkSegment(&Segxdata) } } // 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 var alignment uint32 switch buildcfg.GOARCH { default: Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", buildcfg.GOARCH) case "386", "arm": size = 4 alignment = IMAGE_SCN_ALIGN_4BYTES case "amd64", "arm64": size = 8 alignment = IMAGE_SCN_ALIGN_8BYTES } sect := f.addSection(".ctors", size, size) sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | alignment 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 buildcfg.GOARCH { case "386", "arm": ctxt.Out.Write32(uint32(addr)) case "amd64", "arm64": 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 } 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 := int64(sect.Vaddr + sect.Length) for _, s := range syms { if !ldr.AttrReachable(s) { continue } if ldr.SymValue(s) >= 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 %v/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym())) } } } sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff const relocLen = 4 + 4 + 2 return int(sect.Rellen / relocLen) } type relsect struct { peSect *peSection seg *sym.Segment syms []loader.Sym } sects := []relsect{ {f.textSect, &Segtext, ctxt.Textp}, {f.rdataSect, &Segrodata, ctxt.datap}, {f.dataSect, &Segdata, ctxt.datap}, } if len(sehp.pdata) != 0 { sects = append(sects, relsect{f.pdataSect, &Segpdata, sehp.pdata}) } if len(sehp.xdata) != 0 { sects = append(sects, relsect{f.xdataSect, &Segxdata, sehp.xdata}) } for _, s := range sects { s.peSect.emitRelocations(ctxt.Out, func() int { var n int for _, sect := range s.seg.Sections { n += relocsect(sect, s.syms, s.seg.Vaddr) } return n }) } 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) } if f.ctorsSect == nil { return } f.ctorsSect.emitRelocations(ctxt.Out, func() int { dottext := ldr.Lookup(".text", 0) ctxt.Out.Write32(0) ctxt.Out.Write32(uint32(ldr.SymDynid(dottext))) switch buildcfg.GOARCH { default: ctxt.Errorf(dottext, "unknown architecture for PE: %q\n", buildcfg.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) case "arm64": ctxt.Out.Write16(IMAGE_REL_ARM64_ADDR64) } 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 } var isLabel = make(map[loader.Sym]bool) func AddPELabelSym(ldr *loader.Loader, s loader.Sym) { isLabel[s] = true } // 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) || // TODO(cuonglm): remove this hack // // Previously, windows/386 requires underscore prefix on external symbols, // but that's only applied for SHOSTOBJ/SUNDEFEXT or cgo export symbols. // "go.buildid" is STEXT, "type.*" is STYPE, thus they are not prefixed // with underscore. // // In external linking mode, the external linker can't resolve them as // external symbols. But we are lucky that they have "." in their name, // so the external linker see them as Forwarder RVA exports. See: // // - https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#export-address-table // - https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/pe-dll.c;h=e7b82ba6ffadf74dc1b9ee71dc13d48336941e51;hb=HEAD#l972 // // CL 317917 changes "." to ":" in symbols name, so these symbols can not be // found by external linker anymore. So a hacky way is adding the // underscore prefix for these 2 symbols. I don't have enough knowledge to // verify whether adding the underscore for all STEXT/STYPE symbols are // fine, even if it could be, that would be done in future CL. name == "go:buildid" || name == "type:*") { name = "_" + name } name = mangleABIName(ctxt, ldr, s, name) var peSymType uint16 = IMAGE_SYM_TYPE_NULL switch t { case sym.STEXT, sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: // Microsoft's PE documentation is contradictory. It says that the symbol's complex type // is stored in the pesym.Type most significant byte, but MSVC, LLVM, and mingw store it // in the 4 high bits of the less significant byte. Also, the PE documentation says that // the basic type for a function should be IMAGE_SYM_TYPE_VOID, // but the reality is that it uses IMAGE_SYM_TYPE_NULL instead. peSymType = IMAGE_SYM_DTYPE_FUNCTION<<4 + IMAGE_SYM_TYPE_NULL } sect, value, err := f.mapToPESection(ldr, s, ctxt.LinkMode) if err != nil { switch t { case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: default: 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.SymName(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) default: if len(isLabel) > 0 && isLabel[s] { 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 case sys.ARM64: fh.Machine = pe.IMAGE_FILE_MACHINE_ARM64 } 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_EXECUTABLE_IMAGE 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 = uint64(PEBASE) oh.ImageBase = uint32(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. if needPEBaseReloc(ctxt) { 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. // // 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 if ctxt.Arch.PtrSize == 8 { // 64-bit architectures pe64 = 1 PEBASE = 1 << 32 if ctxt.Arch.Family == sys.AMD64 { // TODO(rsc): For cgo we currently use 32-bit relocations // that fail when PEBASE is too large. // We need to fix this, but for now, use a smaller PEBASE. PEBASE = 1 << 22 } var oh64 pe.OptionalHeader64 l = binary.Size(&oh64) } else { // 32-bit architectures PEBASE = 1 << 22 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 // We are creating an object file. The absolute address is irrelevant. PEBASE = 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 *FlagRound == -1 { *FlagRound = PESECTALIGN } if *FlagTextAddr == -1 { *FlagTextAddr = Rnd(PEBASE, *FlagRound) + int64(PESECTHEADR) } } 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) - uint64(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) // .reloc section must be 32-bit aligned if out.Offset()&3 != 0 { Errorf(nil, "internal error, start of .reloc not 32-bit aligned") } for _, p := range rt.pages { b := rt.blocks[p] // Add a dummy entry at the end of the list if we have an // odd number of entries, so as to ensure that the next // block starts on a 32-bit boundary (see issue 68260). if len(b.entries)&1 != 0 { b.entries = append(b.entries, peBaseRelocEntry{}) } 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() if rs == 0 { continue } if !ldr.AttrReachable(s) { continue } switch r.Type() { default: case objabi.R_ADDR: rt.addentry(ldr, s, &r) } } } func needPEBaseReloc(ctxt *Link) bool { // Non-PIE x86 binaries don't need the base relocation table. // Everyone else does. if (ctxt.Arch.Family == sys.I386 || ctxt.Arch.Family == sys.AMD64) && ctxt.BuildMode != BuildModePIE { return false } return true } func addPEBaseReloc(ctxt *Link) { if !needPEBaseReloc(ctxt) { return } 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) writeSEH(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) { t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length)) t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ if ctxt.LinkMode == LinkExternal { // some data symbols (e.g. masks) end up in the .text section, and they normally // expect larger alignment requirement than the default text section alignment. t.characteristics |= IMAGE_SCN_ALIGN_32BYTES } 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.addSEH(ctxt) 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) }