PageRenderTime 4229ms CodeModel.GetById 48ms RepoModel.GetById 3ms app.codeStats 0ms

/edk2/BaseTools/Scripts/ConvertMasmToNasm.py

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