| #!/usr/bin/env python |
| # Work around x86emu bugs by replacing problematic instructions. |
| # |
| # Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net> |
| # |
| # This file may be distributed under the terms of the GNU GPLv3 license. |
| |
| # The x86emu code widely used in Linux distributions when running Xorg |
| # in vesamode is known to have issues with "retl", "leavel", "entryl", |
| # "leal", and some variants of "calll". This code modifies those |
| # instructions that are known to be generated by gcc to avoid |
| # triggering the x86emu bugs. |
| |
| # It is also known that the Windows vgabios emulator has issues with |
| # addressing negative offsets to the %esp register. That has been |
| # worked around by not using the gcc parameter "-fomit-frame-pointer" |
| # when compiling. |
| |
| import sys, re |
| |
| # leal parameter regex - example string: -3(%edx,%eax,8), %eax |
| re_leal = re.compile( |
| r'^\s*(?P<offset>[^(]*?)\s*' |
| r'\(\s*(?P<base>[^,)]*?)\s*(?:,\s*(?P<index>[^,)]*?)\s*)?' |
| r'(?:,\s*(?P<scale>[^,)]*?)\s*)?\)\s*' |
| r',\s*(?P<dest>.*?)\s*$') |
| |
| # Find an alternate set of instructions for a given "leal" instruction |
| def handle_leal(sline): |
| m = re_leal.match(sline[5:]) |
| if m is None or m.group('index') == '%esp': |
| print("Unable to fixup leal instruction: %s" % (sline,)) |
| sys.exit(-1) |
| offset, base, index, scale, dest = m.group( |
| 'offset', 'base', 'index', 'scale', 'dest') |
| if dest == '%esp': |
| # If destination is %esp then just use 16bit leaw instead |
| return 'leaw %s\n' % (sline[5:].replace('%e', '%'),) |
| if not scale: |
| scale = '1' |
| scale = {1: 0, 2: 1, 4: 2, 8: 3}[int(scale, 0)] |
| # Try to rearrange arguments to simplify 'base' (to improve code gen) |
| if not scale and base == index: |
| base, index, scale = '', index, 1 |
| elif not index or (not scale and base in (dest, '%esp') and index != dest): |
| base, index, scale = index, base, 0 |
| # Produce instructions to calculate "leal" |
| insns = ['pushfw'] |
| if base != dest: |
| # Calculate "leal" directly in dest register |
| if index != dest: |
| insns.insert(0, 'movl %s, %s' % (index, dest)) |
| if scale: |
| insns.append('shll $%d, %s' % (scale, dest)) |
| if base: |
| if base == '%esp': |
| offset += '+2' |
| insns.append('addl %s, %s' % (base, dest)) |
| elif base == index: |
| # Use "imull" method |
| insns.append('imull $%d, %s' % ((1<<scale)+1, dest)) |
| else: |
| # Backup/restore index register and do scaling in index register |
| insns.append('pushl %s' % (index,)) |
| insns.append('shll $%d, %s' % (scale, index)) |
| insns.append('addl %s, %s' % (index, dest)) |
| insns.append('popl %s' % (index,)) |
| if offset and offset != '0': |
| insns.append('addl $%s, %s' % (offset, dest)) |
| insns.append('popfw\n') |
| return ' ; '.join(insns) |
| |
| def main(): |
| infilename, outfilename = sys.argv[1:] |
| infile = open(infilename, 'r') |
| out = [] |
| for line in infile: |
| sline = line.strip() |
| if sline == 'ret': |
| out.append('retw $2\n') |
| elif sline == 'leave': |
| out.append('movl %ebp, %esp ; popl %ebp\n') |
| elif sline.startswith('call'): |
| out.append('pushw %ax ; callw' + sline[4:] + '\n') |
| elif sline.startswith('leal'): |
| out.append(handle_leal(sline)) |
| #print("-> %s\n %s" % (sline, out[-1].strip())) |
| else: |
| out.append(line) |
| infile.close() |
| outfile = open(outfilename, 'w') |
| outfile.write(''.join(out)) |
| outfile.close() |
| |
| if __name__ == '__main__': |
| main() |