Use ifuncs on Linux to avoid dispatch table overhead.

These tell the linker to generate GNU_IFUNC relocs that rewrite the
PLT entries in the user's address space to point to our resolved GL
function, so there's no extra function pointer.  It also, as a bonus,
cuts 400k out of the library.

This requires a toolchain from 2010 or so.  Unfortunately, it's going
to take a bit more investigation to find out what specific bits are
required.

Fixes #4
macos/v1.5.9
Eric Anholt 11 years ago
parent 6553c08752
commit 1e443b2daf
  1. 18
      README.md
  2. 44
      src/gen_dispatch.py

@ -80,3 +80,21 @@ We had to solve some of GLEW's problems for piglit and solving them
meant replacing every single piece of GLEW, so we built
piglit-dispatch from scratch. And since we wanted to reuse it in
other GL-related projects, this is the result.
Caveats
-------
* libepoxy isn't intended to be dlopen()ed. On ifunc platforms,
RTLD_NOW would cause early resolution of all GL symbols, and the
following error from reentering the linker:
```
Inconsistency detected by ld.so: dl-open.c: 235: dl_open_worker:
Assertion `_dl_debug_initialize (0, args->nsid)->r_state ==
RT_CONSISTENT' failed!
```
* libepoxy isn't intended to be built statically. It usess ifuncs on
linux, since it can avoid its dispatch table entirely in that case.
Building statically disables ifuncs, dropping you to a
higher-overhead path!

@ -560,6 +560,23 @@ class Generator(object):
self.outln('}')
self.outln('')
def write_ifunc(self, func):
# Writes out the ifunc symbol that will have its PLT entry
# replaced with the resolved GL symbol when it's called.
# Tell the linker that even if we're in a static build, we need a PLT.
self.outln('__asm__(".type epoxy_{0}, @gnu_indirect_function");'.format(func.name));
# Tell the linker that epoxy_glWhatever() needs to be resolved
# by epoxy_ifunc_glWhatever().
self.outln('void *epoxy_ifunc_{0}(void) __asm__("epoxy_{0}");'.format(func.wrapped_name))
# Write out the actual epoxy_ifunc_glWhatever().
self.outln('{0} void *epoxy_ifunc_{1}(void)'.format(func.public, func.wrapped_name))
self.outln('{')
self.outln(' return epoxy_{0}_resolver();'.format(func.alias_name))
self.outln('}')
def write_provider_enums(self):
self.outln('enum {0}_provider {{'.format(self.target))
@ -634,6 +651,22 @@ class Generator(object):
self.outln('#include "epoxy/{0}.h"'.format(self.target))
self.outln('')
# ifuncs are a newer-gcc and newer-dynamic-linker feature that
# let the library rewrite the application's PLT entry with our
# resolved function pointer.
#
# Note that dlsym() when we're called from the dynamic linker
# while it's loading libraries will break. So, libepoxy can't
# be loaded with RTLD_NOW when ifuncs are in use. We also
# can't use ifuncs for static builds, since static builds
# resolve just like RTLD_NOW.
self.outln('#if defined(__PIC__) && defined(__linux__)')
self.outln('#define USING_IFUNCS 1')
self.outln('#else')
self.outln('#define USING_IFUNCS 0')
self.outln('#endif')
self.outln('')
self.outln('struct dispatch_table {')
for func in self.sorted_functions:
# Aliases don't get their own slot, since they use a shared resolver.
@ -646,9 +679,11 @@ class Generator(object):
# bottom. (I want the function_ptr_resolver as the first
# per-GL-call code, since it's the most interesting to see
# when you search for the implementation of a call)
self.outln('#if !USING_IFUNCS')
self.outln('static inline struct dispatch_table *')
self.outln('get_dispatch_table(void);')
self.outln('')
self.outln('#endif')
self.write_provider_enums()
self.write_provider_enum_strings()
@ -658,6 +693,8 @@ class Generator(object):
if not func.alias_func:
self.write_function_ptr_resolver(func)
self.outln('#if !USING_IFUNCS')
for func in self.sorted_functions:
if not func.alias_func:
self.write_dispatch_table_rewrite_stub(func)
@ -681,6 +718,13 @@ class Generator(object):
self.outln('}')
self.outln('')
self.outln('#else /* !USING_IFUNCS */')
for func in self.sorted_functions:
self.write_ifunc(func)
self.outln('#endif /* !USING_IFUNCS */')
argparser = argparse.ArgumentParser(description='Generate GL dispatch wrappers.')
argparser.add_argument('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed')
argparser.add_argument('--dir', metavar='dir', required=True, help='Destination directory')

Loading…
Cancel
Save