https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Bob Feng <bob.c.feng@intel.com>
		
			
				
	
	
		
			1006 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1006 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| # @file ConvertMasmToNasm.py
 | |
| # This script assists with conversion of MASM assembly syntax to NASM
 | |
| #
 | |
| #  Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
 | |
| #
 | |
| #  SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| #
 | |
| 
 | |
| from __future__ import print_function
 | |
| 
 | |
| #
 | |
| # Import Modules
 | |
| #
 | |
| import argparse
 | |
| import io
 | |
| import os.path
 | |
| import re
 | |
| import subprocess
 | |
| import sys
 | |
| 
 | |
| 
 | |
| class UnsupportedConversion(Exception):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class NoSourceFile(Exception):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class UnsupportedArch(Exception):
 | |
|     unsupported = ('aarch64', 'arm', 'ebc', 'ipf')
 | |
| 
 | |
| 
 | |
| class CommonUtils:
 | |
| 
 | |
|     # Version and Copyright
 | |
|     VersionNumber = "0.01"
 | |
|     __version__ = "%prog Version " + VersionNumber
 | |
|     __copyright__ = "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved."
 | |
|     __usage__ = "%prog [options] source.asm [destination.nasm]"
 | |
| 
 | |
|     def __init__(self, clone=None):
 | |
|         if clone is None:
 | |
|             self.args = self.ProcessCommandLine()
 | |
|         else:
 | |
|             self.args = clone.args
 | |
| 
 | |
|         self.unsupportedSyntaxSeen = False
 | |
|         self.src = self.args.source
 | |
|         self.keep = self.args.keep
 | |
|         assert(os.path.exists(self.src))
 | |
|         self.dirmode = os.path.isdir(self.src)
 | |
|         srcExt = os.path.splitext(self.src)[1]
 | |
|         assert (self.dirmode or srcExt != '.nasm')
 | |
|         self.infmode = not self.dirmode and srcExt == '.inf'
 | |
|         self.diff = self.args.diff
 | |
|         self.git = self.args.git
 | |
|         self.force = self.args.force
 | |
| 
 | |
|         if clone is None:
 | |
|             self.rootdir = os.getcwd()
 | |
|             self.DetectGit()
 | |
|         else:
 | |
|             self.rootdir = clone.rootdir
 | |
|             self.gitdir = clone.gitdir
 | |
|             self.gitemail = clone.gitemail
 | |
| 
 | |
|     def ProcessCommandLine(self):
 | |
|         parser = argparse.ArgumentParser(description=self.__copyright__)
 | |
|         parser.add_argument('--version', action='version',
 | |
|                             version='%(prog)s ' + self.VersionNumber)
 | |
|         parser.add_argument("-q", "--quiet", action="store_true",
 | |
|                             help="Disable all messages except FATAL ERRORS.")
 | |
|         parser.add_argument("--git", action="store_true",
 | |
|                             help="Use git to create commits for each file converted")
 | |
|         parser.add_argument("--keep", action="append", choices=('asm', 's'),
 | |
|                             default=[],
 | |
|                             help="Don't remove files with this extension")
 | |
|         parser.add_argument("--diff", action="store_true",
 | |
|                             help="Show diff of conversion")
 | |
|         parser.add_argument("-f", "--force", action="store_true",
 | |
|                             help="Force conversion even if unsupported")
 | |
|         parser.add_argument('source', help='MASM input file')
 | |
|         parser.add_argument('dest', nargs='?',
 | |
|                             help='NASM output file (default=input.nasm; - for stdout)')
 | |
| 
 | |
|         return parser.parse_args()
 | |
| 
 | |
|     def RootRelative(self, path):
 | |
|         result = path
 | |
|         if result.startswith(self.rootdir):
 | |
|             result = result[len(self.rootdir):]
 | |
|             while len(result) > 0 and result[0] in '/\\':
 | |
|                 result = result[1:]
 | |
|         return result
 | |
| 
 | |
|     def MatchAndSetMo(self, regexp, string):
 | |
|         self.mo = regexp.match(string)
 | |
|         return self.mo is not None
 | |
| 
 | |
|     def SearchAndSetMo(self, regexp, string):
 | |
|         self.mo = regexp.search(string)
 | |
|         return self.mo is not None
 | |
| 
 | |
|     def ReplacePreserveSpacing(self, string, find, replace):
 | |
|         if len(find) >= len(replace):
 | |
|             padded = replace + (' ' * (len(find) - len(replace)))
 | |
|             return string.replace(find, padded)
 | |
|         elif find.find(replace) >= 0:
 | |
|             return string.replace(find, replace)
 | |
|         else:
 | |
|             lenDiff = len(replace) - len(find)
 | |
|             result = string
 | |
|             for i in range(lenDiff, -1, -1):
 | |
|                 padded = find + (' ' * i)
 | |
|                 result = result.replace(padded, replace)
 | |
|             return result
 | |
| 
 | |
|     def DetectGit(self):
 | |
|         lastpath = os.path.realpath(self.src)
 | |
|         self.gitdir = None
 | |
|         while True:
 | |
|             path = os.path.split(lastpath)[0]
 | |
|             if path == lastpath:
 | |
|                 self.gitemail = None
 | |
|                 return
 | |
|             candidate = os.path.join(path, '.git')
 | |
|             if os.path.isdir(candidate):
 | |
|                 self.gitdir = candidate
 | |
|                 self.gitemail = self.FormatGitEmailAddress()
 | |
|                 return
 | |
|             lastpath = path
 | |
| 
 | |
|     def FormatGitEmailAddress(self):
 | |
|         if not self.git or not self.gitdir:
 | |
|             return ''
 | |
| 
 | |
