Add WoA object file emission support
authorSaleem Abdulrasool <compnerd@compnerd.org>
Sun, 27 Apr 2014 03:48:22 +0000 (03:48 +0000)
committerSaleem Abdulrasool <compnerd@compnerd.org>
Sun, 27 Apr 2014 03:48:22 +0000 (03:48 +0000)
Introduce support for WoA PE/COFF object file emission from LLVM.  Add the new
target specific PE/COFF Streamer (ARMWinCOFFStreamer) that handles the ARM
specific behaviour of PE/COFF object emission.  ARM exception information is not
yet emitted and is a TODO item.

The ARM specific object writer (ARMWinCOFFObjectWriter) handles the ARM specific
relocation handling in conjunction with the WinCOFFObjectWriter in the MC layer.
The MC layer needs to be updated to deal with the relocation adjustments.
Branch relocations are adjusted by 4 bytes (unlikely their ELF counterparts).

Minor tweaks to switch multiple conditional checks into equivalent switch
statements.  The ObjectFileInfo is updated to relax the object file setup for
Windows COFF.  Move the architecture checks into an assertion.  Windows COFF is
currently only supported on x86, x86_64, and ARM (thumb).  Rather than
defaulting to ELF, we will refuse to generate an object file.  This is better
though as you do not get an (arbitrary) object file which is different from the
request.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@207345 91177308-0d34-0410-b5e6-96231b3b80d8

lib/MC/MCObjectFileInfo.cpp
lib/MC/WinCOFFObjectWriter.cpp
lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp
lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.cpp
lib/Target/ARM/MCTargetDesc/ARMMCTargetDesc.h
lib/Target/ARM/MCTargetDesc/ARMWinCOFFObjectWriter.cpp [new file with mode: 0644]
lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp [new file with mode: 0644]
lib/Target/ARM/MCTargetDesc/CMakeLists.txt
test/MC/COFF/arm-relocations.s [new file with mode: 0644]

index 26d2b61d5894f9cc1e1fd6f7552024df45ab6269..efdf677fbb82021a17774b0c120b2d06cf815705 100644 (file)
@@ -780,8 +780,10 @@ void MCObjectFileInfo::InitMCObjectFileInfo(StringRef TT, Reloc::Model relocm,
       (T.isOSDarwin() || T.isOSBinFormatMachO())) {
     Env = IsMachO;
     InitMachOMCObjectFileInfo(T);
-  } else if ((Arch == Triple::x86 || Arch == Triple::x86_64) &&
-             T.getObjectFormat() != Triple::ELF && T.isOSWindows()) {
+  } else if (T.isOSWindows() && T.getObjectFormat() == Triple::COFF) {
+    assert((Arch == Triple::x86 || Arch == Triple::x86_64 ||
+            Arch == Triple::arm || Arch == Triple::thumb) &&
+           "unsupported Windows COFF architecture");
     Env = IsCOFF;
     InitCOFFMCObjectFileInfo(T);
   } else {
index 60e046ec4fe8ff6b150d11dbb7a21c809d96d90d..5a562ae0efa7d8f7c7eb86f9a14a9d6e2f5c5fe7 100644 (file)
@@ -761,6 +761,44 @@ void WinCOFFObjectWriter::RecordRelocation(const MCAssembler &Asm,
        Reloc.Data.Type == COFF::IMAGE_REL_I386_REL32))
     FixedValue += 4;
 
