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