This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Are ifuncs intended to be allowed to resolve to symbols in another DSO?


On 1/9/20 3:37 PM, Florian Weimer wrote:
> * Zack Weinberg:
> 
>> Suppose I have two major versions of the same shared library
>> (libfoo.so.1 and libfoo.so.2) and the only difference is that
>> libfoo.so.2 drops a whole bunch of compatibility aliases.  For
>> instance, libfoo.so.1 defines two names, `blurf` and `xblurf`, for the
>> same function, but libfoo.so.2 defines only the `blurf` name.
>>
>> Any program that winds up loading both shared libraries (via
>> transitive dependencies) is going to have two copies of the actual
>> code for `blurf` in memory.  I could eliminate this duplication by
>> having libfoo.so.1 be a thin wrapper around libfoo.so.2, providing
>> only a definition for `xblurf` that calls `blurf`.  Good so far, but
>> now old applications are making two jumps through the PLT whenever
>> they call `xblurf`.  It occurred to me to wonder whether I could
>> eliminate the extra indirection (on the second and subsequent calls)
>> by making xblurf an ifunc:
>>
>> extern int blurf(char *arg1, int arg2); // defined in libfoo.so.2
>> static int (*resolve_xblurf(void))(char *, int)
>> {
>>   return blurf;
>> }
>> int xblurf(char *, int) __attribute__((ifunc("resolve_xblurf")));
> 
> This only works if libfoo.so.1 has already been relocated when the IFUNC
> resolver is called.  Old glibcs always relocated objects in the wrong
> order (bug 12892, fixed in glibc 2.15, probably not widely backported).
> 
> Even with that fixed, missing DT_NEEDED entries and LD_PRELOAD can
> result in calls to yet-unrelocated IFUNC resolvers.  I had a patch to
> mostly fix this, but it added quite a bit of complexity (delayed
> relocation processing), and there are still difficult-to-describe
> failure cases for complex dependency trees.  The community wasn't
> enthusiastic about it.
> 
> Lazy binding obscures some of these problems, to the degree that some
> programmers call getenv in IFUNC resolvers.

If an IFUNC resolver calls a function which is not yet resolved then
isn't that a defect in the IFUNC resolver?

The code which contains such an IFUNC should have had a DT_NEEDED entry
on all objects to which the resolver called into?

In Zach's case it really means that the new libfoo.so.2 needs to depend
on libfoo.so.1 so the latter is loaded and relocated first. There are
some hairy scenarios with circular dependencies which can mean libfoo.so.1
is *not* relocated first, and Chung-Ling Tang's patches to fix the sort
orders are a step in the right direction to solving this, but arguably
you have undefined behaviour with circular dependencies which we're trying
to make more deterministic.

I like solutions that ensure that IFUNC resolvers, which are just foreign
functions, run at a stage *after* their own dependencies have been resolved,
but before their own initializers have run.

In summary:
- Zach's solution works if libfoo.so.2 depends on libfoo.so.1 and also doesn't
  invoke undefined behaviour with circular dependencies.
- Yes, lazy binding hides these problems by deferring the complex ordering
  requirements until a point where it doesn't matter (all DSO relocation is
  complete). We should still fix the problem for BIND_NOW though.

-- 
Cheers,
Carlos.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]