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 meant replacing every single piece of GLEW, so we built
piglit-dispatch from scratch. And since we wanted to reuse it in piglit-dispatch from scratch. And since we wanted to reuse it in
other GL-related projects, this is the result. 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('}')
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): def write_provider_enums(self):
self.outln('enum {0}_provider {{'.format(self.target)) 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('#include "epoxy/{0}.h"'.format(self.target))
self.outln('') 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 {') self.outln('struct dispatch_table {')
for func in self.sorted_functions: for func in self.sorted_functions:
# Aliases don't get their own slot, since they use a shared resolver. # 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 # bottom. (I want the function_ptr_resolver as the first
# per-GL-call code, since it's the most interesting to see # per-GL-call code, since it's the most interesting to see
# when you search for the implementation of a call) # when you search for the implementation of a call)
self.outln('#if !USING_IFUNCS')
self.outln('static inline struct dispatch_table *') self.outln('static inline struct dispatch_table *')
self.outln('get_dispatch_table(void);') self.outln('get_dispatch_table(void);')
self.outln('') self.outln('')
self.outln('#endif')
self.write_provider_enums() self.write_provider_enums()
self.write_provider_enum_strings() self.write_provider_enum_strings()
@ -658,6 +693,8 @@ class Generator(object):
if not func.alias_func: if not func.alias_func:
self.write_function_ptr_resolver(func) self.write_function_ptr_resolver(func)
self.outln('#if !USING_IFUNCS')
for func in self.sorted_functions: for func in self.sorted_functions:
if not func.alias_func: if not func.alias_func:
self.write_dispatch_table_rewrite_stub(func) self.write_dispatch_table_rewrite_stub(func)
@ -681,6 +718,13 @@ class Generator(object):
self.outln('}') self.outln('}')
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 = 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('files', metavar='file.xml', nargs='+', help='GL API XML files to be parsed')
argparser.add_argument('--dir', metavar='dir', required=True, help='Destination directory') argparser.add_argument('--dir', metavar='dir', required=True, help='Destination directory')

Loading…
Cancel
Save