Depending on optimization level the output differ as follows:
With unexpected output:
$ gcc -Wall -O3 otest.c -o otest
$ otest
*x: 0
y: 2048.899902
y: 0.000000
With expected output:
$ gcc -Wall -O2 otest.c -o otest
$ otest
*x: 45000e66
y: 0.000000
y: 2048.899902
source code :
#include <stdio.h>
int main(void)
{
float y = 2048.9;
void *p = &y;
unsigned int *x = p;
printf(" *x: %x \n",*x);
*x = 0;
printf(" y: %f \n",y);
*x = 0x45000e66;
printf(" y: %f \n",y);
return 0;
}
gcc version is 4.2.1.
Am I missing any important directive ?
Yes. Your code is violating the strict aliasing rule (when you have a float, but you access it through a pointer to unsigned int, which is an incompatible type), invoking undefined behavior, so the compiler is allowed to do anything it pleases with your code, including entirely eliminating parts of it.
Related
I encountered a weird situation where performing pointer arithmetic involving
dynamically linked symbols leads to incorrect results. I'm unsure if there
are simply missing some linker parameters or if it's a linker bug. Can someone
explain what's wrong in the following example?
Consider the following code (lib.c) of a simple shared library:
#include <inttypes.h>
#include <stdio.h>
uintptr_t getmask()
{
return 0xffffffff;
}
int fn1()
{
return 42;
}
void fn2()
{
uintptr_t mask;
uintptr_t p;
mask = getmask();
p = (uintptr_t)fn1 & mask;
printf("mask: %08x\n", mask);
printf("fn1: %p\n", fn1);
printf("p: %08x\n", p);
}
The operation in question is the bitwise AND between the address of fn1 and
the variable mask. The application (app.c) just calls fn2 like that:
extern int fn2();
int main()
{
fn2();
return 0;
}
It leads to the following output ...
mask: ffffffff
fn1: 0x2aab43c0
p: 000003c0
... which is obviously incorrect, because the same result is expected for fn1
and p. The code runs on an AVR32 architecture and is compiled as follows:
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -c -o lib.o lib.c
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -shared -o libfoo.so lib.o
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -o app app.c -L. -lfoo
The compiler thinks, it is the optimal solution to load the variable
mask into 32 bit register 7 and splitting the &-operation into two assembler
operations with immediate operands.
$ avr32-linux-uclibc-objdump -d libfoo.so
000003ce <fn1>:
3ce: 32 ac mov r12,42
3d0: 5e fc retal r12
000003d2 <fn2>:
...
3f0: e4 17 00 00 andh r7,0x0
3f4: e0 17 03 ce andl r7,0x3ce
I assume the immediate operands of the and instructions are not relocated
to the loading address of fn1 when the shared library is loaded into the
applications address space:
Is this behaviour intentional?
How can I investigate whether problem occurs when linking the shared library or when loading the executable?
Background: This is not an academic questions. OpenSSL and LibreSSL
use similar code, so changing the C source is not an option. The code runs
well on other architectures and certainly there is an unapparent reason for
doing bitwise operations on function pointers.
after correcting all the 'slopiness' in the code, the result is:
#include <inttypes.h>
#include <stdio.h>
int fn1( void );
void fn2( void );
uintptr_t getmask( void );
int main( void )
{
fn2();
return 0;
}
uintptr_t getmask()
{
return 0xffffffff;
}
int fn1()
{
return 42;
}
void fn2()
{
uintptr_t mask;
uintptr_t p;
mask = getmask();
p = (uintptr_t)fn1 & mask;
printf("mask: %08x\n", (unsigned int)mask);
printf("fn1: %p\n", fn1);
printf("p: %08x\n", (unsigned int)p);
}
and the output (on my linux 64bit computer) is:
mask: ffffffff
fn1: 0x4007c1
p: 004007c1
I have a problem with my function waveres. It is supposed to return a amplitude as a float, but does not. It return a random high number. I think it is the definition in my header file that is not "seen" by the main function. The other functions does work so I did not include them. When the waveres function runs, it prints correct values of amp.
Header file
#include <stdio.h>
#include <math.h>
#include <time.h> /* til random funksjonen */
#include <stdlib.h>
void faseforskyvning(float epsi[]);
float waveres(float S[],float w[],float *x, float *t, float epsi[]);
void lespar(float S[], float w[]);
Main program
#include "sim.h"
main()
{
float epsi[9], t = 1.0, x = 1.0;
float S[9], w[9];
float amp;
faseforskyvning(epsi);
lespar(S,w);
amp=waveres(S,w,&x,&t,epsi);
printf("%f\n", amp);
}
waveres:
#include "sim.h"
float waveres(float S[],float w[],float *x, float *t, float epsi[])
{
float amp = 0, k;
int i;
for(i=0;i<10;i++)
{
k = pow(w[i],2)/9.81;
amp = amp + sqrt(2*S[i]*0.2)*cos(w[i]*(*t)+k*(*x)+epsi[i]);
printf("%f\n",amp);
}
return(amp);
}
Sample output where the two last number are supposed to be the same.
0.000000
0.261871
3.750682
3.784552
3.741382
3.532950
3.759173
3.734213
3.418669
3.237864
1078933760.000000
A source to the error might be me compiling wrong. Here is a output from compiler:
make
gcc -c -o test.o test.c
gcc -c -o faseforskyvning.o faseforskyvning.c
gcc -c -o waveres.o waveres.c
gcc -c -o lespar.o lespar.c
gcc test.o faseforskyvning.o waveres.o lespar.o -o test -lm -E
gcc: warning: test.o: linker input file unused because linking not done
gcc: warning: faseforskyvning.o: linker input file unused because linking not done
gcc: warning: waveres.o: linker input file unused because linking not done
gcc: warning: lespar.o: linker input file unused because linking not done
You have undefined behavior, you iterate untill 10
for(i=0;i<10;i++)
But your arrays has size 9 which means the biggest index is 8
float epsi[9], t = 1.0, x = 1.0;
float S[9], w[9];
You need to change your loop to
for(i=0;i<9;i++)
Also your arrays are not initialized, this is also provokes undefined behavior. For example
float w[9]={0};
initializes all elements of array w with 0
Consider the following snippets:
short x = 2000000000;
short x = (short)2000000000;
int x = 1000000000 * 1000000000;
Can we get an warning(/error) for these in Clang? How? Starting with what version?
Thanks,
Ciprian.
As of clang 3.3, at least, you get warnings in both cases without even trying:
/* main.c */
short x = 2000000000;
int y = 1000000000 * 1000000000;
int main()
{
return 0;
}
Compile:
$ clang -c main.c
main.c:1:11: warning: implicit conversion from 'int' to 'short' changes value
from 2000000000 to -27648 [-Wconstant-conversion]
short x = 2000000000;
~ ^~~~~~~~~~
main.c:2:20: warning: overflow in expression; result is -1486618624 with type
'int' [-Winteger-overflow]
int y = 1000000000 * 1000000000;
^
2 warnings generated.
Using gcc 4.7:
$ gcc --version
gcc (GCC) 4.7.0 20120505 (prerelease)
Code listing (test.c):
#include <stdint.h>
struct test {
int before;
char start[0];
unsigned int v1;
unsigned int v2;
unsigned int v3;
char end[0];
int after;
};
int main(int argc, char **argv)
{
int x, y;
x = ((uintptr_t)(&((struct test*)0)->end)) - ((uintptr_t)(&((struct test*)0)->start));
y = ((&((struct test*)0)->end)) - ((&((struct test*)0)->start));
return x + y;
}
Compile & execute
$ gcc -Wall -o test test.c && ./test
Floating point exception
The SIGFPE is caused by the second assignment (y = ...). In the assembly listing, there is a division on this line? Note that the only difference between x= and y= is casting to (uintptr_t).
Disregarding the undefined behaviour due to violation of constarints in the standard, what gcc does here is to calculate the difference between two pointers to char[0] - &(((struct test*)0)->start) and &(((struct test*)0)->end), and divide that difference by the size of a char[0], which of course is 0, so you get a division by 0.
My code is trying to find the entropy of a signal (stored in 'data' and 'interframe' - in the full code these would contain the signal, here I've just put in some random values). When I compile with 'gcc temp.c' it compiles and runs fine.
Output:
entropy: 40.174477
features: 0022FD06
features[0]: 40
entropy: 40
But when I compile with 'gcc -mstackrealign -msse -Os -ftree-vectorize temp.c' then it compiles, but fails to execute beyond line 48. It needs to have all four flags in order to fail - any three of them and it runs fine.
The code probably looks weird - I've chopped just the failing bits out of a much bigger program. I only have the foggiest idea of what the compiler flags do, someone else put them in (and there's usually more of them, but I worked out that these were the bad ones).
All help much appreciated!
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <math.h>
static void calc_entropy(volatile int16_t *features, const int16_t* data,
const int16_t* interframe, int frame_length);
int main()
{
int frame_length = 128;
int16_t data[128] = {1, 2, 3, 4};
int16_t interframe[128] = {1, 1, 1};
int16_t a = 0;
int16_t* features = &a;
calc_entropy(features, data, interframe, frame_length);
features += 1;
fprintf(stderr, "\nentropy: %d", a);
return 0;
}
static void calc_entropy(volatile int16_t *features, const int16_t* data,
const int16_t* interframe, int frame_length)
{
float histo[65536] = {0};
float* histo_zero = histo + 32768;
volatile float entropy = 0.0f;
int i;
for(i=0; i<frame_length; i++){
histo_zero[data[i]]++;
histo_zero[interframe[i]]++;
}
for(i=-32768; i < 32768; i++){
if(histo_zero[i])
entropy -= histo_zero[i]*logf(histo_zero[i]/(float)(frame_length*2));
}
fprintf(stderr, "\nentropy: %f", entropy);
fprintf(stderr, "\nfeatures: %p", features);
features[0] = entropy; //execution fails here
fprintf(stderr, "\nfeatures[0]: %d", features[0]);
}
Edit: I'm using gcc 4.5.2, with x86 architecture. Also, if I compile and run it on VirtualBox running ubuntu (gcc -lm -mstackrealign -msse -Os -ftree-vectorize temp.c) it executes correctly.
Edit2: I get
entropy: 40.174477
features: 00000000
and then a message from windows telling me that the program has stopped running.
Edit3: In the five months since I originally posted the question I've updated to gcc 4.7.0, and the code now runs fine. I went back to gcc 4.5.2, and it failed. Still don't know why!
ottavio#magritte:/tmp$ gcc x.c -o x -lm -mstackrealign -msse -Os -ftree-vectorize
ottavio#magritte:/tmp$ ./x
entropy: 40.174477
features: 0x7fff5fe151ce
features[0]: 40
entropy: 40
ottavio#magritte;/tmp$ gcc x.c -o x -lm
ottavio#magritte:/tmp$ ./x
entropy: 40.174477
features: 0x7fffd7eff73e
features[0]: 40
entropy: 40
ottavio#magritte:/tmp$
So, what's wrong with it? gcc 4.6.1 and x86_64 architecture.
It seems to be running here as well, and the only thing I see that might be funky is that you are taking a 16 bit value (features[0]) and converting a 32 bit float (entropy)
features[0] = entropy; //execution fails here
into that value, which of course will shave it off.
It shouldn't matter, but for the heck of it, see if it makes any difference if change your int16_t values to int32_t values.