It should print out the following:
program Project1; {$APPTYPE CONSOLE} uses SysUtils; begin try MSecsToTimeStamp(-1); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
EIntOverflow: Integer overflow
Now run the exact same (32bit) binary on a Win64 system, this is what you’ll get:
EDivByZero: Division by zero
Give it a try. Weird, no? So I set out to figure out what is going on. MSecsToTimeStamp() is implemented down in SysUtils and is implemented in assembler code:
function MSecsToTimeStamp(MSecs: Comp): TTimeStamp; asm PUSH EBX XOR EBX,EBX MOV ECX,EAX MOV EAX,MSecs.Integer[0] MOV EDX,MSecs.Integer[4] DIV [EBX].IMSecsPerDay MOV [ECX].TTimeStamp.Time,EDX MOV [ECX].TTimeStamp.Date,EAX POP EBX end;
Notice the DIV instruction. That’s where all this funny business takes place. In this particular test case, the divisor is most certainly not 0 and according to the Intel documentation, if the quotient is too large to fit in EAX, then a #DE (Divide Error) exception is raised. If the divisor is 0, then the same exception is raised. Clearly, Windows 32bit is some how figuring out the difference while Windows 64bit doesn't bother. It is probably by disassembling the instruction and inspecting the machine state, which seems a little dangerous in the context of multicore CPUs. For instance, in the above case, the divisor is in a memory location. If another CPU were to modify that memory location before the exception was processed, the OS may report a different status from what actually happened. Yet, from what I've been able to find out, there is no sure way to know whether or not #DE happened from a real divide by zero or an overflow condition.
This took me a while to track down. It wasn’t until one engineer tried it and go the “expected” EIntOverflow and another tried it and got the EDivByZero. The only difference was that of the “bitness” of the OS (32bit vs. 64bit).
May be extend Delphi for support protected mode supervisor programming with Delphi driver injecting with some magic class TIDTCallgateDelegates and make this analyse of instruction opcodes in it. Why not? :)))
ReplyDeletePure Assembler is always dangerous, when platforms or compilers change. I would do the job with Pascal code. A good compiler produces almost the same code quality as pure asm.
ReplyDeletePeter,
ReplyDeleteOf course, however in this case it isn't about the assembly code. What if a compiler generated that code? Also, the same binary behaves differently depending between the two OSs.
is not possible, that the CPU in 64bit have a little difference between the 32bit?
ReplyDeleteSpecially in a DIV operation, XOR EBX,EBX = 0 but all 64bit register is 0?? , or there is a bug in the CPU when you use 32bit register in 64bit processor mode?
only to try in asm code tring to do the same operation usign RBX like XOR RBX,RBX is possible ?
ReplyDelete