* Optimize a jump (or call) instruction. */
| 215 | * Optimize a jump (or call) instruction. |
| 216 | */ |
| 217 | static void optimizeJump(const Binary *B, intptr_t addr, uint8_t *bytes, |
| 218 | size_t size) |
| 219 | { |
| 220 | if (!option_Opeephole || size == 0) |
| 221 | return; |
| 222 | |
| 223 | bool jcc = false, jmp = false; |
| 224 | switch (bytes[0]) |
| 225 | { |
| 226 | case 0xE9: |
| 227 | jmp = true; |
| 228 | // Fallthrough: |
| 229 | case 0xE8: |
| 230 | if (size != /*sizeof(jmpq/call rel32)=*/5) |
| 231 | return; |
| 232 | break; |
| 233 | case 0x0F: |
| 234 | if (size != /*sizeof(jcc rel32)=*/6) |
| 235 | return; |
| 236 | jcc = true; |
| 237 | switch (bytes[1]) |
| 238 | { |
| 239 | case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: |
| 240 | case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: |
| 241 | case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: |
| 242 | case 0x8F: |
| 243 | break; |
| 244 | default: |
| 245 | return; |
| 246 | } |
| 247 | break; |
| 248 | default: |
| 249 | return; |
| 250 | } |
| 251 | |
| 252 | int32_t rel32 = *(int32_t *)(bytes + (jcc? 2: 1)); |
| 253 | intptr_t target = addr + (intptr_t)size + (intptr_t)rel32; |
| 254 | const Instr *J = findInstr(B, target); |
| 255 | if (J == nullptr) |
| 256 | return; |
| 257 | target = getTrampolineEntry(B->Es, J); |
| 258 | if (target == INTPTR_MIN) |
| 259 | target = getCFTTarget(J->addr, J->PATCH, J->size, CFT_JMP); |
| 260 | if (target == INTPTR_MIN) |
| 261 | return; |
| 262 | |
| 263 | intptr_t diff = target - (addr + (intptr_t)size); |
| 264 | if (jmp && diff == 0) |
| 265 | { |
| 266 | // As a special case, we can replace this JMP with a 5-byte NOP. |
| 267 | bytes[0] = 0x0F; bytes[1] = 0x1F; bytes[2] = 0x44; |
| 268 | bytes[3] = 0x00; bytes[4] = 0x00; |
| 269 | return; |
| 270 | } |
| 271 | if (diff < INT32_MIN || diff > INT32_MAX) |
| 272 | return; |
| 273 | *(int32_t *)(bytes + (jcc? 2: 1)) = (int32_t)diff; |
| 274 | } |
no test coverage detected