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