|         cmd = ('git', 'config', 'user.name')
 | |
|         name = self.RunAndCaptureOutput(cmd).strip()
 | |
|         cmd = ('git', 'config', 'user.email')
 | |
|         email = self.RunAndCaptureOutput(cmd).strip()
 | |
|         if name.find(',') >= 0:
 | |
|             name = '"' + name + '"'
 | |
|         return name + ' <' + email + '>'
 | |
| 
 | |
|     def RunAndCaptureOutput(self, cmd, checkExitCode=True, pipeIn=None):
 | |
|         if pipeIn:
 | |
|             subpStdin = subprocess.PIPE
 | |
|         else:
 | |
|             subpStdin = None
 | |
|         p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stdin=subpStdin)
 | |
|         (stdout, stderr) = p.communicate(pipeIn)
 | |
|         if checkExitCode:
 | |
|             if p.returncode != 0:
 | |
|                 print('command:', ' '.join(cmd))
 | |
|                 print('stdout:', stdout)
 | |
|                 print('stderr:', stderr)
 | |
|                 print('return:', p.returncode)
 | |
|             assert p.returncode == 0
 | |
|         return stdout.decode('utf-8', 'ignore')
 | |
| 
 | |
|     def FileUpdated(self, path):
 | |
|         if not self.git or not self.gitdir:
 | |
|             return
 | |
| 
 | |
|         cmd = ('git', 'add', path)
 | |
|         self.RunAndCaptureOutput(cmd)
 | |
| 
 | |
|     def FileAdded(self, path):
 | |
|         self.FileUpdated(path)
 | |
| 
 | |
|     def RemoveFile(self, path):
 | |
|         if not self.git or not self.gitdir:
 | |
|             return
 | |
| 
 | |
|         if self.ShouldKeepFile(path):
 | |
|             return
 | |
| 
 | |
|         cmd = ('git', 'rm', path)
 | |
|         self.RunAndCaptureOutput(cmd)
 | |
| 
 | |
|     def ShouldKeepFile(self, path):
 | |
|         ext = os.path.splitext(path)[1].lower()
 | |
|         if ext.startswith('.'):
 | |
|             ext = ext[1:]
 | |
|         return ext in self.keep
 | |
| 
 | |
|     def FileConversionFinished(self, pkg, module, src, dst):
 | |
|         if not self.git or not self.gitdir:
 | |
|             return
 | |
| 
 | |
|         if not self.args.quiet:
 | |
|             print('Committing: Conversion of', dst)
 | |
| 
 | |
|         prefix = ' '.join(filter(lambda a: a, [pkg, module]))
 | |
|         message = ''
 | |
|         if self.unsupportedSyntaxSeen:
 | |
|             message += 'ERROR! '
 | |
|         message += '%s: Convert %s to NASM\n' % (prefix, src)
 | |
|         message += '\n'
 | |
|         message += 'The %s script was used to convert\n' % sys.argv[0]
 | |
|         message += '%s to %s\n' % (src, dst)
 | |
|         message += '\n'
 | |
|         message += 'Contributed-under: TianoCore Contribution Agreement 1.0\n'
 | |
|         assert(self.gitemail is not None)
 | |
|         message += 'Signed-off-by: %s\n' % self.gitemail
 | |
|         message = message.encode('utf-8', 'ignore')
 | |
| 
 | |
|         cmd = ('git', 'commit', '-F', '-')
 | |
|         self.RunAndCaptureOutput(cmd, pipeIn=message)
 | |
| 
 | |
| 
 | |
| class ConvertAsmFile(CommonUtils):
 | |
| 
 | |
|     def __init__(self, src, dst, clone):
 | |
|         CommonUtils.__init__(self, clone)
 | |
|         self.ConvertAsmFile(src, dst)
 | |
|         self.FileAdded(dst)
 | |
|         self.RemoveFile(src)
 | |
| 
 | |
|     def ConvertAsmFile(self, inputFile, outputFile=None):
 | |
|         self.globals = set()
 | |
|         self.unsupportedSyntaxSeen = False
 | |
|         self.inputFilename = inputFile
 | |
|         if not outputFile:
 | |
|             outputFile = os.path.splitext(inputFile)[0] + '.nasm'
 | |
|         self.outputFilename = outputFile
 | |
| 
 | |
|         fullSrc = os.path.realpath(inputFile)
 | |
|         srcParentDir = os.path.basename(os.path.split(fullSrc)[0])
 | |
|         maybeArch = srcParentDir.lower()
 | |
|         if maybeArch in UnsupportedArch.unsupported:
 | |
|             raise UnsupportedArch
 | |
|         self.ia32 = maybeArch == 'ia32'
 | |
|         self.x64 = maybeArch == 'x64'
 | |
| 
 | |
|         self.inputFileBase = os.path.basename(self.inputFilename)
 | |
|         self.outputFileBase = os.path.basename(self.outputFilename)
 | |
|         self.output = io.BytesIO()
 | |
|         if not self.args.quiet:
 | |
|             dirpath, src = os.path.split(self.inputFilename)
 | |
|             dirpath = self.RootRelative(dirpath)
 | |
|             dst = os.path.basename(self.outputFilename)
 | |
|             print('Converting:', dirpath, src, '->', dst)
 | |
|         lines = io.open(self.inputFilename).readlines()
 | |
|         self.Convert(lines)
 | |
|         if self.outputFilename == '-' and not self.diff:
 | |
|             output_data = self.output.getvalue()
 | |
|             if sys.version_info >= (3, 0):
 | |
|                 output_data = output_data.decode('utf-8', 'ignore')
 | |
|             sys.stdout.write(output_data)
 | |
