cross-vendor TLS fix: 0x66 prefixes are forced into 64bit call on AMD

This commit is contained in:
nmzik
2026-09-03 23:01:51 +02:00
parent f27a136430
commit 5b8e3b51b0
2 changed files with 16 additions and 12 deletions
+9 -6
View File
@@ -1143,7 +1143,8 @@ static void PatchProgram(Program* program, uint64_t address, uint64_t size) {
}
}
if (!program->elf->IsShared() && program->tls.handler_vaddr != 0) {
if (!program->elf->IsShared() && program->tls.handler_vaddr != 0 &&
size >= Jit::Call9::GetSize()) {
// Replace:
// 66 66 66
// mov <reg>, qword ptr fs:[0x00]
@@ -1151,7 +1152,8 @@ static void PatchProgram(Program* program, uint64_t address, uint64_t size) {
// call <handler>
// mov <reg>,rax
// nop ...
const uint8_t tls_pattern[5] = {0x64, 0x48, 0x8B, 0x00, 0x25};
const uint8_t tls_pattern[5] = {0x64, 0x48, 0x8B, 0x00, 0x25};
const uint8_t zero_displacement[4] = {};
EXIT_IF(Jit::Call9::GetSize() != 9);
@@ -1173,14 +1175,15 @@ static void PatchProgram(Program* program, uint64_t address, uint64_t size) {
const uint8_t modrm = inst_ptr[3];
if (memcmp(inst_ptr, tls_pattern, 3) == 0 && (modrm & 0xc7u) == 0x04u &&
inst_ptr[4] == tls_pattern[4] &&
*reinterpret_cast<const uint32_t*>(inst_ptr + 5) == 0) {
LOGF("Patch tls at addr: [%016" PRIx64 "]\n",
reinterpret_cast<uint64_t>(inst_ptr));
memcmp(inst_ptr + 5, zero_displacement, sizeof(zero_displacement)) == 0) {
LOGF("Patch tls at addr: [%016" PRIx64 "]\n", reinterpret_cast<uint64_t>(inst_ptr));
const auto reg = (modrm >> 3u) & 7u;
EXIT_NOT_IMPLEMENTED(reg == 4u);
// A raw scan can encounter a 0x66 in the preceding instruction.
// A raw scan can encounter a 0x66 in the preceding instruction, so do not
// overwrite it. Call9 starts with REX.W to neutralize genuine 0x66
// prefixes on AMD processors (before it could turn E8 into callw 16bit).
auto* code = new (inst_ptr) Jit::Call9;
code->SetFunc(reg == 0
? program->tls.handler_vaddr