x86 Assembly (NASM): Floating Point Exception, not dividing by 0?
I see 2 problems with your program.
The
div ebx
instruction uses EDX:EAX as the dividend and you fail to set it up. Just insert anxor edx,edx
xor edx, edx ; <--------------------------------- Add this !
div ebx
mov ebx, eax ; ebx = (A + B) / (A - B)After the division you store the quotient in EBX but you never pick it up again to display the result!
mov ecx, [B]
mov eax, [A]
mul ecx
mov ecx, eax ; ecx = A * B
mov eax, ebx ; <--------------------------------- Add this !
add eax, ecx ; eax = A * B + (A + B) / (A - B)
The 2nd issue can be solved in a shorter way:
mov ecx, [B]
mov eax, [A]
mul ecx
add eax, ebx ; eax = A * B + (A + B) / (A - B)
EDIT (late catch, sorry)
I check for division by 0.
A * B + ( A + B ) / ( A - B )
You've based your check on the divider being (A - B)
and thus exit if A
is equal to B
.
Correct, but the program code erroneously calculates (A - B) / (A + B)
and so is using (A + B)
as the divider!
This is my version of calculating A * B + ( A + B ) / ( A - B )
:
mov ebx, [A]
sub ebx, [B] ; EBX = (A - B)
jz error ; Guard against division by zero!
mov eax, [A]
add eax, [B] ; EAX = (A + B)
xor edx, edx ; EDX:EAX = (A + B)
div ebx ; EAX = (A + B) / (A - B)
mov ebx, [A]
imul ebx, [B] ; EBX = A * B
add eax, ebx ; EAX = A * B + (A + B) / (A - B)
Getting floating point exception while trying to use div in assembly
The div
instruction divides the two-word parameter dx/ax
by the operand. If the quotient is too large to fit into a word, it will throw that exception.
Reference: http://siyobik.info.gf/main/reference/instruction/DIV
What do you have in the dx
register? Most likely dx/ax
divided by 15
does not fit in a 16-bit word.
Assembly x86-64 idivq - floating point exception
idivq %rcx
will divide a 128-bit numerator, whose high half is in %rdx
and whose low half is in %rax
, by the 64-bit denominator in %rcx
. See the description of IDIV
.
Your %rdx
seems to contain your 64-bit numerator and you haven't initialized %rax
at all. So you are dividing some 128-bit garbage number by whatever's in %rcx
. If the result doesn't fit in 64 bits, you get a divide overflow exception, which Unix OSes typically handle by delivering SIGFPE, "Floating point exception", although no floating point is involved. (There isn't a specific signal for integer divide overflow in Unix, and so they adopted this one as the closest fit.)
If your numerator is only 64 bits, load it into %rax
and execute cqto
(called CQO in Intel's naming) to sign-extend it into %rdx
. Then idivq %rcx
will divide it by the 64-bit denominator in %rcx
. Note that overflow is only possible in this case if you divide the most negative signed 64-bit integer (0x8000000000000000
) by -1
, or if you divide by zero.
Floating point exception, but no division by zero. What is it?
Replace mov al, [i]
with movzx ax, [i]
.
You are leaving at least 0x30 in ah
which leads to ax
>= 0x3000 that in turn prevents the result of the division between ax
and 10 to fit in al
.
ah
is not zero because div bl
sets it (it is the remainder of the division) but it is never cleared after the loop jumps back.
This also means that the first iteration works by accident: ah
is initially zero because SYS_READ
is 3 and thus the needed mov eax, 3
sets ah
to zero before the loop is entered.
Also, you are not correctly setting the starting address of the string to print (it's always one byte less).
Why IDIV with -1 causes floating point exception?
Note that I know I need to sign extend
edx:eax
...
If you don't sign-extend eax
, edx:eax
is interpreted as 64-bit signed number:
In your case, this would be 0x00000000fffffffb which is 4294967291 (and not -5).
div
and idiv
will cause an exception in two cases:
- You divide by zero
- The division result is not in the range that can be represented by the
eax
register
eax
can hold signed numbers in the range from -2147483648 to +2147483647, but -4294967291 lies outside that range. You'll get an exception.
should not cause an FPE.
Indeed, div
and idiv
will cause an "integer division exception", not "a floating-point exception".
However, many OSs will show the message "floating point exception"; POSIX defines SIGFPE as covering any arithmetic exception.
Calling printf() in assembly causes a 'floating point exception'
Take another look at the x86-64 ABI calling conventions. Specifically, on page 15:
Registers %rbp, %rbx and
%r12 through %r15 “belong” to the calling function and the called function is
required to preserve their values. In other words, a called function must preserve
these registers’ values for its caller. Remaining registers “belong” to the called
function. If a calling function wants to preserve such a register value across a
function call, it must save the value in its local stack frame.
So when you call printf
, you must assume that all registers except rbp, rbx, r12..r15
are clobbered. This has two effects for your program:
You attempt to save
ax
around the call toprintf
by stashing its value in thesi
register and then putting it back later, but this doesn't help becausesi
can be clobbered.Your
div cx
instruction divides the contents ofdx:ax
bycx
, butdx
might also have been clobbered.
The latter is the specific cause of the SIGFPE (which despite its name is also raised on an integer divide overflow), at least in my test runs. After the printf
returns, dx
contains some huge number, such that dx:ax
divided by cx
does not fit in 16 bits, which is an overflow.
(This also explains why the crash went away when you took out the printf
call - it was no longer there to clobber your registers.)
The other lesson here is that you can't check for divide overflow on x86 by doing a jo
afterwards; this error is primarily signaled by an exception, not by flags. So you really have to check your operands before executing the instruction, or else arrange to handle the exception if it occurs (which is more complicated and beyond the scope of this answer).
A couple of other notes:
Before your final call to
printf
(just beforejmp exit
), you don't load anything intorsi
, so the value printed is garbage.16-bit arithmetic is usually not to be preferred on x86-32 or x86-64 unless there is a really good reason for it. It is no faster and bloats your code with operand size prefixes. Better to do all your work with 32-bit arithmetic.
Since you're using your own entry point rather than letting the C library call your
main
, this means that libc has not had the opportunity to run its own initialization code. Therefore, it is not necessarily safe to call any libc function, particularly stdio and allocation functions. It seems thatprintf
happens to work okay in this case, and maybe it is all right for debugging, but you shouldn't plan to write your program this way for production.
Why does integer division by zero result in a floating point exception?
Does integer division actually use the FPU under the hood?
No, Linux just generates SIGFPE in this case too (it's a legacy name whose usage has now been extended). Indeed, the Single Unix Specification defines SIGFPE as "Erroneous arithmetic operation".
Related Topics
Run Meteor as a Daemon Process
Headless Protractor Tests Don't Plug on Xvfb
Unexpected Eof While Looking for Matching '"'
How to Create a Folder with a Folder Name Containing Spaces in Linux
Perl Signal Processing Only Works Once When Sighandler Calls Subroutine
Starting with Kde Frameworks 5 and Qt Creator
Cmake: How to Suppress "Entering Directory" Messages
I2C Write Acknowledge Polling in Linux Kernel
Timeouting a While Loop in Linux Shell Script
Running a Program Through Ssh Fails with "Error Opening Terminal: Unknown."
How to Enter Private Key Password with Ansible
How to Export Daily Disk Usage to CSV Format in Shell Scripting
Is There Some Ansible Equivalent to "Failed_When" for Success
Make Install Error 'Nothing to Be Done'