|             self.output.close()
 | |
|         else:
 | |
|             f = io.open(self.outputFilename, 'wb')
 | |
|             f.write(self.output.getvalue())
 | |
|             f.close()
 | |
|             self.output.close()
 | |
| 
 | |
|     endOfLineRe = re.compile(r'''
 | |
|                                  \s* ( ; .* )? \n $
 | |
|                              ''',
 | |
|                              re.VERBOSE | re.MULTILINE
 | |
|                              )
 | |
|     begOfLineRe = re.compile(r'''
 | |
|                                  \s*
 | |
|                              ''',
 | |
|                              re.VERBOSE
 | |
|                              )
 | |
| 
 | |
|     def Convert(self, lines):
 | |
|         self.proc = None
 | |
|         self.anonLabelCount = -1
 | |
|         output = self.output
 | |
|         self.oldAsmEmptyLineCount = 0
 | |
|         self.newAsmEmptyLineCount = 0
 | |
|         for line in lines:
 | |
|             mo = self.begOfLineRe.search(line)
 | |
|             assert mo is not None
 | |
|             self.indent = mo.group()
 | |
|             lineWithoutBeginning = line[len(self.indent):]
 | |
|             mo = self.endOfLineRe.search(lineWithoutBeginning)
 | |
|             if mo is None:
 | |
|                 endOfLine = ''
 | |
|             else:
 | |
|                 endOfLine = mo.group()
 | |
|             oldAsm = line[len(self.indent):len(line) - len(endOfLine)]
 | |
|             self.originalLine = line.rstrip()
 | |
|             if line.strip() == '':
 | |
|                 self.oldAsmEmptyLineCount += 1
 | |
|             self.TranslateAsm(oldAsm, endOfLine)
 | |
|             if line.strip() != '':
 | |
|                 self.oldAsmEmptyLineCount = 0
 | |
| 
 | |
|     procDeclRe = re.compile(r'''
 | |
|                                 (?: ASM_PFX \s* [(] \s* )?
 | |
|                                 ([\w@][\w@0-9]*) \s*
 | |
|                                 [)]? \s+
 | |
|                                 PROC
 | |
|                                 (?: \s+ NEAR | FAR )?
 | |
|                                 (?: \s+ C )?
 | |
|                                 (?: \s+ (PUBLIC | PRIVATE) )?
 | |
|                                 (?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )?
 | |
|                                 \s* $
 | |
|                             ''',
 | |
|                             re.VERBOSE | re.IGNORECASE
 | |
|                             )
 | |
| 
 | |
|     procEndRe = re.compile(r'''
 | |
|                                ([\w@][\w@0-9]*) \s+
 | |
|                                ENDP
 | |
|                                \s* $
 | |
|                            ''',
 | |
|                            re.VERBOSE | re.IGNORECASE
 | |
|                            )
 | |
| 
 | |
|     varAndTypeSubRe = r' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? '
 | |
|     publicRe = re.compile(r'''
 | |
|                               PUBLIC \s+
 | |
|                               ( %s (?: \s* , \s* %s )* )
 | |
|                               \s* $
 | |
|                           ''' % (varAndTypeSubRe, varAndTypeSubRe),
 | |
|                           re.VERBOSE | re.IGNORECASE
 | |
|                           )
 | |
| 
 | |
|     varAndTypeSubRe = re.compile(varAndTypeSubRe, re.VERBOSE | re.IGNORECASE)
 | |
| 
 | |
|     macroDeclRe = re.compile(r'''
 | |
|                                  ([\w@][\w@0-9]*) \s+
 | |
|                                  MACRO
 | |
|                                  \s* $
 | |
|                              ''',
 | |
|                              re.VERBOSE | re.IGNORECASE
 | |
|                              )
 | |
| 
 | |
|     sectionDeclRe = re.compile(r'''
 | |
|                                    ([\w@][\w@0-9]*) \s+
 | |
|                                    ( SECTION | ENDS )
 | |
|                                    \s* $
 | |
|                                ''',
 | |
|                                re.VERBOSE | re.IGNORECASE
 | |
|                                )
 | |
| 
 | |
|     externRe = re.compile(r'''
 | |
|                               EXTE?RN \s+ (?: C \s+ )?
 | |
|                               ([\w@][\w@0-9]*) \s* : \s* (\w+)
 | |
|                               \s* $
 | |
|                            ''',
 | |
|                           re.VERBOSE | re.IGNORECASE
 | |
|                           )
 | |
| 
 | |
|     externdefRe = re.compile(r'''
 | |
|                                  EXTERNDEF \s+ (?: C \s+ )?
 | |
|                                  ([\w@][\w@0-9]*) \s* : \s* (\w+)
 | |
|                                  \s* $
 | |
|                              ''',
 | |
|                              re.VERBOSE | re.IGNORECASE
 | |
|                              )
 | |
| 
 | |
|     protoRe = re.compile(r'''
 | |
|                              ([\w@][\w@0-9]*) \s+
 | |
|                              PROTO
 | |
|                              (?: \s+ .* )?
 | |
|                              \s* $
 | |
|                          ''',
 | |
|                          re.VERBOSE | re.IGNORECASE
 | |
|                          )
 | |
| 
 | |
|     defineDataRe = re.compile(r'''
 | |
|                                   ([\w@][\w@0-9]*) \s+
 | |
|                                   ( db | dw | dd | dq ) \s+
 | |
|                                   ( .*? )
 | |
|                                   \s* $
 | |
|                               ''',
 | |
|                               re.VERBOSE | re.IGNORECASE
 | |
|                               )
 | |
| 
 | |
