#include "../NativePal.h"
// XFESystem implementation of x64 DIV/IDIV operand decoding. Both zero divisors
// and quotient overflow cause #DE. Microsoft's MIT-licensed NativeAOT Unix PAL
// (v10.0.12 HardwareExceptions.cpp) similarly inspects the divisor before handing
// the exception to the unchanged official managed exception handler.
namespace {
struct Instruction {
const volatile uint8_t* bytes;
unsigned offset;
bool byte(uint8_t& result) { if(offset==15)return false;result=bytes[offset++];return true; }
bool displacement(unsigned count,int64_t& result) {
uint32_t value=0;uint8_t next;
for(unsigned i=0;i<count;i++){if(!byte(next))return false;value|=(uint32_t)next<<(8*i);}
result=count==1?(int8_t)value:(int32_t)value;return true;
}
};
uint64_t reg(const XfeRuntimeInterruptFrame& f,unsigned number) {
switch(number) {
case 0:return f.rax;case 1:return f.rcx;case 2:return f.rdx;case 3:return f.rbx;
case 4:return f.rsp;case 5:return f.rbp;case 6:return f.rsi;case 7:return f.rdi;
case 8:return f.r8;case 9:return f.r9;case 10:return f.r10;case 11:return f.r11;
case 12:return f.r12;case 13:return f.r13;case 14:return f.r14;case 15:return f.r15;
default:return 0;
}
}
uint64_t segment_base(unsigned segment) {
if(segment!=0x64 && segment!=0x65)return 0;
uint32_t low,high;
__asm__ volatile("rdmsr":"=a"(low),"=d"(high):"c"(segment==0x64?0xc0000100U:0xc0000101U));
return ((uint64_t)high<<32)|low;
}
}
extern "C" int XfeRuntimeClassifyDivideFault(const XfeRuntimeInterruptFrame* frame,uint32_t* code)
{
if(!frame || !frame->rip || !code)return 0;
Instruction instruction{(const volatile uint8_t*)frame->rip,0};
bool operand16=false,address32=false;uint8_t rex=0,segment=0,opcode;
for(;;) {
if(!instruction.byte(opcode))return 0;
if(opcode>=0x40 && opcode<=0x4f){rex=opcode;continue;}
bool prefix=true;
switch(opcode) {
case 0x66:operand16=true;break;
case 0x67:address32=true;break;
case 0x26:case 0x2e:case 0x36:case 0x3e:case 0x64:case 0x65:segment=opcode;break;
case 0xf0:case 0xf2:case 0xf3:break;
default:prefix=false;break;
}
if(!prefix)break;
rex=0; // A REX prefix has effect only after the last legacy prefix.
}
if(opcode!=0xf6 && opcode!=0xf7)return 0;
uint8_t modrm;if(!instruction.byte(modrm))return 0;
unsigned operation=(modrm>>3)&7;if(operation!=6 && operation!=7)return 0;
unsigned width=opcode==0xf6?8:(rex&8)?64:operand16?16:32;
unsigned mode=modrm>>6,operand=modrm&7;
uint64_t divisor;
if(mode==3) {
if(width==8 && !rex && operand>=4)divisor=reg(*frame,operand-4)>>8;
else divisor=reg(*frame,operand|((rex&1)?8:0));
} else {
uint64_t address=0;int64_t displacement=0;bool ripRelative=false;
if(operand==4) {
uint8_t sib;if(!instruction.byte(sib))return 0;
unsigned base=sib&7,index=(sib>>3)&7;
if(mode==0 && base==5) { if(!instruction.displacement(4,displacement))return 0; }
else address=reg(*frame,base|((rex&1)?8:0));
if(index!=4 || (rex&2))address+=reg(*frame,index|((rex&2)?8:0))<<(sib>>6);
} else if(mode==0 && operand==5) {
if(!instruction.displacement(4,displacement))return 0;ripRelative=true;
} else address=reg(*frame,operand|((rex&1)?8:0));
if(mode==1 && !instruction.displacement(1,displacement))return 0;
if(mode==2 && !instruction.displacement(4,displacement))return 0;
if(ripRelative)address=frame->rip+instruction.offset;
address+=(uint64_t)displacement;
if(address32)address=(uint32_t)address;
address+=segment_base(segment);
// The processor successfully read this operand before raising #DE.
// The BSP remains stopped on its exception stack throughout decoding.
divisor=0;auto bytes=(const volatile uint8_t*)address;
for(unsigned i=0;i<width/8;i++)divisor|=(uint64_t)bytes[i]<<(8*i);
}
if(width<64)divisor&=(1ULL<<width)-1;
*code=divisor?0xc0000095U:0xc0000094U;
return 1;
}
#include "../NativePal.h"
// XFESystem implementation of x64 DIV/IDIV operand decoding. Both zero divisors
// and quotient overflow cause #DE. Microsoft's MIT-licensed NativeAOT Unix PAL
// (v10.0.12 HardwareExceptions.cpp) similarly inspects the divisor before handing
// the exception to the unchanged official managed exception handler.
namespace {
struct Instruction {
const volatile uint8_t* bytes;
unsigned offset;
bool byte(uint8_t& result) { if(offset==15)return false;result=bytes[offset++];return true; }
bool displacement(unsigned count,int64_t& result) {
uint32_t value=0;uint8_t next;
for(unsigned i=0;i<count;i++){if(!byte(next))return false;value|=(uint32_t)next<<(8*i);}
result=count==1?(int8_t)value:(int32_t)value;return true;
}
};
uint64_t reg(const XfeRuntimeInterruptFrame& f,unsigned number) {
switch(number) {
case 0:return f.rax;case 1:return f.rcx;case 2:return f.rdx;case 3:return f.rbx;
case 4:return f.rsp;case 5:return f.rbp;case 6:return f.rsi;case 7:return f.rdi;
case 8:return f.r8;case 9:return f.r9;case 10:return f.r10;case 11:return f.r11;
case 12:return f.r12;case 13:return f.r13;case 14:return f.r14;case 15:return f.r15;
default:return 0;
}
}
uint64_t segment_base(unsigned segment) {
if(segment!=0x64 && segment!=0x65)return 0;
uint32_t low,high;
__asm__ volatile("rdmsr":"=a"(low),"=d"(high):"c"(segment==0x64?0xc0000100U:0xc0000101U));
return ((uint64_t)high<<32)|low;
}
}
extern "C" int XfeRuntimeClassifyDivideFault(const XfeRuntimeInterruptFrame* frame,uint32_t* code)
{
if(!frame || !frame->rip || !code)return 0;
Instruction instruction{(const volatile uint8_t*)frame->rip,0};
bool operand16=false,address32=false;uint8_t rex=0,segment=0,opcode;
for(;;) {
if(!instruction.byte(opcode))return 0;
if(opcode>=0x40 && opcode<=0x4f){rex=opcode;continue;}
bool prefix=true;
switch(opcode) {
case 0x66:operand16=true;break;
case 0x67:address32=true;break;
case 0x26:case 0x2e:case 0x36:case 0x3e:case 0x64:case 0x65:segment=opcode;break;
case 0xf0:case 0xf2:case 0xf3:break;
default:prefix=false;break;
}
if(!prefix)break;
rex=0; // A REX prefix has effect only after the last legacy prefix.
}
if(opcode!=0xf6 && opcode!=0xf7)return 0;
uint8_t modrm;if(!instruction.byte(modrm))return 0;
unsigned operation=(modrm>>3)&7;if(operation!=6 && operation!=7)return 0;
unsigned width=opcode==0xf6?8:(rex&8)?64:operand16?16:32;
unsigned mode=modrm>>6,operand=modrm&7;
uint64_t divisor;
if(mode==3) {
if(width==8 && !rex && operand>=4)divisor=reg(*frame,operand-4)>>8;
else divisor=reg(*frame,operand|((rex&1)?8:0));
} else {
uint64_t address=0;int64_t displacement=0;bool ripRelative=false;
if(operand==4) {
uint8_t sib;if(!instruction.byte(sib))return 0;
unsigned base=sib&7,index=(sib>>3)&7;
if(mode==0 && base==5) { if(!instruction.displacement(4,displacement))return 0; }
else address=reg(*frame,base|((rex&1)?8:0));
if(index!=4 || (rex&2))address+=reg(*frame,index|((rex&2)?8:0))<<(sib>>6);
} else if(mode==0 && operand==5) {
if(!instruction.displacement(4,displacement))return 0;ripRelative=true;
} else address=reg(*frame,operand|((rex&1)?8:0));
if(mode==1 && !instruction.displacement(1,displacement))return 0;
if(mode==2 && !instruction.displacement(4,displacement))return 0;
if(ripRelative)address=frame->rip+instruction.offset;
address+=(uint64_t)displacement;
if(address32)address=(uint32_t)address;
address+=segment_base(segment);
// The processor successfully read this operand before raising #DE.
// The BSP remains stopped on its exception stack throughout decoding.
divisor=0;auto bytes=(const volatile uint8_t*)address;
for(unsigned i=0;i<width/8;i++)divisor|=(uint64_t)bytes[i]<<(8*i);
}
if(width<64)divisor&=(1ULL<<width)-1;
*code=divisor?0xc0000095U:0xc0000094U;
return 1;
}