2011-08-19 2 views
2

x86 용 명령어 버퍼가있는 경우 opcode가 SSE 명령어의 범위 내에 있는지 확인하지 않고도 명령어가 SSE 명령어인지 쉽게 확인할 수 있습니다. 이 말은 공통 명령 접두사 나 프로세서 상태 (예 : 레지스터)를 확인할 수 있다는 뜻입니까?버퍼의 SSE 명령어

답변

6

더 :

이 명령의 형식은 Intel 64 and IA-32 Architectures Software Developer's Manual Combined Volumes 2A and 2B: Instruction Set Reference, A-Z의 2 장에 설명되어 있습니다.하지 문제가되는 부분 중 하나는 접두사입니다. 일부 SSE 명령어 (66F2F3)는 다른 opcode (오퍼랜드 크기 오버라이드, REPNZREPZ)와는 다른 의미를 가지지 만 일부 명령어는 필수입니다.

0f 58 c0        addps xmm0,xmm0 
66 0f 58 c0        addpd xmm0,xmm0 
f3 0f 58 c0        addss xmm0,xmm0 
f2 0f 58 c0        addsd xmm0,xmm0 

기본적으로 두 가지를 추가하는 것 같다 :

은 XMM 함께 등록 (출력 objdump -D -b binary -m i386:x86-64:intel --insn-width=12 얻을)이 추가 이러한 4 개 형태를 고려, 접두사가 서로 다른 지침을 구분하는 데 사용되는 방법을 보려면 double precision 버전을 선택하면 F3 (repz)은 압축 단일 버전을 선택하고 마지막으로 F2 (repnz)은 압축 된 double 버전을 선택합니다.

또한 때때로 결합 될 수 있으며 64 비트 모드에서는 REX 접두사 (pg. 2-9)에 대해서도 걱정해야합니다. 다음은 64 비트 모드에서 서로 다른 접두어를 사용하는 대략 동일한 기본 명령어의 서로 다른 버전을 보여주는 예입니다. 당신은 AVX 명령에 대해 신경 나도 몰라하지만 난 예를 들어 어쨌든 하나를 포함 :

이 가
0f 51 ca        sqrtps xmm1,xmm2 
0f 51 0c 85 0a 00 00 00     sqrtps xmm1,XMMWORD PTR [rax*4+0xa] 
65 0f 51 0c 85 0a 00 00 00    sqrtps xmm1,XMMWORD PTR gs:[rax*4+0xa] 
67 0f 51 0c 85 0a 00 00 00    sqrtps xmm1,XMMWORD PTR [eax*4+0xa] 
65 67 0f 51 0c 85 0a 00 00 00   sqrtps xmm1,XMMWORD PTR gs:[eax*4+0xa] 
f0 65 67 0f 51 0c 85 0a 00 00 00  lock sqrtps xmm1,XMMWORD PTR gs:[eax*4+0xa] 
c5 fd 51 ca        vsqrtpd ymm1,ymm2 
c5 fc 51 0c 85 0a 00 00 00    vsqrtps ymm1,YMMWORD PTR [rax*4+0xa] 
65 c5 fc 51 0c 85 0a 00 00 00   vsqrtps ymm1,YMMWORD PTR gs:[rax*4+0xa] 
67 c5 fc 51 0c 85 0a 00 00 00   vsqrtps ymm1,YMMWORD PTR [eax*4+0xa] 
65 67 c5 fc 51 0c 85 0a 00 00 00  vsqrtps ymm1,YMMWORD PTR gs:[eax*4+0xa] 
f0 65 67 c5 fc 51 0c 85 0a 00 00 00  lock vsqrtps ymm1,YMMWORD PTR gs:[eax*4+0xa] 

그래서 지금까지 내가 항상 결정하기 위해 모든 접두사를 반복 할 것입니다 볼 수있는 명령이있는 경우 SSE 명령.

업데이트 : 추가로 복잡한 것은 ModRM 인코딩 만 다른 지침이 있다는 것입니다. 고려 :