|     equRe = re.compile(r'''
 | |
|                            ([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?)
 | |
|                            \s* $
 | |
|                        ''',
 | |
|                        re.VERBOSE | re.IGNORECASE
 | |
|                        )
 | |
| 
 | |
|     ignoreRe = re.compile(r'''
 | |
|                               \. (?: const |
 | |
|                                      mmx |
 | |
|                                      model |
 | |
|                                      xmm |
 | |
|                                      x?list |
 | |
|                                      [3-6]86p?
 | |
|                                  ) |
 | |
|                               page
 | |
|                               (?: \s+ .* )?
 | |
|                               \s* $
 | |
|                           ''',
 | |
|                           re.VERBOSE | re.IGNORECASE
 | |
|                           )
 | |
| 
 | |
|     whitespaceRe = re.compile(r'\s+', re.MULTILINE)
 | |
| 
 | |
|     def TranslateAsm(self, oldAsm, endOfLine):
 | |
|         assert(oldAsm.strip() == oldAsm)
 | |
| 
 | |
|         endOfLine = endOfLine.replace(self.inputFileBase, self.outputFileBase)
 | |
| 
 | |
|         oldOp = oldAsm.split()
 | |
|         if len(oldOp) >= 1:
 | |
|             oldOp = oldOp[0]
 | |
|         else:
 | |
|             oldOp = ''
 | |
| 
 | |
|         if oldAsm == '':
 | |
|             newAsm = oldAsm
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif oldOp in ('#include', ):
 | |
|             newAsm = oldAsm
 | |
|             self.EmitLine(oldAsm + endOfLine)
 | |
|         elif oldOp.lower() in ('end', 'title', 'text'):
 | |
|             newAsm = ''
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif oldAsm.lower() == '@@:':
 | |
|             self.anonLabelCount += 1
 | |
|             self.EmitLine(self.anonLabel(self.anonLabelCount) + ':')
 | |
|         elif self.MatchAndSetMo(self.ignoreRe, oldAsm):
 | |
|             newAsm = ''
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif oldAsm.lower() == 'ret':
 | |
|             for i in range(len(self.uses) - 1, -1, -1):
 | |
|                 register = self.uses[i]
 | |
|                 self.EmitNewContent('pop     ' + register)
 | |
|             newAsm = 'ret'
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|             self.uses = tuple()
 | |
|         elif oldOp.lower() == 'lea':
 | |
|             newAsm = self.ConvertLea(oldAsm)
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif oldAsm.lower() == 'end':
 | |
|             newAsm = ''
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|             self.uses = tuple()
 | |
|         elif self.MatchAndSetMo(self.equRe, oldAsm):
 | |
|             equ = self.mo.group(1)
 | |
|             newAsm = '%%define %s %s' % (equ, self.mo.group(2))
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif self.MatchAndSetMo(self.externRe, oldAsm) or \
 | |
|                 self.MatchAndSetMo(self.protoRe, oldAsm):
 | |
|             extern = self.mo.group(1)
 | |
|             self.NewGlobal(extern)
 | |
|             newAsm = 'extern ' + extern
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif self.MatchAndSetMo(self.externdefRe, oldAsm):
 | |
|             newAsm = ''
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif self.MatchAndSetMo(self.macroDeclRe, oldAsm):
 | |
|             newAsm = '%%macro %s 0' % self.mo.group(1)
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif oldOp.lower() == 'endm':
 | |
|             newAsm = r'%endmacro'
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif self.MatchAndSetMo(self.sectionDeclRe, oldAsm):
 | |
|             name = self.mo.group(1)
 | |
|             ty = self.mo.group(2)
 | |
|             if ty.lower() == 'section':
 | |
|                 newAsm = '.' + name
 | |
|             else:
 | |
|                 newAsm = ''
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif self.MatchAndSetMo(self.procDeclRe, oldAsm):
 | |
|             proc = self.proc = self.mo.group(1)
 | |
|             visibility = self.mo.group(2)
 | |
|             if visibility is None:
 | |
|                 visibility = ''
 | |
|             else:
 | |
|                 visibility = visibility.lower()
 | |
|             if visibility != 'private':
 | |
|                 self.NewGlobal(self.proc)
 | |
|                 proc = 'ASM_PFX(' + proc + ')'
 | |
|                 self.EmitNewContent('global ' + proc)
 | |
|             newAsm = proc + ':'
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|             uses = self.mo.group(3)
 | |
|             if uses is not None:
 | |
|                 uses = tuple(filter(None, uses.split()))
 | |
|             else:
 | |
|                 uses = tuple()
 | |
|             self.uses = uses
 | |
|             for register in self.uses:
 | |
|                 self.EmitNewContent('    push    ' + register)
 | |
|         elif self.MatchAndSetMo(self.procEndRe, oldAsm):
 | |
|             newAsm = ''
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif self.MatchAndSetMo(self.publicRe, oldAsm):
 | |
|             publics = re.findall(self.varAndTypeSubRe, self.mo.group(1))
 | |
|             publics = tuple(map(lambda p: p.split(':')[0].strip(), publics))
 | |
|             for i in range(len(publics) - 1):
 | |
|                 name = publics[i]
 | |
|                 self.EmitNewContent('global ASM_PFX(%s)' % publics[i])
 | |
|                 self.NewGlobal(name)
 | |
|             name = publics[-1]
 | |
|             self.NewGlobal(name)
 | |
|             newAsm = 'global ASM_PFX(%s)' % name
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         elif self.MatchAndSetMo(self.defineDataRe, oldAsm):
 | |
|             name = self.mo.group(1)
 | |
|             ty = self.mo.group(2)
 | |
|             value = self.mo.group(3)
 | |
|             if value == '?':
 | |
|                 value = 0
 | |
