1# @file ConvertMasmToNasm.py 2# This script assists with conversion of MASM assembly syntax to NASM 3# 4# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> 5# 6# This program and the accompanying materials 7# are licensed and made available under the terms and conditions of the BSD License 8# which accompanies this distribution. The full text of the license may be found at 9# http://opensource.org/licenses/bsd-license.php 10# 11# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13# 14 15# 16# Import Modules 17# 18import os.path 19import re 20import StringIO 21import subprocess 22import sys 23from optparse import OptionParser 24 25 26class UnsupportedConversion(Exception): 27 pass 28 29 30class NoSourceFile(Exception): 31 pass 32 33 34class UnsupportedArch(Exception): 35 unsupported = ('aarch64', 'arm', 'ebc', 'ipf') 36 37 38class CommonUtils: 39 40 # Version and Copyright 41 VersionNumber = "0.01" 42 __version__ = "%prog Version " + VersionNumber 43 __copyright__ = "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved." 44 __usage__ = "%prog [options] source.asm [destination.nasm]" 45 46 def __init__(self, clone=None): 47 if clone is None: 48 (self.Opt, self.Args) = self.ProcessCommandLine() 49 else: 50 (self.Opt, self.Args) = (clone.Opt, clone.Args) 51 52 self.unsupportedSyntaxSeen = False 53 self.src = self.Args[0] 54 assert(os.path.exists(self.src)) 55 self.dirmode = os.path.isdir(self.src) 56 srcExt = os.path.splitext(self.src)[1] 57 assert (self.dirmode or srcExt != '.nasm') 58 self.infmode = not self.dirmode and srcExt == '.inf' 59 self.diff = self.Opt.diff 60 self.git = self.Opt.git 61 self.force = self.Opt.force 62 63 if clone is None: 64 self.rootdir = os.getcwd() 65 self.DetectGit() 66 else: 67 self.rootdir = clone.rootdir 68 self.gitdir = clone.gitdir 69 self.gitemail = clone.gitemail 70 71 def ProcessCommandLine(self): 72 Parser = OptionParser(description=self.__copyright__, 73 version=self.__version__, 74 prog=sys.argv[0], 75 usage=self.__usage__ 76 ) 77 Parser.add_option("-q", "--quiet", action="store_true", type=None, 78 help="Disable all messages except FATAL ERRORS.") 79 Parser.add_option("--git", action="store_true", type=None, 80 help="Use git to create commits for each file converted") 81 Parser.add_option("--diff", action="store_true", type=None, 82 help="Show diff of conversion") 83 Parser.add_option("-f", "--force", action="store_true", type=None, 84 help="Force conversion even if unsupported") 85 86 (Opt, Args) = Parser.parse_args() 87 88 if not Opt.quiet: 89 print self.__copyright__ 90 Parser.print_version() 91 92 return (Opt, Args) 93 94 def RootRelative(self, path): 95 result = path 96 if result.startswith(self.rootdir): 97 result = result[len(self.rootdir):] 98 while len(result) > 0 and result[0] in '/\\': 99 result = result[1:] 100 return result 101 102 def MatchAndSetMo(self, regexp, string): 103 self.mo = regexp.match(string) 104 return self.mo is not None 105 106 def SearchAndSetMo(self, regexp, string): 107 self.mo = regexp.search(string) 108 return self.mo is not None 109 110 def ReplacePreserveSpacing(self, string, find, replace): 111 if len(find) >= len(replace): 112 padded = replace + (' ' * (len(find) - len(replace))) 113 return string.replace(find, padded) 114 elif find.find(replace) >= 0: 115 return string.replace(find, replace) 116 else: 117 lenDiff = len(replace) - len(find) 118 result = string 119 for i in range(lenDiff, -1, -1): 120 padded = find + (' ' * i) 121 result = result.replace(padded, replace) 122 return result 123 124 def DetectGit(self): 125 lastpath = os.path.realpath(self.src) 126 self.gitdir = None 127 while True: 128 path = os.path.split(lastpath)[0] 129 if path == lastpath: 130 return 131 candidate = os.path.join(path, '.git') 132 if os.path.isdir(candidate): 133 self.gitdir = candidate 134 self.gitemail = self.FormatGitEmailAddress() 135 return 136 lastpath = path 137 138 def FormatGitEmailAddress(self): 139 if not self.git or not self.gitdir: 140 return '' 141 142 cmd = ('git', 'config', 'user.name') 143 name = self.RunAndCaptureOutput(cmd).strip() 144 cmd = ('git', 'config', 'user.email') 145 email = self.RunAndCaptureOutput(cmd).strip() 146 if name.find(',') >= 0: 147 name = '"' + name + '"' 148 return name + ' <' + email + '>' 149 150 def RunAndCaptureOutput(self, cmd, checkExitCode=True, pipeIn=None): 151 if pipeIn: 152 subpStdin = subprocess.PIPE 153 else: 154 subpStdin = None 155 p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stdin=subpStdin) 156 (stdout, stderr) = p.communicate(pipeIn) 157 if checkExitCode: 158 if p.returncode != 0: 159 print 'command:', ' '.join(cmd) 160 print 'stdout:', stdout 161 print 'stderr:', stderr 162 print 'return:', p.returncode 163 assert p.returncode == 0 164 return stdout 165 166 def FileUpdated(self, path): 167 if not self.git or not self.gitdir: 168 return 169 170 cmd = ('git', 'add', path) 171 self.RunAndCaptureOutput(cmd) 172 173 def FileAdded(self, path): 174 self.FileUpdated(path) 175 176 def RemoveFile(self, path): 177 if not self.git or not self.gitdir: 178 return 179 180 cmd = ('git', 'rm', path) 181 self.RunAndCaptureOutput(cmd) 182 183 def FileConversionFinished(self, pkg, module, src, dst): 184 if not self.git or not self.gitdir: 185 return 186 187 if not self.Opt.quiet: 188 print 'Committing: Conversion of', dst 189 190 prefix = ' '.join(filter(lambda a: a, [pkg, module])) 191 message = '' 192 if self.unsupportedSyntaxSeen: 193 message += 'ERROR! ' 194 message += '%s: Convert %s to NASM\n' % (prefix, src) 195 message += '\n' 196 message += 'The %s script was used to convert\n' % sys.argv[0] 197 message += '%s to %s\n' % (src, dst) 198 message += '\n' 199 message += 'Contributed-under: TianoCore Contribution Agreement 1.0\n' 200 message += 'Signed-off-by: %s\n' % self.gitemail 201 202 cmd = ('git', 'commit', '-F', '-') 203 self.RunAndCaptureOutput(cmd, pipeIn=message) 204 205 206class ConvertAsmFile(CommonUtils): 207 208 def __init__(self, src, dst, clone): 209 CommonUtils.__init__(self, clone) 210 self.ConvertAsmFile(src, dst) 211 self.FileAdded(dst) 212 self.RemoveFile(src) 213 214 def ConvertAsmFile(self, inputFile, outputFile=None): 215 self.globals = set() 216 self.unsupportedSyntaxSeen = False 217 self.inputFilename = inputFile 218 if not outputFile: 219 outputFile = os.path.splitext(inputFile)[0] + '.nasm' 220 self.outputFilename = outputFile 221 222 fullSrc = os.path.realpath(inputFile) 223 srcParentDir = os.path.basename(os.path.split(fullSrc)[0]) 224 maybeArch = srcParentDir.lower() 225 if maybeArch in UnsupportedArch.unsupported: 226 raise UnsupportedArch 227 self.ia32 = maybeArch == 'ia32' 228 self.x64 = maybeArch == 'x64' 229 230 self.inputFileBase = os.path.basename(self.inputFilename) 231 self.outputFileBase = os.path.basename(self.outputFilename) 232 if self.outputFilename == '-' and not self.diff: 233 self.output = sys.stdout 234 else: 235 self.output = StringIO.StringIO() 236 if not self.Opt.quiet: 237 dirpath, src = os.path.split(self.inputFilename) 238 dirpath = self.RootRelative(dirpath) 239 dst = os.path.basename(self.outputFilename) 240 print 'Converting:', dirpath, src, '->', dst 241 lines = open(self.inputFilename).readlines() 242 self.Convert(lines) 243 if self.outputFilename == '-': 244 if self.diff: 245 sys.stdout.write(self.output.getvalue()) 246 self.output.close() 247 else: 248 f = open(self.outputFilename, 'wb') 249 f.write(self.output.getvalue()) 250 f.close() 251 self.output.close() 252 253 endOfLineRe = re.compile(r''' 254 \s* ( ; .* )? \n $ 255 ''', 256 re.VERBOSE | re.MULTILINE 257 ) 258 begOfLineRe = re.compile(r''' 259 \s* 260 ''', 261 re.VERBOSE 262 ) 263 264 def Convert(self, lines): 265 self.proc = None 266 self.anonLabelCount = -1 267 output = self.output 268 self.oldAsmEmptyLineCount = 0 269 self.newAsmEmptyLineCount = 0 270 for line in lines: 271 mo = self.begOfLineRe.search(line) 272 assert mo is not None 273 self.indent = mo.group() 274 lineWithoutBeginning = line[len(self.indent):] 275 mo = self.endOfLineRe.search(lineWithoutBeginning) 276 if mo is None: 277 endOfLine = '' 278 else: 279 endOfLine = mo.group() 280 oldAsm = line[len(self.indent):len(line) - len(endOfLine)] 281 self.originalLine = line.rstrip() 282 if line.strip() == '': 283 self.oldAsmEmptyLineCount += 1 284 self.TranslateAsm(oldAsm, endOfLine) 285 if line.strip() != '': 286 self.oldAsmEmptyLineCount = 0 287 288 procDeclRe = re.compile(r''' 289 ([\w@][\w@0-9]*) \s+ 290 PROC 291 (?: \s+ NEAR | FAR )? 292 (?: \s+ C )? 293 (?: \s+ (PUBLIC | PRIVATE) )? 294 (?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )? 295 \s* $ 296 ''', 297 re.VERBOSE | re.IGNORECASE 298 ) 299 300 procEndRe = re.compile(r''' 301 ([\w@][\w@0-9]*) \s+ 302 ENDP 303 \s* $ 304 ''', 305 re.VERBOSE | re.IGNORECASE 306 ) 307 308 varAndTypeSubRe = r' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? ' 309 publicRe = re.compile(r''' 310 PUBLIC \s+ 311 ( %s (?: \s* , \s* %s )* ) 312 \s* $ 313 ''' % (varAndTypeSubRe, varAndTypeSubRe), 314 re.VERBOSE | re.IGNORECASE 315 ) 316 317 varAndTypeSubRe = re.compile(varAndTypeSubRe, re.VERBOSE | re.IGNORECASE) 318 319 macroDeclRe = re.compile(r''' 320 ([\w@][\w@0-9]*) \s+ 321 MACRO 322 \s* $ 323 ''', 324 re.VERBOSE | re.IGNORECASE 325 ) 326 327 sectionDeclRe = re.compile(r''' 328 ([\w@][\w@0-9]*) \s+ 329 ( SECTION | ENDS ) 330 \s* $ 331 ''', 332 re.VERBOSE | re.IGNORECASE 333 ) 334 335 externRe = re.compile(r''' 336 EXTE?RN \s+ (?: C \s+ )? 337 ([\w@][\w@0-9]*) \s* : \s* (\w+) 338 \s* $ 339 ''', 340 re.VERBOSE | re.IGNORECASE 341 ) 342 343 externdefRe = re.compile(r''' 344 EXTERNDEF \s+ (?: C \s+ )? 345 ([\w@][\w@0-9]*) \s* : \s* (\w+) 346 \s* $ 347 ''', 348 re.VERBOSE | re.IGNORECASE 349 ) 350 351 protoRe = re.compile(r''' 352 ([\w@][\w@0-9]*) \s+ 353 PROTO 354 (?: \s+ .* )? 355 \s* $ 356 ''', 357 re.VERBOSE | re.IGNORECASE 358 ) 359 360 defineDataRe = re.compile(r''' 361 ([\w@][\w@0-9]*) \s+ 362 ( db | dw | dd | dq ) \s+ 363 ( .*? ) 364 \s* $ 365 ''', 366 re.VERBOSE | re.IGNORECASE 367 ) 368 369 equRe = re.compile(r''' 370 ([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?) 371 \s* $ 372 ''', 373 re.VERBOSE | re.IGNORECASE 374 ) 375 376 ignoreRe = re.compile(r''' 377 \. (?: const | 378 mmx | 379 model | 380 xmm | 381 x?list | 382 [3-6]86p? 383 ) | 384 page 385 (?: \s+ .* )? 386 \s* $ 387 ''', 388 re.VERBOSE | re.IGNORECASE 389 ) 390 391 whitespaceRe = re.compile(r'\s+', re.MULTILINE) 392 393 def TranslateAsm(self, oldAsm, endOfLine): 394 assert(oldAsm.strip() == oldAsm) 395 396 endOfLine = endOfLine.replace(self.inputFileBase, self.outputFileBase) 397 398 oldOp = oldAsm.split() 399 if len(oldOp) >= 1: 400 oldOp = oldOp[0] 401 else: 402 oldOp = '' 403 404 if oldAsm == '': 405 newAsm = oldAsm 406 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 407 elif oldOp in ('#include', ): 408 newAsm = oldAsm 409 self.EmitLine(oldAsm + endOfLine) 410 elif oldOp.lower() in ('end', 'title', 'text'): 411 newAsm = '' 412 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 413 elif oldAsm.lower() == '@@:': 414 self.anonLabelCount += 1 415 self.EmitLine(self.anonLabel(self.anonLabelCount) + ':') 416 elif self.MatchAndSetMo(self.ignoreRe, oldAsm): 417 newAsm = '' 418 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 419 elif oldAsm.lower() == 'ret': 420 for i in range(len(self.uses) - 1, -1, -1): 421 register = self.uses[i] 422 self.EmitNewContent('pop ' + register) 423 newAsm = 'ret' 424 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 425 self.uses = tuple() 426 elif oldOp.lower() == 'lea': 427 newAsm = self.ConvertLea(oldAsm) 428 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 429 elif oldAsm.lower() == 'end': 430 newAsm = '' 431 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 432 self.uses = tuple() 433 elif self.MatchAndSetMo(self.equRe, oldAsm): 434 equ = self.mo.group(1) 435 newAsm = '%%define %s %s' % (equ, self.mo.group(2)) 436 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 437 elif self.MatchAndSetMo(self.externRe, oldAsm) or \ 438 self.MatchAndSetMo(self.protoRe, oldAsm): 439 extern = self.mo.group(1) 440 self.NewGlobal(extern) 441 newAsm = 'extern ' + extern 442 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 443 elif self.MatchAndSetMo(self.externdefRe, oldAsm): 444 newAsm = '' 445 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 446 elif self.MatchAndSetMo(self.macroDeclRe, oldAsm): 447 newAsm = '%%macro %s 0' % self.mo.group(1) 448 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 449 elif oldOp.lower() == 'endm': 450 newAsm = r'%endmacro' 451 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 452 elif self.MatchAndSetMo(self.sectionDeclRe, oldAsm): 453 name = self.mo.group(1) 454 ty = self.mo.group(2) 455 if ty.lower() == 'section': 456 newAsm = '.' + name 457 else: 458 newAsm = '' 459 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 460 elif self.MatchAndSetMo(self.procDeclRe, oldAsm): 461 proc = self.proc = self.mo.group(1) 462 visibility = self.mo.group(2) 463 if visibility is None: 464 visibility = '' 465 else: 466 visibility = visibility.lower() 467 if visibility != 'private': 468 self.NewGlobal(self.proc) 469 proc = 'ASM_PFX(' + proc + ')' 470 self.EmitNewContent('global ' + proc) 471 newAsm = proc + ':' 472 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 473 uses = self.mo.group(3) 474 if uses is not None: 475 uses = filter(None, uses.split()) 476 else: 477 uses = tuple() 478 self.uses = uses 479 for register in self.uses: 480 self.EmitNewContent(' push ' + register) 481 elif self.MatchAndSetMo(self.procEndRe, oldAsm): 482 newAsm = '' 483 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 484 elif self.MatchAndSetMo(self.publicRe, oldAsm): 485 publics = re.findall(self.varAndTypeSubRe, self.mo.group(1)) 486 publics = map(lambda p: p.split(':')[0].strip(), publics) 487 for i in range(len(publics) - 1): 488 name = publics[i] 489 self.EmitNewContent('global ASM_PFX(%s)' % publics[i]) 490 self.NewGlobal(name) 491 name = publics[-1] 492 self.NewGlobal(name) 493 newAsm = 'global ASM_PFX(%s)' % name 494 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 495 elif self.MatchAndSetMo(self.defineDataRe, oldAsm): 496 name = self.mo.group(1) 497 ty = self.mo.group(2) 498 value = self.mo.group(3) 499 if value == '?': 500 value = 0 501 newAsm = '%s: %s %s' % (name, ty, value) 502 newAsm = self.CommonConversions(newAsm) 503 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 504 else: 505 newAsm = self.CommonConversions(oldAsm) 506 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 507 508 def NewGlobal(self, name): 509 regex = re.compile(r'(?<![_\w\d])(?<!ASM_PFX\()(' + re.escape(name) + 510 r')(?![_\w\d])') 511 self.globals.add(regex) 512 513 def ConvertAnonymousLabels(self, oldAsm): 514 newAsm = oldAsm 515 anonLabel = self.anonLabel(self.anonLabelCount) 516 newAsm = newAsm.replace('@b', anonLabel) 517 newAsm = newAsm.replace('@B', anonLabel) 518 anonLabel = self.anonLabel(self.anonLabelCount + 1) 519 newAsm = newAsm.replace('@f', anonLabel) 520 newAsm = newAsm.replace('@F', anonLabel) 521 return newAsm 522 523 def anonLabel(self, count): 524 return '.%d' % count 525 526 def EmitString(self, string): 527 self.output.write(string) 528 529 def EmitLineWithDiff(self, old, new): 530 newLine = (self.indent + new).rstrip() 531 if self.diff: 532 if old is None: 533 print '+%s' % newLine 534 elif newLine != old: 535 print '-%s' % old 536 print '+%s' % newLine 537 else: 538 print '', newLine 539 if newLine != '': 540 self.newAsmEmptyLineCount = 0 541 self.EmitString(newLine + '\r\n') 542 543 def EmitLine(self, string): 544 self.EmitLineWithDiff(self.originalLine, string) 545 546 def EmitNewContent(self, string): 547 self.EmitLineWithDiff(None, string) 548 549 def EmitAsmReplaceOp(self, oldAsm, oldOp, newOp, endOfLine): 550 newAsm = oldAsm.replace(oldOp, newOp, 1) 551 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 552 553 hexNumRe = re.compile(r'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re.IGNORECASE) 554 555 def EmitAsmWithComment(self, oldAsm, newAsm, endOfLine): 556 for glblRe in self.globals: 557 newAsm = glblRe.sub(r'ASM_PFX(\1)', newAsm) 558 559 newAsm = self.hexNumRe.sub(r'0x\1', newAsm) 560 561 newLine = newAsm + endOfLine 562 emitNewLine = ((newLine.strip() != '') or 563 ((oldAsm + endOfLine).strip() == '')) 564 if emitNewLine and newLine.strip() == '': 565 self.newAsmEmptyLineCount += 1 566 if self.newAsmEmptyLineCount > 1: 567 emitNewLine = False 568 if emitNewLine: 569 self.EmitLine(newLine.rstrip()) 570 elif self.diff: 571 print '-%s' % self.originalLine 572 573 leaRe = re.compile(r''' 574 (lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?) 575 \s* $ 576 ''', 577 re.VERBOSE | re.IGNORECASE 578 ) 579 580 def ConvertLea(self, oldAsm): 581 newAsm = oldAsm 582 if self.MatchAndSetMo(self.leaRe, oldAsm): 583 lea = self.mo.group(1) 584 dst = self.mo.group(2) 585 src = self.mo.group(3) 586 if src.find('[') < 0: 587 src = '[' + src + ']' 588 newAsm = lea + dst + ', ' + src 589 newAsm = self.CommonConversions(newAsm) 590 return newAsm 591 592 ptrRe = re.compile(r''' 593 (?<! \S ) 594 ([dfq]?word|byte) \s+ (?: ptr ) (\s*) 595 (?= [[\s] ) 596 ''', 597 re.VERBOSE | re.IGNORECASE 598 ) 599 600 def ConvertPtr(self, oldAsm): 601 newAsm = oldAsm 602 while self.SearchAndSetMo(self.ptrRe, newAsm): 603 ty = self.mo.group(1) 604 if ty.lower() == 'fword': 605 ty = '' 606 else: 607 ty += self.mo.group(2) 608 newAsm = newAsm[:self.mo.start(0)] + ty + newAsm[self.mo.end(0):] 609 return newAsm 610 611 labelByteRe = re.compile(r''' 612 (?: \s+ label \s+ (?: [dfq]?word | byte ) ) 613 (?! \S ) 614 ''', 615 re.VERBOSE | re.IGNORECASE 616 ) 617 618 def ConvertLabelByte(self, oldAsm): 619 newAsm = oldAsm 620 if self.SearchAndSetMo(self.labelByteRe, newAsm): 621 newAsm = newAsm[:self.mo.start(0)] + ':' + newAsm[self.mo.end(0):] 622 return newAsm 623 624 unaryBitwiseOpRe = re.compile(r''' 625 ( NOT ) 626 (?= \s+ \S ) 627 ''', 628 re.VERBOSE | re.IGNORECASE 629 ) 630 binaryBitwiseOpRe = re.compile(r''' 631 ( \S \s+ ) 632 ( AND | OR | SHL | SHR ) 633 (?= \s+ \S ) 634 ''', 635 re.VERBOSE | re.IGNORECASE 636 ) 637 bitwiseOpReplacements = { 638 'not': '~', 639 'and': '&', 640 'shl': '<<', 641 'shr': '>>', 642 'or': '|', 643 } 644 645 def ConvertBitwiseOp(self, oldAsm): 646 newAsm = oldAsm 647 while self.SearchAndSetMo(self.binaryBitwiseOpRe, newAsm): 648 prefix = self.mo.group(1) 649 op = self.bitwiseOpReplacements[self.mo.group(2).lower()] 650 newAsm = newAsm[:self.mo.start(0)] + prefix + op + \ 651 newAsm[self.mo.end(0):] 652 while self.SearchAndSetMo(self.unaryBitwiseOpRe, newAsm): 653 op = self.bitwiseOpReplacements[self.mo.group(1).lower()] 654 newAsm = newAsm[:self.mo.start(0)] + op + newAsm[self.mo.end(0):] 655 return newAsm 656 657 sectionRe = re.compile(r''' 658 \. ( code | 659 data 660 ) 661 (?: \s+ .* )? 662 \s* $ 663 ''', 664 re.VERBOSE | re.IGNORECASE 665 ) 666 667 segmentRe = re.compile(r''' 668 ( code | 669 data ) 670 (?: \s+ SEGMENT ) 671 (?: \s+ .* )? 672 \s* $ 673 ''', 674 re.VERBOSE | re.IGNORECASE 675 ) 676 677 def ConvertSection(self, oldAsm): 678 newAsm = oldAsm 679 if self.MatchAndSetMo(self.sectionRe, newAsm) or \ 680 self.MatchAndSetMo(self.segmentRe, newAsm): 681 name = self.mo.group(1).lower() 682 if name == 'code': 683 if self.x64: 684 self.EmitLine('DEFAULT REL') 685 name = 'text' 686 newAsm = 'SECTION .' + name 687 return newAsm 688 689 fwordRe = re.compile(r''' 690 (?<! \S ) 691 fword 692 (?! \S ) 693 ''', 694 re.VERBOSE | re.IGNORECASE 695 ) 696 697 def FwordUnsupportedCheck(self, oldAsm): 698 newAsm = oldAsm 699 if self.SearchAndSetMo(self.fwordRe, newAsm): 700 newAsm = self.Unsupported(newAsm, 'fword used') 701 return newAsm 702 703 __common_conversion_routines__ = ( 704 ConvertAnonymousLabels, 705 ConvertPtr, 706 FwordUnsupportedCheck, 707 ConvertBitwiseOp, 708 ConvertLabelByte, 709 ConvertSection, 710 ) 711 712 def CommonConversions(self, oldAsm): 713 newAsm = oldAsm 714 for conv in self.__common_conversion_routines__: 715 newAsm = conv(self, newAsm) 716 return newAsm 717 718 def Unsupported(self, asm, message=None): 719 if not self.force: 720 raise UnsupportedConversion 721 722 self.unsupportedSyntaxSeen = True 723 newAsm = '%error conversion unsupported' 724 if message: 725 newAsm += '; ' + message 726 newAsm += ': ' + asm 727 return newAsm 728 729 730class ConvertInfFile(CommonUtils): 731 732 def __init__(self, inf, clone): 733 CommonUtils.__init__(self, clone) 734 self.inf = inf 735 self.ScanInfAsmFiles() 736 if self.infmode: 737 self.ConvertInfAsmFiles() 738 739 infSrcRe = re.compile(r''' 740 \s* 741 ( [\w@][\w@0-9/]* \.(asm|s) ) 742 \s* (?: \| [^#]* )? 743 \s* (?: \# .* )? 744 $ 745 ''', 746 re.VERBOSE | re.IGNORECASE 747 ) 748 749 def GetInfAsmFileMapping(self): 750 srcToDst = {'order': []} 751 for line in self.lines: 752 line = line.rstrip() 753 if self.MatchAndSetMo(self.infSrcRe, line): 754 src = self.mo.group(1) 755 srcExt = self.mo.group(2) 756 dst = os.path.splitext(src)[0] + '.nasm' 757 if src not in srcToDst: 758 srcToDst[src] = dst 759 srcToDst['order'].append(src) 760 return srcToDst 761 762 def ScanInfAsmFiles(self): 763 src = self.inf 764 assert os.path.isfile(src) 765 f = open(src) 766 self.lines = f.readlines() 767 f.close() 768 769 path = os.path.realpath(self.inf) 770 (self.dir, inf) = os.path.split(path) 771 parent = os.path.normpath(self.dir) 772 (lastpath, self.moduleName) = os.path.split(parent) 773 self.packageName = None 774 while True: 775 lastpath = os.path.normpath(lastpath) 776 (parent, basename) = os.path.split(lastpath) 777 if parent == lastpath: 778 break 779 if basename.endswith('Pkg'): 780 self.packageName = basename 781 break 782 lastpath = parent 783 784 self.srcToDst = self.GetInfAsmFileMapping() 785 786 self.dstToSrc = {'order': []} 787 for src in self.srcToDst['order']: 788 srcExt = os.path.splitext(src)[1] 789 dst = self.srcToDst[src] 790 if dst not in self.dstToSrc: 791 self.dstToSrc[dst] = [src] 792 self.dstToSrc['order'].append(dst) 793 else: 794 self.dstToSrc[dst].append(src) 795 796 def __len__(self): 797 return len(self.dstToSrc['order']) 798 799 def __iter__(self): 800 return iter(self.dstToSrc['order']) 801 802 def ConvertInfAsmFiles(self): 803 notConverted = [] 804 unsupportedArchCount = 0 805 for dst in self: 806 didSomething = False 807 fileChanged = self.UpdateInfAsmFile(dst) 808 try: 809 self.UpdateInfAsmFile(dst) 810 didSomething = True 811 except UnsupportedConversion: 812 if not self.Opt.quiet: 813 print 'MASM=>NASM conversion unsupported for', dst 814 notConverted.append(dst) 815 except NoSourceFile: 816 if not self.Opt.quiet: 817 print 'Source file missing for', reldst 818 notConverted.append(dst) 819 except UnsupportedArch: 820 unsupportedArchCount += 1 821 else: 822 if didSomething: 823 self.ConversionFinished(dst) 824 if len(notConverted) > 0 and not self.Opt.quiet: 825 for dst in notConverted: 826 reldst = self.RootRelative(dst) 827 print 'Unabled to convert', reldst 828 if unsupportedArchCount > 0 and not self.Opt.quiet: 829 print 'Skipped', unsupportedArchCount, 'files based on architecture' 830 831 def UpdateInfAsmFile(self, dst, IgnoreMissingAsm=False): 832 infPath = os.path.split(os.path.realpath(self.inf))[0] 833 asmSrc = os.path.splitext(dst)[0] + '.asm' 834 fullSrc = os.path.join(infPath, asmSrc) 835 fullDst = os.path.join(infPath, dst) 836 srcParentDir = os.path.basename(os.path.split(fullSrc)[0]) 837 if srcParentDir.lower() in UnsupportedArch.unsupported: 838 raise UnsupportedArch 839 elif not os.path.exists(fullSrc): 840 if not IgnoreMissingAsm: 841 raise NoSourceFile 842 else: # not os.path.exists(fullDst): 843 conv = ConvertAsmFile(fullSrc, fullDst, self) 844 self.unsupportedSyntaxSeen = conv.unsupportedSyntaxSeen 845 846 lastLine = '' 847 fileChanged = False 848 for i in range(len(self.lines)): 849 line = self.lines[i].rstrip() 850 updatedLine = line 851 for src in self.dstToSrc[dst]: 852 assert self.srcToDst[src] == dst 853 updatedLine = self.ReplacePreserveSpacing( 854 updatedLine, src, dst) 855 856 lineChanged = updatedLine != line 857 if lineChanged: 858 if lastLine.strip() == updatedLine.strip(): 859 self.lines[i] = None 860 else: 861 self.lines[i] = updatedLine + '\r\n' 862 863 if self.diff: 864 if lineChanged: 865 print '-%s' % line 866 if self.lines[i] is not None: 867 print '+%s' % updatedLine 868 else: 869 print '', line 870 871 fileChanged |= lineChanged 872 if self.lines[i] is not None: 873 lastLine = self.lines[i] 874 875 if fileChanged: 876 self.lines = filter(lambda l: l is not None, self.lines) 877 878 for src in self.dstToSrc[dst]: 879 if not src.endswith('.asm'): 880 fullSrc = os.path.join(infPath, src) 881 if os.path.exists(fullSrc): 882 self.RemoveFile(fullSrc) 883 884 if fileChanged: 885 f = open(self.inf, 'wb') 886 f.writelines(self.lines) 887 f.close() 888 self.FileUpdated(self.inf) 889 890 def ConversionFinished(self, dst): 891 asmSrc = os.path.splitext(dst)[0] + '.asm' 892 self.FileConversionFinished( 893 self.packageName, self.moduleName, asmSrc, dst) 894 895 896class ConvertInfFiles(CommonUtils): 897 898 def __init__(self, infs, clone): 899 CommonUtils.__init__(self, clone) 900 infs = map(lambda i: ConvertInfFile(i, self), infs) 901 infs = filter(lambda i: len(i) > 0, infs) 902 dstToInfs = {'order': []} 903 for inf in infs: 904 for dst in inf: 905 fulldst = os.path.realpath(os.path.join(inf.dir, dst)) 906 pair = (inf, dst) 907 if fulldst in dstToInfs: 908 dstToInfs[fulldst].append(pair) 909 else: 910 dstToInfs['order'].append(fulldst) 911 dstToInfs[fulldst] = [pair] 912 913 notConverted = [] 914 unsupportedArchCount = 0 915 for dst in dstToInfs['order']: 916 didSomething = False 917 try: 918 for inf, reldst in dstToInfs[dst]: 919 inf.UpdateInfAsmFile(reldst, IgnoreMissingAsm=didSomething) 920 didSomething = True 921 except UnsupportedConversion: 922 if not self.Opt.quiet: 923 print 'MASM=>NASM conversion unsupported for', reldst 924 notConverted.append(dst) 925 except NoSourceFile: 926 if not self.Opt.quiet: 927 print 'Source file missing for', reldst 928 notConverted.append(dst) 929 except UnsupportedArch: 930 unsupportedArchCount += 1 931 else: 932 if didSomething: 933 inf.ConversionFinished(reldst) 934 if len(notConverted) > 0 and not self.Opt.quiet: 935 for dst in notConverted: 936 reldst = self.RootRelative(dst) 937 print 'Unabled to convert', reldst 938 if unsupportedArchCount > 0 and not self.Opt.quiet: 939 print 'Skipped', unsupportedArchCount, 'files based on architecture' 940 941 942class ConvertDirectories(CommonUtils): 943 944 def __init__(self, paths, clone): 945 CommonUtils.__init__(self, clone) 946 self.paths = paths 947 self.ConvertInfAndAsmFiles() 948 949 def ConvertInfAndAsmFiles(self): 950 infs = list() 951 for path in self.paths: 952 assert(os.path.exists(path)) 953 for path in self.paths: 954 for root, dirs, files in os.walk(path): 955 for d in ('.svn', '.git'): 956 if d in dirs: 957 dirs.remove(d) 958 for f in files: 959 if f.lower().endswith('.inf'): 960 inf = os.path.realpath(os.path.join(root, f)) 961 infs.append(inf) 962 963 ConvertInfFiles(infs, self) 964 965 966class ConvertAsmApp(CommonUtils): 967 968 def __init__(self): 969 CommonUtils.__init__(self) 970 971 numArgs = len(self.Args) 972 assert(numArgs >= 1) 973 if self.infmode: 974 ConvertInfFiles(self.Args, self) 975 elif self.dirmode: 976 ConvertDirectories(self.Args, self) 977 elif not self.dirmode: 978 assert(numArgs <= 2) 979 src = self.Args[0] 980 if numArgs > 1: 981 dst = self.Args[1] 982 else: 983 dst = None 984 ConvertAsmFile(src, dst, self) 985 986ConvertAsmApp() 987