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 // tbyte 101 // xmmword 102 // ymmword 103 104 // Listing control directives 105 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref"); 106 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list"); 107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall"); 108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif"); 109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro"); 110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall"); 111 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref"); 112 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist"); 113 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif"); 114 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro"); 115 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page"); 116 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle"); 117 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond"); 118 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title"); 119 120 // Macro directives 121 // exitm 122 // goto 123 // purge 124 125 // Miscellaneous directives 126 addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias"); 127 // assume 128 // .fpo 129 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>( 130 "includelib"); 131 // option 132 // popcontext 133 // pushcontext 134 // .safeseh 135 136 // Procedure directives 137 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp"); 138 // invoke (32-bit only) 139 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc"); 140 // proto 141 142 // Processor directives; all ignored 143 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386"); 144 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386P"); 145 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387"); 146 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486"); 147 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486P"); 148 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586"); 149 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586P"); 150 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686"); 151 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686P"); 152 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d"); 153 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx"); 154 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm"); 155 156 // Repeat blocks directives 157 // goto 158 159 // Scope directives 160 // comm 161 // externdef 162 163 // Segment directives 164 // .alpha (32-bit only, order segments alphabetically) 165 // .dosseg (32-bit only, order segments in DOS convention) 166 // .seq (32-bit only, order segments sequentially) 167 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends"); 168 // group (32-bit only) 169 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment"); 170 171 // Simplified segment directives 172 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code"); 173 // .const 174 addDirectiveHandler< 175 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data"); 176 addDirectiveHandler< 177 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?"); 178 // .exit 179 // .fardata 180 // .fardata? 181 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model"); 182 // .stack 183 // .startup 184 185 // String directives, written <name> <directive> <params> 186 // catstr (equivalent to <name> TEXTEQU <params>) 187 // instr (equivalent to <name> = @InStr(<params>)) 188 // sizestr (equivalent to <name> = @SizeStr(<params>)) 189 // substr (equivalent to <name> TEXTEQU @SubStr(<params>)) 190 191 // Structure and record directives 192 // record 193 // typedef 194 } 195 196 bool ParseSectionDirectiveCode(StringRef, SMLoc) { 197 return ParseSectionSwitch(".text", 198 COFF::IMAGE_SCN_CNT_CODE 199 | COFF::IMAGE_SCN_MEM_EXECUTE 200 | COFF::IMAGE_SCN_MEM_READ, 201 SectionKind::getText()); 202 } 203 204 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) { 205 return ParseSectionSwitch(".data", 206 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA 207 | COFF::IMAGE_SCN_MEM_READ 208 | COFF::IMAGE_SCN_MEM_WRITE, 209 SectionKind::getData()); 210 } 211 212 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) { 213 return ParseSectionSwitch(".bss", 214 COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA 215 | COFF::IMAGE_SCN_MEM_READ 216 | COFF::IMAGE_SCN_MEM_WRITE, 217 SectionKind::getBSS()); 218 } 219 220 StringRef CurrentProcedure; 221 bool CurrentProcedureFramed; 222 223 public: 224 COFFMasmParser() = default; 225 }; 226 227 } // end anonymous namespace. 228 229 static SectionKind computeSectionKind(unsigned Flags) { 230 if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) 231 return SectionKind::getText(); 232 if (Flags & COFF::IMAGE_SCN_MEM_READ && 233 (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0) 234 return SectionKind::getReadOnly(); 235 return SectionKind::getData(); 236 } 237 238 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 239 unsigned Characteristics, 240 SectionKind Kind) { 241 return ParseSectionSwitch(Section, Characteristics, Kind, "", 242 (COFF::COMDATType)0); 243 } 244 245 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 246 unsigned Characteristics, 247 SectionKind Kind, 248 StringRef COMDATSymName, 249 COFF::COMDATType Type) { 250 if (getLexer().isNot(AsmToken::EndOfStatement)) 251 return TokError("unexpected token in section switching directive"); 252 Lex(); 253 254 getStreamer().SwitchSection(getContext().getCOFFSection( 255 Section, Characteristics, Kind, COMDATSymName, Type)); 256 257 return false; 258 } 259 260 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) { 261 StringRef SegmentName; 262 if (!getLexer().is(AsmToken::Identifier)) 263 return TokError("expected identifier in directive"); 264 SegmentName = getTok().getIdentifier(); 265 Lex(); 266 267 StringRef SectionName = SegmentName; 268 SmallVector<char, 247> SectionNameVector; 269 unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 270 COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; 271 if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) { 272 if (SegmentName.size() == 5) { 273 SectionName = ".text"; 274 } else { 275 SectionName = 276 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector); 277 } 278 Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE | 279 COFF::IMAGE_SCN_MEM_READ; 280 } 281 SectionKind Kind = computeSectionKind(Flags); 282 getStreamer().SwitchSection(getContext().getCOFFSection( 283 SectionName, Flags, Kind, "", (COFF::COMDATType)(0))); 284 return false; 285 } 286 287 /// ParseDirectiveSegmentEnd 288 /// ::= identifier "ends" 289 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) { 290 StringRef SegmentName; 291 if (!getLexer().is(AsmToken::Identifier)) 292 return TokError("expected identifier in directive"); 293 SegmentName = getTok().getIdentifier(); 294 295 // Ignore; no action necessary. 296 Lex(); 297 return false; 298 } 299 300 /// ParseDirectiveIncludelib 301 /// ::= "includelib" identifier 302 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) { 303 StringRef Lib; 304 if (getParser().parseIdentifier(Lib)) 305 return TokError("expected identifier in includelib directive"); 306 307 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT; 308 SectionKind Kind = computeSectionKind(Flags); 309 getStreamer().PushSection(); 310 getStreamer().SwitchSection(getContext().getCOFFSection( 311 ".drectve", Flags, Kind, "", (COFF::COMDATType)(0))); 312 getStreamer().emitBytes("/DEFAULTLIB:"); 313 getStreamer().emitBytes(Lib); 314 getStreamer().emitBytes(" "); 315 getStreamer().PopSection(); 316 return false; 317 } 318 319 /// ParseDirectiveProc 320 /// TODO(epastor): Implement parameters and other attributes. 321 /// ::= label "proc" [[distance]] 322 /// statements 323 /// label "endproc" 324 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) { 325 StringRef Label; 326 if (getParser().parseIdentifier(Label)) 327 return Error(Loc, "expected identifier for procedure"); 328 if (getLexer().is(AsmToken::Identifier)) { 329 StringRef nextVal = getTok().getString(); 330 SMLoc nextLoc = getTok().getLoc(); 331 if (nextVal.equals_lower("far")) { 332 // TODO(epastor): Handle far procedure definitions. 333 Lex(); 334 return Error(nextLoc, "far procedure definitions not yet supported"); 335 } else if (nextVal.equals_lower("near")) { 336 Lex(); 337 nextVal = getTok().getString(); 338 nextLoc = getTok().getLoc(); 339 } 340 } 341 MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label)); 342 343 // Define symbol as simple external function 344 Sym->setExternal(true); 345 Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT); 346 347 bool Framed = false; 348 if (getLexer().is(AsmToken::Identifier) && 349 getTok().getString().equals_lower("frame")) { 350 Lex(); 351 Framed = true; 352 getStreamer().EmitWinCFIStartProc(Sym, Loc); 353 } 354 getStreamer().emitLabel(Sym, Loc); 355 356 CurrentProcedure = Label; 357 CurrentProcedureFramed = Framed; 358 return false; 359 } 360 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) { 361 StringRef Label; 362 SMLoc LabelLoc = getTok().getLoc(); 363 if (getParser().parseIdentifier(Label)) 364 return Error(LabelLoc, "expected identifier for procedure end"); 365 366 if (CurrentProcedure.empty()) 367 return Error(Loc, "endp outside of procedure block"); 368 else if (CurrentProcedure != Label) 369 return Error(LabelLoc, "endp does not match current procedure '" + 370 CurrentProcedure + "'"); 371 372 if (CurrentProcedureFramed) { 373 getStreamer().EmitWinCFIEndProc(Loc); 374 } 375 CurrentProcedure = ""; 376 CurrentProcedureFramed = false; 377 return false; 378 } 379 380 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) { 381 std::string AliasName, ActualName; 382 if (getTok().isNot(AsmToken::Less) || 383 getParser().parseAngleBracketString(AliasName)) 384 return Error(getTok().getLoc(), "expected <aliasName>"); 385 if (getParser().parseToken(AsmToken::Equal)) 386 return addErrorSuffix(" in " + Directive + " directive"); 387 if (getTok().isNot(AsmToken::Less) || 388 getParser().parseAngleBracketString(ActualName)) 389 return Error(getTok().getLoc(), "expected <actualName>"); 390 391 MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName); 392 MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName); 393 394 getStreamer().emitWeakReference(Alias, Actual); 395 396 return false; 397 } 398 399 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive, 400 SMLoc Loc) { 401 int64_t Size; 402 SMLoc SizeLoc = getTok().getLoc(); 403 if (getParser().parseAbsoluteExpression(Size)) 404 return Error(SizeLoc, "expected integer size"); 405 if (Size % 8 != 0) 406 return Error(SizeLoc, "stack size must be a multiple of 8"); 407 getStreamer().EmitWinCFIAllocStack(static_cast<unsigned>(Size), Loc); 408 return false; 409 } 410 411 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive, 412 SMLoc Loc) { 413 getStreamer().EmitWinCFIEndProlog(Loc); 414 return false; 415 } 416 417 namespace llvm { 418 419 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; } 420 421 } // end namespace llvm 422