blob: 3b97061aa07d0b6705a47291cc2667fe73c2384e [file] [edit]
// 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 (
"bytes"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/pe"
"encoding/binary"
"fmt"
"internal/buildcfg"
"math"
"os"
"path/filepath"
"slices"
"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
)
// IMAGE_LOAD_CONFIG_DIRECTORY64.GuardFlags and IMAGE_LOAD_CONFIG_DIRECTORY32.GuardFlags
// values. These can be combined together.
const (
IMAGE_GUARD_CF_INSTRUMENTED = 0x00000100 // Module performs control flow integrity checks using system-supplied support
IMAGE_GUARD_CFW_INSTRUMENTED = 0x00000200 // Module performs control flow and write integrity checks
IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT = 0x00000400 // Module contains valid control flow target metadata
IMAGE_GUARD_SECURITY_COOKIE_UNUSED = 0x00000800 // Module does not make use of the /GS security cookie
IMAGE_GUARD_PROTECT_DELAYLOAD_IAT = 0x00001000 // Module supports read only delay load IAT
IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION = 0x00002000 // Delayload import table in its own .didat section (with nothing else in it) that can be freely reprotected
IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT = 0x00004000 // Module contains suppressed export information
IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION = 0x00008000 // Module enables suppression of exports
IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT = 0x00010000 // Module contains longjmp target information
IMAGE_GUARD_RF_INSTRUMENTED = 0x00020000 // Module contains return flow instrumentation and metadata
IMAGE_GUARD_RF_ENABLE = 0x00040000 // Module requests that the OS enable return flow protection
IMAGE_GUARD_RF_STRICT = 0x00080000 // Module requests that the OS enable return flow protection in strict mode
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK = 0xF0000000 // Stride of Guard CF function table encoded in these bits (additional count of bytes per element)
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT = 28 // Shift to right-justify Guard CF function table stride
)
type IMAGE_LOAD_CONFIG_CODE_INTEGRITY struct {
Flags uint16
Catalog uint16
CatalogOffset uint32
Reserved uint32
}
type IMAGE_LOAD_CONFIG_DIRECTORY32 struct {
Size uint32
TimeDateStamp uint32
MajorVersion uint16
MinorVersion uint16
GlobalFlagsClear uint32
GlobalFlagsSet uint32
CriticalSectionDefaultTimeout uint32
DeCommitFreeBlockThreshold uint32
DeCommitTotalFreeThreshold uint32
LockPrefixTable uint32
MaximumAllocationSize uint32
VirtualMemoryThreshold uint32
ProcessHeapFlags uint32
ProcessAffinityMask uint32
CSDVersion uint16
DependentLoadFlags uint16
EditList uint32
SecurityCookie uint32
SEHandlerTable uint32
SEHandlerCount uint32
GuardCFCheckFunctionPointer uint32
GuardCFDispatchFunctionPointer uint32
GuardCFFunctionTable uint32
GuardCFFunctionCount uint32
GuardFlags uint32
CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY
GuardAddressTakenIatEntryTable uint32
GuardAddressTakenIatEntryCount uint32
GuardLongJumpTargetTable uint32
GuardLongJumpTargetCount uint32
DynamicValueRelocTable uint32
CHPEMetadataPointer uint32
GuardRFFailureRoutine uint32
GuardRFFailureRoutineFunctionPointer uint32
DynamicValueRelocTableOffset uint32
DynamicValueRelocTableSection uint16
Reserved2 uint16
GuardRFVerifyStackPointerFunctionPointer uint32
HotPatchTableOffset uint32
Reserved3 uint32
EnclaveConfigurationPointer uint32
VolatileMetadataPointer uint32
GuardEHContinuationTable uint32
GuardEHContinuationCount uint32
GuardXFGCheckFunctionPointer uint32
GuardXFGDispatchFunctionPointer uint32
GuardXFGTableDispatchFunctionPointer uint32
CastGuardOsDeterminedFailureMode uint32
GuardMemcpyFunctionPointer uint32
}
type IMAGE_LOAD_CONFIG_DIRECTORY64 struct {
Size uint32
TimeDateStamp uint32
MajorVersion uint16
MinorVersion uint16
GlobalFlagsClear uint32
GlobalFlagsSet uint32
CriticalSectionDefaultTimeout uint32
DeCommitFreeBlockThreshold uint64
DeCommitTotalFreeThreshold uint64
LockPrefixTable uint64
MaximumAllocationSize uint64
VirtualMemoryThreshold uint64
ProcessAffinityMask uint64
ProcessHeapFlags uint32
CSDVersion uint16
DependentLoadFlags uint16
EditList uint64
SecurityCookie uint64
SEHandlerTable uint64
SEHandlerCount uint64
GuardCFCheckFunctionPointer uint64
GuardCFDispatchFunctionPointer uint64
GuardCFFunctionTable uint64
GuardCFFunctionCount uint64
GuardFlags uint32
CodeIntegrity IMAGE_LOAD_CONFIG_CODE_INTEGRITY
GuardAddressTakenIatEntryTable uint64
GuardAddressTakenIatEntryCount uint64
GuardLongJumpTargetTable uint64
GuardLongJumpTargetCount uint64
DynamicValueRelocTable uint64
CHPEMetadataPointer uint64
GuardRFFailureRoutine uint64
GuardRFFailureRoutineFunctionPointer uint64
DynamicValueRelocTableOffset uint32
DynamicValueRelocTableSection uint16
Reserved2 uint16
GuardRFVerifyStackPointerFunctionPointer uint64
HotPatchTableOffset uint32
Reserved3 uint32
EnclaveConfigurationPointer uint64
VolatileMetadataPointer uint64
GuardEHContinuationTable uint64
GuardEHContinuationCount uint64
GuardXFGCheckFunctionPointer uint64
GuardXFGDispatchFunctionPointer uint64
GuardXFGTableDispatchFunctionPointer uint64
CastGuardOsDeterminedFailureMode uint64
GuardMemcpyFunctionPointer uint64
}
const (
PeMinimumTargetMajorVersion = 10
PeMinimumTargetMinorVersion = 0
)
// 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 bool
dr *Dll
dexport []loader.Sym
)
// 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("%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("%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("%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 *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
if pe64 {
size = 8
alignment = IMAGE_SCN_ALIGN_8BYTES
} else {
size = 4
alignment = IMAGE_SCN_ALIGN_4BYTES
}
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
if pe64 {
ctxt.Out.Write64(addr)
} else {
ctxt.Out.Write32(uint32(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() != 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("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).IsDATA() {
return f.dataSect.index, int64(v), nil
}
// Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section
// it still belongs to the .data section, not the .bss section.
if v < Segdata.Filelen {
return f.dataSect.index, int64(v), nil
}
return f.bssSect.index, int64(v - Segdata.Filelen), nil
}
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 {
case t.IsText(), t == sym.SDYNIMPORT, t == sym.SHOSTOBJ, t == 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).IsText() {
addsym(s)
}
s = ldr.Lookup("runtime.etext", 0)
if ldr.SymType(s).IsText() {
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.SFirstUnallocated { // 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.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 {
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 {
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 {
oh64.DataDirectory = f.dataDirectory
} else {
oh.DataDirectory = f.dataDirectory
}
if pe64 {
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 = true
var oh64 pe.OptionalHeader64
l = binary.Size(&oh64)
} else {
// 32-bit architectures
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
} else {
// Use the same base image address as MSVC and LLVM.
if pe64 {
PEBASE = 0x140000000
} else {
PEBASE = 0x400000
}
}
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)
}
// The _load_config_used symbol is required to be present on modern
// Windows. We later wire this up to the PE data directory.
sb := ctxt.loader.CreateSymForUpdate("_load_config_used", 0)
sb.SetType(sym.SRODATA)
sb.SetAlign(int32(ctxt.Arch.PtrSize))
var buf bytes.Buffer
if pe64 {
lc := IMAGE_LOAD_CONFIG_DIRECTORY64{
Size: uint32(binary.Size(&IMAGE_LOAD_CONFIG_DIRECTORY64{})),
GuardFlags: IMAGE_GUARD_SECURITY_COOKIE_UNUSED,
}
binary.Write(&buf, binary.LittleEndian, &lc)
} else {
lc := IMAGE_LOAD_CONFIG_DIRECTORY32{
Size: uint32(binary.Size(&IMAGE_LOAD_CONFIG_DIRECTORY32{})),
GuardFlags: IMAGE_GUARD_SECURITY_COOKIE_UNUSED,
}
binary.Write(&buf, binary.LittleEndian, &lc)
}
sb.SetData(buf.Bytes())
sb.SetSize(int64(buf.Len()))
ctxt.loader.SetAttrReachable(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 {
ctxt.Out.Write64(m.off)
} else {
ctxt.Out.Write32(uint32(m.off))
}
}
if pe64 {
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 {
ctxt.Out.Write64(m.off)
} else {
ctxt.Out.Write32(uint32(m.off))
}
}
if pe64 {
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) >= math.MaxUint16 {
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
// 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()) - 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
slices.Sort(rt.pages)
// .reloc section must be 32-bit aligned
if out.Offset()&3 != 0 {
Errorf("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
}
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)
}
func setpersrc(ctxt *Link, syms []loader.Sym) {
if len(rsrcsyms) != 0 {
Errorf("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
// This should have been added in Peinit and by now is part of the
// just-written .rdata section.
s := ctxt.loader.Lookup("_load_config_used", 0)
if s != 0 {
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = uint32(ctxt.loader.SymValue(s) - PEBASE)
pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = uint32(ctxt.loader.SymSize(s))
}
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)
}
// peCreateExportFile creates a file with exported symbols for Windows .def files.
// ld will export all symbols, even those not marked for export, unless a .def file is provided.
func peCreateExportFile(ctxt *Link, libName string) (fname string) {
fname = filepath.Join(*flagTmpdir, "export_file.def")
var buf bytes.Buffer
if ctxt.BuildMode == BuildModeCShared {
fmt.Fprintf(&buf, "LIBRARY %s\n", libName)
}
buf.WriteString("EXPORTS\n")
ldr := ctxt.loader
var exports []string
for s := range ldr.ForAllCgoExportStatic() {
extname := ldr.SymExtname(s)
if !strings.HasPrefix(extname, "_cgoexp_") {
continue
}
if ldr.IsFileLocal(s) {
continue // Only export non-static symbols
}
// Retrieve the name of the initial symbol
// exported by cgo.
// The corresponding Go symbol is:
// _cgoexp_hashcode_symname.
name := strings.SplitN(extname, "_", 4)[3]
exports = append(exports, name)
}
if len(exports) == 0 {
// See runtime/cgo/windows.go for details.
exports = append(exports, "_cgo_stub_export")
}
sort.Strings(exports)
buf.WriteString(strings.Join(exports, "\n"))
err := os.WriteFile(fname, buf.Bytes(), 0666)
if err != nil {
Errorf("WriteFile %s failed: %v", fname, err)
}
return fname
}