// 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.

package ld

import (
	"cmd/internal/objabi"
	"cmd/internal/sys"
	"encoding/binary"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
)

type IMAGE_FILE_HEADER struct {
	Machine              uint16
	NumberOfSections     uint16
	TimeDateStamp        uint32
	PointerToSymbolTable uint32
	NumberOfSymbols      uint32
	SizeOfOptionalHeader uint16
	Characteristics      uint16
}

type IMAGE_DATA_DIRECTORY struct {
	VirtualAddress uint32
	Size           uint32
}

type IMAGE_OPTIONAL_HEADER struct {
	Magic                       uint16
	MajorLinkerVersion          uint8
	MinorLinkerVersion          uint8
	SizeOfCode                  uint32
	SizeOfInitializedData       uint32
	SizeOfUninitializedData     uint32
	AddressOfEntryPoint         uint32
	BaseOfCode                  uint32
	BaseOfData                  uint32
	ImageBase                   uint32
	SectionAlignment            uint32
	FileAlignment               uint32
	MajorOperatingSystemVersion uint16
	MinorOperatingSystemVersion uint16
	MajorImageVersion           uint16
	MinorImageVersion           uint16
	MajorSubsystemVersion       uint16
	MinorSubsystemVersion       uint16
	Win32VersionValue           uint32
	SizeOfImage                 uint32
	SizeOfHeaders               uint32
	CheckSum                    uint32
	Subsystem                   uint16
	DllCharacteristics          uint16
	SizeOfStackReserve          uint32
	SizeOfStackCommit           uint32
	SizeOfHeapReserve           uint32
	SizeOfHeapCommit            uint32
	LoaderFlags                 uint32
	NumberOfRvaAndSizes         uint32
	DataDirectory               [16]IMAGE_DATA_DIRECTORY
}

type IMAGE_SECTION_HEADER struct {
	Name                 [8]uint8
	VirtualSize          uint32
	VirtualAddress       uint32
	SizeOfRawData        uint32
	PointerToRawData     uint32
	PointerToRelocations uint32
	PointerToLineNumbers uint32
	NumberOfRelocations  uint16
	NumberOfLineNumbers  uint16
	Characteristics      uint32
}

type IMAGE_IMPORT_DESCRIPTOR struct {
	OriginalFirstThunk uint32
	TimeDateStamp      uint32
	ForwarderChain     uint32
	Name               uint32
	FirstThunk         uint32
}

type IMAGE_EXPORT_DIRECTORY struct {
	Characteristics       uint32
	TimeDateStamp         uint32
	MajorVersion          uint16
	MinorVersion          uint16
	Name                  uint32
	Base                  uint32
	NumberOfFunctions     uint32
	NumberOfNames         uint32
	AddressOfFunctions    uint32
	AddressOfNames        uint32
	AddressOfNameOrdinals uint32
}

const (
	PEBASE = 0x00400000
)

var (
	// SectionAlignment must be greater than or equal to FileAlignment.
	// The default is the page size for the architecture.
	PESECTALIGN int64 = 0x1000

	// FileAlignment should be a power of 2 between 512 and 64 K, inclusive.
	// The default is 512. If the SectionAlignment is less than
	// the architecture's page size, then FileAlignment must match SectionAlignment.
	PEFILEALIGN int64 = 2 << 8
)

const (
	IMAGE_FILE_MACHINE_I386              = 0x14c
	IMAGE_FILE_MACHINE_AMD64             = 0x8664
	IMAGE_FILE_RELOCS_STRIPPED           = 0x0001
	IMAGE_FILE_EXECUTABLE_IMAGE          = 0x0002
	IMAGE_FILE_LINE_NUMS_STRIPPED        = 0x0004
	IMAGE_FILE_LARGE_ADDRESS_AWARE       = 0x0020
	IMAGE_FILE_32BIT_MACHINE             = 0x0100
	IMAGE_FILE_DEBUG_STRIPPED            = 0x0200
	IMAGE_SCN_CNT_CODE                   = 0x00000020
	IMAGE_SCN_CNT_INITIALIZED_DATA       = 0x00000040
	IMAGE_SCN_CNT_UNINITIALIZED_DATA     = 0x00000080
	IMAGE_SCN_MEM_EXECUTE                = 0x20000000
	IMAGE_SCN_MEM_READ                   = 0x40000000
	IMAGE_SCN_MEM_WRITE                  = 0x80000000
	IMAGE_SCN_MEM_DISCARDABLE            = 0x2000000
	IMAGE_SCN_LNK_NRELOC_OVFL            = 0x1000000
	IMAGE_SCN_ALIGN_32BYTES              = 0x600000
	IMAGE_DIRECTORY_ENTRY_EXPORT         = 0
	IMAGE_DIRECTORY_ENTRY_IMPORT         = 1
	IMAGE_DIRECTORY_ENTRY_RESOURCE       = 2
	IMAGE_DIRECTORY_ENTRY_EXCEPTION      = 3
	IMAGE_DIRECTORY_ENTRY_SECURITY       = 4
	IMAGE_DIRECTORY_ENTRY_BASERELOC      = 5
	IMAGE_DIRECTORY_ENTRY_DEBUG          = 6
	IMAGE_DIRECTORY_ENTRY_COPYRIGHT      = 7
	IMAGE_DIRECTORY_ENTRY_ARCHITECTURE   = 7
	IMAGE_DIRECTORY_ENTRY_GLOBALPTR      = 8
	IMAGE_DIRECTORY_ENTRY_TLS            = 9
	IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    = 10
	IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   = 11
	IMAGE_DIRECTORY_ENTRY_IAT            = 12
	IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   = 13
	IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
	IMAGE_SUBSYSTEM_WINDOWS_GUI          = 2
	IMAGE_SUBSYSTEM_WINDOWS_CUI          = 3
)

