[llvm-commits] patch: teach deadargelim to work on externally visible functions!

Török Edwin edwintorok at gmail.com
Tue Jan 5 02:58:08 PST 2010


On 2010-01-05 07:08, Chris Lattner wrote:
> On Jan 4, 2010, at 5:18 AM, Török Edwin wrote:
>
>   
>>>> It returns false for externalLinkage, yet you can override the symbol
>>>> from another shared lib.
>>>>         
>>> sure, but then you are invoking undefined behaviour.
>>>       
>> If the optimizer already assumes that externalLinkage means no override
>> (which is a perfectly reasonable thing to do),
>>     
>
> Yes it does.
>
>   
>> then why not do further optimizations based on this assumption, and
>> really call the function we see
>> (instead of going through the .plt and calling whatever that resolves to
>> at runtime).
>>     
>
> Can you elaborate?
>   

Currently an externally visible function is called like this (with
-fPIC), even when we know
the function is in the current module:
callq   foo at PLT 

Which does allow for an override at runtime (see example at bottom), but
that is not what the optimizer assumed.

My point is that:
   - the optimizer already assumes that foo at PLT == the foo() it sees
   - this is different from gcc's behavior already (see below)
   - there is a well defined way of making a function overridable: weak
functions
   - currently all external functions are called via the PLT, which does
allow for runtime override:
        callq foo at PLT
   - however runtime override only works with clang if optimizer didn't
optimize it away
   - you get different behavior from optimized and non-optimized code
   - you get different behavior from placing the function in same source
file, or another
   - overriding at runtime externally visible functions is considered
undefined behavior by the optimizer (right?)

My suggestion is:
   - why go through all the trouble of calling it via PLT, and allowing
runtime override, when what we really want
is call the function we see? We could call it directly, i.e.:
        callq foo
   - from an ELF symbol visibility point of view this would mean the
symbols should get protected visibility, instead of default visibility
   - then we would get consistent behavior from optimized and
non-optimized code
   - you get same behavior regardless which file you place your function
into, it will always get called directly, instead of indirectly via the PLT

Here is an example where the link order of shared libs causes a
different function to be called:
a.c:
#include <string.h>
#include <stdio.h>
const char* foo()
{
    return "this is a";
}

void test()
{
    if (!strcmp(foo(),"this is a"))
        printf("ok\n");
    else
        printf("overridden\n");
}

b.c:
const char* foo()
{
    return "this is b";
}

main.c:
void test();
int main()
{
    test();
    return 0;
}

With gcc and no optimizations you can override foo:
$ gcc a.c -fPIC -DPIC -shared -o a.so
$ gcc b.c -fPIC -DPIC -shared -o b.so
$ gcc main.c ./a.so ./b.so -o ab
$ ./ab
ok
$ gcc main.c ./b.so ./a.so -o ba
$ ./ba
overridden

With gcc, no optimizations and protected visibility you can't override foo:
$ gcc a.c -fPIC -DPIC -shared -fvisibility=protected -o a_prot.so
$ gcc main.c ./b.so ./a_prot.so -o ba_prot
$ ./ba_prot
ok

With an optimized clang build you can't override, optimizer
constant-propagated:
$ clang a.c -fPIC -DPIC -shared -o a_opt_clang.so -O1
$ gcc main.c ./b.so ./a_opt.so -o ba_opt_clang
$ ./ba_opt_clang
ok

With an optimized gcc build you can still override:
$ gcc a.c -fPIC -DPIC -shared -o a_opt_clang.so -O2
$ gcc main.c ./b.so ./a_opt.so -o ba_opt_gcc
$ ./ba_opt_gcc
overridden

Best regards,
--Edwin



More information about the llvm-commits mailing list