MCWinCOFFStreamer.cpp revision 360784
1//===- llvm/MC/MCWinCOFFStreamer.cpp --------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains an implementation of a Windows COFF object file streamer.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/ADT/SmallString.h"
14#include "llvm/ADT/SmallVector.h"
15#include "llvm/ADT/Triple.h"
16#include "llvm/ADT/Twine.h"
17#include "llvm/BinaryFormat/COFF.h"
18#include "llvm/MC/MCAsmBackend.h"
19#include "llvm/MC/MCAssembler.h"
20#include "llvm/MC/MCCodeEmitter.h"
21#include "llvm/MC/MCContext.h"
22#include "llvm/MC/MCExpr.h"
23#include "llvm/MC/MCFixup.h"
24#include "llvm/MC/MCFragment.h"
25#include "llvm/MC/MCObjectFileInfo.h"
26#include "llvm/MC/MCObjectStreamer.h"
27#include "llvm/MC/MCObjectWriter.h"
28#include "llvm/MC/MCSection.h"
29#include "llvm/MC/MCSymbolCOFF.h"
30#include "llvm/MC/MCWinCOFFStreamer.h"
31#include "llvm/Support/Casting.h"
32#include "llvm/Support/ErrorHandling.h"
33#include "llvm/Support/MathExtras.h"
34#include "llvm/Support/SMLoc.h"
35#include "llvm/Support/raw_ostream.h"
36#include <algorithm>
37#include <cassert>
38#include <cstdint>
39
40using namespace llvm;
41
42#define DEBUG_TYPE "WinCOFFStreamer"
43
44MCWinCOFFStreamer::MCWinCOFFStreamer(MCContext &Context,
45                                     std::unique_ptr<MCAsmBackend> MAB,
46                                     std::unique_ptr<MCCodeEmitter> CE,
47                                     std::unique_ptr<MCObjectWriter> OW)
48    : MCObjectStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)),
49      CurSymbol(nullptr) {}
50
51void MCWinCOFFStreamer::EmitInstToData(const MCInst &Inst,
52                                       const MCSubtargetInfo &STI) {
53  MCDataFragment *DF = getOrCreateDataFragment();
54
55  SmallVector<MCFixup, 4> Fixups;
56  SmallString<256> Code;
57  raw_svector_ostream VecOS(Code);
58  getAssembler().getEmitter().encodeInstruction(Inst, VecOS, Fixups, STI);
59
60  // Add the fixups and data.
61  for (unsigned i = 0, e = Fixups.size(); i != e; ++i) {
62    Fixups[i].setOffset(Fixups[i].getOffset() + DF->getContents().size());
63    DF->getFixups().push_back(Fixups[i]);
64  }
65  DF->setHasInstructions(STI);
66  DF->getContents().append(Code.begin(), Code.end());
67}
68
69void MCWinCOFFStreamer::InitSections(bool NoExecStack) {
70  // FIXME: this is identical to the ELF one.
71  // This emulates the same behavior of GNU as. This makes it easier
72  // to compare the output as the major sections are in the same order.
73  SwitchSection(getContext().getObjectFileInfo()->getTextSection());
74  EmitCodeAlignment(4);
75
76  SwitchSection(getContext().getObjectFileInfo()->getDataSection());
77  EmitCodeAlignment(4);
78
79  SwitchSection(getContext().getObjectFileInfo()->getBSSSection());
80  EmitCodeAlignment(4);
81
82  SwitchSection(getContext().getObjectFileInfo()->getTextSection());
83}
84
85void MCWinCOFFStreamer::EmitLabel(MCSymbol *S, SMLoc Loc) {
86  auto *Symbol = cast<MCSymbolCOFF>(S);
87  MCObjectStreamer::EmitLabel(Symbol, Loc);
88}
89
90void MCWinCOFFStreamer::EmitAssemblerFlag(MCAssemblerFlag Flag) {
91  // Let the target do whatever target specific stuff it needs to do.
92  getAssembler().getBackend().handleAssemblerFlag(Flag);
93
94  switch (Flag) {
95  // None of these require COFF specific handling.
96  case MCAF_SyntaxUnified:
97  case MCAF_Code16:
98  case MCAF_Code32:
99  case MCAF_Code64:
100    break;
101  case MCAF_SubsectionsViaSymbols:
102    llvm_unreachable("COFF doesn't support .subsections_via_symbols");
103  }
104}
105
106void MCWinCOFFStreamer::EmitThumbFunc(MCSymbol *Func) {
107  llvm_unreachable("not implemented");
108}
109
110bool MCWinCOFFStreamer::EmitSymbolAttribute(MCSymbol *S,
111                                            MCSymbolAttr Attribute) {
112  auto *Symbol = cast<MCSymbolCOFF>(S);
113  getAssembler().registerSymbol(*Symbol);
114
115  switch (Attribute) {
116  default: return false;
117  case MCSA_WeakReference:
118  case MCSA_Weak:
119    Symbol->setIsWeakExternal();
120    Symbol->setExternal(true);
121    break;
122  case MCSA_Global:
123    Symbol->setExternal(true);
124    break;
125  case MCSA_AltEntry:
126    llvm_unreachable("COFF doesn't support the .alt_entry attribute");
127  }
128
129  return true;
130}
131
132void MCWinCOFFStreamer::EmitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) {
133  llvm_unreachable("not implemented");
134}
135
136void MCWinCOFFStreamer::BeginCOFFSymbolDef(MCSymbol const *S) {
137  auto *Symbol = cast<MCSymbolCOFF>(S);
138  if (CurSymbol)
139    Error("starting a new symbol definition without completing the "
140          "previous one");
141  CurSymbol = Symbol;
142}
143
144void MCWinCOFFStreamer::EmitCOFFSymbolStorageClass(int StorageClass) {
145  if (!CurSymbol) {
146    Error("storage class specified outside of symbol definition");
147    return;
148  }
149
150  if (StorageClass & ~COFF::SSC_Invalid) {
151    Error("storage class value '" + Twine(StorageClass) +
152               "' out of range");
153    return;
154  }
155
156  getAssembler().registerSymbol(*CurSymbol);
157  cast<MCSymbolCOFF>(CurSymbol)->setClass((uint16_t)StorageClass);
158}
159
160void MCWinCOFFStreamer::EmitCOFFSymbolType(int Type) {
161  if (!CurSymbol) {
162    Error("symbol type specified outside of a symbol definition");
163    return;
164  }
165
166  if (Type & ~0xffff) {
167    Error("type value '" + Twine(Type) + "' out of range");
168    return;
169  }
170
171  getAssembler().registerSymbol(*CurSymbol);
172  cast<MCSymbolCOFF>(CurSymbol)->setType((uint16_t)Type);
173}
174
175void MCWinCOFFStreamer::EndCOFFSymbolDef() {
176  if (!CurSymbol)
177    Error("ending symbol definition without starting one");
178  CurSymbol = nullptr;
179}
180
181void MCWinCOFFStreamer::EmitCOFFSafeSEH(MCSymbol const *Symbol) {
182  // SafeSEH is a feature specific to 32-bit x86.  It does not exist (and is
183  // unnecessary) on all platforms which use table-based exception dispatch.
184  if (getContext().getObjectFileInfo()->getTargetTriple().getArch() !=
185      Triple::x86)
186    return;
187
188  const MCSymbolCOFF *CSymbol = cast<MCSymbolCOFF>(Symbol);
189  if (CSymbol->isSafeSEH())
190    return;
191
192  MCSection *SXData = getContext().getObjectFileInfo()->getSXDataSection();
193  getAssembler().registerSection(*SXData);
194  if (SXData->getAlignment() < 4)
195    SXData->setAlignment(Align(4));
196
197  new MCSymbolIdFragment(Symbol, SXData);
198
199  getAssembler().registerSymbol(*Symbol);
200  CSymbol->setIsSafeSEH();
201
202  // The Microsoft linker requires that the symbol type of a handler be
203  // function. Go ahead and oblige it here.
204  CSymbol->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION
205                   << COFF::SCT_COMPLEX_TYPE_SHIFT);
206}
207
208void MCWinCOFFStreamer::EmitCOFFSymbolIndex(MCSymbol const *Symbol) {
209  MCSection *Sec = getCurrentSectionOnly();
210  getAssembler().registerSection(*Sec);
211  if (Sec->getAlignment() < 4)
212    Sec->setAlignment(Align(4));
213
214  new MCSymbolIdFragment(Symbol, getCurrentSectionOnly());
215
216  getAssembler().registerSymbol(*Symbol);
217}
218
219void MCWinCOFFStreamer::EmitCOFFSectionIndex(const MCSymbol *Symbol) {
220  visitUsedSymbol(*Symbol);
221  MCDataFragment *DF = getOrCreateDataFragment();
222  const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(Symbol, getContext());
223  MCFixup Fixup = MCFixup::create(DF->getContents().size(), SRE, FK_SecRel_2);
224  DF->getFixups().push_back(Fixup);
225  DF->getContents().resize(DF->getContents().size() + 2, 0);
226}
227
228void MCWinCOFFStreamer::EmitCOFFSecRel32(const MCSymbol *Symbol,
229                                         uint64_t Offset) {
230  visitUsedSymbol(*Symbol);
231  MCDataFragment *DF = getOrCreateDataFragment();
232  // Create Symbol A for the relocation relative reference.
233  const MCExpr *MCE = MCSymbolRefExpr::create(Symbol, getContext());
234  // Add the constant offset, if given.
235  if (Offset)
236    MCE = MCBinaryExpr::createAdd(
237        MCE, MCConstantExpr::create(Offset, getContext()), getContext());
238  // Build the secrel32 relocation.
239  MCFixup Fixup = MCFixup::create(DF->getContents().size(), MCE, FK_SecRel_4);
240  // Record the relocation.
241  DF->getFixups().push_back(Fixup);
242  // Emit 4 bytes (zeros) to the object file.
243  DF->getContents().resize(DF->getContents().size() + 4, 0);
244}
245
246void MCWinCOFFStreamer::EmitCOFFImgRel32(const MCSymbol *Symbol,
247                                         int64_t Offset) {
248  visitUsedSymbol(*Symbol);
249  MCDataFragment *DF = getOrCreateDataFragment();
250  // Create Symbol A for the relocation relative reference.
251  const MCExpr *MCE = MCSymbolRefExpr::create(
252      Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32, getContext());
253  // Add the constant offset, if given.
254  if (Offset)
255    MCE = MCBinaryExpr::createAdd(
256        MCE, MCConstantExpr::create(Offset, getContext()), getContext());
257  // Build the imgrel relocation.
258  MCFixup Fixup = MCFixup::create(DF->getContents().size(), MCE, FK_Data_4);
259  // Record the relocation.
260  DF->getFixups().push_back(Fixup);
261  // Emit 4 bytes (zeros) to the object file.
262  DF->getContents().resize(DF->getContents().size() + 4, 0);
263}
264
265void MCWinCOFFStreamer::EmitCommonSymbol(MCSymbol *S, uint64_t Size,
266                                         unsigned ByteAlignment) {
267  auto *Symbol = cast<MCSymbolCOFF>(S);
268
269  const Triple &T = getContext().getObjectFileInfo()->getTargetTriple();
270  if (T.isWindowsMSVCEnvironment()) {
271    if (ByteAlignment > 32)
272      report_fatal_error("alignment is limited to 32-bytes");
273
274    // Round size up to alignment so that we will honor the alignment request.
275    Size = std::max(Size, static_cast<uint64_t>(ByteAlignment));
276  }
277
278  getAssembler().registerSymbol(*Symbol);
279  Symbol->setExternal(true);
280  Symbol->setCommon(Size, ByteAlignment);
281
282  if (!T.isWindowsMSVCEnvironment() && ByteAlignment > 1) {
283    SmallString<128> Directive;
284    raw_svector_ostream OS(Directive);
285    const MCObjectFileInfo *MFI = getContext().getObjectFileInfo();
286
287    OS << " -aligncomm:\"" << Symbol->getName() << "\","
288       << Log2_32_Ceil(ByteAlignment);
289
290    PushSection();
291    SwitchSection(MFI->getDrectveSection());
292    EmitBytes(Directive);
293    PopSection();
294  }
295}
296
297void MCWinCOFFStreamer::EmitLocalCommonSymbol(MCSymbol *S, uint64_t Size,
298                                              unsigned ByteAlignment) {
299  auto *Symbol = cast<MCSymbolCOFF>(S);
300
301  MCSection *Section = getContext().getObjectFileInfo()->getBSSSection();
302  PushSection();
303  SwitchSection(Section);
304  EmitValueToAlignment(ByteAlignment, 0, 1, 0);
305  EmitLabel(Symbol);
306  Symbol->setExternal(false);
307  EmitZeros(Size);
308  PopSection();
309}
310
311void MCWinCOFFStreamer::EmitZerofill(MCSection *Section, MCSymbol *Symbol,
312                                     uint64_t Size, unsigned ByteAlignment,
313                                     SMLoc Loc) {
314  llvm_unreachable("not implemented");
315}
316
317void MCWinCOFFStreamer::EmitTBSSSymbol(MCSection *Section, MCSymbol *Symbol,
318                                       uint64_t Size, unsigned ByteAlignment) {
319  llvm_unreachable("not implemented");
320}
321
322// TODO: Implement this if you want to emit .comment section in COFF obj files.
323void MCWinCOFFStreamer::EmitIdent(StringRef IdentString) {
324  llvm_unreachable("not implemented");
325}
326
327void MCWinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) {
328  llvm_unreachable("not implemented");
329}
330
331void MCWinCOFFStreamer::FinishImpl() {
332  MCObjectStreamer::FinishImpl();
333}
334
335void MCWinCOFFStreamer::Error(const Twine &Msg) const {
336  getContext().reportError(SMLoc(), Msg);
337}
338