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