df 00 fild    WORD PTR [rax] # Non-SSE instruction: DF /0 
df 08 fisttp   WORD PTR [rax] # SSE instruction: DF /1 

그들은 그것이 opcode map를 사용하는 가장 쉬운 인코딩 할 수있는 이러한 모든 다른 방법을 찾을 수 있습니다.

어쨌든 나는 디스어셈블러를 쓰는 것을 보았 기 때문에 그것이 무엇이 필요한지 보는 재미있는 도전이 될 것이라고 생각했습니다. 그것은 분명히 내가 그것을 보장 할 수는 없지만 보장하지는 않지만, 대부분의 SSE 지침을 찾아야한다. 위의 opcode 맵을 코드가 통과하는 일련의 테스트로 변환했습니다 (tests.c - 인라인하기에 너무 큼). 이 코드는 opcode 인코딩의 16 진수를 포함하는 일련의 텍스트 문자열을 테스트합니다 (첫 번째 16 진수가 아닌 숫자에서 구문 분석을 중지합니다. 문자열의 마지막 문자는 SSE 명령어인지 여부를 나타냄).

먼저 모든 접두어를 검색 한 다음 opcode 테이블을 사용하여 멀티 바이트 opcode에 필요한 중첩 테이블을 처리하는 추가 로직과 일치하는지 테스트하고 다음 modrm 바이트의 숫자와 일치시킬 필요가 있는지 테스트합니다.

ssedetect.c :

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdint.h> 
#include <ctype.h> 

#include "inst_table.h" 

enum { PREFIX_66=OP_66_SSE, PREFIX_F2=OP_F2_SSE, PREFIX_F3=OP_F3_SSE }; 

static int check_prefixes(int prefixes, int op_type) { 
    if (op_type & OP_ALWAYS_SSE) return 1; 
    if ((op_type & OP_66_SSE) && (prefixes & PREFIX_66)) return 1; 
    if ((op_type & OP_F2_SSE) && (prefixes & PREFIX_F2)) return 1; 
    if ((op_type & OP_F3_SSE) && (prefixes & PREFIX_F3)) return 1; 
    return 0; 
} 

int isInstructionSSE(const uint8_t* code, int length) 
{ 
    int position = 0; 

    // read prefixes 
    int prefixes = 0; 
    while (position < length) { 
     uint8_t b = code[position]; 

     if (b == 0x66) { 
      prefixes |= PREFIX_66; 
      position++; 
     } else if (b == 0xF2) { 
      prefixes |= PREFIX_F2; 
      position++; 
     } else if (b == 0xF3) { 
      prefixes |= PREFIX_F3; 
      position++; 
     } else if (b >= 0x40 && b <= 0x4F) { 
      //prefixes |= PREFIX_REX; 
      position++; 
      break; // opcode must follow REX 
     } else if (b == 0x2E || b == 0x3E || b == 0x26 || b == 0x36 || b == 0x64 || b == 0x65 || b == 0x67 || b == 0xF0) { 
      // ignored prefix 
      position++;  
     } else { 
      break; 
     } 
    } 

    // read opcode 
    const uint16_t* op_table = op; 
    int op_length = 0; 
    while (position < length) { 
     uint8_t b = code[position]; 
     uint16_t op_type = op_table[b]; 
     if (op_type & OP_EXTENDED) { 
      op_length++; 
      position++; 
      // hackish 
      if (op_length == 1 && b == 0x0F) op_table = op_0F; 
      else if (op_length == 2 && b == 0x01) op_table = op_0F_01; 
      else if (op_length == 2 && b == 0x38) op_table = op_0F_38; 
      else if (op_length == 2 && b == 0x3A) op_table = op_0F_3A; 
      else { printf("\n\n%2.2X\n",b); abort(); } 
     } else if (op_type & OP_DIGIT) { 
      break; 
     } else { 
      return check_prefixes(prefixes, op_type); 
     } 
    } 

    // optionally read a digit 

    // find digits we need can match in table 
    uint8_t match_digits = (op_table[code[position]] & OP_DIGIT_MASK) >> OP_DIGIT_SHIFT; 

    // consume the byte 
    op_length++; 
    position++; 
    if (position >= length) { 
     return 0; 
    } 

    uint8_t digit = (code[position]>>3)&7; // reg part of modrm 

    return (match_digits & (1 << digit)) != 0; 
} 

