]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: support windows/arm
authorJordan Rhee <jordanrh@microsoft.com>
Tue, 24 Jul 2018 22:13:41 +0000 (15:13 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 21 Aug 2018 22:11:05 +0000 (22:11 +0000)
Enable the Go linker to generate executables for windows/arm.

Generates PE relocation tables, which are used by Windows to
dynamically relocate the Go binary in memory. Windows on ARM
requires all modules to be relocatable, unlike x86/amd64 which are
permitted to have fixed base addresses.

Updates #26148

Change-Id: Ie63964ff52c2377e121b2885e9d05ec3ed8dc1cd
Reviewed-on: https://go-review.googlesource.com/125648
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/cmd/internal/objfile/pe.go
src/cmd/link/internal/arm/asm.go
src/cmd/link/internal/arm/obj.go
src/cmd/link/internal/ld/config.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/pe.go
src/cmd/link/internal/loadpe/ldpe.go

index 80db6f0f1872f269fd98b365c893907e7124ca8d..259b59a4f4aeb2f6f66f50b8eb661ace6e5da717 100644 (file)
@@ -190,6 +190,9 @@ func (f *peFile) goarch() string {
        if _, err := findPESymbol(f.pe, "_rt0_amd64_windows"); err == nil {
                return "amd64"
        }
+       if _, err := findPESymbol(f.pe, "_rt0_arm_windows"); err == nil {
+               return "arm"
+       }
        return ""
 }
 
index 5e4ddea88e1fa77f8986d8460490e85331a570f0..b1d44b5896fd7424cc3d095d272903a08551e1ba 100644 (file)
@@ -411,6 +411,35 @@ func machoreloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, se
        return true
 }
 
+func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, sectoff int64) bool {
+       rs := r.Xsym
+
+       if rs.Dynid < 0 {
+               ld.Errorf(s, "reloc %d (%s) to non-coff symbol %s type=%d (%s)", r.Type, sym.RelocName(arch, r.Type), rs.Name, rs.Type, rs.Type)
+               return false
+       }
+
+       out.Write32(uint32(sectoff))
+       out.Write32(uint32(rs.Dynid))
+
+       var v uint32
+       switch r.Type {
+       default:
+               // unsupported relocation type
+               return false
+
+       case objabi.R_DWARFSECREF:
+               v = ld.IMAGE_REL_ARM_SECREL
+
+       case objabi.R_ADDR:
+               v = ld.IMAGE_REL_ARM_ADDR32
+       }
+
+       out.Write16(uint16(v))
+
+       return true
+}
+
 // sign extend a 24-bit integer
 func signext24(x int64) int32 {
        return (int32(x) << 8) >> 8
@@ -799,6 +828,10 @@ func asmb(ctxt *ld.Link) {
 
                case objabi.Hdarwin:
                        symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(*ld.FlagRound))) + uint64(machlink))
+
+               case objabi.Hwindows:
+                       symo = uint32(ld.Segdwarf.Fileoff + ld.Segdwarf.Filelen)
+                       symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN))
                }
 
                ctxt.Out.SeekSet(int64(symo))
@@ -828,6 +861,11 @@ func asmb(ctxt *ld.Link) {
                                ctxt.Out.Flush()
                        }
 
