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