1 //===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===// 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 #include "llvm/ADT/StringRef.h" 10 #include "llvm/ADT/StringSwitch.h" 11 #include "llvm/ADT/Triple.h" 12 #include "llvm/ADT/Twine.h" 13 #include "llvm/BinaryFormat/COFF.h" 14 #include "llvm/MC/MCContext.h" 15 #include "llvm/MC/MCDirectives.h" 16 #include "llvm/MC/MCObjectFileInfo.h" 17 #include "llvm/MC/MCParser/MCAsmLexer.h" 18 #include "llvm/MC/MCParser/MCAsmParserExtension.h" 19 #include "llvm/MC/MCParser/MCAsmParserUtils.h" 20 #include "llvm/MC/MCParser/MCTargetAsmParser.h" 21 #include "llvm/MC/MCRegisterInfo.h" 22 #include "llvm/MC/MCSectionCOFF.h" 23 #include "llvm/MC/MCStreamer.h" 24 #include "llvm/MC/SectionKind.h" 25 #include "llvm/Support/SMLoc.h" 26 #include <cassert> 27 #include <cstdint> 28 #include <limits> 29 #include <utility> 30 31 using namespace llvm; 32 33 namespace { 34 35 class COFFMasmParser : public MCAsmParserExtension { 36 template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)> 37 void addDirectiveHandler(StringRef Directive) { 38 MCAsmParser::ExtensionDirectiveHandler Handler = 39 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>); 40 getParser().addDirectiveHandler(Directive, Handler); 41 } 42 43 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics, 44 SectionKind Kind); 45 46 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics, 47 SectionKind Kind, StringRef COMDATSymName, 48 COFF::COMDATType Type); 49 50 bool ParseDirectiveProc(StringRef, SMLoc); 51 bool ParseDirectiveEndProc(StringRef, SMLoc); 52 bool ParseDirectiveSegment(StringRef, SMLoc); 53 bool ParseDirectiveSegmentEnd(StringRef, SMLoc); 54 bool ParseDirectiveIncludelib(StringRef, SMLoc); 55 56 bool ParseSEHDirectiveAllocStack(StringRef, SMLoc); 57 bool ParseSEHDirectiveEndProlog(StringRef, SMLoc); 58 59 bool IgnoreDirective(StringRef, SMLoc) { 60 while (!getLexer().is(AsmToken::EndOfStatement)) { 61 Lex(); 62 } 63 return false; 64 } 65 66 void Initialize(MCAsmParser &Parser) override { 67 // Call the base implementation. 68 MCAsmParserExtension::Initialize(Parser); 69 70 // x64 directives 71 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>( 72 ".allocstack"); 73 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>( 74 ".endprolog"); 75 76 // Code label directives 77 // label 78 // org 79 80 // Conditional control flow directives 81 // .break 82 // .continue 83 // .else 84 // .elseif 85 // .endif 86 // .endw 87 // .if 88 // .repeat 89 // .until 90 // .untilcxz 91 // .while 92 93 // Data allocation directives 94 // align 95 // even 96 // mmword 97 // real10 98 // tbyte 99 // xmmword 100 // ymmword 101 102 // Listing control directives 103 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref"); 104 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list"); 105 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall"); 106 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif"); 107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro"); 108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall"); 109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref"); 110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist"); 111 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif"); 112 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro"); 113 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page"); 114 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle"); 115 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond"); 116 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title"); 117 118 // Macro directives 119 // endm 120 // exitm 121 // goto 122 // local 123 // macro 124 // purge 125 126 // Miscellaneous directives 127 // alias 128 // assume 129 // .fpo 130 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>( 131 "includelib"); 132 // option 133 // popcontext 134 // pushcontext 135 // .radix 136 // .safeseh 137 138 // Procedure directives 139 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp"); 140 // invoke (32-bit only) 141 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc"); 142 // proto 143 144 // Processor directives; all ignored 145 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386"); 146 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386P"); 147 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387"); 148 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486"); 149 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486P"); 150 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586"); 151 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586P"); 152 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686"); 153 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686P"); 154 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d"); 155 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx"); 156 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm"); 157 158 // Repeat blocks directives 159 // for 160 // forc 161 // goto 162 // repeat 163 // while 164 165 // Scope directives 166 // comm 167 // externdef 168 169 // Segment directives 170 // .alpha (32-bit only, order segments alphabetically) 171 // .dosseg (32-bit only, order segments in DOS convention) 172 // .seq (32-bit only, order segments sequentially) 173 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends"); 174 // group (32-bit only) 175 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment"); 176 177 // Simplified segment directives 178 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code"); 179 // .const 180 addDirectiveHandler< 181 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data"); 182 addDirectiveHandler< 183 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?"); 184 // .exit 185 // .fardata 186 // .fardata? 187 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model"); 188 // .stack 189 // .startup 190 191 // String directives, written <name> <directive> <params> 192 // catstr (equivalent to <name> TEXTEQU <params>) 193 // instr (equivalent to <name> = @InStr(<params>)) 194 // sizestr (equivalent to <name> = @SizeStr(<params>)) 195 // substr (equivalent to <name> TEXTEQU @SubStr(<params>)) 196 197 // Structure and record directives 198 // record 199 // typedef 200 } 201 202 bool ParseSectionDirectiveCode(StringRef, SMLoc) { 203 return ParseSectionSwitch(".text", 204 COFF::IMAGE_SCN_CNT_CODE 205 | COFF::IMAGE_SCN_MEM_EXECUTE 206 | COFF::IMAGE_SCN_MEM_READ, 207 SectionKind::getText()); 208 } 209 210 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) { 211 return ParseSectionSwitch(".data", 212 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA 213 | COFF::IMAGE_SCN_MEM_READ 214 | COFF::IMAGE_SCN_MEM_WRITE, 215 SectionKind::getData()); 216 } 217 218 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) { 219 return ParseSectionSwitch(".bss", 220 COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA 221 | COFF::IMAGE_SCN_MEM_READ 222 | COFF::IMAGE_SCN_MEM_WRITE, 223 SectionKind::getBSS()); 224 } 225 226 StringRef CurrentProcedure; 227 bool CurrentProcedureFramed; 228 229 public: 230 COFFMasmParser() = default; 231 }; 232 233 } // end anonymous namespace. 234 235 static SectionKind computeSectionKind(unsigned Flags) { 236 if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) 237 return SectionKind::getText(); 238 if (Flags & COFF::IMAGE_SCN_MEM_READ && 239 (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0) 240 return SectionKind::getReadOnly(); 241 return SectionKind::getData(); 242 } 243 244 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 245 unsigned Characteristics, 246 SectionKind Kind) { 247 return ParseSectionSwitch(Section, Characteristics, Kind, "", 248 (COFF::COMDATType)0); 249 } 250 251 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 252 unsigned Characteristics, 253 SectionKind Kind, 254 StringRef COMDATSymName, 255 COFF::COMDATType Type) { 256 if (getLexer().isNot(AsmToken::EndOfStatement)) 257 return TokError("unexpected token in section switching directive"); 258 Lex(); 259 260 getStreamer().SwitchSection(getContext().getCOFFSection( 261 Section, Characteristics, Kind, COMDATSymName, Type)); 262 263 return false; 264 } 265 266 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) { 267 StringRef SegmentName; 268 if (!getLexer().is(AsmToken::Identifier)) 269 return TokError("expected identifier in directive"); 270 SegmentName = getTok().getIdentifier(); 271 Lex(); 272 273 StringRef SectionName = SegmentName; 274 SmallVector<char, 247> SectionNameVector; 275 unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 276 COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; 277 if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) { 278 if (SegmentName.size() == 5) { 279 SectionName = ".text"; 280 } else { 281 SectionName = 282 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector); 283 } 284 Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE | 285 COFF::IMAGE_SCN_MEM_READ; 286 } 287 SectionKind Kind = computeSectionKind(Flags); 288 getStreamer().SwitchSection(getContext().getCOFFSection( 289 SectionName, Flags, Kind, "", (COFF::COMDATType)(0))); 290 return false; 291 } 292 293 /// ParseDirectiveSegmentEnd 294 /// ::= identifier "ends" 295 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) { 296 StringRef SegmentName; 297 if (!getLexer().is(AsmToken::Identifier)) 298 return TokError("expected identifier in directive"); 299 SegmentName = getTok().getIdentifier(); 300 301 // Ignore; no action necessary. 302 Lex(); 303 return false; 304 } 305 306 /// ParseDirectiveIncludelib 307 /// ::= "includelib" identifier 308 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) { 309 StringRef Lib; 310 if (getParser().parseIdentifier(Lib)) 311 return TokError("expected identifier in includelib directive"); 312 313 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT; 314 SectionKind Kind = computeSectionKind(Flags); 315 getStreamer().PushSection(); 316 getStreamer().SwitchSection(getContext().getCOFFSection( 317 ".drectve", Flags, Kind, "", (COFF::COMDATType)(0))); 318 getStreamer().emitBytes("/DEFAULTLIB:"); 319 getStreamer().emitBytes(Lib); 320 getStreamer().emitBytes(" "); 321 getStreamer().PopSection(); 322 return false; 323 } 324 325 /// ParseDirectiveProc 326 /// TODO(epastor): Implement parameters and other attributes. 327 /// ::= label "proc" [[distance]] 328 /// statements 329 /// label "endproc" 330 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) { 331 StringRef Label; 332 if (getParser().parseIdentifier(Label)) 333 return Error(Loc, "expected identifier for procedure"); 334 if (getLexer().is(AsmToken::Identifier)) { 335 StringRef nextVal = getTok().getString(); 336 SMLoc nextLoc = getTok().getLoc(); 337 if (nextVal.equals_lower("far")) { 338 // TODO(epastor): Handle far procedure definitions. 339 Lex(); 340 return Error(nextLoc, "far procedure definitions not yet supported"); 341 } else if (nextVal.equals_lower("near")) { 342 Lex(); 343 nextVal = getTok().getString(); 344 nextLoc = getTok().getLoc(); 345 } 346 } 347 MCSymbol *Sym = getContext().getOrCreateSymbol(Label); 348 349 // Define symbol as simple function 350 getStreamer().BeginCOFFSymbolDef(Sym); 351 getStreamer().EmitCOFFSymbolStorageClass(2); 352 getStreamer().EmitCOFFSymbolType(0x20); 353 getStreamer().EndCOFFSymbolDef(); 354 355 bool Framed = false; 356 if (getLexer().is(AsmToken::Identifier) && 357 getTok().getString().equals_lower("frame")) { 358 Lex(); 359 Framed = true; 360 getStreamer().EmitWinCFIStartProc(Sym, Loc); 361 } 362 getStreamer().emitLabel(Sym, Loc); 363 364 CurrentProcedure = Label; 365 CurrentProcedureFramed = Framed; 366 return false; 367 } 368 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) { 369 StringRef Label; 370 SMLoc LabelLoc = getTok().getLoc(); 371 if (getParser().parseIdentifier(Label)) 372 return Error(LabelLoc, "expected identifier for procedure end"); 373 374 if (CurrentProcedure.empty()) 375 return Error(Loc, "endp outside of procedure block"); 376 else if (CurrentProcedure != Label) 377 return Error(LabelLoc, "endp does not match current procedure '" + 378 CurrentProcedure + "'"); 379 380 if (CurrentProcedureFramed) { 381 getStreamer().EmitWinCFIEndProc(Loc); 382 } 383 CurrentProcedure = ""; 384 CurrentProcedureFramed = false; 385 return false; 386 } 387 388 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive, 389 SMLoc Loc) { 390 int64_t Size; 391 SMLoc SizeLoc = getTok().getLoc(); 392 if (getParser().parseAbsoluteExpression(Size)) 393 return Error(SizeLoc, "expected integer size"); 394 if (Size % 8 != 0) 395 return Error(SizeLoc, "stack size must be a multiple of 8"); 396 getStreamer().EmitWinCFIAllocStack(static_cast<unsigned>(Size), Loc); 397 return false; 398 } 399 400 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive, 401 SMLoc Loc) { 402 getStreamer().EmitWinCFIEndProlog(Loc); 403 return false; 404 } 405 406 namespace llvm { 407 408 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; } 409 410 } // end namespace llvm 411