// X64
type PE64_IMAGE_OPTIONAL_HEADER struct {
	Magic                       uint16
	MajorLinkerVersion          uint8
	MinorLinkerVersion          uint8
	SizeOfCode                  uint32
	SizeOfInitializedData       uint32
	SizeOfUninitializedData     uint32
	AddressOfEntryPoint         uint32
	BaseOfCode                  uint32
	ImageBase                   uint64
	SectionAlignment            uint32
	FileAlignment               uint32
	MajorOperatingSystemVersion uint16
	MinorOperatingSystemVersion uint16
	MajorImageVersion           uint16
	MinorImageVersion           uint16
	MajorSubsystemVersion       uint16
	MinorSubsystemVersion       uint16
	Win32VersionValue           uint32
	SizeOfImage                 uint32
	SizeOfHeaders               uint32
	CheckSum                    uint32
	Subsystem                   uint16
	DllCharacteristics          uint16
	SizeOfStackReserve          uint64
	SizeOfStackCommit           uint64
	SizeOfHeapReserve           uint64
	SizeOfHeapCommit            uint64
	LoaderFlags                 uint32
	NumberOfRvaAndSizes         uint32
	DataDirectory               [16]IMAGE_DATA_DIRECTORY
}

// 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
// http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx

// DOS stub that prints out
// "This program cannot be run in DOS mode."
var dosstub = []uint8{
	0x4d,
	0x5a,
	0x90,
	0x00,
	0x03,
	0x00,
	0x04,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0xff,
	0xff,
	0x00,
	0x00,
	0x8b,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x40,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x80,
	0x00,
	0x00,
	0x00,
	0x0e,
	0x1f,
	0xba,
	0x0e,
	0x00,
	0xb4,
	0x09,
	0xcd,
	0x21,
	0xb8,
	0x01,
	0x4c,
	0xcd,
	0x21,
	0x54,
	0x68,
	0x69,
	0x73,
	0x20,
	0x70,
	0x72,
	0x6f,
	0x67,
	0x72,
	0x61,
	0x6d,
	0x20,
	0x63,
	0x61,
	0x6e,
	0x6e,
	0x6f,
	0x74,
	0x20,
	0x62,
	0x65,
	0x20,
	0x72,
	0x75,
	0x6e,
	0x20,
	0x69,
	0x6e,
	0x20,
	0x44,
	0x4f,
	0x53,
	0x20,
	0x6d,
	0x6f,
	0x64,
	0x65,
	0x2e,
	0x0d,
	0x0d,
	0x0a,
	0x24,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
}

var rsrcsym *Symbol

var strtbl []byte

var PESECTHEADR int32

var PEFILEHEADR int32

var pe64 int

var pensect int

var nextsectoff int

var nextfileoff int

var textsect int

var datasect int

var bsssect int

var fh IMAGE_FILE_HEADER

var oh IMAGE_OPTIONAL_HEADER

var oh64 PE64_IMAGE_OPTIONAL_HEADER

var sh [16]IMAGE_SECTION_HEADER

// shNames stores full names of PE sections stored in sh.
var shNames []string

var dd []IMAGE_DATA_DIRECTORY

type Imp struct {
	s       *Symbol
	off     uint64
	next    *Imp
	argsize int
}

type Dll struct {
	name     string
	nameoff  uint64
	thunkoff uint64
	ms       *Imp
	next     *Dll
}

var dr *Dll

var dexport [1024]*Symbol

