// 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"
"cmd/link/internal/sym"
"debug/pe"
"encoding/binary"
"fmt"
"sort"
"strconv"
"strings"
)
type IMAGE_IMPORT_DESCRIPTOR struct {
OriginalFirstThunk uint32
TimeDateStamp uint32
ForwarderChain uint32
Name uint32
FirstThunk uint32
}
type IMAGE_EXPORT_DIRECTORY struct {
Characteristics uint32
TimeDateStamp uint32
MajorVersion uint16
MinorVersion uint16
Name uint32
Base uint32
NumberOfFunctions uint32
NumberOfNames uint32
AddressOfFunctions uint32
AddressOfNames uint32
AddressOfNameOrdinals uint32
}
const (
PEBASE = 0x00400000
)
var (
// SectionAlignment must be greater than or equal to FileAlignment.
// The default is the page size for the architecture.
PESECTALIGN int64 = 0x1000
// FileAlignment should be a power of 2 between 512 and 64 K, inclusive.
// The default is 512. If the SectionAlignment is less than
// the architecture's page size, then FileAlignment must match SectionAlignment.
PEFILEALIGN int64 = 2 << 8
)
const (
IMAGE_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
)
// TODO(crawshaw): add these constants to debug/pe.
const (
// TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2
IMAGE_SYM_TYPE_NULL = 0
IMAGE_SYM_TYPE_STRUCT = 8
IMAGE_SYM_DTYPE_FUNCTION = 0x20
IMAGE_SYM_DTYPE_ARRAY = 0x30
IMAGE_SYM_CLASS_EXTERNAL = 2
IMAGE_SYM_CLASS_STATIC = 3
IMAGE_REL_I386_DIR32 = 0x0006
IMAGE_REL_I386_SECREL = 0x000B
IMAGE_REL_I386_REL32 = 0x0014
IMAGE_REL_AMD64_ADDR64 = 0x0001
IMAGE_REL_AMD64_ADDR32 = 0x0002
IMAGE_REL_AMD64_REL32 = 0x0004
IMAGE_REL_AMD64_SECREL = 0x000B
)
// 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,
}
type Imp struct {
s *sym.Symbol
off uint64
next *Imp
argsize int
}
type Dll struct {
name string
nameoff uint64
thunkoff uint64
ms *Imp
next *Dll
}
var (
rsrcsym *sym.Symbol
PESECTHEADR int32
PEFILEHEADR int32
pe64 int
dr *Dll
dexport [1024]*sym.Symbol
nexport int
)
// peStringTable is a COFF string table.
type peStringTable struct {
strings []string
stringsLen int
}
// size resturns size of string table t.
func (t *peStringTable) size() int {
// string table starts with 4-byte length at the beginning
return t.stringsLen + 4
}
// add adds string str to string table t.
func (t *peStringTable) add(str string) int {
off := t.size()
t.strings = append(t.strings, str)
t.stringsLen += len(str) + 1 // each string will have 0 appended to it
return off
}
// write writes string table t into the output file.
func (t *peStringTable) write(out *OutBuf) {
out.Write32(uint32(t.size()))
for _, s := range t.strings {
out.WriteString(s)
out.Write8(0)
}
}
// peSection represents section from COFF section table.
type peSection struct {
name string
shortName string
index int // one-based index into the Section Table
virtualSize uint32
virtualAddress uint32
sizeOfRawData uint32
pointerToRawData uint32
pointerToRelocations uint32
numberOfRelocations uint16
characteristics uint32
}
// checkOffset verifies COFF section sect offset in the file.
func (sect *peSection) checkOffset(off int64) {
if off != int64(sect.pointerToRawData) {
Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off))
errorexit()
}
}
// checkSegment verifies COFF section sect matches address
// and file offset provided in segment seg.
func (sect *peSection) checkSegment(seg *sym.Segment) {
if seg.Vaddr-PEBASE != uint64(sect.virtualAddress) {
Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-PEBASE)))
errorexit()
}
if seg.Fileoff != uint64(sect.pointerToRawData) {
Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff)))
errorexit()
}
}
// pad adds zeros to the section sect. It writes as many bytes
// as necessary to make section sect.SizeOfRawData bytes long.
// It assumes that n bytes are already written to the file.
func (sect *peSection) pad(out *OutBuf, n uint32) {
out.WriteStringN("", int(sect.sizeOfRawData-n))
}
// write writes COFF section sect into the output file.
func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error {
h := pe.SectionHeader32{
VirtualSize: sect.virtualSize,
SizeOfRawData: sect.sizeOfRawData,
PointerToRawData: sect.pointerToRawData,
PointerToRelocations: sect.pointerToRelocations,
NumberOfRelocations: sect.numberOfRelocations,
Characteristics: sect.characteristics,
}
if linkmode != LinkExternal {
h.VirtualAddress = sect.virtualAddress
}
copy(h.Name[:], sect.shortName)
return binary.Write(out, binary.LittleEndian, h)
}
// emitRelocations emits the relocation entries for the sect.
// The actual relocations are emitted by relocfn.
// This updates the corresponding PE section table entry
// with the relocation offset and count.
func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) {
sect.pointerToRelocations = uint32(out.Offset())
// first entry: extended relocs
out.Write32(0) // placeholder for number of relocation + 1
out.Write32(0)
out.Write16(0)
n := relocfn() + 1
cpos := out.Offset()
out.SeekSet(int64(sect.pointerToRelocations))
out.Write32(uint32(n))
out.SeekSet(cpos)
if n > 0x10000 {
n = 0x10000
sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL
} else {
sect.pointerToRelocations += 10 // skip the extend reloc entry
}
sect.numberOfRelocations = uint16(n - 1)
}
// peFile is used to build COFF file.
type peFile struct {
sections []*peSection
stringTable peStringTable
textSect *peSection
dataSect *peSection
bssSect *peSection
ctorsSect *peSection
nextSectOffset uint32
nextFileOffset uint32
symtabOffset int64 // offset to the start of symbol table
symbolCount int // number of symbol table records written
dataDirectory [16]pe.DataDirectory
}
// addSection adds section to the COFF file f.
func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection {
sect := &peSection{
name: name,
shortName: name,
index: len(f.sections) + 1,
virtualSize: uint32(sectsize),
virtualAddress: f.nextSectOffset,
pointerToRawData: f.nextFileOffset,
}
f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN))
if filesize > 0 {
sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
f.nextFileOffset += sect.sizeOfRawData
}
f.sections = append(f.sections, sect)
return sect
}
// addDWARFSection adds DWARF section to the COFF file f.
// This function is similar to addSection, but DWARF section names are
// longer than 8 characters, so they need to be stored in the string table.
func (f *peFile) addDWARFSection(name string, size int) *peSection {
if size == 0 {
Exitf("DWARF section %q is empty", name)
}
// DWARF section names are longer than 8 characters.
// PE format requires such names to be stored in string table,
// and section names replaced with slash (/) followed by
// correspondent string table index.
// see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx
// for details
off := f.stringTable.add(name)
h := f.addSection(name, size, size)
h.shortName = fmt.Sprintf("/%d", off)
h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
return h
}
// addDWARF adds DWARF information to the COFF file f.
func (f *peFile) addDWARF() {
if *FlagS { // disable symbol table
return
}
if *FlagW { // disable dwarf
return
}
for _, sect := range Segdwarf.Sections {
h := f.addDWARFSection(sect.Name, int(sect.Length))
fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
if uint64(h.pointerToRawData) != fileoff {
Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff)
}
}
}
// addInitArray adds .ctors COFF section to the file f.
func (f *peFile) addInitArray(ctxt *Link) *peSection {
// The size below was determined by the specification for array relocations,
// and by observing what GCC writes here. If the initarray section grows to
// contain more than one constructor entry, the size will need to be 8 * constructor_count.
// However, the entire Go runtime is initialized from just one function, so it is unlikely
// that this will need to grow in the future.
var size int
switch objabi.GOARCH {
default:
Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", objabi.GOARCH)
case "386":
size = 4
case "amd64":
size = 8
}
sect := f.addSection(".ctors", size, size)
sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
sect.sizeOfRawData = uint32(size)
ctxt.Out.SeekSet(int64(sect.pointerToRawData))
sect.checkOffset(ctxt.Out.Offset())
init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0)
addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr
switch objabi.GOARCH {
case "386":
ctxt.Out.Write32(uint32(addr))
case "amd64":
ctxt.Out.Write64(addr)
}
return sect
}
// emitRelocations emits relocation entries for go.o in external linking.
func (f *peFile) emitRelocations(ctxt *Link) {
for ctxt.Out.Offset()&7 != 0 {
ctxt.Out.Write8(0)
}
// relocsect relocates symbols from first in section sect, and returns
// the total number of relocations emitted.
relocsect := func(sect *sym.Section, syms []*sym.Symbol, base uint64) int {
// If main section has no bits, nothing to relocate.
if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
return 0
}
relocs := 0
sect.Reloff = uint64(ctxt.Out.Offset())
for i, s := range syms {
if !s.Attr.Reachable() {
continue
}
if uint64(s.Value) >= sect.Vaddr {
syms = syms[i:]
break
}
}
eaddr := int32(sect.Vaddr + sect.Length)
for _, sym := range syms {
if !sym.Attr.Reachable() {
continue
}
if sym.Value >= int64(eaddr) {
break
}
for ri := 0; ri < len(sym.R); ri++ {
r := &sym.R[ri]
if r.Done {
continue
}
if r.Xsym == nil {
Errorf(sym, "missing xsym in relocation")
continue
}
if r.Xsym.Dynid < 0 {
Errorf(sym, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type)
}
if !Thearch.PEreloc1(ctxt.Arch, ctxt.Out, sym, r, int64(uint64(sym.Value+int64(r.Off))-base)) {
Errorf(sym, "unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name)
}
relocs++
}
}
sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
return relocs
}
f.textSect.emitRelocations(ctxt.Out, func() int {
n := relocsect(Segtext.Sections[0], ctxt.Textp, Segtext.Vaddr)
for _, sect := range Segtext.Sections[1:] {
n += relocsect(sect, datap, Segtext.Vaddr)
}
return n
})
f.dataSect.emitRelocations(ctxt.Out, func() int {
var n int
for _, sect := range Segdata.Sections {
n += relocsect(sect, datap, Segdata.Vaddr)
}
return n
})
dwarfLoop:
for _, sect := range Segdwarf.Sections {
for _, pesect := range f.sections {
if sect.Name == pesect.name {
pesect.emitRelocations(ctxt.Out, func() int {
return relocsect(sect, dwarfp, sect.Vaddr)
})
continue dwarfLoop
}
}
Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
}
f.ctorsSect.emitRelocations(ctxt.Out, func() int {
dottext := ctxt.Syms.Lookup(".text", 0)
ctxt.Out.Write32(0)
ctxt.Out.Write32(uint32(dottext.Dynid))
switch objabi.GOARCH {
default:
Errorf(dottext, "unknown architecture for PE: %q\n", objabi.GOARCH)
case "386":
ctxt.Out.Write16(IMAGE_REL_I386_DIR32)
case "amd64":
ctxt.Out.Write16(IMAGE_REL_AMD64_ADDR64)
}
return 1
})
}
// writeSymbol appends symbol s to file f symbol table.
// It also sets s.Dynid to written symbol number.
func (f *peFile) writeSymbol(out *OutBuf, s *sym.Symbol, value int64, sectidx int, typ uint16, class uint8) {
if len(s.Name) > 8 {
out.Write32(0)
out.Write32(uint32(f.stringTable.add(s.Name)))
} else {
out.WriteStringN(s.Name, 8)
}
out.Write32(uint32(value))
out.Write16(uint16(sectidx))
out.Write16(typ)
out.Write8(class)
out.Write8(0) // no aux entries
s.Dynid = int32(f.symbolCount)
f.symbolCount++
}
// mapToPESection searches peFile f for s symbol's location.
// It returns PE section index, and offset within that section.
func (f *peFile) mapToPESection(s *sym.Symbol, linkmode LinkMode) (pesectidx int, offset int64, err error) {
if s.Sect == nil {
return 0, 0, fmt.Errorf("could not map %s symbol with no section", s.Name)
}
if s.Sect.Seg == &Segtext {
return f.textSect.index, int64(uint64(s.Value) - Segtext.Vaddr), nil
}
if s.Sect.Seg != &Segdata {
return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .data section", s.Name)
}
v := uint64(s.Value) - Segdata.Vaddr
if linkmode != LinkExternal {
return f.dataSect.index, int64(v), nil
}
if s.Type == sym.SDATA {
return f.dataSect.index, int64(v), nil
}
// Note: although address of runtime.edata (type sym.SDATA) is at the start of .bss section
// it still belongs to the .data section, not the .bss section.
if v < Segdata.Filelen {
return f.dataSect.index, int64(v), nil
}
return f.bssSect.index, int64(v - Segdata.Filelen), nil
}
// writeSymbols writes all COFF symbol table records.
func (f *peFile) writeSymbols(ctxt *Link) {
put := func(ctxt *Link, s *sym.Symbol, name string, type_ SymbolType, addr int64, gotype *sym.Symbol) {
if s == nil {
return
}
if s.Sect == nil && type_ != UndefinedSym {
return
}
switch type_ {
default:
return
case DataSym, BSSSym, TextSym, UndefinedSym:
}
// Only windows/386 requires underscore prefix on external symbols.
if ctxt.Arch.Family == sys.I386 &&
ctxt.LinkMode == LinkExternal &&
(s.Type == sym.SHOSTOBJ || s.Attr.CgoExport()) {
s.Name = "_" + s.Name
}
var typ uint16
if ctxt.LinkMode == LinkExternal {
typ = IMAGE_SYM_TYPE_NULL
} else {
// TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308
typ = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT
typ = 0x0308 // "array of structs"
}
sect, value, err := f.mapToPESection(s, ctxt.LinkMode)
if err != nil {
if type_ == UndefinedSym {
typ = IMAGE_SYM_DTYPE_FUNCTION
} else {
Errorf(s, "addpesym: %v", err)
}
}
class := IMAGE_SYM_CLASS_EXTERNAL
if s.Version != 0 || s.Attr.VisibilityHidden() || s.Attr.Local() {
class = IMAGE_SYM_CLASS_STATIC
}
f.writeSymbol(ctxt.Out, s, value, sect, typ, uint8(class))
}
if ctxt.LinkMode == LinkExternal {
// Include section symbols as external, because
// .ctors and .debug_* section relocations refer to it.
for _, pesect := range f.sections {
sym := ctxt.Syms.Lookup(pesect.name, 0)
f.writeSymbol(ctxt.Out, sym, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC)
}
}
genasmsym(ctxt, put)
}
// writeSymbolTableAndStringTable writes out symbol and string tables for peFile f.
func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) {
f.symtabOffset = ctxt.Out.Offset()
// write COFF symbol table
if !*FlagS || ctxt.LinkMode == LinkExternal {
f.writeSymbols(ctxt)
}
// update COFF file header and section table
size := f.stringTable.size() + 18*f.symbolCount
var h *peSection
if ctxt.LinkMode != LinkExternal {
// We do not really need .symtab for go.o, and if we have one, ld
// will also include it in the exe, and that will confuse windows.
h = f.addSection(".symtab", size, size)
h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
h.checkOffset(f.symtabOffset)
}
// write COFF string table
f.stringTable.write(ctxt.Out)
if ctxt.LinkMode != LinkExternal {
h.pad(ctxt.Out, uint32(size))
}
}
// writeFileHeader writes COFF file header for peFile f.
func (f *peFile) writeFileHeader(arch *sys.Arch, out *OutBuf, linkmode LinkMode) {
var fh pe.FileHeader
switch arch.Family {
default:
Exitf("unknown PE architecture: %v", arch.Family)
case sys.AMD64:
fh.Machine = IMAGE_FILE_MACHINE_AMD64
case sys.I386:
fh.Machine = IMAGE_FILE_MACHINE_I386
}
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 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 {
var oh64 pe.OptionalHeader64
fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64))
fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE
} else {
var oh pe.OptionalHeader32
fh.SizeOfOptionalHeader = uint16(binary.Size(&oh))
fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE
}
fh.PointerToSymbolTable = uint32(f.symtabOffset)
fh.NumberOfSymbols = uint32(f.symbolCount)
binary.Write(out, binary.LittleEndian, &fh)
}
// writeOptionalHeader writes COFF optional header for peFile f.
func (f *peFile) writeOptionalHeader(ctxt *Link) {
var oh pe.OptionalHeader32
var oh64 pe.OptionalHeader64
if pe64 != 0 {
oh64.Magic = 0x20b // PE32+
} else {
oh.Magic = 0x10b // PE32
oh.BaseOfData = f.dataSect.virtualAddress
}
// Fill out both oh64 and oh. We only use one. Oh well.
oh64.MajorLinkerVersion = 3
oh.MajorLinkerVersion = 3
oh64.MinorLinkerVersion = 0
oh.MinorLinkerVersion = 0
oh64.SizeOfCode = f.textSect.sizeOfRawData
oh.SizeOfCode = f.textSect.sizeOfRawData
oh64.SizeOfInitializedData = f.dataSect.sizeOfRawData
oh.SizeOfInitializedData = f.dataSect.sizeOfRawData
oh64.SizeOfUninitializedData = 0
oh.SizeOfUninitializedData = 0
if ctxt.LinkMode != LinkExternal {
oh64.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
oh.AddressOfEntryPoint = uint32(Entryvalue(ctxt) - PEBASE)
}
oh64.BaseOfCode = f.textSect.virtualAddress
oh.BaseOfCode = f.textSect.virtualAddress
oh64.ImageBase = PEBASE
oh.ImageBase = PEBASE
oh64.SectionAlignment = uint32(PESECTALIGN)
oh.SectionAlignment = uint32(PESECTALIGN)
oh64.FileAlignment = uint32(PEFILEALIGN)
oh.FileAlignment = uint32(PEFILEALIGN)
oh64.MajorOperatingSystemVersion = 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 = f.nextSectOffset
oh.SizeOfImage = f.nextSectOffset
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
if !iscgo {
oh64.SizeOfStackCommit = 0x00001000
} else {
// TODO(brainman): Maybe remove optional header writing altogether for cgo.
// For cgo it is the external linker that is building final executable.
// And it probably does not use any information stored in optional header.
oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages
}
oh.SizeOfStackReserve = 0x00100000
if !iscgo {
oh.SizeOfStackCommit = 0x00001000
} else {
oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages
}
oh64.SizeOfHeapReserve = 0x00100000
oh.SizeOfHeapReserve = 0x00100000
oh64.SizeOfHeapCommit = 0x00001000
oh.SizeOfHeapCommit = 0x00001000
oh64.NumberOfRvaAndSizes = 16
oh.NumberOfRvaAndSizes = 16
if pe64 != 0 {
oh64.DataDirectory = f.dataDirectory
} else {
oh.DataDirectory = f.dataDirectory
}
if pe64 != 0 {
binary.Write(ctxt.Out, binary.LittleEndian, &oh64)
} else {
binary.Write(ctxt.Out, binary.LittleEndian, &oh)
}
}
var pefile peFile
func Peinit(ctxt *Link) {
var l int
switch ctxt.Arch.Family {
// 64-bit architectures
case sys.AMD64:
pe64 = 1
var oh64 pe.OptionalHeader64
l = binary.Size(&oh64)
// 32-bit architectures
default:
var oh pe.OptionalHeader32
l = binary.Size(&oh)
}
if ctxt.LinkMode == LinkExternal {
PESECTALIGN = 0
PEFILEALIGN = 0
}
var sh [16]pe.SectionHeader32
var fh pe.FileHeader
PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN))
if ctxt.LinkMode != LinkExternal {
PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN))
} else {
PESECTHEADR = 0
}
pefile.nextSectOffset = uint32(PESECTHEADR)
pefile.nextFileOffset = uint32(PEFILEHEADR)
if ctxt.LinkMode == LinkInternal {
// some mingw libs depend on this symbol, for example, FindPESectionByName
ctxt.xdefine("__image_base__", sym.SDATA, PEBASE)
ctxt.xdefine("_image_base__", sym.SDATA, PEBASE)
}
HEADR = PEFILEHEADR
if *FlagTextAddr == -1 {
*FlagTextAddr = PEBASE + int64(PESECTHEADR)
}
if *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(ctxt *Link) {
ctxt.Out.SeekSet(0)
if ctxt.LinkMode != LinkExternal {
ctxt.Out.Write(dosstub)
ctxt.Out.WriteStringN("PE", 4)
}
pefile.writeFileHeader(ctxt.Arch, ctxt.Out, ctxt.LinkMode)
pefile.writeOptionalHeader(ctxt)
for _, sect := range pefile.sections {
sect.write(ctxt.Out, ctxt.LinkMode)
}
}
func strput(out *OutBuf, s string) {
out.WriteString(s)
out.Write8(0)
// string must be padded to even size
if (len(s)+1)%2 != 0 {
out.Write8(0)
}
}
func initdynimport(ctxt *Link) *Dll {
var d *Dll
dr = nil
var m *Imp
for _, s := range ctxt.Syms.Allsym {
if !s.Attr.Reachable() || s.Type != sym.SDYNIMPORT {
continue
}
for d = dr; d != nil; d = d.next {
if d.name == s.Dynimplib {
m = new(Imp)
break
}
}
if d == nil {
d = new(Dll)
d.name = s.Dynimplib
d.next = dr
dr = d
m = new(Imp)
}
// Because external link requires properly stdcall decorated name,
// all external symbols in runtime use %n to denote that the number
// of uinptrs this function consumes. Store the argsize and discard
// the %n suffix if any.
m.argsize = -1
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 *= ctxt.Arch.PtrSize
s.Extname = s.Extname[:i]
}
m.s = s
m.next = d.ms
d.ms = m
}
if ctxt.LinkMode == LinkExternal {
// Add real symbol name
for d := dr; d != nil; d = d.next {
for m = d.ms; m != nil; m = m.next {
m.s.Type = sym.SDATA
m.s.Grow(int64(ctxt.Arch.PtrSize))
dynName := m.s.Extname
// only windows/386 requires stdcall decoration
if ctxt.Arch.Family == sys.I386 && m.argsize >= 0 {
dynName += fmt.Sprintf("@%d", m.argsize)
}
dynSym := ctxt.Syms.Lookup(dynName, 0)
dynSym.Attr |= sym.AttrReachable
dynSym.Type = sym.SHOSTOBJ
r := m.s.AddRel()
r.Sym = dynSym
r.Off = 0
r.Siz = uint8(ctxt.Arch.PtrSize)
r.Type = objabi.R_ADDR
}
}
} else {
dynamic := ctxt.Syms.Lookup(".windynamic", 0)
dynamic.Attr |= sym.AttrReachable
dynamic.Type = sym.SWINDOWS
for d := dr; d != nil; d = d.next {
for m = d.ms; m != nil; m = m.next {
m.s.Type = sym.SWINDOWS
m.s.Attr |= sym.AttrSubSymbol
m.s.Sub = dynamic.Sub
dynamic.Sub = m.s
m.s.Value = dynamic.Size
dynamic.Size += int64(ctxt.Arch.PtrSize)
}
dynamic.Size += int64(ctxt.Arch.PtrSize)
}
}
return dr
}
// peimporteddlls returns the gcc command line argument to link all imported
// DLLs.
func peimporteddlls() []string {
var dlls []string
for d := dr; d != nil; d = d.next {
dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll"))
}
return dlls
}
func addimports(ctxt *Link, datsect *peSection) {
startoff := ctxt.Out.Offset()
dynamic := ctxt.Syms.Lookup(".windynamic", 0)
// skip import descriptor table (will write it later)
n := uint64(0)
for d := dr; d != nil; d = d.next {
n++
}
ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1))
// write dll names
for d := dr; d != nil; d = d.next {
d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff)
strput(ctxt.Out, d.name)
}
// write function names
var m *Imp
for d := dr; d != nil; d = d.next {
for m = d.ms; m != nil; m = m.next {
m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff)
ctxt.Out.Write16(0) // hint
strput(ctxt.Out, m.s.Extname)
}
}
// write OriginalFirstThunks
oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff)
n = uint64(ctxt.Out.Offset())
for d := dr; d != nil; d = d.next {
d.thunkoff = uint64(ctxt.Out.Offset()) - n
for m = d.ms; m != nil; m = m.next {
if pe64 != 0 {
ctxt.Out.Write64(m.off)
} else {
ctxt.Out.Write32(uint32(m.off))
}
}
if pe64 != 0 {
ctxt.Out.Write64(0)
} else {
ctxt.Out.Write32(0)
}
}
// add pe section and pad it at the end
n = uint64(ctxt.Out.Offset()) - uint64(startoff)
isect := pefile.addSection(".idata", int(n), int(n))
isect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
isect.checkOffset(startoff)
isect.pad(ctxt.Out, uint32(n))
endoff := ctxt.Out.Offset()
// write FirstThunks (allocated in .data section)
ftbase := uint64(dynamic.Value) - uint64(datsect.virtualAddress) - PEBASE
ctxt.Out.SeekSet(int64(uint64(datsect.pointerToRawData) + ftbase))
for d := dr; d != nil; d = d.next {
for m = d.ms; m != nil; m = m.next {
if pe64 != 0 {
ctxt.Out.Write64(m.off)
} else {
ctxt.Out.Write32(uint32(m.off))
}
}
if pe64 != 0 {
ctxt.Out.Write64(0)
} else {
ctxt.Out.Write32(0)
}
}
// finally write import descriptor table
out := ctxt.Out
out.SeekSet(startoff)
for d := dr; d != nil; d = d.next {
out.Write32(uint32(uint64(isect.virtualAddress) + oftbase + d.thunkoff))
out.Write32(0)
out.Write32(0)
out.Write32(uint32(uint64(isect.virtualAddress) + d.nameoff))
out.Write32(uint32(uint64(datsect.virtualAddress) + ftbase + d.thunkoff))
}
out.Write32(0) //end
out.Write32(0)
out.Write32(0)
out.Write32(0)
out.Write32(0)
// update data directory
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size)
out.SeekSet(endoff)
}
type byExtname []*sym.Symbol
func (s byExtname) Len() int { return len(s) }
func (s byExtname) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byExtname) Less(i, j int) bool { return s[i].Extname < s[j].Extname }
func initdynexport(ctxt *Link) {
nexport = 0
for _, s := range ctxt.Syms.Allsym {
if !s.Attr.Reachable() || !s.Attr.CgoExportDynamic() {
continue
}
if nexport+1 > len(dexport) {
Errorf(s, "pe dynexport table is full")
errorexit()
}
dexport[nexport] = s
nexport++
}
sort.Sort(byExtname(dexport[:nexport]))
}
func addexports(ctxt *Link) {
var e IMAGE_EXPORT_DIRECTORY
size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1
for i := 0; i < nexport; i++ {
size += len(dexport[i].Extname) + 1
}
if nexport == 0 {
return
}
sect := pefile.addSection(".edata", size, size)
sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
sect.checkOffset(ctxt.Out.Offset())
va := int(sect.virtualAddress)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va)
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize
vaName := va + binary.Size(&e) + nexport*4
vaAddr := va + binary.Size(&e)
vaNa := va + binary.Size(&e) + nexport*8
e.Characteristics = 0
e.MajorVersion = 0
e.MinorVersion = 0
e.NumberOfFunctions = uint32(nexport)
e.NumberOfNames = uint32(nexport)
e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names.
e.Base = 1
e.AddressOfFunctions = uint32(vaAddr)
e.AddressOfNames = uint32(vaName)
e.AddressOfNameOrdinals = uint32(vaNa)
out := ctxt.Out
// put IMAGE_EXPORT_DIRECTORY
binary.Write(out, binary.LittleEndian, &e)
// put EXPORT Address Table
for i := 0; i < nexport; i++ {
out.Write32(uint32(dexport[i].Value - PEBASE))
}
// put EXPORT Name Pointer Table
v := int(e.Name + uint32(len(*flagOutfile)) + 1)
for i := 0; i < nexport; i++ {
out.Write32(uint32(v))
v += len(dexport[i].Extname) + 1
}
// put EXPORT Ordinal Table
for i := 0; i < nexport; i++ {
out.Write16(uint16(i))
}
// put Names
out.WriteStringN(*flagOutfile, len(*flagOutfile)+1)
for i := 0; i < nexport; i++ {
out.WriteStringN(dexport[i].Extname, len(dexport[i].Extname)+1)
}
sect.pad(out, uint32(size))
}
func (ctxt *Link) dope() {
/* relocation table */
rel := ctxt.Syms.Lookup(".rel", 0)
rel.Attr |= sym.AttrReachable
rel.Type = sym.SELFROSECT
initdynimport(ctxt)
initdynexport(ctxt)
}
func setpersrc(ctxt *Link, sym *sym.Symbol) {
if rsrcsym != nil {
Errorf(sym, "too many .rsrc sections")
}
rsrcsym = sym
}
func addpersrc(ctxt *Link) {
if rsrcsym == nil {
return
}
h := pefile.addSection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size))
h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA
h.checkOffset(ctxt.Out.Offset())
// relocation
var p []byte
var r *sym.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)
}
ctxt.Out.Write(rsrcsym.P)
h.pad(ctxt.Out, uint32(rsrcsym.Size))
// update data directory
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize
}
func Asmbpe(ctxt *Link) {
switch ctxt.Arch.Family {
default:
Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
case sys.AMD64, sys.I386:
}
t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
if ctxt.LinkMode == LinkExternal {
// some data symbols (e.g. masks) end up in the .text section, and they normally
// expect larger alignment requirement than the default text section alignment.
t.characteristics |= IMAGE_SCN_ALIGN_32BYTES
}
t.checkSegment(&Segtext)
pefile.textSect = t
var d *peSection
if ctxt.LinkMode != LinkExternal {
d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen))
d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
d.checkSegment(&Segdata)
pefile.dataSect = d
} else {
d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen))
d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
d.checkSegment(&Segdata)
pefile.dataSect = d
b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0)
b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
b.pointerToRawData = 0
pefile.bssSect = b
}
pefile.addDWARF()
if ctxt.LinkMode == LinkExternal {
pefile.ctorsSect = pefile.addInitArray(ctxt)
}
ctxt.Out.SeekSet(int64(pefile.nextFileOffset))
if ctxt.LinkMode != LinkExternal {
addimports(ctxt, d)
addexports(ctxt)
}
pefile.writeSymbolTableAndStringTable(ctxt)
addpersrc(ctxt)
if ctxt.LinkMode == LinkExternal {
pefile.emitRelocations(ctxt)
}
pewrite(ctxt)
}