mirror of
https://github.com/golang/go.git
synced 2025-05-19 22:33:25 +00:00
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>
1573 lines
42 KiB
Go
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)
|
|
}
|