+               case objabi.Hwindows:
+                       if ctxt.Debugvlog != 0 {
+                               ctxt.Logf("%5.2f dwarf\n", ld.Cputime())
+                       }
+
                case objabi.Hdarwin:
                        if ctxt.LinkMode == ld.LinkExternal {
                                ld.Machoemitreloc(ctxt)
@@ -860,6 +898,9 @@ func asmb(ctxt *ld.Link) {
 
        case objabi.Hdarwin:
                ld.Asmbmacho(ctxt)
+
+       case objabi.Hwindows:
+               ld.Asmbpe(ctxt)
        }
 
        ctxt.Out.Flush()
index 788be68522fa5e924eeaa61f99681a52da485326..77716bb954dfdd994f6a2a154afbef446a348ca4 100644 (file)
@@ -57,6 +57,7 @@ func Init() (*sys.Arch, ld.Arch) {
                Elfsetupplt:      elfsetupplt,
                Gentext:          gentext,
                Machoreloc1:      machoreloc1,
+               PEreloc1:         pereloc1,
 
                Linuxdynld:     "/lib/ld-linux.so.3", // 2 for OABI, 3 for EABI
                Freebsddynld:   "/usr/libexec/ld-elf.so.1",
@@ -130,6 +131,10 @@ func archinit(ctxt *ld.Link) {
                if *ld.FlagRound == -1 {
                        *ld.FlagRound = 4096
                }
+
+       case objabi.Hwindows: /* PE executable */
+               // ld.HEADR, ld.FlagTextAddr, ld.FlagDataAddr and ld.FlagRound are set in ld.Peinit
+               return
        }
 
        if *ld.FlagDataAddr != 0 && *ld.FlagRound != 0 {
index 18fbea62eef98d1fcfef3d0f1ffaf299d2913d1f..77b03b67f9b82becdb58609024bd72b4a74564dd 100644 (file)
@@ -60,7 +60,7 @@ func (mode *BuildMode) Set(s string) error {
                        }
                case "windows":
                        switch objabi.GOARCH {
-                       case "amd64", "386":
+                       case "amd64", "386", "arm":
                        default:
                                return badmode()
                        }
index 5dd4aac03e37641dab122392f12733e0ef275d00..730e9a0bf7612b7071a9c22bbbf9e738dc2f86fc 100644 (file)
@@ -539,13 +539,17 @@ func windynrelocsym(ctxt *Link, s *sym.Symbol) {
                        r.Add = int64(targ.Plt)
 
                        // jmp *addr
-                       if ctxt.Arch.Family == sys.I386 {
+                       switch ctxt.Arch.Family {
+                       default:
+                               Errorf(s, "unsupported arch %v", ctxt.Arch.Family)
+                               return
+                       case sys.I386:
                                rel.AddUint8(0xff)
                                rel.AddUint8(0x25)
                                rel.AddAddr(ctxt.Arch, targ)
                                rel.AddUint8(0x90)
                                rel.AddUint8(0x90)
-                       } else {
+                       case sys.AMD64:
                                rel.AddUint8(0xff)
                                rel.AddUint8(0x24)
                                rel.AddUint8(0x25)
index c81e3d6af577641e95b2cb9c381d53a6b9390656..0e60ef76d218f5401552415acc304a29c2eaee5d 100644 (file)
@@ -54,41 +54,45 @@ var (
 )
 
 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.
@@ -109,6 +113,15 @@ const (
        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.
@@ -477,6 +490,8 @@ func (f *peFile) addInitArray(ctxt *Link) *peSection {
                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
@@ -487,7 +502,7 @@ func (f *peFile) addInitArray(ctxt *Link) *peSection {
        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)
@@ -592,6 +607,8 @@ dwarfLoop:
                        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
        })
@@ -743,6 +760,8 @@ func (f *peFile) writeFileHeader(arch *sys.Arch, out *OutBuf, linkmode LinkMode)
                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))
@@ -754,7 +773,14 @@ func (f *peFile) writeFileHeader(arch *sys.Arch, out *OutBuf, linkmode LinkMode)
        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
@@ -831,6 +857,12 @@ func (f *peFile) writeOptionalHeader(ctxt *Link) {
                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
@@ -1271,6 +1303,162 @@ func addexports(ctxt *Link) {
        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)
@@ -1326,7 +1514,7 @@ func Asmbpe(ctxt *Link) {
        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))
@@ -1380,6 +1568,7 @@ func Asmbpe(ctxt *Link) {
        if ctxt.LinkMode != LinkExternal {
                addimports(ctxt, d)
                addexports(ctxt)
+               addPEBaseReloc(ctxt)
        }
        pefile.writeSymbolTableAndStringTable(ctxt)
        addpersrc(ctxt)
index c8fae3789839e59567772d74c7bbbed6f07c5273..f78252c283a348d2f834946973d291289efc33a5 100644 (file)
@@ -101,6 +101,19 @@ const (
        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.
@@ -241,30 +254,56 @@ func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, leng
                        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