|             newAsm = '%s: %s %s' % (name, ty, value)
 | |
|             newAsm = self.CommonConversions(newAsm)
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
|         else:
 | |
|             newAsm = self.CommonConversions(oldAsm)
 | |
|             self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
| 
 | |
|     def NewGlobal(self, name):
 | |
|         regex = re.compile(r'(?<![_\w\d])(?<!ASM_PFX\()(' + re.escape(name) +
 | |
|                            r')(?![_\w\d])')
 | |
|         self.globals.add(regex)
 | |
| 
 | |
|     def ConvertAnonymousLabels(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         anonLabel = self.anonLabel(self.anonLabelCount)
 | |
|         newAsm = newAsm.replace('@b', anonLabel)
 | |
|         newAsm = newAsm.replace('@B', anonLabel)
 | |
|         anonLabel = self.anonLabel(self.anonLabelCount + 1)
 | |
|         newAsm = newAsm.replace('@f', anonLabel)
 | |
|         newAsm = newAsm.replace('@F', anonLabel)
 | |
|         return newAsm
 | |
| 
 | |
|     def anonLabel(self, count):
 | |
|         return '.%d' % count
 | |
| 
 | |
|     def EmitString(self, string):
 | |
|         self.output.write(string.encode('utf-8', 'ignore'))
 | |
| 
 | |
|     def EmitLineWithDiff(self, old, new):
 | |
|         newLine = (self.indent + new).rstrip()
 | |
|         if self.diff:
 | |
|             if old is None:
 | |
|                 print('+%s' % newLine)
 | |
|             elif newLine != old:
 | |
|                 print('-%s' % old)
 | |
|                 print('+%s' % newLine)
 | |
|             else:
 | |
|                 print('', newLine)
 | |
|         if newLine != '':
 | |
|             self.newAsmEmptyLineCount = 0
 | |
|         self.EmitString(newLine + '\r\n')
 | |
| 
 | |
|     def EmitLine(self, string):
 | |
|         self.EmitLineWithDiff(self.originalLine, string)
 | |
| 
 | |
|     def EmitNewContent(self, string):
 | |
|         self.EmitLineWithDiff(None, string)
 | |
| 
 | |
|     def EmitAsmReplaceOp(self, oldAsm, oldOp, newOp, endOfLine):
 | |
|         newAsm = oldAsm.replace(oldOp, newOp, 1)
 | |
|         self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
 | |
| 
 | |
|     hexNumRe = re.compile(r'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re.IGNORECASE)
 | |
| 
 | |
|     def EmitAsmWithComment(self, oldAsm, newAsm, endOfLine):
 | |
|         for glblRe in self.globals:
 | |
|             newAsm = glblRe.sub(r'ASM_PFX(\1)', newAsm)
 | |
| 
 | |
|         newAsm = self.hexNumRe.sub(r'0x\1', newAsm)
 | |
| 
 | |
|         newLine = newAsm + endOfLine
 | |
|         emitNewLine = ((newLine.strip() != '') or
 | |
|                        ((oldAsm + endOfLine).strip() == ''))
 | |
|         if emitNewLine and newLine.strip() == '':
 | |
|             self.newAsmEmptyLineCount += 1
 | |
|             if self.newAsmEmptyLineCount > 1:
 | |
|                 emitNewLine = False
 | |
|         if emitNewLine:
 | |
|             self.EmitLine(newLine.rstrip())
 | |
|         elif self.diff:
 | |
|             print('-%s' % self.originalLine)
 | |
| 
 | |
|     leaRe = re.compile(r'''
 | |
|                            (lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?)
 | |
|                            \s* $
 | |
|                        ''',
 | |
|                        re.VERBOSE | re.IGNORECASE
 | |
|                        )
 | |
| 
 | |
|     def ConvertLea(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         if self.MatchAndSetMo(self.leaRe, oldAsm):
 | |
|             lea = self.mo.group(1)
 | |
|             dst = self.mo.group(2)
 | |
|             src = self.mo.group(3)
 | |
|             if src.find('[') < 0:
 | |
|                 src = '[' + src + ']'
 | |
|             newAsm = lea + dst + ', ' + src
 | |
|         newAsm = self.CommonConversions(newAsm)
 | |
|         return newAsm
 | |
| 
 | |
|     ptrRe = re.compile(r'''
 | |
|                            (?<! \S )
 | |
|                            ([dfq]?word|byte) \s+ (?: ptr ) (\s*)
 | |
|                            (?= [[\s] )
 | |
|                        ''',
 | |
|                        re.VERBOSE | re.IGNORECASE
 | |
|                        )
 | |
| 
 | |
|     def ConvertPtr(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         while self.SearchAndSetMo(self.ptrRe, newAsm):
 | |
|             ty = self.mo.group(1)
 | |
|             if ty.lower() == 'fword':
 | |
|                 ty = ''
 | |
|             else:
 | |
|                 ty += self.mo.group(2)
 | |
|             newAsm = newAsm[:self.mo.start(0)] + ty + newAsm[self.mo.end(0):]
 | |
|         return newAsm
 | |
| 
 | |
|     labelByteRe = re.compile(r'''
 | |
|                                  (?: \s+ label \s+ (?: [dfq]?word | byte ) )
 | |
|                                  (?! \S )
 | |
|                              ''',
 | |
|                              re.VERBOSE | re.IGNORECASE
 | |
|                              )
 | |
| 
 | |
|     def ConvertLabelByte(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         if self.SearchAndSetMo(self.labelByteRe, newAsm):
 | |
|             newAsm = newAsm[:self.mo.start(0)] + ':' + newAsm[self.mo.end(0):]
 | |
|         return newAsm
 | |
| 
 | |
|     unaryBitwiseOpRe = re.compile(r'''
 | |
|                                       ( NOT )
 | |
|                                       (?= \s+ \S )
 | |
|                                   ''',
 | |
|                                   re.VERBOSE | re.IGNORECASE
 | |
|                                   )
 | |
|     binaryBitwiseOpRe = re.compile(r'''
 | |
|                                        ( \S \s+ )
 | |
|                                        ( AND | OR | SHL | SHR )
 | |
|                                        (?= \s+ \S )
 | |
|                                    ''',
 | |
|                                    re.VERBOSE | re.IGNORECASE
 | |
|                                    )
 | |
|     bitwiseOpReplacements = {
 | |
|         'not': '~',
 | |
|         'and': '&',
 | |
|         'shl': '<<',
 | |
|         'shr': '>>',
 | |
|         'or': '|',
 | |
|     }
 | |
| 
 | |
|     def ConvertBitwiseOp(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         while self.SearchAndSetMo(self.binaryBitwiseOpRe, newAsm):
 | |
|             prefix = self.mo.group(1)
 | |
|             op = self.bitwiseOpReplacements[self.mo.group(2).lower()]
 | |
|             newAsm = newAsm[:self.mo.start(0)] + prefix + op + \
 | |
|                 newAsm[self.mo.end(0):]
 | |
|         while self.SearchAndSetMo(self.unaryBitwiseOpRe, newAsm):
 | |
|             op = self.bitwiseOpReplacements[self.mo.group(1).lower()]
 | |
|             newAsm = newAsm[:self.mo.start(0)] + op + newAsm[self.mo.end(0):]
 | |
|         return newAsm
 | |
| 
 | |
|     sectionRe = re.compile(r'''
 | |
|                                \. ( code |
 | |
|                                     data
 | |
|                                   )
 | |
|                                (?: \s+ .* )?
 | |
|                                \s* $
 | |
|                            ''',
 | |
|                            re.VERBOSE | re.IGNORECASE
 | |
|                            )
 | |
| 
 | |
|     segmentRe = re.compile(r'''
 | |
|                                ( code |
 | |
|                                  data )
 | |
|                                (?: \s+ SEGMENT )
 | |
|                                (?: \s+ .* )?
 | |
|                                \s* $
 | |
|                            ''',
 | |
|                            re.VERBOSE | re.IGNORECASE
 | |
|                            )
 | |
| 
 | |
|     def ConvertSection(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         if self.MatchAndSetMo(self.sectionRe, newAsm) or \
 | |
|            self.MatchAndSetMo(self.segmentRe, newAsm):
 | |
|             name = self.mo.group(1).lower()
 | |
|             if name == 'code':
 | |
|                 if self.x64:
 | |
|                     self.EmitLine('DEFAULT REL')
 | |
|                 name = 'text'
 | |
|             newAsm = 'SECTION .' + name
 | |
|         return newAsm
 | |
| 
 | |
|     fwordRe = re.compile(r'''
 | |
|                              (?<! \S )
 | |
|                              fword
 | |
|                              (?! \S )
 | |
|                          ''',
 | |
|                          re.VERBOSE | re.IGNORECASE
 | |
|                          )
 | |
| 
 | |
|     def FwordUnsupportedCheck(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         if self.SearchAndSetMo(self.fwordRe, newAsm):
 | |
|             newAsm = self.Unsupported(newAsm, 'fword used')
 | |
|         return newAsm
 | |
| 
 | |
|     __common_conversion_routines__ = (
 | |
|         ConvertAnonymousLabels,
 | |
|         ConvertPtr,
 | |
|         FwordUnsupportedCheck,
 | |
|         ConvertBitwiseOp,
 | |
|         ConvertLabelByte,
 | |
|         ConvertSection,
 | |
|     )
 | |
| 
 | |
|     def CommonConversions(self, oldAsm):
 | |
|         newAsm = oldAsm
 | |
|         for conv in self.__common_conversion_routines__:
 | |
|             newAsm = conv(self, newAsm)
 | |
|         return newAsm
 | |
| 
 | |
|     def Unsupported(self, asm, message=None):
 | |
|         if not self.force:
 | |
|             raise UnsupportedConversion
 | |
| 
 | |
|         self.unsupportedSyntaxSeen = True
 | |
|         newAsm = '%error conversion unsupported'
 | |
|         if message:
 | |
|             newAsm += '; ' + message
 | |
|         newAsm += ': ' + asm
 | |
|         return newAsm
 | |
| 
 | |
| 
 | |
| class ConvertInfFile(CommonUtils):
 | |
| 
 | |
|     def __init__(self, inf, clone):
 | |
|         CommonUtils.__init__(self, clone)
 | |
|         self.inf = inf
 | |
|         self.ScanInfAsmFiles()
 | |
|         if self.infmode:
 | |
|             self.ConvertInfAsmFiles()
 | |
| 
 | |
|     infSrcRe = re.compile(r'''
 | |
|                               \s*
 | |
|                               ( [\w@][\w@0-9/]* \.(asm|s) )
 | |
|                               \s* (?: \| [^#]* )?
 | |
|                               \s* (?: \# .* )?
 | |
|                               $
 | |
|                           ''',
 | |
|                           re.VERBOSE | re.IGNORECASE
 | |
|                           )
 | |
| 
 | |
|     def GetInfAsmFileMapping(self):
 | |
|         srcToDst = {'order': []}
 | |
|         for line in self.lines:
 | |
|             line = line.rstrip()
 | |
|             if self.MatchAndSetMo(self.infSrcRe, line):
 | |
|                 src = self.mo.group(1)
 | |
|                 srcExt = self.mo.group(2)
 | |
|                 dst = os.path.splitext(src)[0] + '.nasm'
 | |
|                 fullDst = os.path.join(self.dir, dst)
 | |
|                 if src not in srcToDst and not os.path.exists(fullDst):
 | |
|                     srcToDst[src] = dst
 | |
|                     srcToDst['order'].append(src)
 | |
|         return srcToDst
 | |
| 
 | |
|     def ScanInfAsmFiles(self):
 | |
|         src = self.inf
 | |
|         assert os.path.isfile(src)
 | |
|         f = io.open(src, 'rt')
 | |
|         self.lines = f.readlines()
 | |
|         f.close()
 | |
| 
 | |
|         path = os.path.realpath(self.inf)
 | |
|         (self.dir, inf) = os.path.split(path)
 | |
|         parent = os.path.normpath(self.dir)
 | |
|         (lastpath, self.moduleName) = os.path.split(parent)
 | |
|         self.packageName = None
 | |
|         while True:
 | |
|             lastpath = os.path.normpath(lastpath)
 | |
|             (parent, basename) = os.path.split(lastpath)
 | |
|             if parent == lastpath:
 | |
|                 break
 | |
|             if basename.endswith('Pkg'):
 | |
|                 self.packageName = basename
 | |
|                 break
 | |
|             lastpath = parent
 | |
| 
 | |
|         self.srcToDst = self.GetInfAsmFileMapping()
 | |
| 
 | |
|         self.dstToSrc = {'order': []}
 | |
|         for src in self.srcToDst['order']:
 | |
|             srcExt = os.path.splitext(src)[1]
 | |
|             dst = self.srcToDst[src]
 | |
|             if dst not in self.dstToSrc:
 | |
|                 self.dstToSrc[dst] = [src]
 | |
|                 self.dstToSrc['order'].append(dst)
 | |
|             else:
 | |
|                 self.dstToSrc[dst].append(src)
 | |
| 
 | |
|     def __len__(self):
 | |
|         return len(self.dstToSrc['order'])
 | |
| 
 | |
|     def __iter__(self):
 | |
|         return iter(self.dstToSrc['order'])
 | |
| 
 | |
|     def ConvertInfAsmFiles(self):
 | |
|         notConverted = []
 | |
|         unsupportedArchCount = 0
 | |
|         for dst in self:
 | |
|             didSomething = False
 | |
|             try:
 | |
|                 self.UpdateInfAsmFile(dst)
 | |
|                 didSomething = True
 | |
|             except UnsupportedConversion:
 | |
|                 if not self.args.quiet:
 | |
|                     print('MASM=>NASM conversion unsupported for', dst)
 | |
|                 notConverted.append(dst)
 | |
|             except NoSourceFile:
 | |
|                 if not self.args.quiet:
 | |
|                     print('Source file missing for', reldst)
 | |
|                 notConverted.append(dst)
 | |
|             except UnsupportedArch:
 | |
|                 unsupportedArchCount += 1
 | |
|             else:
 | |
|                 if didSomething:
 | |
|                     self.ConversionFinished(dst)
 | |
|         if len(notConverted) > 0 and not self.args.quiet:
 | |
|             for dst in notConverted:
 | |
|                 reldst = self.RootRelative(dst)
 | |
|                 print('Unabled to convert', reldst)
 | |
|         if unsupportedArchCount > 0 and not self.args.quiet:
 | |
|             print('Skipped', unsupportedArchCount, 'files based on architecture')
 | |
| 
 | |
|     def UpdateInfAsmFile(self, dst, IgnoreMissingAsm=False):
 | |
|         infPath = os.path.split(os.path.realpath(self.inf))[0]
 | |
|         asmSrc = os.path.splitext(dst)[0] + '.asm'
 | |
|         fullSrc = os.path.join(infPath, asmSrc)
 | |
|         fullDst = os.path.join(infPath, dst)
 | |
|         srcParentDir = os.path.basename(os.path.split(fullSrc)[0])
 | |
|         if srcParentDir.lower() in UnsupportedArch.unsupported:
 | |
|             raise UnsupportedArch
 | |
|         elif not os.path.exists(fullSrc):
 | |
|             if not IgnoreMissingAsm:
 | |
|                 raise NoSourceFile
 | |
|         else:  # not os.path.exists(fullDst):
 | |
|             conv = ConvertAsmFile(fullSrc, fullDst, self)
 | |
|             self.unsupportedSyntaxSeen = conv.unsupportedSyntaxSeen
 | |
| 
 | |
|         fileChanged = False
 | |
|         recentSources = list()
 | |
|         i = 0
 | |
|         while i < len(self.lines):
 | |
|             line = self.lines[i].rstrip()
 | |
|             updatedLine = line
 | |
|             lineChanged = False
 | |
|             preserveOldSource = False
 | |
|             for src in self.dstToSrc[dst]:
 | |
|                 assert self.srcToDst[src] == dst
 | |
|                 updatedLine = self.ReplacePreserveSpacing(
 | |
|                     updatedLine, src, dst)
 | |
|                 lineChanged = updatedLine != line
 | |
|                 if lineChanged:
 | |
|                     preserveOldSource = self.ShouldKeepFile(src)
 | |
|                     break
 | |
| 
 | |
|             if lineChanged:
 | |
|                 if preserveOldSource:
 | |
|                     if updatedLine.strip() not in recentSources:
 | |
|                         self.lines.insert(i, updatedLine + '\n')
 | |
|                         recentSources.append(updatedLine.strip())
 | |
|                         i += 1
 | |
|                         if self.diff:
 | |
|                             print('+%s' % updatedLine)
 | |
|                     if self.diff:
 | |
|                         print('', line)
 | |
|                 else:
 | |
|                     if self.diff:
 | |
|                         print('-%s' % line)
 | |
|                     if updatedLine.strip() in recentSources:
 | |
|                         self.lines[i] = None
 | |
|                     else:
 | |
|                         self.lines[i] = updatedLine + '\n'
 | |
|                         recentSources.append(updatedLine.strip())
 | |
|                         if self.diff:
 | |
|                             print('+%s' % updatedLine)
 | |
|             else:
 | |
|                 if len(recentSources) > 0:
 | |
|                     recentSources = list()
 | |
|                 if self.diff:
 | |
|                     print('', line)
 | |
| 
 | |
|             fileChanged |= lineChanged
 | |
|             i += 1
 | |
| 
 | |
|         if fileChanged:
 | |
|             self.lines = list(filter(lambda l: l is not None, self.lines))
 | |
| 
 | |
|         for src in self.dstToSrc[dst]:
 | |
|             if not src.endswith('.asm'):
 | |
|                 fullSrc = os.path.join(infPath, src)
 | |
|                 if os.path.exists(fullSrc):
 | |
|                     self.RemoveFile(fullSrc)
 | |
| 
 | |
|         if fileChanged:
 | |
|             f = io.open(self.inf, 'w', newline='\r\n')
 | |
|             f.writelines(self.lines)
 | |
|             f.close()
 | |
|             self.FileUpdated(self.inf)
 | |
| 
 | |
|     def ConversionFinished(self, dst):
 | |
|         asmSrc = os.path.splitext(dst)[0] + '.asm'
 | |
|         self.FileConversionFinished(
 | |
|             self.packageName, self.moduleName, asmSrc, dst)
 | |
| 
 | |
| 
 | |
| class ConvertInfFiles(CommonUtils):
 | |
| 
 | |
|     def __init__(self, infs, clone):
 | |
|         CommonUtils.__init__(self, clone)
 | |
|         infs = map(lambda i: ConvertInfFile(i, self), infs)
 | |
|         infs = filter(lambda i: len(i) > 0, infs)
 | |
|         dstToInfs = {'order': []}
 | |
|         for inf in infs:
 | |
|             for dst in inf:
 | |
|                 fulldst = os.path.realpath(os.path.join(inf.dir, dst))
 | |
|                 pair = (inf, dst)
 | |
|                 if fulldst in dstToInfs:
 | |
|                     dstToInfs[fulldst].append(pair)
 | |
|                 else:
 | |
|                     dstToInfs['order'].append(fulldst)
 | |
|                     dstToInfs[fulldst] = [pair]
 | |
| 
 | |
|         notConverted = []
 | |
|         unsupportedArchCount = 0
 | |
|         for dst in dstToInfs['order']:
 | |
|             didSomething = False
 | |
|             try:
 | |
|                 for inf, reldst in dstToInfs[dst]:
 | |
|                     inf.UpdateInfAsmFile(reldst, IgnoreMissingAsm=didSomething)
 | |
|                     didSomething = True
 | |
|             except UnsupportedConversion:
 | |
|                 if not self.args.quiet:
 | |
|                     print('MASM=>NASM conversion unsupported for', reldst)
 | |
|                 notConverted.append(dst)
 | |
|             except NoSourceFile:
 | |
|                 if not self.args.quiet:
 | |
|                     print('Source file missing for', reldst)
 | |
|                 notConverted.append(dst)
 | |
|             except UnsupportedArch:
 | |
|                 unsupportedArchCount += 1
 | |
|             else:
 | |
|                 if didSomething:
 | |
|                     inf.ConversionFinished(reldst)
 | |
|         if len(notConverted) > 0 and not self.args.quiet:
 | |
|             for dst in notConverted:
 | |
|                 reldst = self.RootRelative(dst)
 | |
|                 print('Unabled to convert', reldst)
 | |
|         if unsupportedArchCount > 0 and not self.args.quiet:
 | |
|             print('Skipped', unsupportedArchCount, 'files based on architecture')
 | |
| 
 | |
| 
 | |
| class ConvertDirectories(CommonUtils):
 | |
| 
 | |
|     def __init__(self, paths, clone):
 | |
|         CommonUtils.__init__(self, clone)
 | |
|         self.paths = paths
 | |
|         self.ConvertInfAndAsmFiles()
 | |
| 
 | |
|     def ConvertInfAndAsmFiles(self):
 | |
|         infs = list()
 | |
|         for path in self.paths:
 | |
|             assert(os.path.exists(path))
 | |
|         for path in self.paths:
 | |
|             for root, dirs, files in os.walk(path):
 | |
|                 for d in ('.svn', '.git'):
 | |
|                     if d in dirs:
 | |
|                         dirs.remove(d)
 | |
|                 for f in files:
 | |
|                     if f.lower().endswith('.inf'):
 | |
|                         inf = os.path.realpath(os.path.join(root, f))
 | |
|                         infs.append(inf)
 | |
| 
 | |
|         ConvertInfFiles(infs, self)
 | |
| 
 | |
| 
 | |
| class ConvertAsmApp(CommonUtils):
 | |
| 
 | |
|     def __init__(self):
 | |
|         CommonUtils.__init__(self)
 | |
| 
 | |
|         src = self.args.source
 | |
|         dst = self.args.dest
 | |
|         if self.infmode:
 | |
|             ConvertInfFiles((src,), self)
 | |
|         elif self.dirmode:
 | |
|             ConvertDirectories((src,), self)
 | |
|         elif not self.dirmode:
 | |
|             ConvertAsmFile(src, dst, self)
 | |
| 
 | |
| ConvertAsmApp()
 |