среда, 29 июня 2011 г.

udis86 with ssse3 & sse4

а вот например как всем известно последняя версия udis86 не поддерживает ssse3 & sse4 совершенно официально
Первая моя наивная попытка добавить их самостоятельно провалилась с позором. Например пишем в x86optable.xml скажем для psignb что-нть такое:

<instruction mnemonic="psignb">
  <opcode> aso rexr rexx rexb ; sse66 0f 38 08 ; V W </opcode>
  <opcode> aso rexr rexx rexb ; 0f 38 08 ; P Q </opcode>
</instruction>

компилируем и смотрим itab.c
И например видим что дескрипторы для инструкции попали в таблицы ud_itab_entry itab__0f & itab__pfx_sse66__0f

Впрочем такой вариант тоже не работает:

<instruction mnemonic="psignb">
  <opcode> aso rexr rexx rexb ; sse66 0f sse38 08 ; V W </opcode>
  <opcode> aso rexr rexx rexb ; 0f sse38 08 ; P Q </opcode>
</instruction>


Похоже что текущая реализация opgen.py не поддерживает opcodes длиннее 3х байт. Добавить же нужно следующие инструкции

# SSSE3
psignb    (66) 0f 38 08 /r
psignw    (66) 0f 38 09 /r
psignd    (66) 0f 38 0A /r
pshufb    (66) 0F 38 00 /r
pmulhrsw  (66) 0F 38 0B /r
pmaddubsw (66) 0F 38 04 /r
phsubsw   (66) 0F 38 07 /r
phaddsw   (66) 0F 38 03 /r
phaddw    (66) 0F 38 01 /r
phaddd    (66) 0F 38 02 /r
phsubw    (66) 0F 38 05 /r
phsubd    (66) 0F 38 06 /r
palignr   (66) 0F 3A 0F /r ib
pabsb     (66) 0F 38 1C /r
pabsw     (66) 0F 38 1D /r
pabsd     (66) 0F 38 1E /r
# SSE4
blendpd    66 0F 3A 0D /r ib
blendps    66 0F 3A 0C /r ib
blendvpd   66 0F 38 15 /r
blendvps   66 0F 38 14 /r
dppd       66 0F 3A 41 /r ib
dpps       66 0F 3A 40 /r ib
extractps  66 0F 3A 17 /r ib
insertps   66 0F 3A 21 /r ib
movntdqa   66 0F 38 2A /r
mpsadbw    66 0F 3A 42 /r ib
packusdw   66 0F 38 2B /r
pblendvb   66 0F 38 10 /r
pblendw    66 0F 3A 0E /r ib
pextrb     66 0F 3A 14 /r ib
pextrd     66 0F 3A 16 /r ib
phminposuw 66 0F 38 41 /r
pinsrb     66 0F 3A 20 /r ib
pinsrd     66 0F 3A 22 /r ib
pmaxsb     66 0F 38 3C /r
pmaxsd     66 0F 38 3D /r
pmaxud     66 0F 38 3F /r
pmaxuw     66 0F 38 3E /r
pminsb     66 0F 38 38 /r
pminsd     66 0F 38 39 /r
pminud     66 0F 38 3B /r
pminuw     66 0F 38 3A /r
pmovsxbw   66 0f 38 20 /r
pmovsxbd   66 0f 38 21 /r
pmovsxbq   66 0f 38 22 /r
pmovsxwd   66 0f 38 23 /r
pmovsxwq   66 0f 38 24 /r
pmovsxdq   66 0f 38 25 /r
pmovzxbw   66 0f 38 30 /r
pmovzxbd   66 0f 38 31 /r
pmovzxbq   66 0f 38 32 /r
pmovzxwd   66 0f 38 33 /r
pmovzxwq   66 0f 38 34 /r
pmovzxdq   66 0f 38 35 /r
pmuldq     66 0F 38 28 /r
pmulld     66 0F 38 40 /r
ptest      66 0F 38 17 /r
roundpd    66 0F 3A 09 /r ib
roundps    66 0F 3A 08 /r ib
roundsd    66 0F 3A 0B /r ib
roundss    66 0F 3A 0A /r ib

Если внимательно помедитировать на эту табличку, то можно сделать вывод что нам нужны 4 таблицы по 256 элементов каждая:
  • 0f 38 XX - таблица ITAB__PFX_SSE38
  • 0f 3a XX - таблица ITAB__PFX_SSE3A - самая кстати наименее заполненная - там будет дескриптор для всего одной инструкции palignr
  • 66 0f 38 XX - таблица ITAB__PFX_SSE66__SSE38
  • 66 0f 3a XX - таблица ITAB__PFX_SSE66__SSE3A
Для начала нужно отпатчить файл decode.c для их использования:

        if ( 0x66 == u->pfx_insn ) {
            if ( (curr == 0x38) || (curr == 0x3A) )
            {
              table = (curr == 0x38) ? ITAB__PFX_SSE66__SSE38 : ITAB__PFX_SSE66__SSE3A;
              curr  = inp_next(u);
              if ( u->error )
                return -1;
              u->pfx_opr = 0;
            }
            else if ( ud_itab_list[ ITAB__PFX_SSE66__0F ][ curr ].mnemonic != UD_Iinvalid ) {
                table = ITAB__PFX_SSE66__0F;
                u->pfx_opr = 0;
            }
...

else if ( (curr == 0x38) || (curr == 0x3A) )
        {
          table = (curr == 0x38) ? ITAB__PFX_SSE38 : ITAB__PFX_SSE3A;
          curr  = inp_next(u);
          if ( u->error )
            return -1;
        }

Первый if проверят что зачитаны 66 & 0f и текущий байт - либо 38 либо 3a, и устанавливает соотв-щую таблицу opcodes.
Второй if проверяет что зачитан 0f и текущий байт в свою очередь тоже 38 или 3a.

Теперь нам нужно сказать opgen.py сгенерировать эти четыре таблицы. Есс-но это можно сделать бесчисленным множеством способом, я выбрал специальную обработку для opcodes sse38 & sse3A:

            if op == 'SSE3A' or op == 'SSE38':
                # we can have two kind of table - when there already was some sse prefix (like 66)
                # or not. In first case table name will be itab__pfx_SSE__[3a|38]
                # In last case (SSSE3 mostly) itab__pfx__[3a|38]
                if len(old_sse_tab):
                   table_name = "itab__pfx_%s__%s" % (old_sse_tab, op)
                else:
                   table_name = "itab__pfx_%s" % op
                table_sse = op
                table_size = 256
                was38_3a = 1
            elif op[0:3] == 'SSE':
                table_sse = op
                old_sse_tab = op
            elif op == '0F' and len(table_sse) and was38_3a == 0:
                table_name = "itab__pfx_%s__0f" % table_sse
                table_size = 256
                table_sse  = ''
            elif op == '0F' and was38_3a == 0:
                table_name = "itab__0f"
                table_size = 256

IMHO тут все просто и самодокументированно. Единственная сложная вещь - специальная обработка байта 0f. Если снова внимательно посмотреть на табличку с opcodes, то можно увидеть, что инструкция palignr имеет 0f последним байтом. Соотв-но нужно проверить что данная 0f встретилась после sse38 или sse3a и тогда не делать ничего

sources on sourceforge

Update: добавил сегодня инструкции pcmpeqq, pcmpgtq, pclmulqdq & movbe

2 комментария:

  1. там был уже готовый транслятор с мета-языка в таблицы plain C
    но вообще я обычно знаю языки и технологии, над которыми глумлюсь, так что поцчему ви удивляетесь ?

    ОтветитьУдалить