var nexport int

func addpesectionWithLongName(ctxt *Link, shortname, longname string, sectsize int, filesize int) *IMAGE_SECTION_HEADER {
	if pensect == 16 {
		Errorf(nil, "too many sections")
		errorexit()
	}

	h := &sh[pensect]
	pensect++
	copy(h.Name[:], shortname)
	shNames = append(shNames, longname)
	h.VirtualSize = uint32(sectsize)
	h.VirtualAddress = uint32(nextsectoff)
	nextsectoff = int(Rnd(int64(nextsectoff)+int64(sectsize), PESECTALIGN))
	h.PointerToRawData = uint32(nextfileoff)
	if filesize > 0 {
		h.SizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
		nextfileoff += int(h.SizeOfRawData)
	}

	return h
}

func addpesection(ctxt *Link, name string, sectsize int, filesize int) *IMAGE_SECTION_HEADER {
	return addpesectionWithLongName(ctxt, name, name, sectsize, filesize)
}
func chksectoff(ctxt *Link, h *IMAGE_SECTION_HEADER, off int64) {
	if off != int64(h.PointerToRawData) {
		Errorf(nil, "%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(off))
		errorexit()
	}
}

func chksectseg(ctxt *Link, h *IMAGE_SECTION_HEADER, s *Segment) {
	if s.Vaddr-PEBASE != uint64(h.VirtualAddress) {
		Errorf(nil, "%s.VirtualAddress = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.VirtualAddress)), uint64(int64(s.Vaddr-PEBASE)))
		errorexit()
	}

	if s.Fileoff != uint64(h.PointerToRawData) {
		Errorf(nil, "%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(int64(s.Fileoff)))
		errorexit()
	}
}

func Peinit(ctxt *Link) {
	var l int

	switch SysArch.Family {
	// 64-bit architectures
	case sys.AMD64:
		pe64 = 1

		l = binary.Size(&oh64)
		dd = oh64.DataDirectory[:]

	// 32-bit architectures
	default:
		l = binary.Size(&oh)

		dd = oh.DataDirectory[:]
	}

	if Linkmode == LinkExternal {
		PESECTALIGN = 0
		PEFILEALIGN = 0
	}

	PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN))
	if Linkmode != LinkExternal {
		PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN))
	} else {
		PESECTHEADR = 0
	}
	nextsectoff = int(PESECTHEADR)
	nextfileoff = int(PEFILEHEADR)

	if Linkmode == LinkInternal {
		// some mingw libs depend on this symbol, for example, FindPESectionByName
		ctxt.xdefine("__image_base__", SDATA, PEBASE)
		ctxt.xdefine("_image_base__", SDATA, PEBASE)
	}

	HEADR = PEFILEHEADR
	if *FlagTextAddr == -1 {
		*FlagTextAddr = PEBASE + int64(PESECTHEADR)
	}
	if *FlagDataAddr == -1 {
		*FlagDataAddr = 0
	}
	if *FlagRound == -1 {
		*FlagRound = int(PESECTALIGN)
	}
	if *FlagDataAddr != 0 && *FlagRound != 0 {
		fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(*FlagDataAddr), uint32(*FlagRound))
	}
}

func pewrite() {
	Cseek(0)
	if Linkmode != LinkExternal {
		Cwrite(dosstub)
		strnput("PE", 4)
	}

	binary.Write(&coutbuf, binary.LittleEndian, &fh)

	if pe64 != 0 {
		binary.Write(&coutbuf, binary.LittleEndian, &oh64)
	} else {
		binary.Write(&coutbuf, binary.LittleEndian, &oh)
	}
	if Linkmode == LinkExternal {
		for i := range sh[:pensect] {
			sh[i].VirtualAddress = 0
		}
	}
	binary.Write(&coutbuf, binary.LittleEndian, sh[:pensect])
}

func strput(s string) {
	coutbuf.WriteString(s)
	Cput(0)
	// string must be padded to even size
	if (len(s)+1)%2 != 0 {
		Cput(0)
	}
}

