1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
permissions: 0.960
architecture: 0.960
x86: 0.957
device: 0.956
i386: 0.953
user-level: 0.948
arm: 0.946
assembly: 0.946
performance: 0.945
peripherals: 0.942
debug: 0.938
risc-v: 0.938
hypervisor: 0.934
TCG: 0.933
graphic: 0.929
semantic: 0.928
ppc: 0.926
virtual: 0.926
vnc: 0.924
PID: 0.911
kernel: 0.906
KVM: 0.904
register: 0.898
socket: 0.892
files: 0.871
mistranslation: 0.870
network: 0.863
boot: 0.863
VMM: 0.843
Inaccurate Fmod on i386
# Qemu Version
```bash
$ qemu-i386 --version
qemu-i386 version 3.0.1 (qemu-3.0.1-4.fc29)
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers
```
# Failing Source Code (C)
```c
#include <math.h>
#include <stdio.h>
int main()
{
double x = 29860476080414620.0;
double y = 17.0;
double z = fmod(x, y);
printf("%f\n", z);
return 0;
}
```
The code was compiled with GCC (8.3.1) on x86-64 with the flags `-O3 -m32 -lm -static`.
# Emitted (Annotated) Assembly
In order to facilitate debugging the issue, the following assembly was generated to show nothing unusual occurred during compilation. The assembly was generated with flags `-S -O3 -m32 -lm`, and then annotated to show the operands to fmod.
```asm
.file "a.c"
.text
.section .rodata.str1.1,"aMS",@progbits,1
.LC2:
.string "%f\n"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB16:
.cfi_startproc
leal 4(%esp), %ecx
.cfi_def_cfa 1, 0
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, %ebp
pushl %ecx
.cfi_escape 0xf,0x3,0x75,0x7c,0x6
subl $4, %esp
pushl $1076953088 ; high 32-bits of double for y
pushl $0 ; low 32-bits of double for y
pushl $1130005884 ; high 32-bits of double for x
pushl $2003187687 ; low 32-bits of double for x
call fmod
movl $.LC2, (%esp)
fstpl 4(%esp)
call printf
movl -4(%ebp), %ecx
.cfi_def_cfa 1, 0
addl $16, %esp
xorl %eax, %eax
leave
.cfi_restore 5
leal -4(%ecx), %esp
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE16:
.size main, .-main
.ident "GCC: (GNU) 8.3.1 20190223 (Red Hat 8.3.1-2)"
.section .note.GNU-stack,"",@progbits
```
# Result
Running the compiled binary on x86_64 produces the expected value of `15.000000`, while using `qemu-i386 <binary>` produces the unexpected result of `-4.000000`.
This was tested against:
1. Qemu 3.0.1 for Fedora 29.
2. Qemu 4.1.0 built from source, downloaded from https://download.qemu.org/qemu-4.1.0.tar.xz
3. Qemu built-from-source against commit 90b1e3afd33226b6078fec6d77a18373712a975c.
# Building Qemu
Qemu built-from-source was compiled as follows:
```bash
mkdir build && cd build
../configure --disable-kvm --target-list="i386-linux-user"
make -j 5
```
# Results
All built versions of Qemu running the 32-bit failed to produce the accurate result. Using qemu-x86_64 against an x86_64 binary built from the same C source code produces correct results. Running the 32-bit binary natively produces the correct result.
On current head-of-git QEMU I get the correct answer. I think this was probably fixed by the reimplementation of the 'fprem' emulation in commit 5ef396e2ba86, which was in the 5.1.0 release.
|