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