John Papandriopoulos 5e514b76e2 cmd/link: load symbols from .syso in external link mode
Fix linking with a package having a .syso file in external link mode,
that would otherwise cause an error before executing the external
linker because it can't find symbols that are exported in the said
.syso file.

Fixes #33139

Change-Id: Id3ee737fba1c6f1e37910593dfedf9c84486d398
Reviewed-on: https://go-review.googlesource.com/c/go/+/186417
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2019-10-01 00:02:11 +00:00

1573 lines
42 KiB
Go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// PE (Portable Executable) file writing
// https://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
package ld
import (
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/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_MACHINE_ARM = 0x1c0
IMAGE_FILE_MACHINE_ARMNT = 0x1c4
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
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
)
// TODO(crawshaw): add these constants to debug/pe.
const (
// TODO: the Microsoft doco says IMAGE_SYM_DTYPE_ARRAY is 3 and IMAGE_SYM_DTYPE_FUNCTION is 2
IMAGE_SYM_TYPE_NULL = 0
IMAGE_SYM_TYPE_STRUCT = 8
IMAGE_SYM_DTYPE_FUNCTION = 0x20
IMAGE_SYM_DTYPE_ARRAY = 0x30
IMAGE_SYM_CLASS_EXTERNAL = 2
IMAGE_SYM_CLASS_STATIC = 3
IMAGE_REL_I386_DIR32 = 0x0006
IMAGE_REL_I386_SECREL = 0x000B
IMAGE_REL_I386_REL32 = 0x0014
IMAGE_REL_AMD64_ADDR64 = 0x0001
IMAGE_REL_AMD64_ADDR32 = 0x0002
IMAGE_REL_AMD64_REL32 = 0x0004
IMAGE_REL_AMD64_SECREL = 0x000B
IMAGE_REL_ARM_ABSOLUTE = 0x0000
IMAGE_REL_ARM_ADDR32 = 0x0001
IMAGE_REL_ARM_ADDR32NB = 0x0002
IMAGE_REL_ARM_BRANCH24 = 0x0003
IMAGE_REL_ARM_BRANCH11 = 0x0004
IMAGE_REL_ARM_SECREL = 0x000F
IMAGE_REL_BASED_HIGHLOW = 3
)
const (
PeMinimumTargetMajorVersion = 6
PeMinimumTargetMinorVersion = 1
)
// DOS stub that prints out
// "This program cannot be run in DOS mode."
var dosstub = []uint8{
0x4d,
0x5a,
0x90,
0x00,
0x03,
0x00,
0x04,
0x00,
0x00,
0x00,
0x00,
0x00,
0xff,
0xff,
0x00,
0x00,
0x8b,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x40,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x80,
0x00,
0x00,
0x00,
0x0e,
0x1f,
0xba,
0x0e,
0x00,
0xb4,
0x09,
0xcd,
0x21,
0xb8,
0x01,
0x4c,
0xcd,
0x21,
0x54,
0x68,
0x69,
0x73,
0x20,
0x70,
0x72,
0x6f,
0x67,
0x72,
0x61,
0x6d,
0x20,
0x63,
0x61,
0x6e,
0x6e,
0x6f,
0x74,
0x20,
0x62,
0x65,
0x20,
0x72,
0x75,
0x6e,
0x20,
0x69,
0x6e,
0x20,
0x44,
0x4f,
0x53,
0x20,
0x6d,
0x6f,
0x64,
0x65,
0x2e,
0x0d,
0x0d,
0x0a,
0x24,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
}
type Imp struct {
s *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 returns size of string table t.
func (t *peStringTable) size() int {
// string table starts with 4-byte length at the beginning
return t.stringsLen + 4
}
// add adds string str to string table t.
func (t *peStringTable) add(str string) int {
off := t.size()
t.strings = append(t.strings, str)
t.stringsLen += len(str) + 1 // each string will have 0 appended to it
return off
}
// write writes string table t into the output file.
func (t *peStringTable) write(out *OutBuf) {
out.Write32(uint32(t.size()))
for _, s := range t.strings {
out.WriteString(s)
out.Write8(0)
}
}
// peSection represents section from COFF section table.
type peSection struct {
name string
shortName string
index int // one-based index into the Section Table
virtualSize uint32
virtualAddress uint32
sizeOfRawData uint32
pointerToRawData uint32
pointerToRelocations uint32
numberOfRelocations uint16
characteristics uint32
}
// checkOffset verifies COFF section sect offset in the file.
func (sect *peSection) checkOffset(off int64) {
if off != int64(sect.pointerToRawData) {
Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(off))
errorexit()
}
}
// checkSegment verifies COFF section sect matches address
// and file offset provided in segment seg.
func (sect *peSection) checkSegment(seg *sym.Segment) {
if seg.Vaddr-PEBASE != uint64(sect.virtualAddress) {
Errorf(nil, "%s.VirtualAddress = %#x, want %#x", sect.name, uint64(int64(sect.virtualAddress)), uint64(int64(seg.Vaddr-PEBASE)))
errorexit()
}
if seg.Fileoff != uint64(sect.pointerToRawData) {
Errorf(nil, "%s.PointerToRawData = %#x, want %#x", sect.name, uint64(int64(sect.pointerToRawData)), uint64(int64(seg.Fileoff)))
errorexit()
}
}
// pad adds zeros to the section sect. It writes as many bytes
// as necessary to make section sect.SizeOfRawData bytes long.
// It assumes that n bytes are already written to the file.
func (sect *peSection) pad(out *OutBuf, n uint32) {
out.WriteStringN("", int(sect.sizeOfRawData-n))
}
// write writes COFF section sect into the output file.
func (sect *peSection) write(out *OutBuf, linkmode LinkMode) error {
h := pe.SectionHeader32{
VirtualSize: sect.virtualSize,
SizeOfRawData: sect.sizeOfRawData,
PointerToRawData: sect.pointerToRawData,
PointerToRelocations: sect.pointerToRelocations,
NumberOfRelocations: sect.numberOfRelocations,
Characteristics: sect.characteristics,
}
if linkmode != LinkExternal {
h.VirtualAddress = sect.virtualAddress
}
copy(h.Name[:], sect.shortName)
return binary.Write(out, binary.LittleEndian, h)
}
// emitRelocations emits the relocation entries for the sect.
// The actual relocations are emitted by relocfn.
// This updates the corresponding PE section table entry
// with the relocation offset and count.
func (sect *peSection) emitRelocations(out *OutBuf, relocfn func() int) {
sect.pointerToRelocations = uint32(out.Offset())
// first entry: extended relocs
out.Write32(0) // placeholder for number of relocation + 1
out.Write32(0)
out.Write16(0)
n := relocfn() + 1
cpos := out.Offset()
out.SeekSet(int64(sect.pointerToRelocations))
out.Write32(uint32(n))
out.SeekSet(cpos)
if n > 0x10000 {
n = 0x10000
sect.characteristics |= IMAGE_SCN_LNK_NRELOC_OVFL
} else {
sect.pointerToRelocations += 10 // skip the extend reloc entry
}
sect.numberOfRelocations = uint16(n - 1)
}
// peFile is used to build COFF file.
type peFile struct {
sections []*peSection
stringTable peStringTable
textSect *peSection
rdataSect *peSection
dataSect *peSection
bssSect *peSection
ctorsSect *peSection
nextSectOffset uint32
nextFileOffset uint32
symtabOffset int64 // offset to the start of symbol table
symbolCount int // number of symbol table records written
dataDirectory [16]pe.DataDirectory
}
// addSection adds section to the COFF file f.
func (f *peFile) addSection(name string, sectsize int, filesize int) *peSection {
sect := &peSection{
name: name,
shortName: name,
index: len(f.sections) + 1,
virtualSize: uint32(sectsize),
virtualAddress: f.nextSectOffset,
pointerToRawData: f.nextFileOffset,
}
f.nextSectOffset = uint32(Rnd(int64(f.nextSectOffset)+int64(sectsize), PESECTALIGN))
if filesize > 0 {
sect.sizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN))
f.nextFileOffset += sect.sizeOfRawData
}
f.sections = append(f.sections, sect)
return sect
}
// addDWARFSection adds DWARF section to the COFF file f.
// This function is similar to addSection, but DWARF section names are
// longer than 8 characters, so they need to be stored in the string table.
func (f *peFile) addDWARFSection(name string, size int) *peSection {
if size == 0 {
Exitf("DWARF section %q is empty", name)
}
// DWARF section names are longer than 8 characters.
// PE format requires such names to be stored in string table,
// and section names replaced with slash (/) followed by
// correspondent string table index.
// see http://www.microsoft.com/whdc/system/platform/firmware/PECOFFdwn.mspx
// for details
off := f.stringTable.add(name)
h := f.addSection(name, size, size)
h.shortName = fmt.Sprintf("/%d", off)
h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE
return h
}
// addDWARF adds DWARF information to the COFF file f.
func (f *peFile) addDWARF() {
if *FlagS { // disable symbol table
return
}
if *FlagW { // disable dwarf
return
}
for _, sect := range Segdwarf.Sections {
h := f.addDWARFSection(sect.Name, int(sect.Length))
fileoff := sect.Vaddr - Segdwarf.Vaddr + Segdwarf.Fileoff
if uint64(h.pointerToRawData) != fileoff {
Exitf("%s.PointerToRawData = %#x, want %#x", sect.Name, h.pointerToRawData, fileoff)
}
}
}
// addInitArray adds .ctors COFF section to the file f.
func (f *peFile) addInitArray(ctxt *Link) *peSection {
// The size below was determined by the specification for array relocations,
// and by observing what GCC writes here. If the initarray section grows to
// contain more than one constructor entry, the size will need to be 8 * constructor_count.
// However, the entire Go runtime is initialized from just one function, so it is unlikely
// that this will need to grow in the future.
var size int
switch objabi.GOARCH {
default:
Exitf("peFile.addInitArray: unsupported GOARCH=%q\n", objabi.GOARCH)
case "386":
size = 4
case "amd64":
size = 8
case "arm":
size = 4
}
sect := f.addSection(".ctors", size, size)
sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
sect.sizeOfRawData = uint32(size)
ctxt.Out.SeekSet(int64(sect.pointerToRawData))
sect.checkOffset(ctxt.Out.Offset())
init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0)
addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr
switch objabi.GOARCH {
case "386", "arm":
ctxt.Out.Write32(uint32(addr))
case "amd64":
ctxt.Out.Write64(addr)
}
return sect
}
// emitRelocations emits relocation entries for go.o in external linking.
func (f *peFile) emitRelocations(ctxt *Link) {
for ctxt.Out.Offset()&7 != 0 {
ctxt.Out.Write8(0)
}
// 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 := range sym.R {
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
}
sects := []struct {
peSect *peSection
seg *sym.Segment
syms []*sym.Symbol
}{
{f.textSect, &Segtext, ctxt.Textp},
{f.rdataSect, &Segrodata, datap},
{f.dataSect, &Segdata, datap},
}
for _, s := range sects {
s.peSect.emitRelocations(ctxt.Out, func() int {
var n int
for _, sect := range s.seg.Sections {
n += relocsect(sect, s.syms, s.seg.Vaddr)
}
return n
})
}
dwarfLoop:
for _, 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)
case "arm":
ctxt.Out.Write16(IMAGE_REL_ARM_ADDR32)
}
return 1
})
}
// writeSymbol appends symbol s to file f symbol table.
// It also sets s.Dynid to written symbol number.
func (f *peFile) writeSymbol(out *OutBuf, 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 == &Segrodata {
return f.rdataSect.index, int64(uint64(s.Value) - Segrodata.Vaddr), nil
}
if s.Sect.Seg != &Segdata {
return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata 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.Type == sym.SUNDEFEXT || 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.IsFileLocal() || 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
case sys.ARM:
fh.Machine = IMAGE_FILE_MACHINE_ARMNT
}
fh.NumberOfSections = uint16(len(f.sections))
// Being able to produce identical output for identical input is
// much more beneficial than having build timestamp in the header.
fh.TimeDateStamp = 0
if linkmode == LinkExternal {
fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED
} else {
switch arch.Family {
default:
Exitf("write COFF(ext): unknown PE architecture: %v", arch.Family)
case sys.AMD64, sys.I386:
fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
case sys.ARM:
fh.Characteristics = 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 = 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 = IMAGE_SUBSYSTEM_WINDOWS_GUI
oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI
} else {
oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI
}
switch ctxt.Arch.Family {
case sys.ARM:
oh64.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT
oh.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | IMAGE_DLLCHARACTERISTICS_NX_COMPAT
}
// Mark as having awareness of terminal services, to avoid ancient compatibility hacks.
oh64.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
oh.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
// Disable stack growth as we don't want Windows to
// fiddle with the thread stack limits, which we set
// ourselves to circumvent the stack checks in the
// Windows exception dispatcher.
// Commit size must be strictly less than reserve
// size otherwise reserve will be rounded up to a
// larger size, as verified with VMMap.
// On 64-bit, we always reserve 2MB stacks. "Pure" Go code is
// okay with much smaller stacks, but the syscall package
// makes it easy to call into arbitrary C code without cgo,
// and system calls even in "pure" Go code are actually C
// calls that may need more stack than we think.
//
// The default stack reserve size directly affects only the main
// thread, ctrlhandler thread, and profileloop thread. For
// these, it must be greater than the stack size assumed by
// externalthreadhandler.
//
// For other threads, the runtime explicitly asks the kernel
// to use the default stack size so that all stacks are
// consistent.
//
// At thread start, in minit, the runtime queries the OS for
// the actual stack bounds so that the stack size doesn't need
// to be hard-coded into the runtime.
oh64.SizeOfStackReserve = 0x00200000
if !iscgo {
oh64.SizeOfStackCommit = 0x00001000
} else {
// TODO(brainman): Maybe remove optional header writing altogether for cgo.
// For cgo it is the external linker that is building final executable.
// And it probably does not use any information stored in optional header.
oh64.SizeOfStackCommit = 0x00200000 - 0x2000 // account for 2 guard pages
}
oh.SizeOfStackReserve = 0x00100000
if !iscgo {
oh.SizeOfStackCommit = 0x00001000
} else {
oh.SizeOfStackCommit = 0x00100000 - 0x2000 // account for 2 guard pages
}
oh64.SizeOfHeapReserve = 0x00100000
oh.SizeOfHeapReserve = 0x00100000
oh64.SizeOfHeapCommit = 0x00001000
oh.SizeOfHeapCommit = 0x00001000
oh64.NumberOfRvaAndSizes = 16
oh.NumberOfRvaAndSizes = 16
if pe64 != 0 {
oh64.DataDirectory = f.dataDirectory
} else {
oh.DataDirectory = f.dataDirectory
}
if pe64 != 0 {
binary.Write(ctxt.Out, binary.LittleEndian, &oh64)
} else {
binary.Write(ctxt.Out, binary.LittleEndian, &oh)
}
}
var pefile peFile
func Peinit(ctxt *Link) {
var l int
switch ctxt.Arch.Family {
// 64-bit architectures
case sys.AMD64:
pe64 = 1
var oh64 pe.OptionalHeader64
l = binary.Size(&oh64)
// 32-bit architectures
default:
var oh pe.OptionalHeader32
l = binary.Size(&oh)
}
if ctxt.LinkMode == LinkExternal {
// .rdata section will contain "masks" and "shifts" symbols, and they
// need to be aligned to 16-bytes. So make all sections aligned
// to 32-byte and mark them all IMAGE_SCN_ALIGN_32BYTES so external
// linker will honour that requirement.
PESECTALIGN = 32
PEFILEALIGN = 0
}
var sh [16]pe.SectionHeader32
var fh pe.FileHeader
PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN))
if ctxt.LinkMode != LinkExternal {
PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN))
} else {
PESECTHEADR = 0
}
pefile.nextSectOffset = uint32(PESECTHEADR)
pefile.nextFileOffset = uint32(PEFILEHEADR)
if ctxt.LinkMode == LinkInternal {
// some mingw libs depend on this symbol, for example, FindPESectionByName
ctxt.xdefine("__image_base__", sym.SDATA, PEBASE)
ctxt.xdefine("_image_base__", sym.SDATA, PEBASE)
}
HEADR = PEFILEHEADR
if *FlagTextAddr == -1 {
*FlagTextAddr = PEBASE + int64(PESECTHEADR)
}
if *FlagRound == -1 {
*FlagRound = int(PESECTALIGN)
}
}
func pewrite(ctxt *Link) {
ctxt.Out.SeekSet(0)
if ctxt.LinkMode != LinkExternal {
ctxt.Out.Write(dosstub)
ctxt.Out.WriteStringN("PE", 4)
}
pefile.writeFileHeader(ctxt.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
extName := s.Extname()
if i := strings.IndexByte(extName, '%'); i >= 0 {
var err error
m.argsize, err = strconv.Atoi(extName[i+1:])
if err != nil {
Errorf(s, "failed to parse stdcall decoration: %v", err)
}
m.argsize *= ctxt.Arch.PtrSize
s.SetExtname(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
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))
}
// peBaseRelocEntry represents a single relocation entry.
type peBaseRelocEntry struct {
typeOff uint16
rel *sym.Reloc
sym *sym.Symbol // For debug
}
// peBaseRelocBlock represents a Base Relocation Block. A block
// is a collection of relocation entries in a page, where each
// entry describes a single relocation.
// The block page RVA (Relative Virtual Address) is the index
// into peBaseRelocTable.blocks.
type peBaseRelocBlock struct {
entries []peBaseRelocEntry
}
// pePages is a type used to store the list of pages for which there
// are base relocation blocks. This is defined as a type so that
// it can be sorted.
type pePages []uint32
func (p pePages) Len() int { return len(p) }
func (p pePages) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p pePages) Less(i, j int) bool { return p[i] < p[j] }
// A PE base relocation table is a list of blocks, where each block
// contains relocation information for a single page. The blocks
// must be emitted in order of page virtual address.
// See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#the-reloc-section-image-only
type peBaseRelocTable struct {
blocks map[uint32]peBaseRelocBlock
// pePages is a list of keys into blocks map.
// It is stored separately for ease of sorting.
pages pePages
}
func (rt *peBaseRelocTable) init(ctxt *Link) {
rt.blocks = make(map[uint32]peBaseRelocBlock)
}
func (rt *peBaseRelocTable) addentry(ctxt *Link, s *sym.Symbol, r *sym.Reloc) {
// pageSize is the size in bytes of a page
// described by a base relocation block.
const pageSize = 0x1000
const pageMask = pageSize - 1
addr := s.Value + int64(r.Off) - int64(PEBASE)
page := uint32(addr &^ pageMask)
off := uint32(addr & pageMask)
b, ok := rt.blocks[page]
if !ok {
rt.pages = append(rt.pages, page)
}
e := peBaseRelocEntry{
typeOff: uint16(off & 0xFFF),
rel: r,
sym: s,
}
// 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)
}
b.entries = append(b.entries, e)
rt.blocks[page] = b
}
func (rt *peBaseRelocTable) write(ctxt *Link) {
out := ctxt.Out
// sort the pages array
sort.Sort(rt.pages)
for _, p := range rt.pages {
b := rt.blocks[p]
const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32)
blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2)
out.Write32(p)
out.Write32(blockSize)
for _, e := range b.entries {
out.Write16(e.typeOff)
}
}
}
func addPEBaseRelocSym(ctxt *Link, s *sym.Symbol, rt *peBaseRelocTable) {
for ri := 0; ri < len(s.R); ri++ {
r := &s.R[ri]
if r.Sym == nil {
continue
}
if !r.Sym.Attr.Reachable() {
continue
}
if r.Type >= objabi.ElfRelocOffset {
continue
}
if r.Siz == 0 { // informational relocation
continue
}
if r.Type == objabi.R_DWARFFILEREF {
continue
}
switch r.Type {
default:
case objabi.R_ADDR:
rt.addentry(ctxt, s, r)
}
}
}
func addPEBaseReloc(ctxt *Link) {
// We only generate base relocation table for ARM (and ... ARM64), x86, and AMD64 are marked as legacy
// archs and can use fixed base with no base relocation information
switch ctxt.Arch.Family {
default:
return
case sys.ARM:
}
var rt peBaseRelocTable
rt.init(ctxt)
// Get relocation information
for _, s := range ctxt.Textp {
addPEBaseRelocSym(ctxt, s, &rt)
}
for _, s := range datap {
addPEBaseRelocSym(ctxt, 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[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress
pefile.dataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize
}
func (ctxt *Link) dope() {
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
for ri := range rsrcsym.R {
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, sys.ARM:
}
t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
t.characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
if ctxt.LinkMode == LinkExternal {
// some data symbols (e.g. masks) end up in the .text section, and they normally
// expect larger alignment requirement than the default text section alignment.
t.characteristics |= IMAGE_SCN_ALIGN_32BYTES
}
t.checkSegment(&Segtext)
pefile.textSect = t
ro := pefile.addSection(".rdata", int(Segrodata.Length), int(Segrodata.Length))
ro.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
if ctxt.LinkMode == LinkExternal {
// some data symbols (e.g. masks) end up in the .rdata section, and they normally
// expect larger alignment requirement than the default text section alignment.
ro.characteristics |= IMAGE_SCN_ALIGN_32BYTES
}
ro.checkSegment(&Segrodata)
pefile.rdataSect = ro
var d *peSection
if ctxt.LinkMode != LinkExternal {
d = pefile.addSection(".data", int(Segdata.Length), int(Segdata.Filelen))
d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE
d.checkSegment(&Segdata)
pefile.dataSect = d
} else {
d = pefile.addSection(".data", int(Segdata.Filelen), int(Segdata.Filelen))
d.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
d.checkSegment(&Segdata)
pefile.dataSect = d
b := pefile.addSection(".bss", int(Segdata.Length-Segdata.Filelen), 0)
b.characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_ALIGN_32BYTES
b.pointerToRawData = 0
pefile.bssSect = b
}
pefile.addDWARF()
if ctxt.LinkMode == LinkExternal {
pefile.ctorsSect = pefile.addInitArray(ctxt)
}
ctxt.Out.SeekSet(int64(pefile.nextFileOffset))
if ctxt.LinkMode != LinkExternal {
addimports(ctxt, d)
addexports(ctxt)
addPEBaseReloc(ctxt)
}
pefile.writeSymbolTableAndStringTable(ctxt)
addpersrc(ctxt)
if ctxt.LinkMode == LinkExternal {
pefile.emitRelocations(ctxt)
}
pewrite(ctxt)
}