ack icon indicating copy to clipboard operation
ack copied to clipboard

in C, "char *g = (char *)f + 11;" loses the 11

Open kernigh opened this issue 9 years ago • 2 comments

The following program prints "pass" with gcc but "fail" with ack:

#include <stdio.h>
void f(void) {}
char *g = (char *)f + 11;

int main(void) {
	char *h = g - 11;
	if (h == (char *)f) {
		puts("pass");
		return 0;
	} else {
		puts("fail");
		return 1;
	}
}

In OpenBSD/amd64: ack -mlinuxppc -o cka ckpointer.c

In Debian Linux/powerpc:

$ gcc -o ckg ckpointer.c
$ ./cka
fail
$ ./ckg
pass

The bug is with char *g = (char *)f + 11; where g is a global variable and f is a function. Now gcc is correct, but ack silently loses the 11 and effectively does char *g = (char *)f;. After running ack -c.e ckpointer.c, I can see g defined as

g
 con $f

To get this bug, f must be a function, and I must cast it to another pointer type like char *. If I don't cast f, or if I cast f to an integer type, then ack gives an error for an illegal initializer.

kernigh avatar Jan 14 '17 21:01 kernigh

I think that's not technically a bug --- casting a function pointer to an object pointer in C invokes undefined behaviour.

The EM spec has different encodings for reference-to-data-plus-offset and reference-to-procedure; there are different opcodes for loading them onto the EM stack (lae for data, lpi for procedures). Presumably this is to support architectures where pointer-to-data and pointer-to-function are different sizes.

Unfortunately the reference-to-procedure encoding doesn't have an offset field, so (char*)f+5 simply can't be expressed as a single EM value. It's still possible to calculate this in code, by loading the procedure reference and doing maths on it, but that can't be done in an initialiser.

I don't think this should be silently dropping the offset, however --- it's surprising behaviour. I'd expect an invalid initialiser error because from EM's point of view (char*)f+5 isn't constant.

davidgiven avatar Jan 15 '17 09:01 davidgiven

The code at ival.g check_ival() line 542 looks suspicious, because it ignores expr->VL_VALUE when the thing is a function:

			else	/* e.g., int f(); int p = f; */
			if (idf->id_def->df_type->tp_fund == FUNCTION)
				C_con_pnam(idf->id_text);
			else	/* e.g., int a; int *p = &a; */
				C_con_dnam(idf->id_text, expr->VL_VALUE);

Not sure if a new check for expr->VALUE != 0 would go here or somewhere else.

kernigh avatar Sep 03 '19 00:09 kernigh