static int read_code(const char* str, uint8_t** code, int* length) 
{ 
    int size = 1000; 
    *length = 0; 
    *code = malloc(size); 
    if (!*code) { 
     printf("out of memory\n"); 
     return 0; 
    } 

    while (*str) { 
     char* endptr; 
     unsigned long val = strtoul(str, &endptr, 16); 
     if (str == endptr) { 
      break; 
     } 

     if (val > 255) { 
      printf("%lX is out of range\n", val); 
      goto error; 
      return 0; 
     } 

     (*code)[*length] = (uint8_t)val; 

     if (++*length >= size) { 
      printf("needs resize, not implemented\n"); 
      goto error; 
     } 

     str = endptr; 
    } 

    if (*length == 0) { 
     printf("No instruction bytes found\n"); 
     goto error; 
    } 

    return 1; 

error: 
    free(*code); 
    return 0; 
} 

static void test(const char* str) 
{ 
    uint8_t* code; 
    int length; 
    if (!read_code(str, &code, &length)) { 
     puts(str); 
     exit(1); 
    } 
    char is_sse = isInstructionSSE(code, length) ? 'Y' : 'N'; 
    char should_be_sse = str[strlen(str)-1]; 
    free(code); 
    if (should_be_sse != is_sse) { 
     printf("(%c) %c %s\n", should_be_sse, is_sse, str); 
     exit(1); 
    } 
} 

int main() 
{ 
#include "tests.c" 
    test("48 ba 39 00 00 00 00 00 00 00   # movabs rdx,0x39 N"); 
    test("48 b8 00 00 00 00 00 00 00 00   # movabs rax,0x0 N"); 
    test("48 b9 14 00 00 00 00 00 00 00   # movabs rcx,0x14 N"); 
    test("48 6b c0 0a        # imul rax,rax,0xa N"); 
    test("48 83 ea 30        # sub rdx,0x30 N"); 
    test("48 01 d0        # add rax,rdx N"); 
    test("48 ff c9        # dec rcx N"); 
    test("75 f0         # jne 0x1e N"); 
    test("0f 51 ca        # sqrtps xmm1,xmm2 Y"); 
    test("0f 51 0c 85 0a 00 00 00     # sqrtps xmm1,XMMWORD PTR [rax*4+0xa] Y"); 
    test("65 0f 51 0c 85 0a 00 00 00    # sqrtps xmm1,XMMWORD PTR gs:[rax*4+0xa] Y"); 
    test("67 0f 51 0c 85 0a 00 00 00    # sqrtps xmm1,XMMWORD PTR [eax*4+0xa] Y"); 
    test("65 67 0f 51 0c 85 0a 00 00 00   # sqrtps xmm1,XMMWORD PTR gs:[eax*4+0xa] Y"); 
    test("f0 65 67 0f 51 0c 85 0a 00 00 00  # lock sqrtps xmm1,XMMWORD PTR gs:[eax*4+0xa] Y"); 
    test("f0 65 67 f3 43 0f 5c 8c 81 2a 2a 00 00 # lock subss xmm1, [gs:r8d*4+r9d+0x2A2A] Y"); 
    test("0f 58 c0        # addps xmm0,xmm0 Y"); 
    test("66 0f 58 c0        # addpd xmm0,xmm0 Y"); 
    test("f3 0f 58 c0        # addss xmm0,xmm0 Y"); 
    test("f2 0f 58 c0        # addsd xmm0,xmm0 Y"); 
    test("df 04 25 2c 00 00 00     # fild WORD PTR ds:0x2c N"); 
    test("df 0c 25 2c 00 00 00     # fisttp WORD PTR ds:0x2c Y"); 
    test("67 0f ae 10        # ldmxcsr DWORD PTR [eax] Y"); 
    test("67 0f ae 18        # stmxcsr DWORD PTR [eax] Y"); 
    test("0f ae 00        # fxsave [rax] N"); 
    test("0f ae e8        # lfence Y"); 
    test("0f ae f0        # mfence Y"); 
    test("0f ae f8        # sfence Y"); 
    test("67 0f ae 38        # clflush BYTE PTR [eax] Y"); 
    test("67 0f 18 00        # prefetchnta BYTE PTR [eax] Y"); 
    test("0f 18 0b        # prefetcht0 BYTE PTR [rbx] Y"); 
    test("67 0f 18 11        # prefetcht1 BYTE PTR [ecx] Y"); 
    test("0f 18 1a        # prefetcht2 BYTE PTR [rdx] Y"); 
    test("df 08         # fisttp WORD PTR [rax] Y"); 
    test("df 00         # fild WORD PTR [rax] N"); 

    printf("All tests passed\n"); 
    return 0; 
} 