func initdynimport(ctxt *Link) *Dll {
	var d *Dll

	dr = nil
	var m *Imp
	for _, s := range ctxt.Syms.Allsym {
		if !s.Attr.Reachable() || s.Type != SDYNIMPORT {
			continue
		}
		for d = dr; d != nil; d = d.next {
			if d.name == s.Dynimplib {
				m = new(Imp)
				break
			}
		}

		if d == nil {
			d = new(Dll)
			d.name = s.Dynimplib
			d.next = dr
			dr = d
			m = new(Imp)
		}

		// Because external link requires properly stdcall decorated name,
		// all external symbols in runtime use %n to denote that the number
		// of uinptrs this function consumes. Store the argsize and discard
		// the %n suffix if any.
		m.argsize = -1
		if i := strings.IndexByte(s.Extname, '%'); i >= 0 {
			var err error
			m.argsize, err = strconv.Atoi(s.Extname[i+1:])
			if err != nil {
				Errorf(s, "failed to parse stdcall decoration: %v", err)
			}
			m.argsize *= SysArch.PtrSize
			s.Extname = s.Extname[:i]
		}

		m.s = s
		m.next = d.ms
		d.ms = m
	}

	if Linkmode == LinkExternal {
		// Add real symbol name
		for d := dr; d != nil; d = d.next {
			for m = d.ms; m != nil; m = m.next {
				m.s.Type = SDATA
				Symgrow(m.s, int64(SysArch.PtrSize))
				dynName := m.s.Extname
				// only windows/386 requires stdcall decoration
				if SysArch.Family == sys.I386 && m.argsize >= 0 {
					dynName += fmt.Sprintf("@%d", m.argsize)
				}
				dynSym := ctxt.Syms.Lookup(dynName, 0)
				dynSym.Attr |= AttrReachable
				dynSym.Type = SHOSTOBJ
				r := Addrel(m.s)
				r.Sym = dynSym
				r.Off = 0
				r.Siz = uint8(SysArch.PtrSize)
				r.Type = objabi.R_ADDR
			}
		}
	} else {
		dynamic := ctxt.Syms.Lookup(".windynamic", 0)
		dynamic.Attr |= AttrReachable
		dynamic.Type = SWINDOWS
		for d := dr; d != nil; d = d.next {
			for m = d.ms; m != nil; m = m.next {
				m.s.Type = SWINDOWS | SSUB
				m.s.Sub = dynamic.Sub
				dynamic.Sub = m.s
				m.s.Value = dynamic.Size
				dynamic.Size += int64(SysArch.PtrSize)
			}

			dynamic.Size += int64(SysArch.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 *IMAGE_SECTION_HEADER) {
	startoff := coutbuf.Offset()
	dynamic := ctxt.Syms.Lookup(".windynamic", 0)

	// skip import descriptor table (will write it later)
	n := uint64(0)

	for d := dr; d != nil; d = d.next {
		n++
	}
	Cseek(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1))

	// write dll names
	for d := dr; d != nil; d = d.next {
		d.nameoff = uint64(coutbuf.Offset()) - uint64(startoff)
		strput(d.name)
	}

	// write function names
	var m *Imp
	for d := dr; d != nil; d = d.next {
		for m = d.ms; m != nil; m = m.next {
			m.off = uint64(nextsectoff) + uint64(coutbuf.Offset()) - uint64(startoff)
			Wputl(0) // hint
			strput(m.s.Extname)
		}
	}

	// write OriginalFirstThunks
	oftbase := uint64(coutbuf.Offset()) - uint64(startoff)

	n = uint64(coutbuf.Offset())
	for d := dr; d != nil; d = d.next {
		d.thunkoff = uint64(coutbuf.Offset()) - n
		for m = d.ms; m != nil; m = m.next {
			if pe64 != 0 {
				Vputl(m.off)
			} else {
				Lputl(uint32(m.off))
			}
		}

		if pe64 != 0 {
			Vputl(0)
		} else {
			Lputl(0)
		}
	}

	// add pe section and pad it at the end
	n = uint64(coutbuf.Offset()) - uint64(startoff)

	isect := addpesection(ctxt, ".idata", int(n), int(n))
	isect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
	chksectoff(ctxt, isect, startoff)
	strnput("", int(uint64(isect.SizeOfRawData)-n))
	endoff := coutbuf.Offset()

	// write FirstThunks (allocated in .data section)
	ftbase := uint64(dynamic.Value) - uint64(datsect.VirtualAddress) - PEBASE

	Cseek(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 {
				Vputl(m.off)
			} else {
				Lputl(uint32(m.off))
			}
		}

		if pe64 != 0 {
			Vputl(0)
		} else {
			Lputl(0)
		}
	}

	// finally write import descriptor table
	Cseek(startoff)

	for d := dr; d != nil; d = d.next {
		Lputl(uint32(uint64(isect.VirtualAddress) + oftbase + d.thunkoff))
		Lputl(0)
		Lputl(0)
		Lputl(uint32(uint64(isect.VirtualAddress) + d.nameoff))
		Lputl(uint32(uint64(datsect.VirtualAddress) + ftbase + d.thunkoff))
	}

	Lputl(0) //end
	Lputl(0)
	Lputl(0)
	Lputl(0)
	Lputl(0)

	// update data directory
	dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.VirtualAddress
	dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.VirtualSize
	dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE)
	dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size)

	Cseek(endoff)
}

