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/MC/MCWinCOFFStreamer.h" |
14 | #include "llvm/ADT/SmallString.h" |
15 | #include "llvm/ADT/SmallVector.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/MCSectionCOFF.h" |
29 | #include "llvm/MC/MCSymbolCOFF.h" |
30 | #include "llvm/MC/MCTargetOptions.h" |
31 | #include "llvm/MC/MCWinCOFFObjectWriter.h" |
32 | #include "llvm/Support/Casting.h" |
33 | #include "llvm/Support/ErrorHandling.h" |
34 | #include "llvm/Support/MathExtras.h" |
35 | #include "llvm/Support/SMLoc.h" |
36 | #include "llvm/Support/raw_ostream.h" |
37 | #include "llvm/TargetParser/Triple.h" |
38 | #include <algorithm> |
39 | #include <cstdint> |
40 | |
41 | using namespace llvm; |
42 | |
43 | #define DEBUG_TYPE "WinCOFFStreamer" |
44 | |
45 | MCWinCOFFStreamer::MCWinCOFFStreamer(MCContext &Context, |
46 | std::unique_ptr<MCAsmBackend> MAB, |
47 | std::unique_ptr<MCCodeEmitter> CE, |
48 | std::unique_ptr<MCObjectWriter> OW) |
49 | : MCObjectStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)), |
50 | CurSymbol(nullptr) { |
51 | auto *TO = Context.getTargetOptions(); |
52 | if (TO && TO->MCIncrementalLinkerCompatible) |
53 | getWriter().setIncrementalLinkerCompatible(true); |
54 | } |
55 | |
56 | WinCOFFObjectWriter &MCWinCOFFStreamer::getWriter() { |
57 | return static_cast<WinCOFFObjectWriter &>(getAssembler().getWriter()); |
58 | } |
59 | |
60 | void MCWinCOFFStreamer::emitInstToData(const MCInst &Inst, |
61 | const MCSubtargetInfo &STI) { |
62 | MCDataFragment *DF = getOrCreateDataFragment(); |
63 | |
64 | SmallVector<MCFixup, 4> Fixups; |
65 | SmallString<256> Code; |
66 | getAssembler().getEmitter().encodeInstruction(Inst, CB&: Code, Fixups, STI); |
67 | |
68 | // Add the fixups and data. |
69 | for (unsigned i = 0, e = Fixups.size(); i != e; ++i) { |
70 | Fixups[i].setOffset(Fixups[i].getOffset() + DF->getContents().size()); |
71 | DF->getFixups().push_back(Elt: Fixups[i]); |
72 | } |
73 | DF->setHasInstructions(STI); |
74 | DF->getContents().append(in_start: Code.begin(), in_end: Code.end()); |
75 | } |
76 | |
77 | void MCWinCOFFStreamer::initSections(bool NoExecStack, |
78 | const MCSubtargetInfo &STI) { |
79 | // FIXME: this is identical to the ELF one. |
80 | // This emulates the same behavior of GNU as. This makes it easier |
81 | // to compare the output as the major sections are in the same order. |
82 | switchSection(Section: getContext().getObjectFileInfo()->getTextSection()); |
83 | emitCodeAlignment(ByteAlignment: Align(4), STI: &STI); |
84 | |
85 | switchSection(Section: getContext().getObjectFileInfo()->getDataSection()); |
86 | emitCodeAlignment(ByteAlignment: Align(4), STI: &STI); |
87 | |
88 | switchSection(Section: getContext().getObjectFileInfo()->getBSSSection()); |
89 | emitCodeAlignment(ByteAlignment: Align(4), STI: &STI); |
90 | |
91 | switchSection(Section: getContext().getObjectFileInfo()->getTextSection()); |
92 | } |
93 | |
94 | void MCWinCOFFStreamer::changeSection(MCSection *Section, uint32_t Subsection) { |
95 | changeSectionImpl(Section, Subsection); |
96 | // Ensure that the first and the second symbols relative to the section are |
97 | // the section symbol and the COMDAT symbol. |
98 | getAssembler().registerSymbol(Symbol: *Section->getBeginSymbol()); |
99 | if (auto *Sym = cast<MCSectionCOFF>(Val: Section)->getCOMDATSymbol()) |
100 | getAssembler().registerSymbol(Symbol: *Sym); |
101 | } |
102 | |
103 | void MCWinCOFFStreamer::emitLabel(MCSymbol *S, SMLoc Loc) { |
104 | auto *Symbol = cast<MCSymbolCOFF>(Val: S); |
105 | MCObjectStreamer::emitLabel(Symbol, Loc); |
106 | } |
107 | |
108 | void MCWinCOFFStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { |
109 | // Let the target do whatever target specific stuff it needs to do. |
110 | getAssembler().getBackend().handleAssemblerFlag(Flag); |
111 | |
112 | switch (Flag) { |
113 | // None of these require COFF specific handling. |
114 | case MCAF_SyntaxUnified: |
115 | case MCAF_Code16: |
116 | case MCAF_Code32: |
117 | case MCAF_Code64: |
118 | break; |
119 | case MCAF_SubsectionsViaSymbols: |
120 | llvm_unreachable("COFF doesn't support .subsections_via_symbols" ); |
121 | } |
122 | } |
123 | |
124 | void MCWinCOFFStreamer::emitThumbFunc(MCSymbol *Func) { |
125 | llvm_unreachable("not implemented" ); |
126 | } |
127 | |
128 | bool MCWinCOFFStreamer::emitSymbolAttribute(MCSymbol *S, |
129 | MCSymbolAttr Attribute) { |
130 | auto *Symbol = cast<MCSymbolCOFF>(Val: S); |
131 | getAssembler().registerSymbol(Symbol: *Symbol); |
132 | |
133 | switch (Attribute) { |
134 | default: return false; |
135 | case MCSA_WeakReference: |
136 | case MCSA_Weak: |
137 | Symbol->setWeakExternalCharacteristics(COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS); |
138 | Symbol->setExternal(true); |
139 | break; |
140 | case MCSA_WeakAntiDep: |
141 | Symbol->setWeakExternalCharacteristics(COFF::IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY); |
142 | Symbol->setExternal(true); |
143 | Symbol->setIsWeakExternal(true); |
144 | break; |
145 | case MCSA_Global: |
146 | Symbol->setExternal(true); |
147 | break; |
148 | case MCSA_AltEntry: |
149 | llvm_unreachable("COFF doesn't support the .alt_entry attribute" ); |
150 | } |
151 | |
152 | return true; |
153 | } |
154 | |
155 | void MCWinCOFFStreamer::emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) { |
156 | llvm_unreachable("not implemented" ); |
157 | } |
158 | |
159 | void MCWinCOFFStreamer::beginCOFFSymbolDef(MCSymbol const *S) { |
160 | auto *Symbol = cast<MCSymbolCOFF>(Val: S); |
161 | if (CurSymbol) |
162 | Error(Msg: "starting a new symbol definition without completing the " |
163 | "previous one" ); |
164 | CurSymbol = Symbol; |
165 | } |
166 | |
167 | void MCWinCOFFStreamer::emitCOFFSymbolStorageClass(int StorageClass) { |
168 | if (!CurSymbol) { |
169 | Error(Msg: "storage class specified outside of symbol definition" ); |
170 | return; |
171 | } |
172 | |
173 | if (StorageClass & ~COFF::SSC_Invalid) { |
174 | Error(Msg: "storage class value '" + Twine(StorageClass) + |
175 | "' out of range" ); |
176 | return; |
177 | } |
178 | |
179 | getAssembler().registerSymbol(Symbol: *CurSymbol); |
180 | cast<MCSymbolCOFF>(Val: CurSymbol)->setClass((uint16_t)StorageClass); |
181 | } |
182 | |
183 | void MCWinCOFFStreamer::emitCOFFSymbolType(int Type) { |
184 | if (!CurSymbol) { |
185 | Error(Msg: "symbol type specified outside of a symbol definition" ); |
186 | return; |
187 | } |
188 | |
189 | if (Type & ~0xffff) { |
190 | Error(Msg: "type value '" + Twine(Type) + "' out of range" ); |
191 | return; |
192 | } |
193 | |
194 | getAssembler().registerSymbol(Symbol: *CurSymbol); |
195 | cast<MCSymbolCOFF>(Val: CurSymbol)->setType((uint16_t)Type); |
196 | } |
197 | |
198 | void MCWinCOFFStreamer::endCOFFSymbolDef() { |
199 | if (!CurSymbol) |
200 | Error(Msg: "ending symbol definition without starting one" ); |
201 | CurSymbol = nullptr; |
202 | } |
203 | |
204 | void MCWinCOFFStreamer::emitCOFFSafeSEH(MCSymbol const *Symbol) { |
205 | // SafeSEH is a feature specific to 32-bit x86. It does not exist (and is |
206 | // unnecessary) on all platforms which use table-based exception dispatch. |
207 | if (getContext().getTargetTriple().getArch() != Triple::x86) |
208 | return; |
209 | |
210 | const MCSymbolCOFF *CSymbol = cast<MCSymbolCOFF>(Val: Symbol); |
211 | if (CSymbol->isSafeSEH()) |
212 | return; |
213 | |
214 | MCSection *SXData = getContext().getObjectFileInfo()->getSXDataSection(); |
215 | changeSection(Section: SXData); |
216 | SXData->ensureMinAlignment(MinAlignment: Align(4)); |
217 | |
218 | insert(F: getContext().allocFragment<MCSymbolIdFragment>(args&: Symbol)); |
219 | getAssembler().registerSymbol(Symbol: *Symbol); |
220 | CSymbol->setIsSafeSEH(); |
221 | |
222 | // The Microsoft linker requires that the symbol type of a handler be |
223 | // function. Go ahead and oblige it here. |
224 | CSymbol->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION |
225 | << COFF::SCT_COMPLEX_TYPE_SHIFT); |
226 | } |
227 | |
228 | void MCWinCOFFStreamer::emitCOFFSymbolIndex(MCSymbol const *Symbol) { |
229 | MCSection *Sec = getCurrentSectionOnly(); |
230 | Sec->ensureMinAlignment(MinAlignment: Align(4)); |
231 | |
232 | insert(F: getContext().allocFragment<MCSymbolIdFragment>(args&: Symbol)); |
233 | getAssembler().registerSymbol(Symbol: *Symbol); |
234 | } |
235 | |
236 | void MCWinCOFFStreamer::emitCOFFSectionIndex(const MCSymbol *Symbol) { |
237 | visitUsedSymbol(Sym: *Symbol); |
238 | MCDataFragment *DF = getOrCreateDataFragment(); |
239 | const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(Symbol, Ctx&: getContext()); |
240 | MCFixup Fixup = MCFixup::create(Offset: DF->getContents().size(), Value: SRE, Kind: FK_SecRel_2); |
241 | DF->getFixups().push_back(Elt: Fixup); |
242 | DF->getContents().resize(N: DF->getContents().size() + 2, NV: 0); |
243 | } |
244 | |
245 | void MCWinCOFFStreamer::emitCOFFSecRel32(const MCSymbol *Symbol, |
246 | uint64_t Offset) { |
247 | visitUsedSymbol(Sym: *Symbol); |
248 | MCDataFragment *DF = getOrCreateDataFragment(); |
249 | // Create Symbol A for the relocation relative reference. |
250 | const MCExpr *MCE = MCSymbolRefExpr::create(Symbol, Ctx&: getContext()); |
251 | // Add the constant offset, if given. |
252 | if (Offset) |
253 | MCE = MCBinaryExpr::createAdd( |
254 | LHS: MCE, RHS: MCConstantExpr::create(Value: Offset, Ctx&: getContext()), Ctx&: getContext()); |
255 | // Build the secrel32 relocation. |
256 | MCFixup Fixup = MCFixup::create(Offset: DF->getContents().size(), Value: MCE, Kind: FK_SecRel_4); |
257 | // Record the relocation. |
258 | DF->getFixups().push_back(Elt: Fixup); |
259 | // Emit 4 bytes (zeros) to the object file. |
260 | DF->getContents().resize(N: DF->getContents().size() + 4, NV: 0); |
261 | } |
262 | |
263 | void MCWinCOFFStreamer::emitCOFFImgRel32(const MCSymbol *Symbol, |
264 | int64_t Offset) { |
265 | visitUsedSymbol(Sym: *Symbol); |
266 | MCDataFragment *DF = getOrCreateDataFragment(); |
267 | // Create Symbol A for the relocation relative reference. |
268 | const MCExpr *MCE = MCSymbolRefExpr::create( |
269 | Symbol, Kind: MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx&: getContext()); |
270 | // Add the constant offset, if given. |
271 | if (Offset) |
272 | MCE = MCBinaryExpr::createAdd( |
273 | LHS: MCE, RHS: MCConstantExpr::create(Value: Offset, Ctx&: getContext()), Ctx&: getContext()); |
274 | // Build the imgrel relocation. |
275 | MCFixup Fixup = MCFixup::create(Offset: DF->getContents().size(), Value: MCE, Kind: FK_Data_4); |
276 | // Record the relocation. |
277 | DF->getFixups().push_back(Elt: Fixup); |
278 | // Emit 4 bytes (zeros) to the object file. |
279 | DF->getContents().resize(N: DF->getContents().size() + 4, NV: 0); |
280 | } |
281 | |
282 | void MCWinCOFFStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size, |
283 | Align ByteAlignment) { |
284 | auto *Symbol = cast<MCSymbolCOFF>(Val: S); |
285 | |
286 | const Triple &T = getContext().getTargetTriple(); |
287 | if (T.isWindowsMSVCEnvironment()) { |
288 | if (ByteAlignment > 32) |
289 | report_fatal_error(reason: "alignment is limited to 32-bytes" ); |
290 | |
291 | // Round size up to alignment so that we will honor the alignment request. |
292 | Size = std::max(a: Size, b: ByteAlignment.value()); |
293 | } |
294 | |
295 | getAssembler().registerSymbol(Symbol: *Symbol); |
296 | Symbol->setExternal(true); |
297 | Symbol->setCommon(Size, Alignment: ByteAlignment); |
298 | |
299 | if (!T.isWindowsMSVCEnvironment() && ByteAlignment > 1) { |
300 | SmallString<128> Directive; |
301 | raw_svector_ostream OS(Directive); |
302 | const MCObjectFileInfo *MFI = getContext().getObjectFileInfo(); |
303 | |
304 | OS << " -aligncomm:\"" << Symbol->getName() << "\"," |
305 | << Log2_32_Ceil(Value: ByteAlignment.value()); |
306 | |
307 | pushSection(); |
308 | switchSection(Section: MFI->getDrectveSection()); |
309 | emitBytes(Data: Directive); |
310 | popSection(); |
311 | } |
312 | } |
313 | |
314 | void MCWinCOFFStreamer::emitLocalCommonSymbol(MCSymbol *S, uint64_t Size, |
315 | Align ByteAlignment) { |
316 | auto *Symbol = cast<MCSymbolCOFF>(Val: S); |
317 | |
318 | MCSection *Section = getContext().getObjectFileInfo()->getBSSSection(); |
319 | pushSection(); |
320 | switchSection(Section); |
321 | emitValueToAlignment(Alignment: ByteAlignment, Value: 0, ValueSize: 1, MaxBytesToEmit: 0); |
322 | emitLabel(S: Symbol); |
323 | Symbol->setExternal(false); |
324 | emitZeros(NumBytes: Size); |
325 | popSection(); |
326 | } |
327 | |
328 | void MCWinCOFFStreamer::emitWeakReference(MCSymbol *AliasS, |
329 | const MCSymbol *Symbol) { |
330 | auto *Alias = cast<MCSymbolCOFF>(Val: AliasS); |
331 | emitSymbolAttribute(S: Alias, Attribute: MCSA_Weak); |
332 | |
333 | getAssembler().registerSymbol(Symbol: *Symbol); |
334 | Alias->setVariableValue(MCSymbolRefExpr::create( |
335 | Symbol, Kind: MCSymbolRefExpr::VK_WEAKREF, Ctx&: getContext())); |
336 | } |
337 | |
338 | void MCWinCOFFStreamer::emitZerofill(MCSection *Section, MCSymbol *Symbol, |
339 | uint64_t Size, Align ByteAlignment, |
340 | SMLoc Loc) { |
341 | llvm_unreachable("not implemented" ); |
342 | } |
343 | |
344 | void MCWinCOFFStreamer::emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, |
345 | uint64_t Size, Align ByteAlignment) { |
346 | llvm_unreachable("not implemented" ); |
347 | } |
348 | |
349 | // TODO: Implement this if you want to emit .comment section in COFF obj files. |
350 | void MCWinCOFFStreamer::emitIdent(StringRef IdentString) { |
351 | llvm_unreachable("not implemented" ); |
352 | } |
353 | |
354 | void MCWinCOFFStreamer::emitWinEHHandlerData(SMLoc Loc) { |
355 | llvm_unreachable("not implemented" ); |
356 | } |
357 | |
358 | void MCWinCOFFStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, |
359 | const MCSymbolRefExpr *To, |
360 | uint64_t Count) { |
361 | // Ignore temporary symbols for now. |
362 | if (!From->getSymbol().isTemporary() && !To->getSymbol().isTemporary()) |
363 | getWriter().getCGProfile().push_back(Elt: {.From: From, .To: To, .Count: Count}); |
364 | } |
365 | |
366 | void MCWinCOFFStreamer::finalizeCGProfileEntry(const MCSymbolRefExpr *&SRE) { |
367 | const MCSymbol *S = &SRE->getSymbol(); |
368 | if (getAssembler().registerSymbol(Symbol: *S)) |
369 | cast<MCSymbolCOFF>(Val: S)->setExternal(true); |
370 | } |
371 | |
372 | void MCWinCOFFStreamer::finishImpl() { |
373 | MCAssembler &Asm = getAssembler(); |
374 | if (Asm.getWriter().getEmitAddrsigSection()) { |
375 | // Register the section. |
376 | switchSection(Section: Asm.getContext().getCOFFSection(Section: ".llvm_addrsig" , |
377 | Characteristics: COFF::IMAGE_SCN_LNK_REMOVE)); |
378 | } |
379 | if (!Asm.getWriter().getCGProfile().empty()) { |
380 | for (auto &E : Asm.getWriter().getCGProfile()) { |
381 | finalizeCGProfileEntry(SRE&: E.From); |
382 | finalizeCGProfileEntry(SRE&: E.To); |
383 | } |
384 | switchSection(Section: Asm.getContext().getCOFFSection(Section: ".llvm.call-graph-profile" , |
385 | Characteristics: COFF::IMAGE_SCN_LNK_REMOVE)); |
386 | } |
387 | |
388 | MCObjectStreamer::finishImpl(); |
389 | } |
390 | |
391 | void MCWinCOFFStreamer::Error(const Twine &Msg) const { |
392 | getContext().reportError(L: SMLoc(), Msg); |
393 | } |
394 | |