C externs that alias the same address?
Can a C compiler assume that two different extern globals cannot be aliased to the same address?
In my case, I have a situation like this:
extern int array_of_int[], array_end; void some_func(void) { int *t; for (t = &array_of_int[0]; t != &array_end; t++) { ...
The resulting binary compiled with optimization on does not test the
t != &array_endcondition before entering the loo开发者_JAVA技巧p. The compiler's optimization is that the loop must execute at least once since
t
cannot immediately equal &array_end
at the outset.
Of course we found this the hard way. Apparently, some assembler hackery with linker sections resulted in a case where the two externs are the same address.
Thanks for any advice!
In short, yes, it's free to make that assumption. There is nothing special about extern
variables. Two variables may not be aliases of each other. (If the answer was any different, think about the chaos that would ensue. extern int a, b
could alias each other, which would make the semantics of any code using those variables completely insane!)
In fact, you are relying on undefined behaviour here, full stop. It is not valid to compare addresses of unrelated variables in this way.
The C99 says in 6.2.2 "Linages of identifiers":
An identifier declared in different scopes or in the same scope more than once can be made to refer to the same object or function by a process called linkage. (Footnote 21)
...
Footnote 21: There is no linkage between different identifiers.
So unfortunately, this somewhat common assembly language trick (that I've used...) isn't well-defined. You'd be better to have your assembly module define array_end
to be be actual pointer that the asm code loads with the address of the end of the array. That way the C code can be well-defined since the array_end
pointer would be a separate object.
I think here's the fixed code
#include <stdio.h>
extern int array_of_int[];
extern int *array_end;
int main()
{
int *t;
for (t = &array_of_int[0]; t != array_end; t++)
{
printf("%i\n", *t);
}
return 0;
}
in another compilation unit:
int array_of_int[] = { }; // { 1,2,3,4 };
int *array_end = array_of_int + (sizeof(array_of_int)/sizeof(array_of_int[0]));
It compiles into this (-O3, gcc 4.4.5 i686)
080483f0 <main>:
80483f0: 55 push %ebp
80483f1: 89 e5 mov %esp,%ebp
80483f3: 83 e4 f0 and $0xfffffff0,%esp
80483f6: 53 push %ebx
80483f7: 83 ec 1c sub $0x1c,%esp
80483fa: 81 3d 24 a0 04 08 14 cmpl $0x804a014,0x804a024
8048401: a0 04 08
8048404: 74 2f je 8048435 <main+0x45>
8048406: bb 14 a0 04 08 mov $0x804a014,%ebx
804840b: 90 nop
804840c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
8048410: 8b 03 mov (%ebx),%eax
8048412: 83 c3 04 add $0x4,%ebx
8048415: c7 44 24 04 00 85 04 movl $0x8048500,0x4(%esp)
804841c: 08
804841d: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048424: 89 44 24 08 mov %eax,0x8(%esp)
8048428: e8 d7 fe ff ff call 8048304 <__printf_chk@plt>
804842d: 39 1d 24 a0 04 08 cmp %ebx,0x804a024
8048433: 75 db jne 8048410 <main+0x20>
8048435: 83 c4 1c add $0x1c,%esp
8048438: 31 c0 xor %eax,%eax
804843a: 5b pop %ebx
804843b: 89 ec mov %ebp,%esp
804843d: 5d pop %ebp
804843e: c3 ret
804843f: 90 nop
Its very simple in case if we do it in arm code - we have an attribute for it ..
#include <stdio.h>
int oldname = 1;
extern int newname __attribute__((alias("oldname"))); // declaration
void foo(void)
{
printf("newname = %d\n", newname); // prints 1
}
and only extern is enough here. To import it in other files - its seamless. for assembly file - you can use IMPORT command and you have alias there. :)
精彩评论