inst_table.h :

// Table Element format: 
// Bit: 0 SSE instruction if 66 prefix 
//  1 SSE instruction if F2 prefix 
//  2 SSE instruction if F3 prefix 
//  3 Extended table 
//  4 Instruction is always SSE 
//  5 SSE instruction if ModRM byte matches digit(s) 
//  6 ----- 
//  7 ----- 
//  8 SSE if ModRM has reg = 0 
//  9 SSE if ModRM has reg = 1 
//  . 
//  . That is it matches instructoins on the form XX XX /digit 
//  . 
//  15 SSE if modRM has reg = 7 

#define OP_66_SSE  0x0001 // SSE if 66 prefix 
#define OP_F2_SSE  0x0002 // SSE if F2 prefix 
#define OP_F3_SSE  0x0004 // SSE if F3 prefix 
#define OP_EXTENDED 0x0008 // continue with extended table 
#define OP_ALWAYS_SSE 0x0010 
#define OP_DIGIT  0x0020 

#define OP_DIGIT_MASK 0xFF00 
#define OP_DIGIT_SHIFT  8 
#define OP_MATCH_DIGIT(d) (OP_DIGIT | (1 << (d + OP_DIGIT_SHIFT))) 

static const uint16_t op[256] = { 
    [0x0F] = OP_EXTENDED, 
    [0x90] = OP_F3_SSE, 
    [0xDB] = OP_MATCH_DIGIT(1), // DB /1: FISTTP 
    [0xDD] = OP_MATCH_DIGIT(1), 
    [0xDF] = OP_MATCH_DIGIT(1), 
}; 