type byExtname []*Symbol

func (s byExtname) Len() int           { return len(s) }
func (s byExtname) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s byExtname) Less(i, j int) bool { return s[i].Extname < s[j].Extname }

func initdynexport(ctxt *Link) {
	nexport = 0
	for _, s := range ctxt.Syms.Allsym {
		if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() {
			continue
		}
		if nexport+1 > len(dexport) {
			Errorf(s, "pe dynexport table is full")
			errorexit()
		}

		dexport[nexport] = s
		nexport++
	}

	sort.Sort(byExtname(dexport[:nexport]))
}

func addexports(ctxt *Link) {
	var e IMAGE_EXPORT_DIRECTORY

	size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
	for i := 0; i < nexport; i++ {
		size += len(dexport[i].Extname) + 1
	}

	if nexport == 0 {
		return
	}

	sect := addpesection(ctxt, ".edata", size, size)
	sect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
	chksectoff(ctxt, sect, coutbuf.Offset())
	va := int(sect.VirtualAddress)
	dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
	dd[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)

	// put IMAGE_EXPORT_DIRECTORY
	binary.Write(&coutbuf, binary.LittleEndian, &e)

	// put EXPORT Address Table
	for i := 0; i < nexport; i++ {
		Lputl(uint32(dexport[i].Value - PEBASE))
	}

	// put EXPORT Name Pointer Table
	v := int(e.Name + uint32(len(*flagOutfile)) + 1)

	for i := 0; i < nexport; i++ {
		Lputl(uint32(v))
		v += len(dexport[i].Extname) + 1
	}

	// put EXPORT Ordinal Table
	for i := 0; i < nexport; i++ {
		Wputl(uint16(i))
	}

	// put Names
	strnput(*flagOutfile, len(*flagOutfile)+1)

	for i := 0; i < nexport; i++ {
		strnput(dexport[i].Extname, len(dexport[i].Extname)+1)
	}
	strnput("", int(sect.SizeOfRawData-uint32(size)))
}

