//===----------------------------------------------------------------------===//
#include "llvm/MC/MCELFObjectWriter.h"
-#include "MCELF.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCELF.h"
#include "llvm/MC/MCELFSymbolFlags.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCFixupKindInfo.h"
//
//===----------------------------------------------------------------------===//
-#include "MCELF.h"
+#include "llvm/MC/MCELF.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCELFSymbolFlags.h"
#include "llvm/MC/MCFixupKindInfo.h"
//===----------------------------------------------------------------------===//
#include "llvm/MC/MCELFStreamer.h"
-
-#include "MCELF.h"
#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCELF.h"
#include "llvm/MC/MCELFSymbolFlags.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
llvm_unreachable("invalid assembler flag!");
}
-void MCELFStreamer::EmitThumbFunc(MCSymbol *Func) {
- // FIXME: Anything needed here to flag the function as thumb?
-
- getAssembler().setIsThumbFunc(Func);
-
- MCSymbolData &SD = getAssembler().getOrCreateSymbolData(*Func);
- SD.setFlags(SD.getFlags() | ELF_Other_ThumbFunc);
-}
-
void MCELFStreamer::EmitAssignment(MCSymbol *Symbol, const MCExpr *Value) {
// TODO: This is exactly the same as WinCOFFStreamer. Consider merging into
// MCObjectStreamer.
this->MCObjectStreamer::FinishImpl();
}
-
-void MCELFStreamer::EmitTCEntry(const MCSymbol &S)
-{
+void MCELFStreamer::EmitTCEntry(const MCSymbol &S) {
// Creates a R_PPC64_TOC relocation
MCObjectStreamer::EmitSymbolValue(&S, 8, 0);
}
return S;
}
+void MCELFStreamer::EmitThumbFunc(MCSymbol *Func) {
+ llvm_unreachable("Generic ELF doesn't support this directive");
+}
+
void MCELFStreamer::EmitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) {
llvm_unreachable("ELF doesn't support this directive");
}
--- /dev/null
+//===- lib/MC/ARMELFStreamer.cpp - ELF Object Output for ARM --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file assembles .s files and emits ARM ELF .o object files. Different
+// from generic ELF streamer in emitting mapping symbols ($a, $t and $d) to
+// delimit regions of data and code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCELFStreamer.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCSectionELF.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCELF.h"
+#include "llvm/MC/MCELFStreamer.h"
+#include "llvm/MC/MCELFSymbolFlags.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCObjectStreamer.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/MCSymbol.h"
+#include "llvm/MC/MCValue.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ELF.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace {
+
+/// Extend the generic ELFStreamer class so that it can emit mapping symbols at
+/// the appropriate points in the object files. These symbols are defined in the
+/// ARM ELF ABI: infocenter.arm.com/help/topic/com.arm.../IHI0044D_aaelf.pdf.
+///
+/// In brief: $a, $t or $d should be emitted at the start of each contiguous
+/// region of ARM code, Thumb code or data in a section. In practice, this
+/// emission does not rely on explicit assembler directives but on inherent
+/// properties of the directives doing the emission (e.g. ".byte" is data, "add
+/// r0, r0, r0" an instruction).
+///
+/// As a result this system is orthogonal to the DataRegion infrastructure used
+/// by MachO. Beware!
+class ARMELFStreamer : public MCELFStreamer {
+public:
+ ARMELFStreamer(MCContext &Context, MCAsmBackend &TAB,
+ raw_ostream &OS, MCCodeEmitter *Emitter, bool IsThumb)
+ : MCELFStreamer(Context, TAB, OS, Emitter),
+ IsThumb(IsThumb), MappingSymbolCounter(0), LastEMS(EMS_None) {
+ }
+
+ ~ARMELFStreamer() {}
+
+ virtual void ChangeSection(const MCSection *Section) {
+ // We have to keep track of the mapping symbol state of any sections we
+ // use. Each one should start off as EMS_None, which is provided as the
+ // default constructor by DenseMap::lookup.
+ LastMappingSymbols[getPreviousSection()] = LastEMS;
+ LastEMS = LastMappingSymbols.lookup(Section);
+
+ MCELFStreamer::ChangeSection(Section);
+ }
+
+ /// This function is the one used to emit instruction data into the ELF
+ /// streamer. We override it to add the appropriate mapping symbol if
+ /// necessary.
+ virtual void EmitInstruction(const MCInst& Inst) {
+ if (IsThumb)
+ EmitThumbMappingSymbol();
+ else
+ EmitARMMappingSymbol();
+
+ MCELFStreamer::EmitInstruction(Inst);
+ }
+
+ /// This is one of the functions used to emit data into an ELF section, so the
+ /// ARM streamer overrides it to add the appropriate mapping symbol ($d) if
+ /// necessary.
+ virtual void EmitBytes(StringRef Data, unsigned AddrSpace) {
+ EmitDataMappingSymbol();
+ MCELFStreamer::EmitBytes(Data, AddrSpace);
+ }
+
+ /// This is one of the functions used to emit data into an ELF section, so the
+ /// ARM streamer overrides it to add the appropriate mapping symbol ($d) if
+ /// necessary.
+ virtual void EmitValueImpl(const MCExpr *Value, unsigned Size,
+ unsigned AddrSpace) {
+ EmitDataMappingSymbol();
+ MCELFStreamer::EmitValueImpl(Value, Size, AddrSpace);
+ }
+
+ virtual void EmitAssemblerFlag(MCAssemblerFlag Flag) {
+ MCELFStreamer::EmitAssemblerFlag(Flag);
+
+ switch (Flag) {
+ case MCAF_SyntaxUnified:
+ return; // no-op here.
+ case MCAF_Code16:
+ IsThumb = true;
+ return; // Change to Thumb mode
+ case MCAF_Code32:
+ IsThumb = false;
+ return; // Change to ARM mode
+ case MCAF_Code64:
+ return;
+ case MCAF_SubsectionsViaSymbols:
+ return;
+ }
+ }
+
+private:
+ enum ElfMappingSymbol {
+ EMS_None,
+ EMS_ARM,
+ EMS_Thumb,
+ EMS_Data
+ };
+
+ void EmitDataMappingSymbol() {
+ if (LastEMS == EMS_Data) return;
+ EmitMappingSymbol("$d");
+ LastEMS = EMS_Data;
+ }
+
+ void EmitThumbMappingSymbol() {
+ if (LastEMS == EMS_Thumb) return;
+ EmitMappingSymbol("$t");
+ LastEMS = EMS_Thumb;
+ }
+
+ void EmitARMMappingSymbol() {
+ if (LastEMS == EMS_ARM) return;
+ EmitMappingSymbol("$a");
+ LastEMS = EMS_ARM;
+ }
+
+ void EmitMappingSymbol(StringRef Name) {
+ MCSymbol *Start = getContext().CreateTempSymbol();
+ EmitLabel(Start);
+
+ StringRef UniqueName = Name.str() + "." + itostr(MappingSymbolCounter++);
+ MCSymbol *Symbol = getContext().GetOrCreateSymbol(UniqueName);
+
+ MCSymbolData &SD = getAssembler().getOrCreateSymbolData(*Symbol);
+ MCELF::SetType(SD, ELF::STT_NOTYPE);
+ MCELF::SetBinding(SD, ELF::STB_LOCAL);
+ SD.setExternal(false);
+ Symbol->setSection(*getCurrentSection());
+
+ const MCExpr *Value = MCSymbolRefExpr::Create(Start, getContext());
+ Symbol->setVariableValue(Value);
+ }
+
+ void EmitThumbFunc(MCSymbol *Func) {
+ // FIXME: Anything needed here to flag the function as thumb?
+
+ getAssembler().setIsThumbFunc(Func);
+
+ MCSymbolData &SD = getAssembler().getOrCreateSymbolData(*Func);
+ SD.setFlags(SD.getFlags() | ELF_Other_ThumbFunc);
+ }
+
+
+ bool IsThumb;
+ int64_t MappingSymbolCounter;
+
+ DenseMap<const MCSection *, ElfMappingSymbol> LastMappingSymbols;
+ ElfMappingSymbol LastEMS;
+
+ /// @}
+};
+}
+
+namespace llvm {
+ MCELFStreamer* createARMELFStreamer(MCContext &Context, MCAsmBackend &TAB,
+ raw_ostream &OS, MCCodeEmitter *Emitter,
+ bool RelaxAll, bool NoExecStack,
+ bool IsThumb) {
+ ARMELFStreamer *S = new ARMELFStreamer(Context, TAB, OS, Emitter, IsThumb);
+ if (RelaxAll)
+ S->getAssembler().setRelaxAll(true);
+ if (NoExecStack)
+ S->getAssembler().setNoExecStack(true);
+ return S;
+ }
+
+}
+
+
--- /dev/null
+//===-- ARMELFStreamer.h - ELF Streamer for ARM ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements ELF streamer information for the ARM backend.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ARM_ELF_STREAMER_H
+#define ARM_ELF_STREAMER_H
+
+#include "llvm/MC/MCELFStreamer.h"
+
+namespace llvm {
+
+ MCELFStreamer* createARMELFStreamer(MCContext &Context, MCAsmBackend &TAB,
+ raw_ostream &OS, MCCodeEmitter *Emitter,
+ bool RelaxAll, bool NoExecStack,
+ bool IsThumb);
+}
+
+#endif // ARM_ELF_STREAMER_H
//===----------------------------------------------------------------------===//
#include "ARMMCTargetDesc.h"
+#include "ARMELFStreamer.h"
+#include "ARMMCAsmInfo.h"
#include "ARMBaseInfo.h"
#include "ARMMCAsmInfo.h"
#include "InstPrinter/ARMInstPrinter.h"
llvm_unreachable("ARM does not support Windows COFF format");
}
- return createELFStreamer(Ctx, MAB, OS, Emitter, false, NoExecStack);
+ return createARMELFStreamer(Ctx, MAB, OS, Emitter, false, NoExecStack,
+ TheTriple.getArch() == Triple::thumb);
}
static MCInstPrinter *createARMMCInstPrinter(const Target &T,
add_llvm_library(LLVMARMDesc
ARMAsmBackend.cpp
ARMELFObjectWriter.cpp
+ ARMELFStreamer.cpp
ARMMCAsmInfo.cpp
ARMMCCodeEmitter.cpp
ARMMCExpr.cpp
; OBJ: Relocation 0
; OBJ-NEXT: 'r_offset', 0x00000004
-; OBJ-NEXT: 'r_sym', 0x000007
+; OBJ-NEXT: 'r_sym', 0x000009
; OBJ-NEXT: 'r_type', 0x2b
; OBJ: Relocation 1
; OBJ: # Relocation 2
; OBJ-NEXT: 'r_offset', 0x0000000c
-; OBJ-NEXT: 'r_sym', 0x000008
+; OBJ-NEXT: 'r_sym', 0x00000a
; OBJ-NEXT: 'r_type', 0x1c
}
--- /dev/null
+;; RUN: llc -O0 -mtriple=armv7-linux-gnueabi -filetype=obj %s -o - | \
+;; RUN: elf-dump | FileCheck -check-prefix=ARM %s
+
+;; RUN: llc -O0 -mtriple=thumbv7-linux-gnueabi -filetype=obj %s -o - | \
+;; RUN: elf-dump --dump-section-data | FileCheck -check-prefix=TMB %s
+
+;; Ensure that if a jump table is generated that it has Mapping Symbols
+;; marking the data-in-code region.
+
+define void @foo(i32* %ptr) nounwind ssp {
+ %tmp = load i32* %ptr, align 4
+ switch i32 %tmp, label %default [
+ i32 11, label %bb0
+ i32 10, label %bb1
+ i32 8, label %bb2
+ i32 4, label %bb3
+ i32 2, label %bb4
+ i32 6, label %bb5
+ i32 9, label %bb6
+ i32 15, label %bb7
+ i32 1, label %bb8
+ i32 3, label %bb9
+ i32 5, label %bb10
+ i32 30, label %bb11
+ i32 31, label %bb12
+ i32 13, label %bb13
+ i32 14, label %bb14
+ i32 20, label %bb15
+ i32 19, label %bb16
+ i32 17, label %bb17
+ i32 18, label %bb18
+ i32 21, label %bb19
+ i32 22, label %bb20
+ i32 16, label %bb21
+ i32 24, label %bb22
+ i32 25, label %bb23
+ i32 26, label %bb24
+ i32 27, label %bb25
+ i32 28, label %bb26
+ i32 23, label %bb27
+ i32 12, label %bb28
+ ]
+
+default:
+ br label %exit
+bb0:
+ br label %exit
+bb1:
+ br label %exit
+bb2:
+ br label %exit
+bb3:
+ br label %exit
+bb4:
+ br label %exit
+bb5:
+ br label %exit
+bb6:
+ br label %exit
+bb7:
+ br label %exit
+bb8:
+ br label %exit
+bb9:
+ br label %exit
+bb10:
+ br label %exit
+bb11:
+ br label %exit
+bb12:
+ br label %exit
+bb13:
+ br label %exit
+bb14:
+ br label %exit
+bb15:
+ br label %exit
+bb16:
+ br label %exit
+bb17:
+ br label %exit
+bb18:
+ br label %exit
+bb19:
+ br label %exit
+bb20:
+ br label %exit
+bb21:
+ br label %exit
+bb22:
+ br label %exit
+bb23:
+ br label %exit
+bb24:
+ br label %exit
+bb25:
+ br label %exit
+bb26:
+ br label %exit
+bb27:
+ br label %exit
+bb28:
+ br label %exit
+
+
+exit:
+
+ ret void
+}
+
+;; ARM: # Symbol 2
+;; ARM-NEXT: $a
+;; ARM-NEXT: 'st_value', 0x00000000
+;; ARM-NEXT: 'st_size', 0x00000000
+;; ARM-NEXT: 'st_bind', 0x0
+;; ARM-NEXT: 'st_type', 0x0
+;; ARM-NEXT: 'st_other'
+;; ARM-NEXT: 'st_shndx', [[MIXED_SECT:0x[0-9a-f]+]]
+
+;; ARM: # Symbol 3
+;; ARM-NEXT: $a
+;; ARM-NEXT: 'st_value', 0x000000ac
+;; ARM-NEXT: 'st_size', 0x00000000
+;; ARM-NEXT: 'st_bind', 0x0
+;; ARM-NEXT: 'st_type', 0x0
+;; ARM-NEXT: 'st_other'
+;; ARM-NEXT: 'st_shndx', [[MIXED_SECT]]
+
+;; ARM: # Symbol 4
+;; ARM-NEXT: $d
+;; ARM-NEXT: 'st_value', 0x00000000
+;; ARM-NEXT: 'st_size', 0x00000000
+;; ARM-NEXT: 'st_bind', 0x0
+;; ARM-NEXT: 'st_type', 0x0
+
+;; ARM: # Symbol 5
+;; ARM-NEXT: $d
+;; ARM-NEXT: 'st_value', 0x00000030
+;; ARM-NEXT: 'st_size', 0x00000000
+;; ARM-NEXT: 'st_bind', 0x0
+;; ARM-NEXT: 'st_type', 0x0
+;; ARM-NEXT: 'st_other'
+;; ARM-NEXT: 'st_shndx', [[MIXED_SECT]]
+
+;; ARM-NOT: ${{[atd]}}
+
+;; TMB: # Symbol 3
+;; TMB-NEXT: $d
+;; TMB-NEXT: 'st_value', 0x00000016
+;; TMB-NEXT: 'st_size', 0x00000000
+;; TMB-NEXT: 'st_bind', 0x0
+;; TMB-NEXT: 'st_type', 0x0
+;; TMB-NEXT: 'st_other'
+;; TMB-NEXT: 'st_shndx', [[MIXED_SECT:0x[0-9a-f]+]]
+
+;; TMB: # Symbol 4
+;; TMB-NEXT: $t
+;; TMB-NEXT: 'st_value', 0x00000000
+;; TMB-NEXT: 'st_size', 0x00000000
+;; TMB-NEXT: 'st_bind', 0x0
+;; TMB-NEXT: 'st_type', 0x0
+;; TMB-NEXT: 'st_other'
+;; TMB-NEXT: 'st_shndx', [[MIXED_SECT]]
+
+;; TMB: # Symbol 5
+;; TMB-NEXT: $t
+;; TMB-NEXT: 'st_value', 0x00000036
+;; TMB-NEXT: 'st_size', 0x00000000
+;; TMB-NEXT: 'st_bind', 0x0
+;; TMB-NEXT: 'st_type', 0x0
+;; TMB-NEXT: 'st_other'
+;; TMB-NEXT: 'st_shndx', [[MIXED_SECT]]
+
+
+;; TMB-NOT: ${{[atd]}}
+
;; OBJ: Relocation 1
;; OBJ-NEXT: 'r_offset',
-;; OBJ-NEXT: 'r_sym', 0x000002
+;; OBJ-NEXT: 'r_sym', 0x000007
;; OBJ-NEXT: 'r_type', 0x2b
-;; OBJ: Symbol 2
+;; OBJ: Symbol 7
;; OBJ-NEXT: '_MergedGlobals'
;; OBJ-NEXT: 'st_value', 0x00000010
declare void @exit(i32) noreturn nounwind
;; OBJ: Relocation 0
-;; OBJ-NEXT: 'r_offset',
-;; OBJ-NEXT: 'r_sym', 0x000002
+;; OBJ-NEXT: 'r_offset',
+;; OBJ-NEXT: 'r_sym', 0x000005
;; OBJ-NEXT: 'r_type', 0x2b
-;; OBJ: Symbol 2
+;; OBJ: Symbol 5
;; OBJ-NEXT: '.L.str'
declare void @exit(i32) noreturn nounwind
;; OBJ: Relocation 1
-;; OBJ-NEXT: 'r_offset',
-;; OBJ-NEXT: 'r_sym', 0x00000c
+;; OBJ-NEXT: 'r_offset',
+;; OBJ-NEXT: 'r_sym', 0x000010
;; OBJ-NEXT: 'r_type', 0x2b
-;; OBJ: Symbol 12
+;; OBJ: Symbol 16
;; OBJ-NEXT: 'vtable'
// OBJ: .rel.text
// OBJ: 'r_offset', 0x00000000
-// OBJ-NEXT: 'r_sym', 0x000004
+// OBJ-NEXT: 'r_sym', 0x000005
// OBJ-NEXT: 'r_type', 0x1d
// OBJ: 'r_offset', 0x00000004
-// OBJ-NEXT: 'r_sym', 0x000004
+// OBJ-NEXT: 'r_sym', 0x000005
// OBJ-NEXT: 'r_type', 0x1c
// OBJ: 'r_offset', 0x00000008
-// OBJ-NEXT: 'r_sym', 0x000004
+// OBJ-NEXT: 'r_sym', 0x000005
// OBJ-NEXT: 'r_type', 0x1c
// OBJ: 'r_offset', 0x0000000c
-// OBJ-NEXT: 'r_sym', 0x000004
+// OBJ-NEXT: 'r_sym', 0x000005
// OBJ-NEXT: 'r_type', 0x1d
// OBJ: 'r_offset', 0x00000010
-// OBJ-NEXT: 'r_sym', 0x000004
+// OBJ-NEXT: 'r_sym', 0x000005
// OBJ-NEXT: 'r_type', 0x1d
// OBJ: .symtab
-// OBJ: Symbol 4
+// OBJ: Symbol 5
// OBJ-NEXT: some_label
; 00000008 0000070a R_ARM_THM_CALL 00000001 foo
; CHECK: Relocation 0
; CHECK-NEXT: 'r_offset', 0x00000008
-; CHECK-NEXT: 'r_sym', 0x000007
+; CHECK-NEXT: 'r_sym', 0x000009
; CHECK-NEXT: 'r_type', 0x0a
; make sure foo is thumb function: bit 0 = 1
-; CHECK: Symbol 7
+; CHECK: Symbol 9
; CHECK-NEXT: 'foo'
; CHECK-NEXT: 'st_value', 0x00000001
bx lr
@@ make sure foo is thumb function: bit 0 = 1 (st_value)
-@CHECK: Symbol 4
+@CHECK: Symbol 5
@CHECK-NEXT: 'st_name', 0x00000001
@CHECK-NEXT: 'st_value', 0x00000001
@CHECK-NEXT: 'st_size', 0x00000000
--- /dev/null
+@ RUN: llvm-mc -triple=arm-linux-gnueabi -filetype=obj < %s | llvm-objdump -t - | FileCheck %s
+
+ .text
+@ $a at 0x0000
+ add r0, r0, r0
+@ $d at 0x0004
+ .word 42
+ .thumb
+@ $t at 0x0008
+ adds r0, r0, r0
+ adds r0, r0, r0
+@ $a at 0x000c
+ .arm
+ add r0, r0, r0
+@ $t at 0x0010
+ .thumb
+ adds r0, r0, r0
+@ $d at 0x0012
+ .ascii "012"
+ .byte 1
+ .byte 2
+ .byte 3
+@ $a at 0x0018
+ .arm
+ add r0, r0, r0
+
+@ CHECK: 00000000 .text 00000000 $a
+@ CHECK-NEXT: 0000000c .text 00000000 $a
+@ CHECK-NEXT: 00000018 .text 00000000 $a
+@ CHECK-NEXT: 00000004 .text 00000000 $d
+@ CHECK-NEXT: 00000012 .text 00000000 $d
+@ CHECK-NEXT: 00000008 .text 00000000 $t
+@ CHECK-NEXT: 00000010 .text 00000000 $t
--- /dev/null
+@ RUN: llvm-mc -triple=arm-linux-gnueabi -filetype=obj < %s | llvm-objdump -t - | FileCheck %s
+
+ .text
+ add r0, r0, r0
+
+@ .wibble should *not* inherit .text's mapping symbol. It's a completely different section.
+ .section .wibble
+ add r0, r0, r0
+
+@ A section should be able to start with a $t
+ .section .starts_thumb
+ .thumb
+ adds r0, r0, r0
+
+@ A setion should be able to start with a $d
+ .section .starts_data
+ .word 42
+
+@ Changing back to .text should not emit a redundant $a
+ .text
+ .arm
+ add r0, r0, r0
+
+@ With all those constraints, we want:
+@ + .text to have $a at 0 and no others
+@ + .wibble to have $a at 0
+@ + .starts_thumb to have $t at 0
+@ + .starts_data to have $d at 0
+
+@ CHECK: 00000000 .text 00000000 $a
+@ CHECK-NEXT: 00000000 .wibble 00000000 $a
+@ CHECK-NEXT: 00000000 .starts_data 00000000 $d
+@ CHECK-NEXT: 00000000 .starts_thumb 00000000 $t
+@ CHECK-NOT: ${{[adt]}}
+
--- /dev/null
+@ RUN: llvm-mc -triple=arm-linux-gnueabi -filetype=obj < %s | llvm-objdump -t - | FileCheck %s
+
+@ Implementation-detail test (unfortunately): values that are relocated do not
+@ go via MCStreamer::EmitBytes; make sure they still emit a mapping symbol.
+ add r0, r0, r0
+ .word somewhere
+ add r0, r0, r0
+
+@ CHECK: 00000000 .text 00000000 $a
+@ CHECK-NEXT: 00000008 .text 00000000 $a
+@ CHECK-NEXT: 00000004 .text 00000000 $d