Vishal Bhoj | 82c8071 | 2015-12-15 21:13:33 +0530 | [diff] [blame^] | 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 | #
|
| 18 | import os.path
|
| 19 | import re
|
| 20 | import StringIO
|
| 21 | import subprocess
|
| 22 | import sys
|
| 23 | from optparse import OptionParser
|
| 24 |
|
| 25 |
|
| 26 | class UnsupportedConversion(Exception):
|
| 27 | pass
|
| 28 |
|
| 29 |
|
| 30 | class NoSourceFile(Exception):
|
| 31 | pass
|
| 32 |
|
| 33 |
|
| 34 | class UnsupportedArch(Exception):
|
| 35 | unsupported = ('aarch64', 'arm', 'ebc', 'ipf')
|
| 36 |
|
| 37 |
|
| 38 | class 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 |
|
| 206 | class 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 |
|
| 730 | class 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 |
|
| 896 | class 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 |
|
| 942 | class 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 |
|
| 966 | class 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 |
|
| 986 | ConvertAsmApp()
|