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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
qemu-ppc (user) incorrectly translates float32 arithmetics
I'm using qemu-3.1.0 (Gentoo).
When I was running regression test suite via qemu-ppc for GHC I noticed a few uint32_t<->float32 failures I did not expect to encounter.
Here is an example
$ cat a.c
#include <stdio.h>
#include <stdint.h>
int main() {
volatile uint32_t i = 1;
printf("0x1 = %e\n", *(volatile float*)&i);
}
$ powerpc-unknown-linux-gnu-gcc -O2 a.c -Wall -o a -fno-strict-aliasing -fno-stack-protector -static && ./a
0x1 = 2.802597e-45
$ scp a timberdoodle.ppc64.dev.gentoo.org:~/
a 100% 826KB 102.0KB/s 00:08
$ ssh timberdoodle.ppc64.dev.gentoo.org ./a
0x1 = 1.401298e-45
$ qemu-ppc ./a
0x1 = 2.802597e-45
Looks like off-by-one bit somewhere. I'm not sure if it's FPU instruction or some internals of printf() that are emulated incorrectly.
My native system is x86_64-pc-linux-gnu with a few binfmt_misc handlers wired.
Checking other targets I have locally I get the following:
affected targets:
- powerpc
- powerpc64
- powerpc64le
unaffected targets:
- arm
- arm64
- hppa
- sparc
probably unaffected:
- alpha (maybe it's ok as alpha is not quite an IEEE754 platform)
Raw log:
$ for gcc in /usr/bin/*-gcc; do rm -f a; $gcc -O2 a.c -Wall -o a -fno-strict-aliasing -fno-stack-protector 2>/dev/null && ./a 2>/dev/null && echo -n "$gcc: " && file a; done | sort
0x1 = 1.401298e-45 : /usr/bin/aarch64-unknown-linux-gnu-gcc: a: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/aarch64_be-unknown-linux-gnu-gcc: a: ELF 64-bit MSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64_be.so.1, for GNU/Linux 3.7.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/afl-gcc: a: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped
0x1 = 1.401298e-45 : /usr/bin/armv6j-unknown-linux-gnueabihf-gcc: a: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/armv7a-unknown-linux-gnueabihf-gcc: a: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/hppa-unknown-linux-gnu-gcc: a: ELF 32-bit MSB pie executable, PA-RISC, 1.1 version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 3.2.0, with debug_info, not stripped
0x1 = 1.401298e-45 : /usr/bin/hppa2.0-unknown-linux-gnu-gcc: a: ELF 32-bit MSB pie executable, PA-RISC, 1.1 version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 3.2.0, with debug_info, not stripped
0x1 = 1.401298e-45 : /usr/bin/m68k-unknown-linux-gnu-gcc: a: ELF 32-bit MSB pie executable, Motorola m68k, 68020, version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/mips64-unknown-linux-gnuabin64-gcc: a: ELF 64-bit MSB pie executable, MIPS, MIPS-III version 1 (SYSV), dynamically linked, interpreter /lib64/ld.so.1, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/riscv64-unknown-linux-gnu-gcc: a: ELF 64-bit LSB pie executable, UCB RISC-V, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/s390x-unknown-linux-gnu-gcc: a: ELF 64-bit MSB pie executable, IBM S/390, version 1 (SYSV), dynamically linked, interpreter /lib/ld64.so.1, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/sparc-unknown-linux-gnu-gcc: a: ELF 32-bit MSB pie executable, SPARC32PLUS, V8+ Required, total store ordering, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/x86_64-HEAD-linux-gnu-gcc: a: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/x86_64-UNREG-linux-gnu-gcc: a: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped
0x1 = 1.401298e-45 : /usr/bin/x86_64-pc-linux-gnu-gcc: a: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped
0x1 = 2.652494e-315 : /usr/bin/alpha-unknown-linux-gnu-gcc: a: ELF 64-bit LSB pie executable, Alpha (unofficial), version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, not stripped
0x1 = 2.802597e-45 : /usr/bin/powerpc-unknown-linux-gnu-gcc: a: ELF 32-bit MSB pie executable, PowerPC or cisco 4500, version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 3.2.0, not stripped
0x1 = 2.802597e-45 : /usr/bin/powerpc64-unknown-linux-gnu-gcc: a: ELF 64-bit MSB pie executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), dynamically linked, interpreter /lib64/ld64.so.1, for GNU/Linux 3.2.0, not stripped
0x1 = 2.802597e-45 : /usr/bin/powerpc64le-unknown-linux-gnu-gcc: a: ELF 64-bit LSB pie executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), dynamically linked, interpreter /lib64/ld64.so.2, for GNU/Linux 3.10.0, not stripped
Shorter example without relying on printf() implementation. Looks like uint32_t<->float<->double transitions are enough.
$ cat a.c
#include <stdio.h>
#include <stdint.h>
int main() {
volatile uint32_t i = 1;
volatile float f;
volatile double d;
*(volatile uint32_t*)&f = i;
d = f; f = d; // double conversion
i = *(volatile uint32_t*)&f;
printf("0x1 = %x\n", i);
}
$ powerpc-unknown-linux-gnu-gcc -O2 a.c -Wall -o a -fno-strict-aliasing -fno-stack-protector -static && qemu-ppc ./a
0x1 = 2
A bit more investigation:
It looks like the bug happens in float->double conversion direction.
$ cat a.c
#include <stdio.h>
#include <stdint.h>
int main() {
volatile uint32_t i = 1;
volatile float f;
volatile double d;
*(volatile uint32_t*)&f = i;
d = f;
printf("d = %#llx (expect 0x36a0000000000000)\n", *(volatile uint64_t*)&d);
}
$ powerpc-unknown-linux-gnu-gcc -O2 a.c -Wall -o a -fno-strict-aliasing -fno-stack-protector -static && qemu-ppc ./a
d = 0x36b0000000080000 (expect 0x36a0000000000000)
10000400 <main>:
10000404: 39 20 00 01 li r9,1
...
10000434: 91 21 00 10 stw r9,16(r1)
...
1000043c: c0 01 00 10 lfs f0,16(r1)
10000440: d8 01 00 08 stfd f0,8(r1)
...
10000448: 80 a1 00 08 lwz r5,8(r1)
1000044c: 80 c1 00 0c lwz r6,12(r1)
...
10000454: 48 02 01 ad bl 10020600 <___printf_chk>
This is just lfs/stfd conversion. qemu does translates that pair if instructions into:
$ ppc-linux-user/qemu-ppc -d in_asm,out_asm,op,op_opt /tmp/b/a
...
IN: main
...
0x1000043c: c0010010 lfs f0, 0x10(r1)
0x10000440: d8010008 stfd f0, 8(r1)
...
OP:
---- 1000043c
movi_i32 tmp1,$0x10
add_i32 tmp0,r1,tmp1
qemu_ld_i32 tmp1,tmp0,beul,2
call todouble,$0x5,$1,tmp2,tmp1
st_i64 tmp2,env,$0x9198
'todouble' must be a 'uint64_t helper_todouble(uint32_t arg=0x1)' from:
https://github.com/qemu/qemu/blob/master/target/ppc/fpu_helper.c#L55
Attaching the patch and sending for review as:
https://lists.gnu.org/archive/html/qemu-devel/2019-03/msg06562.html
Alternatively 'uint64_t helper_todouble(uint32_t)' could be implemented via
include/fpu/softfloat.h:float64 float32_to_float64(float32, float_status *status);
fix committed with c0e6616b6685ffdb4c5e091bc152e46e14703dd1 and released with 4.2.0
|