static const uint16_t op_0F[256] = { 
    [0x01] = OP_EXTENDED, 
    [0x10] = OP_ALWAYS_SSE, // 0F 10 MOVUPS, F3 0F 10 MOVSS ... 
    [0x11] = OP_ALWAYS_SSE, 
    [0x12] = OP_ALWAYS_SSE, 
    [0x13] = OP_ALWAYS_SSE, 
    [0x14] = OP_ALWAYS_SSE, 
    [0x15] = OP_ALWAYS_SSE, 
    [0x16] = OP_ALWAYS_SSE, 
    [0x17] = OP_ALWAYS_SSE, 
    [0x18] = OP_MATCH_DIGIT(0)|OP_MATCH_DIGIT(1)|OP_MATCH_DIGIT(2)|OP_MATCH_DIGIT(3), 
    [0x28] = OP_ALWAYS_SSE, 
    [0x29] = OP_ALWAYS_SSE, 
    [0x2A] = OP_ALWAYS_SSE, 
    [0x2B] = OP_ALWAYS_SSE, 
    [0x2C] = OP_ALWAYS_SSE, 
    [0x2D] = OP_ALWAYS_SSE, 
    [0x2E] = OP_ALWAYS_SSE, 
    [0x2F] = OP_ALWAYS_SSE, 
    [0x38] = OP_EXTENDED, 
    [0x3A] = OP_EXTENDED, 
    [0x50] = OP_ALWAYS_SSE, 
    [0x51] = OP_ALWAYS_SSE, 
    [0x52] = OP_ALWAYS_SSE, 
    [0x53] = OP_ALWAYS_SSE, 
    [0x54] = OP_ALWAYS_SSE, 
    [0x55] = OP_ALWAYS_SSE, 
    [0x56] = OP_ALWAYS_SSE, 
    [0x57] = OP_ALWAYS_SSE, 
    [0x58] = OP_ALWAYS_SSE, 
    [0x59] = OP_ALWAYS_SSE, 
    [0x5A] = OP_ALWAYS_SSE, 
    [0x5B] = OP_ALWAYS_SSE, 
    [0x5C] = OP_ALWAYS_SSE, 
    [0x5D] = OP_ALWAYS_SSE, 
    [0x5E] = OP_ALWAYS_SSE, 
    [0x5F] = OP_ALWAYS_SSE, 
    [0x60] = OP_66_SSE, 
    [0x61] = OP_66_SSE, 
    [0x62] = OP_66_SSE, 
    [0x63] = OP_66_SSE, 
    [0x64] = OP_66_SSE, 
    [0x65] = OP_66_SSE, 
    [0x66] = OP_66_SSE, 
    [0x67] = OP_66_SSE, 
    [0x68] = OP_66_SSE, 
    [0x69] = OP_66_SSE, 
    [0x6A] = OP_66_SSE, 
    [0x6B] = OP_66_SSE, 
    [0x6C] = OP_66_SSE, 
    [0x6D] = OP_66_SSE, 
    [0x6E] = OP_66_SSE, 
    [0x6F] = OP_66_SSE | OP_F3_SSE, 
    [0x70] = OP_ALWAYS_SSE, 
    [0x71] = OP_66_SSE, 
    [0x72] = OP_66_SSE, 
    [0x73] = OP_66_SSE, 
    [0x74] = OP_66_SSE, 
    [0x75] = OP_66_SSE, 
    [0x76] = OP_66_SSE, 
    [0x77] = OP_66_SSE, 
    [0x78] = OP_66_SSE, 
    [0x79] = OP_66_SSE, 
    [0x7A] = OP_66_SSE, 
    [0x7B] = OP_66_SSE, 
    [0x7C] = OP_66_SSE | OP_F2_SSE, 
    [0x7D] = OP_66_SSE | OP_F2_SSE, 
    [0x7E] = OP_66_SSE | OP_F3_SSE, 
    [0x7F] = OP_66_SSE | OP_F3_SSE, 
    [0xAE] = OP_MATCH_DIGIT(2)|OP_MATCH_DIGIT(3)|OP_MATCH_DIGIT(5)|OP_MATCH_DIGIT(6)|OP_MATCH_DIGIT(7), 
    [0xC2] = OP_ALWAYS_SSE, 
    [0xC3] = OP_ALWAYS_SSE, 
    [0xC4] = OP_ALWAYS_SSE, 
    [0xC5] = OP_ALWAYS_SSE, 
    [0xC6] = OP_ALWAYS_SSE, 
    [0xD0] = OP_66_SSE | OP_F2_SSE, 
    [0xD1] = OP_66_SSE, 
    [0xD2] = OP_66_SSE, 
    [0xD3] = OP_66_SSE, 
    [0xD4] = OP_ALWAYS_SSE, 
    [0xD5] = OP_66_SSE, 
    [0xD6] = OP_66_SSE | OP_F2_SSE | OP_F3_SSE, 
    [0xD7] = OP_ALWAYS_SSE, 
    [0xD8] = OP_66_SSE, 
    [0xD9] = OP_66_SSE, 
    [0xDA] = OP_ALWAYS_SSE, 
    [0xDB] = OP_66_SSE, 
    [0xDC] = OP_66_SSE, 
    [0xDD] = OP_66_SSE, 
    [0xDE] = OP_ALWAYS_SSE, 
    [0xDF] = OP_66_SSE, 
    [0xE0] = OP_ALWAYS_SSE, 
    [0xE1] = OP_66_SSE, 
    [0xE2] = OP_66_SSE, 
    [0xE3] = OP_ALWAYS_SSE, 
    [0xE4] = OP_ALWAYS_SSE, 
    [0xE5] = OP_66_SSE, 
    [0xE6] = OP_66_SSE | OP_F2_SSE | OP_F3_SSE, 
    [0xE7] = OP_ALWAYS_SSE, 
    [0xE8] = OP_66_SSE, 
    [0xE9] = OP_66_SSE, 
    [0xEA] = OP_ALWAYS_SSE, 
    [0xEB] = OP_66_SSE, 
    [0xEC] = OP_66_SSE, 
    [0xED] = OP_66_SSE, 
    [0xEE] = OP_ALWAYS_SSE, 
    [0xEF] = OP_66_SSE, 
    [0xF0] = OP_F2_SSE, 
    [0xF1] = OP_66_SSE, 
    [0xF2] = OP_66_SSE, 
    [0xF3] = OP_66_SSE, 
    [0xF4] = OP_ALWAYS_SSE, 
    [0xF5] = OP_66_SSE, 
    [0xF6] = OP_ALWAYS_SSE, 
    [0xF7] = OP_ALWAYS_SSE, 
    [0xF8] = OP_66_SSE, 
    [0xF9] = OP_66_SSE, 
    [0xFA] = OP_66_SSE, 
    [0xFB] = OP_ALWAYS_SSE, 
    [0xFC] = OP_66_SSE, 
    [0xFD] = OP_66_SSE, 
    [0xFE] = OP_66_SSE, 
}; 

