toolkit/crashreporter/google-breakpad/src/third_party/libdisasm/ia32_operand.c

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 #include <stdio.h>
michael@0 2 #include <stdlib.h>
michael@0 3 #include <string.h>
michael@0 4
michael@0 5 #include "libdis.h"
michael@0 6 #include "ia32_insn.h"
michael@0 7 #include "ia32_operand.h"
michael@0 8 #include "ia32_modrm.h"
michael@0 9 #include "ia32_reg.h"
michael@0 10 #include "x86_imm.h"
michael@0 11 #include "x86_operand_list.h"
michael@0 12
michael@0 13
michael@0 14
michael@0 15 /* apply segment override to memory operand in insn */
michael@0 16 static void apply_seg( x86_op_t *op, unsigned int prefixes ) {
michael@0 17 if (! prefixes ) return;
michael@0 18
michael@0 19 /* apply overrides from prefix */
michael@0 20 switch ( prefixes & PREFIX_REG_MASK ) {
michael@0 21 case PREFIX_CS:
michael@0 22 op->flags |= op_cs_seg; break;
michael@0 23 case PREFIX_SS:
michael@0 24 op->flags |= op_ss_seg; break;
michael@0 25 case PREFIX_DS:
michael@0 26 op->flags |= op_ds_seg; break;
michael@0 27 case PREFIX_ES:
michael@0 28 op->flags |= op_es_seg; break;
michael@0 29 case PREFIX_FS:
michael@0 30 op->flags |= op_fs_seg; break;
michael@0 31 case PREFIX_GS:
michael@0 32 op->flags |= op_gs_seg; break;
michael@0 33 }
michael@0 34
michael@0 35 return;
michael@0 36 }
michael@0 37
michael@0 38 static size_t decode_operand_value( unsigned char *buf, size_t buf_len,
michael@0 39 x86_op_t *op, x86_insn_t *insn,
michael@0 40 unsigned int addr_meth, size_t op_size,
michael@0 41 unsigned int op_value, unsigned char modrm,
michael@0 42 size_t gen_regs ) {
michael@0 43 size_t size = 0;
michael@0 44
michael@0 45 /* ++ Do Operand Addressing Method / Decode operand ++ */
michael@0 46 switch (addr_meth) {
michael@0 47 /* This sets the operand Size based on the Intel Opcode Map
michael@0 48 * (Vol 2, Appendix A). Letter encodings are from section
michael@0 49 * A.1.1, 'Codes for Addressing Method' */
michael@0 50
michael@0 51 /* ---------------------- Addressing Method -------------- */
michael@0 52 /* Note that decoding mod ModR/M operand adjusts the size of
michael@0 53 * the instruction, but decoding the reg operand does not.
michael@0 54 * This should not cause any problems, as every 'reg' operand
michael@0 55 * has an associated 'mod' operand.
michael@0 56 * Goddamn-Intel-Note:
michael@0 57 * Some Intel addressing methods [M, R] specify that modR/M
michael@0 58 * byte may only refer to a memory address/may only refer to
michael@0 59 * a register -- however Intel provides no clues on what to do
michael@0 60 * if, say, the modR/M for an M opcode decodes to a register
michael@0 61 * rather than a memory address ... returning 0 is out of the
michael@0 62 * question, as this would be an Immediate or a RelOffset, so
michael@0 63 * instead these modR/Ms are decoded with total disregard to
michael@0 64 * the M, R constraints. */
michael@0 65
michael@0 66 /* MODRM -- mod operand. sets size to at least 1! */
michael@0 67 case ADDRMETH_E: /* ModR/M present, Gen reg or memory */
michael@0 68 size = ia32_modrm_decode( buf, buf_len, op, insn,
michael@0 69 gen_regs );
michael@0 70 break;
michael@0 71 case ADDRMETH_M: /* ModR/M only refers to memory */
michael@0 72 size = ia32_modrm_decode( buf, buf_len, op, insn,
michael@0 73 gen_regs );
michael@0 74 break;
michael@0 75 case ADDRMETH_Q: /* ModR/M present, MMX or Memory */
michael@0 76 size = ia32_modrm_decode( buf, buf_len, op, insn,
michael@0 77 REG_MMX_OFFSET );
michael@0 78 break;
michael@0 79 case ADDRMETH_R: /* ModR/M mod == gen reg */
michael@0 80 size = ia32_modrm_decode( buf, buf_len, op, insn,
michael@0 81 gen_regs );
michael@0 82 break;
michael@0 83 case ADDRMETH_W: /* ModR/M present, mem or SIMD reg */
michael@0 84 size = ia32_modrm_decode( buf, buf_len, op, insn,
michael@0 85 REG_SIMD_OFFSET );
michael@0 86 break;
michael@0 87
michael@0 88 /* MODRM -- reg operand. does not effect size! */
michael@0 89 case ADDRMETH_C: /* ModR/M reg == control reg */
michael@0 90 ia32_reg_decode( modrm, op, REG_CTRL_OFFSET );
michael@0 91 break;
michael@0 92 case ADDRMETH_D: /* ModR/M reg == debug reg */
michael@0 93 ia32_reg_decode( modrm, op, REG_DEBUG_OFFSET );
michael@0 94 break;
michael@0 95 case ADDRMETH_G: /* ModR/M reg == gen-purpose reg */
michael@0 96 ia32_reg_decode( modrm, op, gen_regs );
michael@0 97 break;
michael@0 98 case ADDRMETH_P: /* ModR/M reg == qword MMX reg */
michael@0 99 ia32_reg_decode( modrm, op, REG_MMX_OFFSET );
michael@0 100 break;
michael@0 101 case ADDRMETH_S: /* ModR/M reg == segment reg */
michael@0 102 ia32_reg_decode( modrm, op, REG_SEG_OFFSET );
michael@0 103 break;
michael@0 104 case ADDRMETH_T: /* ModR/M reg == test reg */
michael@0 105 ia32_reg_decode( modrm, op, REG_TEST_OFFSET );
michael@0 106 break;
michael@0 107 case ADDRMETH_V: /* ModR/M reg == SIMD reg */
michael@0 108 ia32_reg_decode( modrm, op, REG_SIMD_OFFSET );
michael@0 109 break;
michael@0 110
michael@0 111 /* No MODRM : note these set operand type explicitly */
michael@0 112 case ADDRMETH_A: /* No modR/M -- direct addr */
michael@0 113 op->type = op_absolute;
michael@0 114
michael@0 115 /* segment:offset address used in far calls */
michael@0 116 x86_imm_sized( buf, buf_len,
michael@0 117 &op->data.absolute.segment, 2 );
michael@0 118 if ( insn->addr_size == 4 ) {
michael@0 119 x86_imm_sized( buf, buf_len,
michael@0 120 &op->data.absolute.offset.off32, 4 );
michael@0 121 size = 6;
michael@0 122 } else {
michael@0 123 x86_imm_sized( buf, buf_len,
michael@0 124 &op->data.absolute.offset.off16, 2 );
michael@0 125 size = 4;
michael@0 126 }
michael@0 127
michael@0 128 break;
michael@0 129 case ADDRMETH_I: /* Immediate val */
michael@0 130 op->type = op_immediate;
michael@0 131 /* if it ever becomes legal to have imm as dest and
michael@0 132 * there is a src ModR/M operand, we are screwed! */
michael@0 133 if ( op->flags & op_signed ) {
michael@0 134 x86_imm_signsized(buf, buf_len, &op->data.byte,
michael@0 135 op_size);
michael@0 136 } else {
michael@0 137 x86_imm_sized(buf, buf_len, &op->data.byte,
michael@0 138 op_size);
michael@0 139 }
michael@0 140 size = op_size;
michael@0 141 break;
michael@0 142 case ADDRMETH_J: /* Rel offset to add to IP [jmp] */
michael@0 143 /* this fills op->data.near_offset or
michael@0 144 op->data.far_offset depending on the size of
michael@0 145 the operand */
michael@0 146 op->flags |= op_signed;
michael@0 147 if ( op_size == 1 ) {
michael@0 148 /* one-byte near offset */
michael@0 149 op->type = op_relative_near;
michael@0 150 x86_imm_signsized(buf, buf_len,
michael@0 151 &op->data.relative_near, 1);
michael@0 152 } else {
michael@0 153 /* far offset...is this truly signed? */
michael@0 154 op->type = op_relative_far;
michael@0 155 x86_imm_signsized(buf, buf_len,
michael@0 156 &op->data.relative_far, op_size );
michael@0 157 }
michael@0 158 size = op_size;
michael@0 159 break;
michael@0 160 case ADDRMETH_O: /* No ModR/M; op is word/dword offset */
michael@0 161 /* NOTE: these are actually RVAs not offsets to seg!! */
michael@0 162 /* note bene: 'O' ADDR_METH uses addr_size to
michael@0 163 determine operand size */
michael@0 164 op->type = op_offset;
michael@0 165 op->flags |= op_pointer;
michael@0 166 x86_imm_sized( buf, buf_len, &op->data.offset,
michael@0 167 insn->addr_size );
michael@0 168
michael@0 169 size = insn->addr_size;
michael@0 170 break;
michael@0 171
michael@0 172 /* Hard-coded: these are specified in the insn definition */
michael@0 173 case ADDRMETH_F: /* EFLAGS register */
michael@0 174 op->type = op_register;
michael@0 175 op->flags |= op_hardcode;
michael@0 176 ia32_handle_register( &op->data.reg, REG_FLAGS_INDEX );
michael@0 177 break;
michael@0 178 case ADDRMETH_X: /* Memory addressed by DS:SI [string] */
michael@0 179 op->type = op_expression;
michael@0 180 op->flags |= op_hardcode;
michael@0 181 op->flags |= op_ds_seg | op_pointer | op_string;
michael@0 182 ia32_handle_register( &op->data.expression.base,
michael@0 183 REG_DWORD_OFFSET + 6 );
michael@0 184 break;
michael@0 185 case ADDRMETH_Y: /* Memory addressed by ES:DI [string] */
michael@0 186 op->type = op_expression;
michael@0 187 op->flags |= op_hardcode;
michael@0 188 op->flags |= op_es_seg | op_pointer | op_string;
michael@0 189 ia32_handle_register( &op->data.expression.base,
michael@0 190 REG_DWORD_OFFSET + 7 );
michael@0 191 break;
michael@0 192 case ADDRMETH_RR: /* Gen Register hard-coded in opcode */
michael@0 193 op->type = op_register;
michael@0 194 op->flags |= op_hardcode;
michael@0 195 ia32_handle_register( &op->data.reg,
michael@0 196 op_value + gen_regs );
michael@0 197 break;
michael@0 198 case ADDRMETH_RS: /* Seg Register hard-coded in opcode */
michael@0 199 op->type = op_register;
michael@0 200 op->flags |= op_hardcode;
michael@0 201 ia32_handle_register( &op->data.reg,
michael@0 202 op_value + REG_SEG_OFFSET );
michael@0 203 break;
michael@0 204 case ADDRMETH_RF: /* FPU Register hard-coded in opcode */
michael@0 205 op->type = op_register;
michael@0 206 op->flags |= op_hardcode;
michael@0 207 ia32_handle_register( &op->data.reg,
michael@0 208 op_value + REG_FPU_OFFSET );
michael@0 209 break;
michael@0 210 case ADDRMETH_RT: /* TST Register hard-coded in opcode */
michael@0 211 op->type = op_register;
michael@0 212 op->flags |= op_hardcode;
michael@0 213 ia32_handle_register( &op->data.reg,
michael@0 214 op_value + REG_TEST_OFFSET );
michael@0 215 break;
michael@0 216 case ADDRMETH_II: /* Immediate hard-coded in opcode */
michael@0 217 op->type = op_immediate;
michael@0 218 op->data.dword = op_value;
michael@0 219 op->flags |= op_hardcode;
michael@0 220 break;
michael@0 221
michael@0 222 case 0: /* Operand is not used */
michael@0 223 default:
michael@0 224 /* ignore -- operand not used in this insn */
michael@0 225 op->type = op_unused; /* this shouldn't happen! */
michael@0 226 break;
michael@0 227 }
michael@0 228
michael@0 229 return size;
michael@0 230 }
michael@0 231
michael@0 232 static size_t decode_operand_size( unsigned int op_type, x86_insn_t *insn,
michael@0 233 x86_op_t *op ){
michael@0 234 size_t size;
michael@0 235
michael@0 236 /* ++ Do Operand Type ++ */
michael@0 237 switch (op_type) {
michael@0 238 /* This sets the operand Size based on the Intel Opcode Map
michael@0 239 * (Vol 2, Appendix A). Letter encodings are from section
michael@0 240 * A.1.2, 'Codes for Operand Type' */
michael@0 241 /* NOTE: in this routines, 'size' refers to the size
michael@0 242 * of the operand in the raw (encoded) instruction;
michael@0 243 * 'datatype' stores the actual size and datatype
michael@0 244 * of the operand */
michael@0 245
michael@0 246 /* ------------------------ Operand Type ----------------- */
michael@0 247 case OPTYPE_c: /* byte or word [op size attr] */
michael@0 248 size = (insn->op_size == 4) ? 2 : 1;
michael@0 249 op->datatype = (size == 4) ? op_word : op_byte;
michael@0 250 break;
michael@0 251 case OPTYPE_a: /* 2 word or 2 dword [op size attr] */
michael@0 252 /* pointer to a 16:16 or 32:32 BOUNDS operand */
michael@0 253 size = (insn->op_size == 4) ? 8 : 4;
michael@0 254 op->datatype = (size == 4) ? op_bounds32 : op_bounds16;
michael@0 255 break;
michael@0 256 case OPTYPE_v: /* word or dword [op size attr] */
michael@0 257 size = (insn->op_size == 4) ? 4 : 2;
michael@0 258 op->datatype = (size == 4) ? op_dword : op_word;
michael@0 259 break;
michael@0 260 case OPTYPE_p: /* 32/48-bit ptr [op size attr] */
michael@0 261 /* technically these flags are not accurate: the
michael@0 262 * value s a 16:16 pointer or a 16:32 pointer, where
michael@0 263 * the first '16' is a segment */
michael@0 264 size = (insn->addr_size == 4) ? 6 : 4;
michael@0 265 op->datatype = (size == 4) ? op_descr32 : op_descr16;
michael@0 266 break;
michael@0 267 case OPTYPE_b: /* byte, ignore op-size */
michael@0 268 size = 1;
michael@0 269 op->datatype = op_byte;
michael@0 270 break;
michael@0 271 case OPTYPE_w: /* word, ignore op-size */
michael@0 272 size = 2;
michael@0 273 op->datatype = op_word;
michael@0 274 break;
michael@0 275 case OPTYPE_d: /* dword , ignore op-size */
michael@0 276 size = 4;
michael@0 277 op->datatype = op_dword;
michael@0 278 break;
michael@0 279 case OPTYPE_s: /* 6-byte psuedo-descriptor */
michael@0 280 /* ptr to 6-byte value which is 32:16 in 32-bit
michael@0 281 * mode, or 8:24:16 in 16-bit mode. The high byte
michael@0 282 * is ignored in 16-bit mode. */
michael@0 283 size = 6;
michael@0 284 op->datatype = (insn->addr_size == 4) ?
michael@0 285 op_pdescr32 : op_pdescr16;
michael@0 286 break;
michael@0 287 case OPTYPE_q: /* qword, ignore op-size */
michael@0 288 size = 8;
michael@0 289 op->datatype = op_qword;
michael@0 290 break;
michael@0 291 case OPTYPE_dq: /* d-qword, ignore op-size */
michael@0 292 size = 16;
michael@0 293 op->datatype = op_dqword;
michael@0 294 break;
michael@0 295 case OPTYPE_ps: /* 128-bit FP data */
michael@0 296 size = 16;
michael@0 297 /* really this is 4 packed SP FP values */
michael@0 298 op->datatype = op_ssimd;
michael@0 299 break;
michael@0 300 case OPTYPE_pd: /* 128-bit FP data */
michael@0 301 size = 16;
michael@0 302 /* really this is 2 packed DP FP values */
michael@0 303 op->datatype = op_dsimd;
michael@0 304 break;
michael@0 305 case OPTYPE_ss: /* Scalar elem of 128-bit FP data */
michael@0 306 size = 16;
michael@0 307 /* this only looks at the low dword (4 bytes)
michael@0 308 * of the xmmm register passed as a param.
michael@0 309 * This is a 16-byte register where only 4 bytes
michael@0 310 * are used in the insn. Painful, ain't it? */
michael@0 311 op->datatype = op_sssimd;
michael@0 312 break;
michael@0 313 case OPTYPE_sd: /* Scalar elem of 128-bit FP data */
michael@0 314 size = 16;
michael@0 315 /* this only looks at the low qword (8 bytes)
michael@0 316 * of the xmmm register passed as a param.
michael@0 317 * This is a 16-byte register where only 8 bytes
michael@0 318 * are used in the insn. Painful, again... */
michael@0 319 op->datatype = op_sdsimd;
michael@0 320 break;
michael@0 321 case OPTYPE_pi: /* qword mmx register */
michael@0 322 size = 8;
michael@0 323 op->datatype = op_qword;
michael@0 324 break;
michael@0 325 case OPTYPE_si: /* dword integer register */
michael@0 326 size = 4;
michael@0 327 op->datatype = op_dword;
michael@0 328 break;
michael@0 329 case OPTYPE_fs: /* single-real */
michael@0 330 size = 4;
michael@0 331 op->datatype = op_sreal;
michael@0 332 break;
michael@0 333 case OPTYPE_fd: /* double real */
michael@0 334 size = 8;
michael@0 335 op->datatype = op_dreal;
michael@0 336 break;
michael@0 337 case OPTYPE_fe: /* extended real */
michael@0 338 size = 10;
michael@0 339 op->datatype = op_extreal;
michael@0 340 break;
michael@0 341 case OPTYPE_fb: /* packed BCD */
michael@0 342 size = 10;
michael@0 343 op->datatype = op_bcd;
michael@0 344 break;
michael@0 345 case OPTYPE_fv: /* pointer to FPU env: 14 or 28-bytes */
michael@0 346 size = (insn->addr_size == 4)? 28 : 14;
michael@0 347 op->datatype = (size == 28)? op_fpuenv32: op_fpuenv16;
michael@0 348 break;
michael@0 349 case OPTYPE_ft: /* pointer to FPU env: 94 or 108 bytes */
michael@0 350 size = (insn->addr_size == 4)? 108 : 94;
michael@0 351 op->datatype = (size == 108)?
michael@0 352 op_fpustate32: op_fpustate16;
michael@0 353 break;
michael@0 354 case OPTYPE_fx: /* 512-byte register stack */
michael@0 355 size = 512;
michael@0 356 op->datatype = op_fpregset;
michael@0 357 break;
michael@0 358 case OPTYPE_fp: /* floating point register */
michael@0 359 size = 10; /* double extended precision */
michael@0 360 op->datatype = op_fpreg;
michael@0 361 break;
michael@0 362 case OPTYPE_m: /* fake operand type used for "lea Gv, M" */
michael@0 363 size = insn->addr_size;
michael@0 364 op->datatype = (size == 4) ? op_dword : op_word;
michael@0 365 break;
michael@0 366 case OPTYPE_none: /* handle weird instructions that have no encoding but use a dword datatype, like invlpg */
michael@0 367 size = 0;
michael@0 368 op->datatype = op_none;
michael@0 369 break;
michael@0 370 case 0:
michael@0 371 default:
michael@0 372 size = insn->op_size;
michael@0 373 op->datatype = (size == 4) ? op_dword : op_word;
michael@0 374 break;
michael@0 375 }
michael@0 376 return size;
michael@0 377 }
michael@0 378
michael@0 379 size_t ia32_decode_operand( unsigned char *buf, size_t buf_len,
michael@0 380 x86_insn_t *insn, unsigned int raw_op,
michael@0 381 unsigned int raw_flags, unsigned int prefixes,
michael@0 382 unsigned char modrm ) {
michael@0 383 unsigned int addr_meth, op_type, op_size, gen_regs;
michael@0 384 x86_op_t *op;
michael@0 385 size_t size;
michael@0 386
michael@0 387 /* ++ Yank optype and addr mode out of operand flags */
michael@0 388 addr_meth = raw_flags & ADDRMETH_MASK;
michael@0 389 op_type = raw_flags & OPTYPE_MASK;
michael@0 390
michael@0 391 if ( raw_flags == ARG_NONE ) {
michael@0 392 /* operand is not used in this instruction */
michael@0 393 return 0;
michael@0 394 }
michael@0 395
michael@0 396 /* allocate a new operand */
michael@0 397 op = x86_operand_new( insn );
michael@0 398
michael@0 399 /* ++ Copy flags from opcode table to x86_insn_t */
michael@0 400 op->access = (enum x86_op_access) OP_PERM(raw_flags);
michael@0 401 op->flags = (enum x86_op_flags) (OP_FLAGS(raw_flags) >> 12);
michael@0 402
michael@0 403 /* Get size (for decoding) and datatype of operand */
michael@0 404 op_size = decode_operand_size(op_type, insn, op);
michael@0 405
michael@0 406 /* override default register set based on Operand Type */
michael@0 407 /* this allows mixing of 8, 16, and 32 bit regs in insn */
michael@0 408 if (op_size == 1) {
michael@0 409 gen_regs = REG_BYTE_OFFSET;
michael@0 410 } else if (op_size == 2) {
michael@0 411 gen_regs = REG_WORD_OFFSET;
michael@0 412 } else {
michael@0 413 gen_regs = REG_DWORD_OFFSET;
michael@0 414 }
michael@0 415
michael@0 416 size = decode_operand_value( buf, buf_len, op, insn, addr_meth,
michael@0 417 op_size, raw_op, modrm, gen_regs );
michael@0 418
michael@0 419 /* if operand is an address, apply any segment override prefixes */
michael@0 420 if ( op->type == op_expression || op->type == op_offset ) {
michael@0 421 apply_seg(op, prefixes);
michael@0 422 }
michael@0 423
michael@0 424 return size; /* return number of bytes in instruction */
michael@0 425 }

mercurial