// perelocsect relocates symbols from first in section sect, and returns
// the total number of relocations emitted.
func perelocsect(ctxt *Link, sect *Section, syms []*Symbol, base uint64) int {
	// If main section has no bits, nothing to relocate.
	if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
		return 0
	}

	relocs := 0

	sect.Reloff = uint64(coutbuf.Offset())
	for i, s := range syms {
		if !s.Attr.Reachable() {
			continue
		}
		if uint64(s.Value) >= sect.Vaddr {
			syms = syms[i:]
			break
		}
	}

	eaddr := int32(sect.Vaddr + sect.Length)
	for _, sym := range syms {
		if !sym.Attr.Reachable() {
			continue
		}
		if sym.Value >= int64(eaddr) {
			break
		}
		for ri := 0; ri < len(sym.R); ri++ {
			r := &sym.R[ri]
			if r.Done != 0 {
				continue
			}
			if r.Xsym == nil {
				Errorf(sym, "missing xsym in relocation")
				continue
			}

			if r.Xsym.Dynid < 0 {
				Errorf(sym, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
			}
			if !Thearch.PEreloc1(sym, r, int64(uint64(sym.Value+int64(r.Off))-base)) {
				Errorf(sym, "unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
			}

			relocs++
		}
	}

	sect.Rellen = uint64(coutbuf.Offset()) - sect.Reloff

	return relocs
}

// peemitsectreloc emits the relocation entries for sect.
// The actual relocations are emitted by relocfn.
// This updates the corresponding PE section table entry
// with the relocation offset and count.
func peemitsectreloc(sect *IMAGE_SECTION_HEADER, relocfn func() int) {
	sect.PointerToRelocations = uint32(coutbuf.Offset())
	// first entry: extended relocs
	Lputl(0) // placeholder for number of relocation + 1
	Lputl(0)
	Wputl(0)

	n := relocfn() + 1

	cpos := coutbuf.Offset()
	Cseek(int64(sect.PointerToRelocations))
	Lputl(uint32(n))
	Cseek(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)
}

// peemitreloc emits relocation entries for go.o in external linking.
func peemitreloc(ctxt *Link, text, data, ctors *IMAGE_SECTION_HEADER) {
	for coutbuf.Offset()&7 != 0 {
		Cput(0)
	}

	peemitsectreloc(text, func() int {
		n := perelocsect(ctxt, Segtext.Sections[0], ctxt.Textp, Segtext.Vaddr)
		for _, sect := range Segtext.Sections[1:] {
			n += perelocsect(ctxt, sect, datap, Segtext.Vaddr)
		}
		return n
	})

	peemitsectreloc(data, func() int {
		var n int
		for _, sect := range Segdata.Sections {
			n += perelocsect(ctxt, sect, datap, Segdata.Vaddr)
		}
		return n
	})

dwarfLoop:
	for _, sect := range Segdwarf.Sections {
		for i, name := range shNames {
			if sect.Name == name {
				peemitsectreloc(&sh[i], func() int {
					return perelocsect(ctxt, sect, dwarfp, sect.Vaddr)
				})
				continue dwarfLoop
			}
		}
		Errorf(nil, "peemitsectreloc: could not find %q section", sect.Name)
	}

	peemitsectreloc(ctors, func() int {
		dottext := ctxt.Syms.Lookup(".text", 0)
		Lputl(0)
		Lputl(uint32(dottext.Dynid))
		switch objabi.GOARCH {
		default:
			Errorf(dottext, "unknown architecture for PE: %q\n", objabi.GOARCH)
		case "386":
			Wputl(IMAGE_REL_I386_DIR32)
		case "amd64":
			Wputl(IMAGE_REL_AMD64_ADDR64)
		}
		return 1
	})
}

func (ctxt *Link) dope() {
	/* relocation table */
	rel := ctxt.Syms.Lookup(".rel", 0)

	rel.Attr |= AttrReachable
	rel.Type = SELFROSECT

	initdynimport(ctxt)
	initdynexport(ctxt)
}

func strtbladd(name string) int {
	off := len(strtbl) + 4 // offset includes 4-byte length at beginning of table
	strtbl = append(strtbl, name...)
	strtbl = append(strtbl, 0)
	return off
}

/*
 * For more than 8 characters section names, name contains a slash (/) that is
 * followed by an ASCII representation of a decimal number that is an offset into
 * the string table.
 * reference: pecoff_v8.docx Page 24.
 * <http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx>
 */
func newPEDWARFSection(ctxt *Link, name string, size int64) *IMAGE_SECTION_HEADER {
	if size == 0 {
		return nil
	}

	off := strtbladd(name)
	s := fmt.Sprintf("/%d", off)
	h := addpesectionWithLongName(ctxt, s, name, int(size), int(size))
	h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE

	return h
}

// writePESymTableRecords writes all COFF symbol table records.
// It returns number of records written.
func writePESymTableRecords(ctxt *Link) int {
	var symcnt int

	writeOneSymbol := func(s *Symbol, addr int64, sectidx int, typ uint16, class uint8) {
		// write COFF symbol table record
		if len(s.Name) > 8 {
			Lputl(0)
			Lputl(uint32(strtbladd(s.Name)))
		} else {
			strnput(s.Name, 8)
		}
		Lputl(uint32(addr))
		Wputl(uint16(sectidx))
		Wputl(typ)
		Cput(class)
		Cput(0) // no aux entries

		s.Dynid = int32(symcnt)

		symcnt++
	}

	put := func(ctxt *Link, s *Symbol, name string, type_ SymbolType, addr int64, gotype *Symbol) {
		if s == nil {
			return
		}
		if s.Sect == nil && type_ != UndefinedSym {
			return
		}
		switch type_ {
		default:
			return
		case DataSym, BSSSym, TextSym, UndefinedSym:
		}

		// Only windows/386 requires underscore prefix on external symbols.
		if SysArch.Family == sys.I386 &&
			Linkmode == LinkExternal &&
			(s.Type == SHOSTOBJ || s.Attr.CgoExport()) {
			s.Name = "_" + s.Name
		}

		typ := uint16(IMAGE_SYM_TYPE_NULL)
		var sect int
		var value int64
		if s.Sect != nil && s.Sect.Seg == &Segdata {
			// Note: although address of runtime.edata (type SDATA) is at the start of .bss section
			// it still belongs to the .data section, not the .bss section.
			if uint64(s.Value) >= Segdata.Vaddr+Segdata.Filelen && s.Type != SDATA && Linkmode == LinkExternal {
				value = int64(uint64(s.Value) - Segdata.Vaddr - Segdata.Filelen)
				sect = bsssect
			} else {
				value = int64(uint64(s.Value) - Segdata.Vaddr)
				sect = datasect
			}
		} else if s.Sect != nil && s.Sect.Seg == &Segtext {
			value = int64(uint64(s.Value) - Segtext.Vaddr)
			sect = textsect
		} else if type_ == UndefinedSym {
			typ = IMAGE_SYM_DTYPE_FUNCTION
		} else {
			Errorf(s, "addpesym %#x", addr)
		}
		if typ != IMAGE_SYM_TYPE_NULL {
		} else if Linkmode != LinkExternal {
			// TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308
			typ = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT
			typ = 0x0308 // "array of structs"
		}
		class := IMAGE_SYM_CLASS_EXTERNAL
		if s.Version != 0 || (s.Type&SHIDDEN != 0) || s.Attr.Local() {
			class = IMAGE_SYM_CLASS_STATIC
		}
		writeOneSymbol(s, value, sect, typ, uint8(class))
	}

	if Linkmode == LinkExternal {
		// Include section symbols as external, because
		// .ctors and .debug_* section relocations refer to it.
		for idx, name := range shNames {
			sym := ctxt.Syms.Lookup(name, 0)
			writeOneSymbol(sym, 0, idx+1, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC)
		}
	}

	genasmsym(ctxt, put)

	return symcnt
}

func addpesymtable(ctxt *Link) {
	symtabStartPos := coutbuf.Offset()

	// write COFF symbol table
	var symcnt int
	if !*FlagS || Linkmode == LinkExternal {
		symcnt = writePESymTableRecords(ctxt)
	}

	// update COFF file header and section table
	size := len(strtbl) + 4 + 18*symcnt
	var h *IMAGE_SECTION_HEADER
	if 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 = addpesection(ctxt, ".symtab", size, size)
		h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
		chksectoff(ctxt, h, symtabStartPos)
	}
	fh.PointerToSymbolTable = uint32(symtabStartPos)
	fh.NumberOfSymbols = uint32(symcnt)

	// write COFF string table
	Lputl(uint32(len(strtbl)) + 4)
	for i := 0; i < len(strtbl); i++ {
		Cput(strtbl[i])
	}
	if Linkmode != LinkExternal {
		strnput("", int(h.SizeOfRawData-uint32(size)))
	}
}