+  if (Header.Machine == COFF::IMAGE_FILE_MACHINE_ARMNT) {
+    switch (Reloc.Data.Type) {
+    case COFF::IMAGE_REL_ARM_ABSOLUTE:
+    case COFF::IMAGE_REL_ARM_ADDR32:
+    case COFF::IMAGE_REL_ARM_ADDR32NB:
+    case COFF::IMAGE_REL_ARM_TOKEN:
+    case COFF::IMAGE_REL_ARM_SECTION:
+    case COFF::IMAGE_REL_ARM_SECREL:
+      break;
+    case COFF::IMAGE_REL_ARM_BRANCH11:
+    case COFF::IMAGE_REL_ARM_BLX11:
+      // IMAGE_REL_ARM_BRANCH11 and IMAGE_REL_ARM_BLX11 are only used for
+      // pre-ARMv7, which implicitly rules it out of ARMNT (it would be valid
+      // for Windows CE).
+    case COFF::IMAGE_REL_ARM_BRANCH24:
+    case COFF::IMAGE_REL_ARM_BLX24:
+    case COFF::IMAGE_REL_ARM_MOV32A:
+      // IMAGE_REL_ARM_BRANCH24, IMAGE_REL_ARM_BLX24, IMAGE_REL_ARM_MOV32A are
+      // only used for ARM mode code, which is documented as being unsupported
+      // by Windows on ARM.  Emperical proof indicates that masm is able to
+      // generate the relocations however the rest of the MSVC toolchain is
+      // unable to handle it.
+      llvm_unreachable("unsupported relocation");
+      break;
+    case COFF::IMAGE_REL_ARM_MOV32T:
+      break;
+    case COFF::IMAGE_REL_ARM_BRANCH20T:
+    case COFF::IMAGE_REL_ARM_BRANCH24T:
+    case COFF::IMAGE_REL_ARM_BLX23T:
+      // IMAGE_REL_BRANCH20T, IMAGE_REL_ARM_BRANCH24T, IMAGE_REL_ARM_BLX23T all
+      // perform a 4 byte adjustment to the relocation.  Relative branches are
+      // offset by 4 on ARM, however, because there is no RELA relocations, all
+      // branches are offset by 4.
+      FixedValue = FixedValue + 4;
+      break;
+    }
+  }
+
   coff_section->Relocations.push_back(Reloc);
 }
 
