开发者

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_end
condition 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. :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