func setpersrc(ctxt *Link, sym *Symbol) {
	if rsrcsym != nil {
		Errorf(sym, "too many .rsrc sections")
	}

	rsrcsym = sym
}

func addpersrc(ctxt *Link) {
	if rsrcsym == nil {
		return
	}

	h := addpesection(ctxt, ".rsrc", int(rsrcsym.Size), int(rsrcsym.Size))
	h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA
	chksectoff(ctxt, h, coutbuf.Offset())

	// relocation
	var p []byte
	var r *Reloc
	var val uint32
	for ri := 0; ri < len(rsrcsym.R); ri++ {
		r = &rsrcsym.R[ri]
		p = rsrcsym.P[r.Off:]
		val = uint32(int64(h.VirtualAddress) + r.Add)

		// 32-bit little-endian
		p[0] = byte(val)

		p[1] = byte(val >> 8)
		p[2] = byte(val >> 16)
		p[3] = byte(val >> 24)
	}

	Cwrite(rsrcsym.P)
	strnput("", int(int64(h.SizeOfRawData)-rsrcsym.Size))

	// update data directory
	dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.VirtualAddress

	dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.VirtualSize
}

func addinitarray(ctxt *Link) (c *IMAGE_SECTION_HEADER) {
	// The size below was determined by the specification for array relocations,
	// and by observing what GCC writes here. If the initarray section grows to
	// contain more than one constructor entry, the size will need to be 8 * constructor_count.
	// However, the entire Go runtime is initialized from just one function, so it is unlikely
	// that this will need to grow in the future.
	var size int
	switch objabi.GOARCH {
	default:
		fmt.Fprintf(os.Stderr, "link: unknown architecture for PE: %q\n", objabi.GOARCH)
		os.Exit(2)
	case "386":
		size = 4
	case "amd64":
		size = 8
	}

	c = addpesection(ctxt, ".ctors", size, size)
	c.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
	c.SizeOfRawData = uint32(size)

	Cseek(int64(c.PointerToRawData))
	chksectoff(ctxt, c, coutbuf.Offset())
	init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0)
	addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr

	switch objabi.GOARCH {
	case "386":
		Lputl(uint32(addr))
	case "amd64":
		Vputl(addr)
	}

	return c
}

