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 IgnoreDirective(StringRef, SMLoc) { 57 while (!getLexer().is(AsmToken::EndOfStatement)) { 58 Lex(); 59 } 60 return false; 61 } 62 63 void Initialize(MCAsmParser &Parser) override { 64 // Call the base implementation. 65 MCAsmParserExtension::Initialize(Parser); 66 67 // x64 directives 68 // .allocstack 69 // .endprolog 70 // .pushframe 71 // .pushreg 72 // .savereg 73 // .savexmm128 74 // .setframe 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 // byte/sbyte 96 // dword/sdword 97 // even 98 // fword 99 // qword 100 // real4 101 // real8 102 // real10 103 // tbyte 104 // word/sword 105 106 // Listing control directives 107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref"); 108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list"); 109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall"); 110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif"); 111 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro"); 112 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall"); 113 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref"); 114 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist"); 115 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif"); 116 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro"); 117 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page"); 118 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle"); 119 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond"); 120 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title"); 121 122 // Macro directives 123 // endm 124 // exitm 125 // goto 126 // local 127 // macro 128 // purge 129 130 // Miscellaneous directives 131 // alias 132 // assume 133 // .fpo 134 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>( 135 "includelib"); 136 // mmword 137 // option 138 // popcontext 139 // pushcontext 140 // .radix 141 // .safeseh 142 // xmmword 143 // ymmword 144 145 // Procedure directives 146 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp"); 147 // invoke (32-bit only) 148 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc"); 149 // proto 150 151 // Processor directives 152 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386"); 153 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386P"); 154 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387"); 155 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486"); 156 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486P"); 157 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586"); 158 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586P"); 159 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686"); 160 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686P"); 161 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d"); 162 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx"); 163 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm"); 164 165 // Repeat blocks directives 166 // for 167 // forc 168 // goto 169 // repeat 170 // while 171 172 // Scope directives 173 // comm 174 // externdef 175 176 // Segment directives 177 // .alpha (32-bit only, order segments alphabetically) 178 // .dosseg (32-bit only, order segments in DOS convention) 179 // .seq (32-bit only, order segments sequentially) 180 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends"); 181 // group (32-bit only) 182 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment"); 183 184 // Simplified segment directives 185 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code"); 186 // .const 187 addDirectiveHandler< 188 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data"); 189 addDirectiveHandler< 190 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?"); 191 // .exit 192 // .fardata 193 // .fardata? 194 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model"); 195 // .stack 196 // .startup 197 198 // String directives, written <name> <directive> <params> 199 // catstr (equivalent to <name> TEXTEQU <params>) 200 // instr (equivalent to <name> = @InStr(<params>)) 201 // sizestr (equivalent to <name> = @SizeStr(<params>)) 202 // substr (equivalent to <name> TEXTEQU @SubStr(<params>)) 203 204 // Structure and record directives 205 // ends 206 // record 207 // struct 208 // typedef 209 // union 210 } 211 212 bool ParseSectionDirectiveCode(StringRef, SMLoc) { 213 return ParseSectionSwitch(".text", 214 COFF::IMAGE_SCN_CNT_CODE 215 | COFF::IMAGE_SCN_MEM_EXECUTE 216 | COFF::IMAGE_SCN_MEM_READ, 217 SectionKind::getText()); 218 } 219 220 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) { 221 return ParseSectionSwitch(".data", 222 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA 223 | COFF::IMAGE_SCN_MEM_READ 224 | COFF::IMAGE_SCN_MEM_WRITE, 225 SectionKind::getData()); 226 } 227 228 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) { 229 return ParseSectionSwitch(".bss", 230 COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA 231 | COFF::IMAGE_SCN_MEM_READ 232 | COFF::IMAGE_SCN_MEM_WRITE, 233 SectionKind::getBSS()); 234 } 235 236 StringRef CurrentProcedure; 237 238 public: 239 COFFMasmParser() = default; 240 }; 241 242 } // end anonymous namespace. 243 244 static SectionKind computeSectionKind(unsigned Flags) { 245 if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) 246 return SectionKind::getText(); 247 if (Flags & COFF::IMAGE_SCN_MEM_READ && 248 (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0) 249 return SectionKind::getReadOnly(); 250 return SectionKind::getData(); 251 } 252 253 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 254 unsigned Characteristics, 255 SectionKind Kind) { 256 return ParseSectionSwitch(Section, Characteristics, Kind, "", 257 (COFF::COMDATType)0); 258 } 259 260 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 261 unsigned Characteristics, 262 SectionKind Kind, 263 StringRef COMDATSymName, 264 COFF::COMDATType Type) { 265 if (getLexer().isNot(AsmToken::EndOfStatement)) 266 return TokError("unexpected token in section switching directive"); 267 Lex(); 268 269 getStreamer().SwitchSection(getContext().getCOFFSection( 270 Section, Characteristics, Kind, COMDATSymName, Type)); 271 272 return false; 273 } 274 275 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) { 276 StringRef SegmentName; 277 if (!getLexer().is(AsmToken::Identifier)) 278 return TokError("expected identifier in directive"); 279 SegmentName = getTok().getIdentifier(); 280 Lex(); 281 282 StringRef SectionName = SegmentName; 283 SmallVector<char, 247> SectionNameVector; 284 unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 285 COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; 286 if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) { 287 if (SegmentName.size() == 5) { 288 SectionName = ".text"; 289 } else { 290 SectionName = 291 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector); 292 } 293 Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE | 294 COFF::IMAGE_SCN_MEM_READ; 295 } 296 SectionKind Kind = computeSectionKind(Flags); 297 getStreamer().SwitchSection(getContext().getCOFFSection( 298 SectionName, Flags, Kind, "", (COFF::COMDATType)(0))); 299 return false; 300 } 301 302 /// ParseDirectiveSegmentEnd 303 /// ::= identifier "ends" 304 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) { 305 StringRef SegmentName; 306 if (!getLexer().is(AsmToken::Identifier)) 307 return TokError("expected identifier in directive"); 308 SegmentName = getTok().getIdentifier(); 309 310 // Ignore; no action necessary. 311 Lex(); 312 return false; 313 } 314 315 /// ParseDirectiveIncludelib 316 /// ::= "includelib" identifier 317 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) { 318 StringRef Lib; 319 if (getParser().parseIdentifier(Lib)) 320 return TokError("expected identifier in includelib directive"); 321 322 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT; 323 SectionKind Kind = computeSectionKind(Flags); 324 getStreamer().PushSection(); 325 getStreamer().SwitchSection(getContext().getCOFFSection( 326 ".drectve", Flags, Kind, "", (COFF::COMDATType)(0))); 327 getStreamer().emitBytes("/DEFAULTLIB:"); 328 getStreamer().emitBytes(Lib); 329 getStreamer().emitBytes(" "); 330 getStreamer().PopSection(); 331 return false; 332 } 333 334 /// ParseDirectiveProc 335 /// TODO(epastor): Implement parameters and other attributes. 336 /// ::= label "proc" [[distance]] 337 /// statements 338 /// label "endproc" 339 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) { 340 StringRef Label; 341 if (getParser().parseIdentifier(Label)) 342 return Error(Loc, "expected identifier for procedure"); 343 if (getLexer().is(AsmToken::Identifier)) { 344 StringRef nextVal = getTok().getString(); 345 SMLoc nextLoc = getTok().getLoc(); 346 if (nextVal.equals_lower("far")) { 347 // TODO(epastor): Handle far procedure definitions. 348 Lex(); 349 return Error(nextLoc, "far procedure definitions not yet supported"); 350 } else if (nextVal.equals_lower("near")) { 351 Lex(); 352 nextVal = getTok().getString(); 353 nextLoc = getTok().getLoc(); 354 } 355 } 356 MCSymbol *Sym = getContext().getOrCreateSymbol(Label); 357 358 // Define symbol as simple function 359 getStreamer().BeginCOFFSymbolDef(Sym); 360 getStreamer().EmitCOFFSymbolStorageClass(2); 361 getStreamer().EmitCOFFSymbolType(0x20); 362 getStreamer().EndCOFFSymbolDef(); 363 364 getStreamer().emitLabel(Sym, Loc); 365 CurrentProcedure = Label; 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 return false; 380 } 381 382 namespace llvm { 383 384 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; } 385 386 } // end namespace llvm 387