static const uint16_t op_0F_01[256] = { 
    [0xC8] = OP_ALWAYS_SSE, // 0F 01 C8: MONITOR 
    [0xC9] = OP_ALWAYS_SSE, 
}; 


static const uint16_t op_0F_38[256] = { 
    [0xF0] = OP_F2_SSE, // F2 0F 38 F0: CRC32 
    [0xF1] = OP_F2_SSE, 
}; 

static const uint16_t op_0F_3A[256] = { 
    [0x08] = OP_66_SSE, // 66 0F 3A 08: ROUNDPS 
    [0x09] = OP_66_SSE, 
    [0x0A] = OP_66_SSE, 
    [0x0B] = OP_66_SSE, 
    [0x0C] = OP_66_SSE, 
    [0x0D] = OP_66_SSE, 
    [0x0E] = OP_66_SSE, 
    [0x0F] = OP_ALWAYS_SSE, 
    [0x14] = OP_66_SSE, 
    [0x15] = OP_66_SSE, 
    [0x16] = OP_66_SSE, 
    [0x17] = OP_66_SSE, 
    [0x20] = OP_66_SSE, 
    [0x21] = OP_66_SSE, 
    [0x22] = OP_66_SSE, 
    [0x40] = OP_66_SSE, 
    [0x41] = OP_66_SSE, 
    [0x42] = OP_66_SSE, 
    [0x60] = OP_66_SSE, 
    [0x61] = OP_66_SSE, 
    [0x62] = OP_66_SSE, 
    [0x63] = OP_66_SSE, 
}; 
+1

나는 내가 10 번처럼 이것을 upvote 수 있으면 좋겠 :) –

1

SSE 프리픽스가 없습니다. 일부 SSE 명령어는 0F로 시작하고 일부는 F3으로 시작하지만 일부는 0F 및 F3 명령어가 SSE 명령어가 아닙니다. 명령어가 SSE인지 여부를 알기 위해서는보다 포괄적 인 디코더가 필요합니다. x86 명령어는 가변 길이이므로 어쨌든 필요합니다. 당신이 대답은 예 또는입니다 쉽게 정의하는 방법에 따라

(업데이트)
+1

0F 두 바이트 지시하고 모든 SSE 명령어 검사를 많이 배제 두 바이트입니다. 또한 SSE 명령어 (많은 명령어)는 0x66 0xF2 0xF3부터 시작합니다. 지금까지 내가 가지고있는 것은 접두사가있는 2 바이트 명령어 인 경우 확실히 SSE이지만 일부 SSE 명령어에는 접두어가없고 그 중 일부는 문제가되는 것입니다. –

관련 문제