func Asmbpe(ctxt *Link) {
	switch SysArch.Family {
	default:
		Exitf("unknown PE architecture: %v", SysArch.Family)
	case sys.AMD64:
		fh.Machine = IMAGE_FILE_MACHINE_AMD64
	case sys.I386:
		fh.Machine = IMAGE_FILE_MACHINE_I386
	}

	t := addpesection(ctxt, ".text", int(Segtext.Length), int(Segtext.Length))
	t.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
	if 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
	}
	chksectseg(ctxt, t, &Segtext)
	textsect = pensect

	var d *IMAGE_SECTION_HEADER
	var c *IMAGE_SECTION_HEADER
	if Linkmode != LinkExternal {
		d = addpesection(ctxt, ".data", int(Segdata.Length), int(Segdata.Filelen))
		d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
		chksectseg(ctxt, d, &Segdata)
		datasect = pensect
	} else {
		d = addpesection(ctxt, ".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
		chksectseg(ctxt, d, &Segdata)
		datasect = pensect

		b := addpesection(ctxt, ".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
		bsssect = pensect
	}

	if !*FlagS {
		dwarfaddpeheaders(ctxt)
	}

	if Linkmode == LinkExternal {
		c = addinitarray(ctxt)
	}

	Cseek(int64(nextfileoff))
	if Linkmode != LinkExternal {
		addimports(ctxt, d)
		addexports(ctxt)
	}
	addpesymtable(ctxt)
	addpersrc(ctxt)
	if Linkmode == LinkExternal {
		peemitreloc(ctxt, t, d, c)
	}

	fh.NumberOfSections = uint16(pensect)

	// Being able to produce identical output for identical input is
	// much more beneficial than having build timestamp in the header.
	fh.TimeDateStamp = 0

	if Linkmode == LinkExternal {
		fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED
	} else {
		fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
	}
	if pe64 != 0 {
		fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64))
		fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE
		oh64.Magic = 0x20b // PE32+
	} else {
		fh.SizeOfOptionalHeader = uint16(binary.Size(&oh))
		fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE
		oh.Magic = 0x10b // PE32
		oh.BaseOfData = d.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 = t.SizeOfRawData
	oh.SizeOfCode = t.SizeOfRawData
	oh64.SizeOfInitializedData = d.SizeOfRawData
	oh.SizeOfInitializedData = d.SizeOfRawData
	oh64.SizeOfUninitializedData = 0
	oh.SizeOfUninitializedData = 0
	if Linkmode != LinkExternal {
		oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
		oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
	}
	oh64.BaseOfCode = t.VirtualAddress
	oh.BaseOfCode = t.VirtualAddress
	oh64.ImageBase = PEBASE
	oh.ImageBase = PEBASE
	oh64.SectionAlignment = uint32(PESECTALIGN)
	oh.SectionAlignment = uint32(PESECTALIGN)
	oh64.FileAlignment = uint32(PEFILEALIGN)
	oh.FileAlignment = uint32(PEFILEALIGN)
	oh64.MajorOperatingSystemVersion = 4
	oh.MajorOperatingSystemVersion = 4
	oh64.MinorOperatingSystemVersion = 0
	oh.MinorOperatingSystemVersion = 0
	oh64.MajorImageVersion = 1
	oh.MajorImageVersion = 1
	oh64.MinorImageVersion = 0
	oh.MinorImageVersion = 0
	oh64.MajorSubsystemVersion = 4
	oh.MajorSubsystemVersion = 4
	oh64.MinorSubsystemVersion = 0
	oh.MinorSubsystemVersion = 0
	oh64.SizeOfImage = uint32(nextsectoff)
	oh.SizeOfImage = uint32(nextsectoff)
	oh64.SizeOfHeaders = uint32(PEFILEHEADR)
	oh.SizeOfHeaders = uint32(PEFILEHEADR)
	if windowsgui {
		oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
		oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
	} else {
		oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
		oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
	}

	// 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 affects only the main
	// thread, ctrlhandler thread, and profileloop thread. For
	// these, it must be greater than the stack size assumed by
	// externalthreadhandler.
	//
	// For other threads we specify stack size in runtime explicitly.
	// For these, the reserve must match STACKSIZE in
	// runtime/cgo/gcc_windows_{386,amd64}.c and the correspondent
	// CreateThread parameter in runtime.newosproc.
	oh64.SizeOfStackReserve = 0x00200000
	oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages

	// 32-bit is trickier since there much less address space to
	// work with. Here we use large stacks only in cgo binaries as
	// a compromise.
	if !iscgo {
		oh.SizeOfStackReserve = 0x00020000
		oh.SizeOfStackCommit = 0x00001000
	} else {
		oh.SizeOfStackReserve = 0x00100000
		oh.SizeOfStackCommit = 0x00100000 - 0x2000
	}

	oh64.SizeOfHeapReserve = 0x00100000
	oh.SizeOfHeapReserve = 0x00100000
	oh64.SizeOfHeapCommit = 0x00001000
	oh.SizeOfHeapCommit = 0x00001000
	oh64.NumberOfRvaAndSizes = 16
	oh.NumberOfRvaAndSizes = 16

	pewrite()
}
