)
const (
- IMAGE_FILE_MACHINE_I386 = 0x14c
- IMAGE_FILE_MACHINE_AMD64 = 0x8664
- IMAGE_FILE_RELOCS_STRIPPED = 0x0001
- IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002
- IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004
- IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020
- IMAGE_FILE_32BIT_MACHINE = 0x0100
- IMAGE_FILE_DEBUG_STRIPPED = 0x0200
- IMAGE_SCN_CNT_CODE = 0x00000020
- IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
- IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
- IMAGE_SCN_MEM_EXECUTE = 0x20000000
- IMAGE_SCN_MEM_READ = 0x40000000
- IMAGE_SCN_MEM_WRITE = 0x80000000
- IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
- IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
- IMAGE_SCN_ALIGN_32BYTES = 0x600000
- IMAGE_DIRECTORY_ENTRY_EXPORT = 0
- IMAGE_DIRECTORY_ENTRY_IMPORT = 1
- IMAGE_DIRECTORY_ENTRY_RESOURCE = 2
- IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3
- IMAGE_DIRECTORY_ENTRY_SECURITY = 4
- IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
- IMAGE_DIRECTORY_ENTRY_DEBUG = 6
- IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7
- IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7
- IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8
- IMAGE_DIRECTORY_ENTRY_TLS = 9
- IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
- IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11
- IMAGE_DIRECTORY_ENTRY_IAT = 12
- IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13
- IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
- IMAGE_SUBSYSTEM_WINDOWS_GUI = 2
- IMAGE_SUBSYSTEM_WINDOWS_CUI = 3
+ 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
)
// TODO(crawshaw): add these constants to debug/pe.
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
)
// Copyright 2009 The Go Authors. All rights reserved.
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
init_entry := ctxt.Syms.Lookup(*flagEntrySymbol, 0)
addr := uint64(init_entry.Value) - init_entry.Sect.Vaddr
switch objabi.GOARCH {
- case "386":
+ case "386", "arm":
ctxt.Out.Write32(uint32(addr))
case "amd64":
ctxt.Out.Write64(addr)
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
})
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))
if linkmode == LinkExternal {
fh.Characteristics = IMAGE_FILE_LINE_NUMS_STRIPPED
} else {
- fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED
+ 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
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
+ }
+
// 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
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 >= 256 {
+ 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() {
/* relocation table */
rel := ctxt.Syms.Lookup(".rel", 0)
switch ctxt.Arch.Family {
default:
Exitf("unknown PE architecture: %v", ctxt.Arch.Family)
- case sys.AMD64, sys.I386:
+ case sys.AMD64, sys.I386, sys.ARM:
}
t := pefile.addSection(".text", int(Segtext.Length), int(Segtext.Length))
if ctxt.LinkMode != LinkExternal {
addimports(ctxt, d)
addexports(ctxt)
+ addPEBaseReloc(ctxt)
}
pefile.writeSymbolTableAndStringTable(ctxt)
addpersrc(ctxt)
IMAGE_REL_AMD64_SREL32 = 0x000E
IMAGE_REL_AMD64_PAIR = 0x000F
IMAGE_REL_AMD64_SSPAN32 = 0x0010
+ 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_SECTION = 0x000E
+ IMAGE_REL_ARM_SECREL = 0x000F
+ IMAGE_REL_ARM_MOV32 = 0x0010
+ IMAGE_REL_THUMB_MOV32 = 0x0011
+ IMAGE_REL_THUMB_BRANCH20 = 0x0012
+ IMAGE_REL_THUMB_BRANCH24 = 0x0014
+ IMAGE_REL_THUMB_BLX23 = 0x0015
+ IMAGE_REL_ARM_PAIR = 0x0016
)
// TODO(crawshaw): de-duplicate these symbols with cmd/internal/ld, ideally in debug/pe.
rp.Sym = gosym
rp.Siz = 4
rp.Off = int32(r.VirtualAddress)
- switch r.Type {
+ switch arch.Family {
default:
- return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
+ return nil, nil, fmt.Errorf("%s: unsupported arch %v", pn, arch.Family)
+ case sys.I386, sys.AMD64:
+ switch r.Type {
+ default:
+ return nil, nil, fmt.Errorf("%s: %v: unknown relocation type %v", pn, sectsyms[rsect], r.Type)
- case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
- IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
- IMAGE_REL_AMD64_ADDR32NB:
- rp.Type = objabi.R_PCREL
+ case IMAGE_REL_I386_REL32, IMAGE_REL_AMD64_REL32,
+ IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32
+ IMAGE_REL_AMD64_ADDR32NB:
+ rp.Type = objabi.R_PCREL
- rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+ rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
- case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
- rp.Type = objabi.R_ADDR
+ case IMAGE_REL_I386_DIR32NB, IMAGE_REL_I386_DIR32:
+ rp.Type = objabi.R_ADDR
- // load addend from image
- rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+ // load addend from image
+ rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
- case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
- rp.Siz = 8
+ case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64
+ rp.Siz = 8
- rp.Type = objabi.R_ADDR
+ rp.Type = objabi.R_ADDR
- // load addend from image
- rp.Add = int64(binary.LittleEndian.Uint64(sectdata[rsect][rp.Off:]))
+ // load addend from image
+ rp.Add = int64(binary.LittleEndian.Uint64(sectdata[rsect][rp.Off:]))
+ }
+
+ case sys.ARM:
+ switch r.Type {
+ default:
+ return nil, nil, fmt.Errorf("%s: %v: unknown ARM relocation type %v", pn, sectsyms[rsect], r.Type)
+
+ case IMAGE_REL_ARM_SECREL:
+ rp.Type = objabi.R_PCREL
+
+ rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+
+ case IMAGE_REL_ARM_ADDR32:
+ rp.Type = objabi.R_ADDR
+
+ rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+
+ case IMAGE_REL_ARM_BRANCH24:
+ rp.Type = objabi.R_CALLARM
+
+ rp.Add = int64(int32(binary.LittleEndian.Uint32(sectdata[rsect][rp.Off:])))
+ }
}
// ld -r could generate multiple section symbols for the