| // 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/loader" | |
| "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_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 | |
| ) | |
| // 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 | |
| IMAGE_REL_BASED_DIR64 = 10 | |
| ) | |
| 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 loader.Sym | |
| off uint64 | |
| next *Imp | |
| argsize int | |
| } | |
| type Dll struct { | |
| name string | |
| nameoff uint64 | |
| thunkoff uint64 | |
| ms *Imp | |
| next *Dll | |
| } | |
| var ( | |
| rsrcsym loader.Sym | |
| PESECTHEADR int32 | |
| PEFILEHEADR int32 | |
| pe64 int | |
| dr *Dll | |
| dexport = make([]loader.Sym, 0, 1024) | |
| ) | |
| // 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.loader.Lookup(*flagEntrySymbol, 0) | |
| addr := uint64(ctxt.loader.SymValue(init_entry)) - ctxt.loader.SymSect(init_entry).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) | |
| } | |
| ldr := ctxt.loader | |
| // relocsect relocates symbols from first in section sect, and returns | |
| // the total number of relocations emitted. | |
| relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) int { | |
| // If main section has no bits, nothing to relocate. | |
| if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { | |
| return 0 | |
| } | |
| nrelocs := 0 | |
| sect.Reloff = uint64(ctxt.Out.Offset()) | |
| for i, s := range syms { | |
| if !ldr.AttrReachable(s) { | |
| continue | |
| } | |
| if uint64(ldr.SymValue(s)) >= sect.Vaddr { | |
| syms = syms[i:] | |
| break | |
| } | |
| } | |
| eaddr := int32(sect.Vaddr + sect.Length) | |
| for _, s := range syms { | |
| if !ldr.AttrReachable(s) { | |
| continue | |
| } | |
| if ldr.SymValue(s) >= int64(eaddr) { | |
| break | |
| } | |
| // Compute external relocations on the go, and pass to PEreloc1 | |
| // to stream out. | |
| relocs := ldr.Relocs(s) | |
| for ri := 0; ri < relocs.Count(); ri++ { | |
| r := relocs.At(ri) | |
| rr, ok := extreloc(ctxt, ldr, s, r) | |
| if !ok { | |
| continue | |
| } | |
| if rr.Xsym == 0 { | |
| ctxt.Errorf(s, "missing xsym in relocation") | |
| continue | |
| } | |
| if ldr.SymDynid(rr.Xsym) < 0 { | |
| ctxt.Errorf(s, "reloc %d to non-coff symbol %s (outer=%s) %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym())) | |
| } | |
| if !thearch.PEreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) { | |
| ctxt.Errorf(s, "unsupported obj reloc %d/%d to %s", r.Type(), r.Siz(), ldr.SymName(r.Sym())) | |
| } | |
| nrelocs++ | |
| } | |
| } | |
| sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff | |
| return nrelocs | |
| } | |
| sects := []struct { | |
| peSect *peSection | |
| seg *sym.Segment | |
| syms []loader.Sym | |
| }{ | |
| {f.textSect, &Segtext, ctxt.Textp}, | |
| {f.rdataSect, &Segrodata, ctxt.datap}, | |
| {f.dataSect, &Segdata, ctxt.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 i := 0; i < len(Segdwarf.Sections); i++ { | |
| sect := Segdwarf.Sections[i] | |
| si := dwarfp[i] | |
| if si.secSym() != loader.Sym(sect.Sym) || | |
| ldr.SymSect(si.secSym()) != sect { | |
| panic("inconsistency between dwarfp and Segdwarf") | |
| } | |
| for _, pesect := range f.sections { | |
| if sect.Name == pesect.name { | |
| pesect.emitRelocations(ctxt.Out, func() int { | |
| return relocsect(sect, si.syms, sect.Vaddr) | |
| }) | |
| continue dwarfLoop | |
| } | |
| } | |
| Errorf(nil, "emitRelocations: could not find %q section", sect.Name) | |
| } | |
| f.ctorsSect.emitRelocations(ctxt.Out, func() int { | |
| dottext := ldr.Lookup(".text", 0) | |
| ctxt.Out.Write32(0) | |
| ctxt.Out.Write32(uint32(ldr.SymDynid(dottext))) | |
| switch objabi.GOARCH { | |
| default: | |
| ctxt.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, ldr *loader.Loader, s loader.Sym, name string, value int64, sectidx int, typ uint16, class uint8) { | |
| if len(name) > 8 { | |
| out.Write32(0) | |
| out.Write32(uint32(f.stringTable.add(name))) | |
| } else { | |
| out.WriteStringN(name, 8) | |
| } | |
| out.Write32(uint32(value)) | |
| out.Write16(uint16(sectidx)) | |
| out.Write16(typ) | |
| out.Write8(class) | |
| out.Write8(0) // no aux entries | |
| ldr.SetSymDynid(s, int32(f.symbolCount)) | |
| f.symbolCount++ | |
| } | |
| // mapToPESection searches peFile f for s symbol's location. | |
| // It returns PE section index, and offset within that section. | |
| func (f *peFile) mapToPESection(ldr *loader.Loader, s loader.Sym, linkmode LinkMode) (pesectidx int, offset int64, err error) { | |
| sect := ldr.SymSect(s) | |
| if sect == nil { | |
| return 0, 0, fmt.Errorf("could not map %s symbol with no section", ldr.SymName(s)) | |
| } | |
| if sect.Seg == &Segtext { | |
| return f.textSect.index, int64(uint64(ldr.SymValue(s)) - Segtext.Vaddr), nil | |
| } | |
| if sect.Seg == &Segrodata { | |
| return f.rdataSect.index, int64(uint64(ldr.SymValue(s)) - Segrodata.Vaddr), nil | |
| } | |
| if sect.Seg != &Segdata { | |
| return 0, 0, fmt.Errorf("could not map %s symbol with non .text or .rdata or .data section", ldr.SymName(s)) | |
| } | |
| v := uint64(ldr.SymValue(s)) - Segdata.Vaddr | |
| if linkmode != LinkExternal { | |
| return f.dataSect.index, int64(v), nil | |
| } | |
| if ldr.SymType(s) == 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) { | |
| ldr := ctxt.loader | |
| addsym := func(s loader.Sym) { | |
| t := ldr.SymType(s) | |
| if ldr.SymSect(s) == nil && t != sym.SDYNIMPORT && t != sym.SHOSTOBJ && t != sym.SUNDEFEXT { | |
| return | |
| } | |
| name := ldr.SymName(s) | |
| // Only windows/386 requires underscore prefix on external symbols. | |
| if ctxt.Is386() && ctxt.IsExternal() && | |
| (t == sym.SHOSTOBJ || t == sym.SUNDEFEXT || ldr.AttrCgoExport(s)) { | |
| name = "_" + name | |
| } | |
| var peSymType uint16 | |
| if ctxt.IsExternal() { | |
| peSymType = IMAGE_SYM_TYPE_NULL | |
| } else { | |
| // TODO: fix IMAGE_SYM_DTYPE_ARRAY value and use following expression, instead of 0x0308 | |
| // peSymType = IMAGE_SYM_DTYPE_ARRAY<<8 + IMAGE_SYM_TYPE_STRUCT | |
| peSymType = 0x0308 // "array of structs" | |
| } | |
| sect, value, err := f.mapToPESection(ldr, s, ctxt.LinkMode) | |
| if err != nil { | |
| if t == sym.SDYNIMPORT || t == sym.SHOSTOBJ || t == sym.SUNDEFEXT { | |
| peSymType = IMAGE_SYM_DTYPE_FUNCTION | |
| } else { | |
| ctxt.Errorf(s, "addpesym: %v", err) | |
| } | |
| } | |
| class := IMAGE_SYM_CLASS_EXTERNAL | |
| if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) { | |
| class = IMAGE_SYM_CLASS_STATIC | |
| } | |
| f.writeSymbol(ctxt.Out, ldr, s, name, value, sect, peSymType, uint8(class)) | |
| } | |
| if ctxt.LinkMode == LinkExternal { | |
| // Include section symbols as external, because | |
| // .ctors and .debug_* section relocations refer to it. | |
| for _, pesect := range f.sections { | |
| s := ldr.LookupOrCreateSym(pesect.name, 0) | |
| f.writeSymbol(ctxt.Out, ldr, s, pesect.name, 0, pesect.index, IMAGE_SYM_TYPE_NULL, IMAGE_SYM_CLASS_STATIC) | |
| } | |
| } | |
| // Add special runtime.text and runtime.etext symbols. | |
| s := ldr.Lookup("runtime.text", 0) | |
| if ldr.SymType(s) == sym.STEXT { | |
| addsym(s) | |
| } | |
| s = ldr.Lookup("runtime.etext", 0) | |
| if ldr.SymType(s) == sym.STEXT { | |
| addsym(s) | |
| } | |
| // Add text symbols. | |
| for _, s := range ctxt.Textp { | |
| addsym(s) | |
| } | |
| shouldBeInSymbolTable := func(s loader.Sym) bool { | |
| if ldr.AttrNotInSymbolTable(s) { | |
| return false | |
| } | |
| name := ldr.RawSymName(s) // TODO: try not to read the name | |
| if name == "" || name[0] == '.' { | |
| return false | |
| } | |
| return true | |
| } | |
| // Add data symbols and external references. | |
| for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { | |
| if !ldr.AttrReachable(s) { | |
| continue | |
| } | |
| t := ldr.SymType(s) | |
| if t >= sym.SELFRXSECT && t < sym.SXREF { // data sections handled in dodata | |
| if t == sym.STLSBSS { | |
| continue | |
| } | |
| if !shouldBeInSymbolTable(s) { | |
| continue | |
| } | |
| addsym(s) | |
| } | |
| switch t { | |
| case sym.SDYNIMPORT, sym.SHOSTOBJ, sym.SUNDEFEXT: | |
| addsym(s) | |
| } | |
| } | |
| } | |
| // writeSymbolTableAndStringTable writes out symbol and string tables for peFile f. | |
| func (f *peFile) writeSymbolTableAndStringTable(ctxt *Link) { | |
| f.symtabOffset = ctxt.Out.Offset() | |
| // write COFF symbol table | |
| if !*FlagS || ctxt.LinkMode == LinkExternal { | |
| f.writeSymbols(ctxt) | |
| } | |
| // update COFF file header and section table | |
| size := f.stringTable.size() + 18*f.symbolCount | |
| var h *peSection | |
| if ctxt.LinkMode != LinkExternal { | |
| // We do not really need .symtab for go.o, and if we have one, ld | |
| // will also include it in the exe, and that will confuse windows. | |
| h = f.addSection(".symtab", size, size) | |
| h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | |
| h.checkOffset(f.symtabOffset) | |
| } | |
| // write COFF string table | |
| f.stringTable.write(ctxt.Out) | |
| if ctxt.LinkMode != LinkExternal { | |
| h.pad(ctxt.Out, uint32(size)) | |
| } | |
| } | |
| // writeFileHeader writes COFF file header for peFile f. | |
| func (f *peFile) writeFileHeader(ctxt *Link) { | |
| var fh pe.FileHeader | |
| switch ctxt.Arch.Family { | |
| default: | |
| Exitf("unknown PE architecture: %v", ctxt.Arch.Family) | |
| case sys.AMD64: | |
| fh.Machine = pe.IMAGE_FILE_MACHINE_AMD64 | |
| case sys.I386: | |
| fh.Machine = pe.IMAGE_FILE_MACHINE_I386 | |
| case sys.ARM: | |
| fh.Machine = pe.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 ctxt.LinkMode == LinkExternal { | |
| fh.Characteristics = pe.IMAGE_FILE_LINE_NUMS_STRIPPED | |
| } else { | |
| fh.Characteristics = pe.IMAGE_FILE_EXECUTABLE_IMAGE | pe.IMAGE_FILE_DEBUG_STRIPPED | |
| switch ctxt.Arch.Family { | |
| case sys.AMD64, sys.I386: | |
| if ctxt.BuildMode != BuildModePIE { | |
| fh.Characteristics |= pe.IMAGE_FILE_RELOCS_STRIPPED | |
| } | |
| } | |
| } | |
| if pe64 != 0 { | |
| var oh64 pe.OptionalHeader64 | |
| fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) | |
| fh.Characteristics |= pe.IMAGE_FILE_LARGE_ADDRESS_AWARE | |
| } else { | |
| var oh pe.OptionalHeader32 | |
| fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) | |
| fh.Characteristics |= pe.IMAGE_FILE_32BIT_MACHINE | |
| } | |
| fh.PointerToSymbolTable = uint32(f.symtabOffset) | |
| fh.NumberOfSymbols = uint32(f.symbolCount) | |
| binary.Write(ctxt.Out, binary.LittleEndian, &fh) | |
| } | |
| // writeOptionalHeader writes COFF optional header for peFile f. | |
| func (f *peFile) writeOptionalHeader(ctxt *Link) { | |
| var oh pe.OptionalHeader32 | |
| var oh64 pe.OptionalHeader64 | |
| if pe64 != 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 = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI | |
| oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_GUI | |
| } else { | |
| oh64.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI | |
| oh.Subsystem = pe.IMAGE_SUBSYSTEM_WINDOWS_CUI | |
| } | |
| // Mark as having awareness of terminal services, to avoid ancient compatibility hacks. | |
| oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE | |
| oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE | |
| // Enable DEP | |
| oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT | |
| oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_NX_COMPAT | |
| // The DLL can be relocated at load time. | |
| switch ctxt.Arch.Family { | |
| case sys.AMD64, sys.I386: | |
| if ctxt.BuildMode == BuildModePIE { | |
| oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | |
| oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | |
| } | |
| case sys.ARM: | |
| oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | |
| oh.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | |
| } | |
| // Image can handle a high entropy 64-bit virtual address space. | |
| if ctxt.BuildMode == BuildModePIE { | |
| oh64.DllCharacteristics |= pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | |
| } | |
| // Disable stack growth as we don't want Windows to | |
| // fiddle with the thread stack limits, which we set | |
| // ourselves to circumvent the stack checks in the | |
| // Windows exception dispatcher. | |
| // Commit size must be strictly less than reserve | |
| // size otherwise reserve will be rounded up to a | |
| // larger size, as verified with VMMap. | |
| // On 64-bit, we always reserve 2MB stacks. "Pure" Go code is | |
| // okay with much smaller stacks, but the syscall package | |
| // makes it easy to call into arbitrary C code without cgo, | |
| // and system calls even in "pure" Go code are actually C | |
| // calls that may need more stack than we think. | |
| // | |
| // The default stack reserve size directly affects only the main | |
| // thread, 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 | |
| for _, name := range [2]string{"__image_base__", "_image_base__"} { | |
| sb := ctxt.loader.CreateSymForUpdate(name, 0) | |
| sb.SetType(sym.SDATA) | |
| sb.SetValue(PEBASE) | |
| ctxt.loader.SetAttrSpecial(sb.Sym(), true) | |
| ctxt.loader.SetAttrLocal(sb.Sym(), true) | |
| } | |
| } | |
| 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) | |
| pefile.writeOptionalHeader(ctxt) | |
| for _, sect := range pefile.sections { | |
| sect.write(ctxt.Out, ctxt.LinkMode) | |
| } | |
| } | |
| func strput(out *OutBuf, s string) { | |
| out.WriteString(s) | |
| out.Write8(0) | |
| // string must be padded to even size | |
| if (len(s)+1)%2 != 0 { | |
| out.Write8(0) | |
| } | |
| } | |
| func initdynimport(ctxt *Link) *Dll { | |
| ldr := ctxt.loader | |
| var d *Dll | |
| dr = nil | |
| var m *Imp | |
| for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { | |
| if !ldr.AttrReachable(s) || ldr.SymType(s) != sym.SDYNIMPORT { | |
| continue | |
| } | |
| dynlib := ldr.SymDynimplib(s) | |
| for d = dr; d != nil; d = d.next { | |
| if d.name == dynlib { | |
| m = new(Imp) | |
| break | |
| } | |
| } | |
| if d == nil { | |
| d = new(Dll) | |
| d.name = dynlib | |
| d.next = dr | |
| dr = d | |
| m = new(Imp) | |
| } | |
| // Because external link requires properly stdcall decorated name, | |
| // all external symbols in runtime use %n to denote that the number | |
| // of uinptrs this function consumes. Store the argsize and discard | |
| // the %n suffix if any. | |
| m.argsize = -1 | |
| extName := ldr.SymExtname(s) | |
| if i := strings.IndexByte(extName, '%'); i >= 0 { | |
| var err error | |
| m.argsize, err = strconv.Atoi(extName[i+1:]) | |
| if err != nil { | |
| ctxt.Errorf(s, "failed to parse stdcall decoration: %v", err) | |
| } | |
| m.argsize *= ctxt.Arch.PtrSize | |
| ldr.SetSymExtname(s, extName[:i]) | |
| } | |
| m.s = s | |
| m.next = d.ms | |
| d.ms = m | |
| } | |
| if ctxt.IsExternal() { | |
| // Add real symbol name | |
| for d := dr; d != nil; d = d.next { | |
| for m = d.ms; m != nil; m = m.next { | |
| sb := ldr.MakeSymbolUpdater(m.s) | |
| sb.SetType(sym.SDATA) | |
| sb.Grow(int64(ctxt.Arch.PtrSize)) | |
| dynName := sb.Extname() | |
| // only windows/386 requires stdcall decoration | |
| if ctxt.Is386() && m.argsize >= 0 { | |
| dynName += fmt.Sprintf("@%d", m.argsize) | |
| } | |
| dynSym := ldr.CreateSymForUpdate(dynName, 0) | |
| dynSym.SetType(sym.SHOSTOBJ) | |
| r, _ := sb.AddRel(objabi.R_ADDR) | |
| r.SetSym(dynSym.Sym()) | |
| r.SetSiz(uint8(ctxt.Arch.PtrSize)) | |
| } | |
| } | |
| } else { | |
| dynamic := ldr.CreateSymForUpdate(".windynamic", 0) | |
| dynamic.SetType(sym.SWINDOWS) | |
| for d := dr; d != nil; d = d.next { | |
| for m = d.ms; m != nil; m = m.next { | |
| sb := ldr.MakeSymbolUpdater(m.s) | |
| sb.SetType(sym.SWINDOWS) | |
| sb.SetValue(dynamic.Size()) | |
| dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) | |
| dynamic.AddInteriorSym(m.s) | |
| } | |
| dynamic.SetSize(dynamic.Size() + int64(ctxt.Arch.PtrSize)) | |
| } | |
| } | |
| return dr | |
| } | |
| // peimporteddlls returns the gcc command line argument to link all imported | |
| // DLLs. | |
| func peimporteddlls() []string { | |
| var dlls []string | |
| for d := dr; d != nil; d = d.next { | |
| dlls = append(dlls, "-l"+strings.TrimSuffix(d.name, ".dll")) | |
| } | |
| return dlls | |
| } | |
| func addimports(ctxt *Link, datsect *peSection) { | |
| ldr := ctxt.loader | |
| startoff := ctxt.Out.Offset() | |
| dynamic := ldr.LookupOrCreateSym(".windynamic", 0) | |
| // skip import descriptor table (will write it later) | |
| n := uint64(0) | |
| for d := dr; d != nil; d = d.next { | |
| n++ | |
| } | |
| ctxt.Out.SeekSet(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) | |
| // write dll names | |
| for d := dr; d != nil; d = d.next { | |
| d.nameoff = uint64(ctxt.Out.Offset()) - uint64(startoff) | |
| strput(ctxt.Out, d.name) | |
| } | |
| // write function names | |
| for d := dr; d != nil; d = d.next { | |
| for m := d.ms; m != nil; m = m.next { | |
| m.off = uint64(pefile.nextSectOffset) + uint64(ctxt.Out.Offset()) - uint64(startoff) | |
| ctxt.Out.Write16(0) // hint | |
| strput(ctxt.Out, ldr.SymExtname(m.s)) | |
| } | |
| } | |
| // write OriginalFirstThunks | |
| oftbase := uint64(ctxt.Out.Offset()) - uint64(startoff) | |
| n = uint64(ctxt.Out.Offset()) | |
| for d := dr; d != nil; d = d.next { | |
| d.thunkoff = uint64(ctxt.Out.Offset()) - n | |
| for m := d.ms; m != nil; m = m.next { | |
| if pe64 != 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(ldr.SymValue(dynamic)) - 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[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.virtualAddress | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.virtualSize | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(ldr.SymValue(dynamic) - PEBASE) | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(ldr.SymSize(dynamic)) | |
| out.SeekSet(endoff) | |
| } | |
| func initdynexport(ctxt *Link) { | |
| ldr := ctxt.loader | |
| for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ { | |
| if !ldr.AttrReachable(s) || !ldr.AttrCgoExportDynamic(s) { | |
| continue | |
| } | |
| if len(dexport)+1 > cap(dexport) { | |
| ctxt.Errorf(s, "pe dynexport table is full") | |
| errorexit() | |
| } | |
| dexport = append(dexport, s) | |
| } | |
| sort.Slice(dexport, func(i, j int) bool { return ldr.SymExtname(dexport[i]) < ldr.SymExtname(dexport[j]) }) | |
| } | |
| func addexports(ctxt *Link) { | |
| ldr := ctxt.loader | |
| var e IMAGE_EXPORT_DIRECTORY | |
| nexport := len(dexport) | |
| size := binary.Size(&e) + 10*nexport + len(*flagOutfile) + 1 | |
| for _, s := range dexport { | |
| size += len(ldr.SymExtname(s)) + 1 | |
| } | |
| if nexport == 0 { | |
| return | |
| } | |
| sect := pefile.addSection(".edata", size, size) | |
| sect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | |
| sect.checkOffset(ctxt.Out.Offset()) | |
| va := int(sect.virtualAddress) | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.virtualSize | |
| vaName := va + binary.Size(&e) + nexport*4 | |
| vaAddr := va + binary.Size(&e) | |
| vaNa := va + binary.Size(&e) + nexport*8 | |
| e.Characteristics = 0 | |
| e.MajorVersion = 0 | |
| e.MinorVersion = 0 | |
| e.NumberOfFunctions = uint32(nexport) | |
| e.NumberOfNames = uint32(nexport) | |
| e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. | |
| e.Base = 1 | |
| e.AddressOfFunctions = uint32(vaAddr) | |
| e.AddressOfNames = uint32(vaName) | |
| e.AddressOfNameOrdinals = uint32(vaNa) | |
| out := ctxt.Out | |
| // put IMAGE_EXPORT_DIRECTORY | |
| binary.Write(out, binary.LittleEndian, &e) | |
| // put EXPORT Address Table | |
| for _, s := range dexport { | |
| out.Write32(uint32(ldr.SymValue(s) - PEBASE)) | |
| } | |
| // put EXPORT Name Pointer Table | |
| v := int(e.Name + uint32(len(*flagOutfile)) + 1) | |
| for _, s := range dexport { | |
| out.Write32(uint32(v)) | |
| v += len(ldr.SymExtname(s)) + 1 | |
| } | |
| // put EXPORT Ordinal Table | |
| for i := 0; i < nexport; i++ { | |
| out.Write16(uint16(i)) | |
| } | |
| // put Names | |
| out.WriteStringN(*flagOutfile, len(*flagOutfile)+1) | |
| for _, s := range dexport { | |
| name := ldr.SymExtname(s) | |
| out.WriteStringN(name, len(name)+1) | |
| } | |
| sect.pad(out, uint32(size)) | |
| } | |
| // peBaseRelocEntry represents a single relocation entry. | |
| type peBaseRelocEntry struct { | |
| typeOff uint16 | |
| } | |
| // peBaseRelocBlock represents a Base Relocation Block. A block | |
| // is a collection of relocation entries in a page, where each | |
| // entry describes a single relocation. | |
| // The block page RVA (Relative Virtual Address) is the index | |
| // into peBaseRelocTable.blocks. | |
| type peBaseRelocBlock struct { | |
| entries []peBaseRelocEntry | |
| } | |
| // pePages is a type used to store the list of pages for which there | |
| // are base relocation blocks. This is defined as a type so that | |
| // it can be sorted. | |
| type pePages []uint32 | |
| 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(ldr *loader.Loader, s loader.Sym, r *loader.Reloc) { | |
| // pageSize is the size in bytes of a page | |
| // described by a base relocation block. | |
| const pageSize = 0x1000 | |
| const pageMask = pageSize - 1 | |
| addr := ldr.SymValue(s) + int64(r.Off()) - 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), | |
| } | |
| // Set entry type | |
| switch r.Siz() { | |
| default: | |
| Exitf("unsupported relocation size %d\n", r.Siz) | |
| case 4: | |
| e.typeOff |= uint16(IMAGE_REL_BASED_HIGHLOW << 12) | |
| case 8: | |
| e.typeOff |= uint16(IMAGE_REL_BASED_DIR64 << 12) | |
| } | |
| b.entries = append(b.entries, e) | |
| rt.blocks[page] = b | |
| } | |
| func (rt *peBaseRelocTable) write(ctxt *Link) { | |
| out := ctxt.Out | |
| // sort the pages array | |
| 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(ldr *loader.Loader, s loader.Sym, rt *peBaseRelocTable) { | |
| relocs := ldr.Relocs(s) | |
| for ri := 0; ri < relocs.Count(); ri++ { | |
| r := relocs.At(ri) | |
| if r.Type() >= objabi.ElfRelocOffset { | |
| continue | |
| } | |
| if r.Siz() == 0 { // informational relocation | |
| continue | |
| } | |
| if r.Type() == objabi.R_DWARFFILEREF { | |
| continue | |
| } | |
| rs := r.Sym() | |
| rs = ldr.ResolveABIAlias(rs) | |
| if rs == 0 { | |
| continue | |
| } | |
| if !ldr.AttrReachable(s) { | |
| continue | |
| } | |
| switch r.Type() { | |
| default: | |
| case objabi.R_ADDR: | |
| rt.addentry(ldr, s, &r) | |
| } | |
| } | |
| } | |
| func addPEBaseReloc(ctxt *Link) { | |
| // Arm does not work without base relocation table. | |
| // 386 and amd64 will only require the table for BuildModePIE. | |
| switch ctxt.Arch.Family { | |
| default: | |
| return | |
| case sys.I386, sys.AMD64: | |
| if ctxt.BuildMode != BuildModePIE { | |
| return | |
| } | |
| case sys.ARM: | |
| } | |
| var rt peBaseRelocTable | |
| rt.init(ctxt) | |
| // Get relocation information | |
| ldr := ctxt.loader | |
| for _, s := range ctxt.Textp { | |
| addPEBaseRelocSym(ldr, s, &rt) | |
| } | |
| for _, s := range ctxt.datap { | |
| addPEBaseRelocSym(ldr, s, &rt) | |
| } | |
| // Write relocation information | |
| startoff := ctxt.Out.Offset() | |
| rt.write(ctxt) | |
| size := ctxt.Out.Offset() - startoff | |
| // Add a PE section and pad it at the end | |
| rsect := pefile.addSection(".reloc", int(size), int(size)) | |
| rsect.characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE | |
| rsect.checkOffset(startoff) | |
| rsect.pad(ctxt.Out, uint32(size)) | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = rsect.virtualAddress | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = rsect.virtualSize | |
| } | |
| func (ctxt *Link) dope() { | |
| initdynimport(ctxt) | |
| initdynexport(ctxt) | |
| } | |
| func setpersrc(ctxt *Link, sym loader.Sym) { | |
| if rsrcsym != 0 { | |
| Errorf(nil, "too many .rsrc sections") | |
| } | |
| rsrcsym = sym | |
| } | |
| func addpersrc(ctxt *Link) { | |
| if rsrcsym == 0 { | |
| return | |
| } | |
| data := ctxt.loader.Data(rsrcsym) | |
| size := len(data) | |
| h := pefile.addSection(".rsrc", size, size) | |
| h.characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA | |
| h.checkOffset(ctxt.Out.Offset()) | |
| // relocation | |
| relocs := ctxt.loader.Relocs(rsrcsym) | |
| for i := 0; i < relocs.Count(); i++ { | |
| r := relocs.At(i) | |
| p := data[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(data) | |
| h.pad(ctxt.Out, uint32(size)) | |
| // update data directory | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.virtualAddress | |
| pefile.dataDirectory[pe.IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.virtualSize | |
| } | |
| func asmbPe(ctxt *Link) { | |
| 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) | |
| } |