index c16b3554e297332e942a870ca32fc43479b1b4b3..aade4328db37fa25bc6501020f81d3000a48c02c 100644 (file)
@@ -737,6 +737,15 @@ void ARMAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
 }
 
 namespace {
+// FIXME: This should be in a separate file.
+class ARMWinCOFFAsmBackend : public ARMAsmBackend {
+public:
+  ARMWinCOFFAsmBackend(const Target &T, const StringRef &Triple)
+    : ARMAsmBackend(T, Triple, true) { }
+  MCObjectWriter *createObjectWriter(raw_ostream &OS) const override {
+    return createARMWinCOFFObjectWriter(OS, /*Is64Bit=*/false);
+  }
+};
 
 // FIXME: This should be in a separate file.
 // ELF is an ELF of course...
@@ -777,7 +786,9 @@ MCAsmBackend *llvm::createARMAsmBackend(const Target &T,
                                         bool isLittle) {
   Triple TheTriple(TT);
 
-  if (TheTriple.isOSBinFormatMachO()) {
+  switch (TheTriple.getObjectFormat()) {
+  default: llvm_unreachable("unsupported object format");
+  case Triple::MachO: {
     MachO::CPUSubTypeARM CS =
       StringSwitch<MachO::CPUSubTypeARM>(TheTriple.getArchName())
       .Cases("armv4t", "thumbv4t", MachO::CPU_SUBTYPE_ARM_V4T)
@@ -792,15 +803,14 @@ MCAsmBackend *llvm::createARMAsmBackend(const Target &T,
 
     return new DarwinARMAsmBackend(T, TT, CS);
   }
-
-#if 0
-  // FIXME: Introduce yet another checker but assert(0).
-  if (TheTriple.isOSBinFormatCOFF())
-    assert(0 && "Windows not supported on ARM");
-#endif
-
-  uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(Triple(TT).getOS());
-  return new ELFARMAsmBackend(T, TT, OSABI, isLittle);
+  case Triple::COFF:
+    assert(TheTriple.isOSWindows() && "non-Windows ARM COFF is not supported");
+    return new ARMWinCOFFAsmBackend(T, TT);
+  case Triple::ELF:
+    assert(TheTriple.isOSBinFormatELF() && "using ELF for non-ELF target");
+    uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(Triple(TT).getOS());
+    return new ELFARMAsmBackend(T, TT, OSABI, isLittle);
+  }
 }
 
 MCAsmBackend *llvm::createARMLEAsmBackend(const Target &T,
index da7eb32cfd9049684a586db60656e1151b838391..701a6320d487a6e7b8472430411dcf104b8d4ff3 100644 (file)
@@ -1029,6 +1029,9 @@ ARMMCCodeEmitter::getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx,
     switch (ARM16Expr->getKind()) {
     default: llvm_unreachable("Unsupported ARMFixup");
     case ARMMCExpr::VK_ARM_HI16:
+      if (Triple(STI.getTargetTriple()).isOSWindows())
+        return 0;
+
       Kind = MCFixupKind(isThumb2(STI) ? ARM::fixup_t2_movt_hi16
                                        : ARM::fixup_arm_movt_hi16);
       break;
@@ -1037,6 +1040,7 @@ ARMMCCodeEmitter::getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx,
                                        : ARM::fixup_arm_movw_lo16);
       break;
     }
+
     Fixups.push_back(MCFixup::Create(0, E, Kind, MI.getLoc()));
     return 0;
   }
index 0d2aaa2eed80dfb0ea967424f361a3bfc13d0cd5..04d63a7e6d460bb4139e4ee69ef3b1b7e70c5e38 100644 (file)
@@ -21,6 +21,7 @@
 #include "llvm/MC/MCInstrAnalysis.h"
 #include "llvm/MC/MCInstrInfo.h"
 #include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSubtargetInfo.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/TargetRegistry.h"
@@ -275,18 +276,20 @@ static MCStreamer *createMCStreamer(const Target &T, StringRef TT,
                                     bool NoExecStack) {
   Triple TheTriple(TT);
 
-  if (TheTriple.isOSBinFormatMachO()) {
+  switch (TheTriple.getObjectFormat()) {
+  default: llvm_unreachable("unsupported object format");
+  case Triple::MachO: {
     MCStreamer *S = createMachOStreamer(Ctx, MAB, OS, Emitter, false);
     new ARMTargetStreamer(*S);
     return S;
   }
-
-  if (TheTriple.isOSWindows()) {
-    llvm_unreachable("ARM does not support Windows COFF format");
+  case Triple::COFF:
+    assert(TheTriple.isOSWindows() && "non-Windows ARM COFF is not supported");
+    return createARMWinCOFFStreamer(Ctx, MAB, *Emitter, OS);
+  case Triple::ELF:
+    return createARMELFStreamer(Ctx, MAB, OS, Emitter, false, NoExecStack,
+                                TheTriple.getArch() == Triple::thumb);
   }
-
-  return createARMELFStreamer(Ctx, MAB, OS, Emitter, false, NoExecStack,
-                              TheTriple.getArch() == Triple::thumb);
 }
 
 static MCInstPrinter *createARMMCInstPrinter(const Target &T,
index e81876f6248456a197065a6648ec2a9653318ae7..37b136aa4965c4b44afd00748f9ae36c9610a5c0 100644 (file)
@@ -78,6 +78,11 @@ MCAsmBackend *createThumbLEAsmBackend(const Target &T, const MCRegisterInfo &MRI
 MCAsmBackend *createThumbBEAsmBackend(const Target &T, const MCRegisterInfo &MRI,
                                       StringRef TT, StringRef CPU);
 
+/// createARMWinCOFFStreamer - Construct a PE/COFF machine code streamer which
+/// will generate a PE/COFF object file.
+MCStreamer *createARMWinCOFFStreamer(MCContext &Context, MCAsmBackend &MAB,
+                                     MCCodeEmitter &Emitter, raw_ostream &OS);
+
 /// createARMELFObjectWriter - Construct an ELF Mach-O object writer.
 MCObjectWriter *createARMELFObjectWriter(raw_ostream &OS,
                                          uint8_t OSABI,
@@ -89,6 +94,8 @@ MCObjectWriter *createARMMachObjectWriter(raw_ostream &OS,
                                           uint32_t CPUType,
                                           uint32_t CPUSubtype);
 
+/// createARMWinCOFFObjectWriter - Construct an ARM PE/COFF object writer.
+MCObjectWriter *createARMWinCOFFObjectWriter(raw_ostream &OS, bool Is64Bit);
 
 /// createARMMachORelocationInfo - Construct ARM Mach-O relocation info.
 MCRelocationInfo *createARMMachORelocationInfo(MCContext &Ctx);
diff --git a/lib/Target/ARM/MCTargetDesc/ARMWinCOFFObjectWriter.cpp b/lib/Target/ARM/MCTargetDesc/ARMWinCOFFObjectWriter.cpp
new file mode 100644 (file)
index 0000000..d1c8926
--- /dev/null
@@ -0,0 +1,74 @@
+//===-- ARMWinCOFFObjectWriter.cpp - ARM Windows COFF Object Writer -- C++ -==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/ARMFixupKinds.h"
+#include "llvm/MC/MCFixup.h"
+#include "llvm/MC/MCValue.h"
+#include "llvm/MC/MCWinCOFFObjectWriter.h"
+#include "llvm/Support/COFF.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+namespace {
+class ARMWinCOFFObjectWriter : public MCWinCOFFObjectTargetWriter {
+public:
+  ARMWinCOFFObjectWriter(bool Is64Bit)
+    : MCWinCOFFObjectTargetWriter(COFF::IMAGE_FILE_MACHINE_ARMNT) {
+    assert(!Is64Bit && "AArch64 support not yet implemented");
+  }
+  virtual ~ARMWinCOFFObjectWriter() = default;
+
+  unsigned getRelocType(const MCValue &Target, const MCFixup &Fixup,
+                        bool IsCrossSection) const override;
+};
+
+unsigned ARMWinCOFFObjectWriter::getRelocType(const MCValue &Target,
+                                              const MCFixup &Fixup,
+                                              bool IsCrossSection) const {
+  assert(getMachine() == COFF::IMAGE_FILE_MACHINE_ARMNT &&
+         "AArch64 support not yet implemented");
+
+  MCSymbolRefExpr::VariantKind Modifier =
+    Target.isAbsolute() ? MCSymbolRefExpr::VK_None : Target.getSymA()->getKind();
+
+  switch (static_cast<unsigned>(Fixup.getKind())) {
+  default: llvm_unreachable("unsupported relocation type");
+  case FK_Data_4:
+    switch (Modifier) {
+    case MCSymbolRefExpr::VK_COFF_IMGREL32:
+      return COFF::IMAGE_REL_ARM_ADDR32NB;
+    case MCSymbolRefExpr::VK_SECREL:
+      return COFF::IMAGE_REL_ARM_SECREL;
+    default:
+      return COFF::IMAGE_REL_ARM_ADDR32;
+    }
+  case ARM::fixup_t2_condbranch:
+    return COFF::IMAGE_REL_ARM_BRANCH20T;
+  case ARM::fixup_t2_uncondbranch:
+    return COFF::IMAGE_REL_ARM_BRANCH24T;
+  case ARM::fixup_arm_thumb_bl:
+  case ARM::fixup_arm_thumb_blx:
+    return COFF::IMAGE_REL_ARM_BLX23T;
+  case ARM::fixup_t2_movw_lo16:
+    return COFF::IMAGE_REL_ARM_MOV32T;
+  case ARM::fixup_t2_movt_hi16:
+    llvm_unreachable("High-word for pair-wise relocations are contiguously "
+                     "addressed as an IMAGE_REL_ARM_MOV32T relocation");
+  }
+}
+}
+
+namespace llvm {
+MCObjectWriter *createARMWinCOFFObjectWriter(raw_ostream &OS, bool Is64Bit) {
+  MCWinCOFFObjectTargetWriter *MOTW = new ARMWinCOFFObjectWriter(Is64Bit);
+  return createWinCOFFObjectWriter(MOTW, OS);
+}
+}
+
diff --git a/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMWinCOFFStreamer.cpp
new file mode 100644 (file)
index 0000000..b344ced
--- /dev/null
@@ -0,0 +1,46 @@
+//===-- ARMWinCOFFStreamer.cpp - ARM Target WinCOFF Streamer ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ARMMCTargetDesc.h"
+#include "llvm/MC/MCWinCOFFStreamer.h"
+
+using namespace llvm;
+
+namespace {
+class ARMWinCOFFStreamer : public MCWinCOFFStreamer {
+public:
+  ARMWinCOFFStreamer(MCContext &C, MCAsmBackend &AB, MCCodeEmitter &CE,
+                     raw_ostream &OS)
+    : MCWinCOFFStreamer(C, AB, CE, OS) { }
+
+  void EmitAssemblerFlag(MCAssemblerFlag Flag) override;
+  void EmitThumbFunc(MCSymbol *Symbol) override;
+};
+
+void ARMWinCOFFStreamer::EmitAssemblerFlag(MCAssemblerFlag Flag) {
+  switch (Flag) {
+  default: llvm_unreachable("not implemented");
+  case MCAF_SyntaxUnified:
+  case MCAF_Code16:
+    break;
+  }
+}
+
+void ARMWinCOFFStreamer::EmitThumbFunc(MCSymbol *Symbol) {
+  getAssembler().setIsThumbFunc(Symbol);
+}
+}
+
+namespace llvm {
+MCStreamer *createARMWinCOFFStreamer(MCContext &Context, MCAsmBackend &MAB,
+                                     MCCodeEmitter &Emitter, raw_ostream &OS) {
+  return new ARMWinCOFFStreamer(Context, MAB, Emitter, OS);
+}
+}
+
index 533a93a65e46e8a3b578daa1c786f465d2d335eb..9582e8cbef47ab2ba413b9255a0662258af142da 100644 (file)
@@ -11,4 +11,6 @@ add_llvm_library(LLVMARMDesc
   ARMMCTargetDesc.cpp
   ARMTargetStreamer.cpp
   ARMUnwindOpAsm.cpp
+  ARMWinCOFFObjectWriter.cpp
+  ARMWinCOFFStreamer.cpp
   )
diff --git a/test/MC/COFF/arm-relocations.s b/test/MC/COFF/arm-relocations.s
new file mode 100644 (file)
index 0000000..6ebae70
--- /dev/null
@@ -0,0 +1,101 @@
+@ RUN: llvm-mc -triple thumbv7-windows-itanium -filetype obj -o - %s \
+@ RUN:   | llvm-readobj -r - | FileCheck %s -check-prefix CHECK-RELOCATION
+
+@ RUN: llvm-mc -triple thumbv7-windows-itanium -filetype obj -o - %s \
+@ RUN:   | llvm-objdump -d - | FileCheck %s -check-prefix CHECK-ENCODING
+
+       .syntax unified
+       .text
+       .thumb
+
+       .global target
+
+       .thumb_func
+branch24t:
+       b target
+
+@ CHECK-ENCODING-LABEL: branch24t
+@ CHECK-ENCODING-NEXT: b.w #0
+
+       .thumb_func
+branch20t:
+       bcc target
+
+@ CHECK-ENCODING-LABEL: branch20t
+@ CHECK-ENCODING-NEXT: blo.w #0
+
+       .thumb_func
+blx23t:
+       bl target
+
+@ CHECK-ENCODING-LABEL: blx23t
+@ CHECK-ENCODING-NEXT: bl #0
+
+       .thumb_func
+mov32t:
+       movw r0, :lower16:target
+       movt r0, :upper16:target
+       blx r0
+
+@ CHECK-ENCODING-LABEL: mov32t
+@ CHECK-ENCODING-NEXT: movw r0, #0
+@ CHECK-ENCODING-NEXT: movt r0, #0
+@ CHECK-ENCODING-NEXT: blx r0
+
+       .thumb_func
+addr32:
+       ldr r0, .Laddr32
+       bx r0
+       trap
+.Laddr32:
+       .long target
+
+@ CHECK-ENCODING-LABEL: addr32
+@ CHECK-ENCODING-NEXT: ldr r0, [pc, #4]
+@ CHECK-ENCODING-NEXT: bx r0
+@ CHECK-ENCODING-NEXT: trap
+@ CHECK-ENCODING-NEXT: movs r0, r0
+@ CHECK-ENCODING-NEXT: movs r0, r0
+
+       .thumb_func
+addr32nb:
+       ldr r0, .Laddr32nb
+       bx r0
+       trap
+.Laddr32nb:
+       .long target(imgrel)
+
+@ CHECK-ENCODING-LABEL: addr32nb
+@ CHECK-ENCODING-NEXT: ldr.w r0, [pc, #4]
+@ CHECK-ENCODING-NEXT: bx r0
+@ CHECK-ENCODING-NEXT: trap
+@ CHECK-ENCODING-NEXT: movs r0, r0
+@ CHECK-ENCODING-NEXT: movs r0, r0
+
+       .thumb_func
+secrel:
+       ldr r0, .Lsecrel
+       bx r0
+       trap
+.Lsecrel:
+       .long target(secrel32)
+
+@ CHECK-ENCODING-LABEL: secrel
+@ CHECK-ENCODING-NEXT: ldr.w r0, [pc, #4]
+@ CHECK-ENCODING-NEXT: bx r0
+@ CHECK-ENCODING-NEXT: trap
+@ CHECK-ENCODING-NEXT: movs r0, r0
+@ CHECK-ENCODING-NEXT: movs r0, r0
+
+@ CHECK-RELOCATION: Relocations [
+@ CHECK-RELOCATION:   Section (1) .text {
+@ CHCEK-RELOCATION:     0x0 IMAGE_REL_ARM_BRANCH24T
+@ CHECK-RELOCATION:     0x4 IMAGE_REL_ARM_BRANCH20T
+@ CHECK-RELOCATION:     0x8 IMAGE_REL_ARM_BLX23T
+@ CHECK-RELOCATION:     0xC IMAGE_REL_ARM_MOV32T
+@ CHECK-RELOCATION:     0x1C IMAGE_REL_ARM_ADDR32
+@ CHECK-RELOCATION:     0x28 IMAGE_REL_ARM_ADDR32NB
+@ CHECK-RELOCATION:     0x34 IMAGE_REL_ARM_SECREL
+@ CHECK-RELOCATION:   }
+@ CHECK-RELOCATION: ]
+