Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change st0 operand visibility as specified by SDM #12

Merged
merged 1 commit into from
Apr 26, 2022

Conversation

athre0z
Copy link
Member

@athre0z athre0z commented Apr 23, 2022

As explained in zyantific/zydis#189, we previously had st0 as an explicit operand where it is supposed to be suppressed. This PR fixes all such cases.

The changes were created using the following Python script. Comment/uncomment calls in last lines to select mode. Essentially, it interactively goes through all instructions with a visible st0 operand and prompts on whether you want to add it to the list for fixup. In the apply step, for all selected instructions, it then moves the st0 operand to the back of the operand list and adds the visible: false flag.

Some of the matching encodings were undocumented. In these cases, I selected the visibility based on the formatting for similar encodings / instructions.

from pprint import pprint

import random
import json
import os
import copy


with open('instructions.json') as f:
  insns = json.load(f)

try:
  with open('state', 'r') as f:
    choices = json.load(f)
except FileNotFoundError:
  choices = []


def calc_key(defi):
  key_filters = [f'{k}={v}' for k, v in defi.get('filters', {}).items()]
  return '/'.join([defi.get("opcode_map", "base"), defi["opcode"], *key_filters])


def classify():
  try:
    for defi in insns:
      key = calc_key(defi)
      print()
      print(f'Key: {key}')

      if key in [x for x, _ in choices]:
        print("skipping def due to ignore list match")
        continue

      st0_idx = None
      for idx, op in enumerate(defi.get('operands', [])):
        if op.get('visible', True) and op.get('register', None) == 'st0':
          st0_idx = idx
          break

      if st0_idx is not None:
        disp = copy.deepcopy(defi)
        del disp['comment']
        del disp['affected_flags']
        pprint(disp)

        mnem = disp['mnemonic']
        op = int(disp['opcode'], 16)
        filters = disp['filters']
        mod = filters.get('modrm_mod')
        reg = filters.get('modrm_reg')
        rm = filters.get('modrm_rm')

        if mod is not None and reg is not None and rm is not None:
          print(f"Intel DEF: {mnem} {op:02X} {int(rm) | int(reg) << 3 | int(mod) << 6:02X}")
        elif reg is not None and mod is not None and rm is None:
          if mod[0] == '!':
            assert mod == '!3'
            pat = f'/{reg} [mem]'
          else:
            pat = f'{int(reg) << 3 | int(mod) << 6:02X}+i [reg]'

          print(f"Intel DEF: {mnem} {op:02X} {pat}")
        elif reg is not None and mod is None and rm is None:
          print(f"Intel DEF: {mnem} {op:02X} /{reg}")
        else:
          print(f"Intel DEF: {mnem} N/A, {' && '.join(key_filters)}")


        x = input('Hide st0 for this def? (y = yes, n = no, s = skip, ignore = i) ')
        match x:
          case 'y' | 'Y':
            adjust_defi(defi, st0_idx)
            choices.append([key, 'yes'])
          case 'n' | 'N':
            choices.append([key, 'no'])
          case 'i' | 'I':
            choices.append([key, 'ignore'])
  # except Exception as exc:
  #   print(exc)
  finally:
    print("saving state ...")
    try:
      os.rename('state', 'state.old')
    except FileNotFoundError:
      pass
    with open('state', 'w') as f:
      json.dump(choices, f, indent=2)


def apply():
  kv = {k: v for k, v in choices}
  for defi in insns:
    key = calc_key(defi)
    if key not in kv:
      continue

    if kv[key] == 'yes':
      operands = defi['operands']

      st0_idx = None
      for i, op in enumerate(operands):
        if op.get('register') == 'st0':
          st0_idx = i
          break
      else:
        assert False
      
      st0_op = operands[st0_idx]
      st0_op['visible'] = False
      del operands[st0_idx]
      operands.append(st0_op)

  with open('instructions.fixed.json', 'w') as f:
    text = json.dumps(insns, indent=2)
    # replace hack to match delphi formatting
    text = text.replace('"mask_flags": []', '"mask_flags": [\n      ]')
    f.write(text + '\n')


# classify()
apply()

The corresponding state file for the choices I made is here: https://gist.github.com/athre0z/24488080a06c295d8bdb9a78709a1876

Copy link
Member

@flobernd flobernd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for the effort 😃 Let's merge it and monitor if we have to do some adjustments afterwards.

@athre0z athre0z merged commit 85a0619 into master Apr 26, 2022
@athre0z athre0z deleted the st0-operand-visibility branch April 26, 2022 10:20
athre0z added a commit to zyantific/zydis that referenced this pull request Apr 26, 2022
As explained in #189, we previously had st0 as an explicit operand where it is
supposed to be suppressed. This PR fixes all such cases.

Corresponding zydis-db PR: zyantific/zydis-db#12

Resolves #189
athre0z added a commit to zyantific/zydis that referenced this pull request Apr 26, 2022
As explained in #189, we previously had st0 as an explicit operand where it is
supposed to be suppressed. This PR fixes all such cases.

Corresponding zydis-db PR: zyantific/zydis-db#12

Resolves #189
athre0z added a commit to zyantific/zydis that referenced this pull request Apr 26, 2022
As explained in #189, we previously had st0 as an explicit operand where it is
supposed to be suppressed. This PR fixes all such cases.

Corresponding zydis-db PR: zyantific/zydis-db#12

Resolves #189
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants